Files
Cloud-CMS/views/xibo-audience-connector-form-javascript.twig

496 lines
20 KiB
Twig
Raw Permalink Normal View History

2025-12-02 10:32:59 -05:00
{#
/**
* Copyright (C) 2022 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
#}
{% import "forms.twig" as forms %}
<script type="text/javascript" nonce="{{ cspNonce }}">
{% autoescape "js" %}
window.audienceDaysOfWeek = {
1: "{{ "Monday"|trans }}",
2: "{{ "Tuesday"|trans }}",
3: "{{ "Wednesday"|trans }}",
4: "{{ "Thursday"|trans }}",
5: "{{ "Friday"|trans }}",
6: "{{ "Saturday"|trans }}",
7: "{{ "Sunday"|trans }}",
};
{% endautoescape %}
window.audienceFormOpen = function($audienceConnector) {
// Make a datatable
var dialog = $audienceConnector.closest('.modal');
var $table = $('#audience-dma');
var audienceDmaTable;
audienceDmaTable = $table.DataTable({
language: dataTablesLanguage,
dom: dataTablesTemplate,
serverSide: false,
stateSave: false,
responsive: true,
filter: true,
searchDelay: 3000,
order: [[ 0, 'asc']],
ajax: {
url: $table.data('proxyUrl').replace(':method', 'dmaSearch'),
data: function (d) {
$.extend(d, $table.closest('.XiboGrid').find('.FilterDiv form').serializeObject());
}
},
columns: [
{ data: 'name', responsivePriority: 1 },
{ data: 'costPerPlay', responsivePriority: 1 },
{ data: 'impressionsPerPlay', responsivePriority: 1 },
{ data: 'impressionSource', responsivePriority: 10 },
{
data: 'startDate',
responsivePriority: 2,
render: function(data, type) {
if (type !== 'display' && type !== 'export' || data == null) {
return data;
}
return moment(data, systemDateFormat).format(jsDateOnlyFormat);
},
},
{
data: 'endDate',
responsivePriority: 2,
render: function(data, type) {
if (type !== 'display' && type !== 'export' || data == null) {
return data;
}
return moment(data, systemDateFormat).format(jsDateOnlyFormat);
},
},
{
data: 'daysOfWeek',
responsivePriority: 2,
render: function(data) {
let daysOfWeek = [];
$.each(data, function(index, el) {
daysOfWeek.push(audienceDaysOfWeek[el]);
});
return daysOfWeek.join(', ');
},
},
{ data: 'startTime', responsivePriority: 2 },
{ data: 'endTime', responsivePriority: 2 },
{
data: 'geoFence',
responsivePriority: 3,
render: function(data) {
let icon;
if (data && data.length > 0)
icon = "fa-check";
else
icon = "fa-times";
return '<span class="fa ' + icon + '"></span>';
},
},
{ data: 'priority', responsivePriority: 2 },
{
data: function(data) {
if (data.displays) {
return data.displays.length;
} else {
return 0;
}
},
responsivePriority: 1,
},
{
data: function(data, type, set, meta) {
if (type !== 'display') {
return '';
}
return '<a class="btn btn-sm" href="#" id="addDmaBtn"><span class="fa fa-pencil" title="{% trans %}Edit{% endtrans %}"></span></a>'
+ '<a class="btn btn-sm" href="#" id="delDmaBtn"><span class="fa fa-trash" title="{% trans %}Delete{% endtrans %}"></span></a>';
},
responsivePriority: 1,
}
],
});
table.on('draw', dataTableDraw);
table.on('processing.dt', dataTableProcessing);
// Add button
new $.fn.dataTable.Buttons(audienceDmaTable, {
buttons: [
{
text: '{{ "Add DMA"|trans }}',
action: function (e, dt, node, config) {
openDmaForm();
},
},
]
});
audienceDmaTable.buttons().container().css('margin-bottom', '10px').appendTo('#audience-dma_wrapper .dataTables_folder');
setTimeout(function() {
audienceDmaTable.columns.adjust()
}, 500);
// Handle btn click
$('#ssp-activity tbody').on('click', 'a', function (e) {
let btnType = $(this).attr('id');
let id = table.row(e.target.closest('tr')).index();
if (btnType === 'addDmaBtn') {
openDmaForm(id);
} else if (btnType === 'delDmaBtn') {
deleteDmaForm(id);
}
});
// Add an add button.
var dmaTemplate = Handlebars.compile($('#xibo-audience-connector-dma-form').html());
window.openDmaForm = function(id = null) {
let title;
let method;
let dma;
if (id !== null) {
dma = audienceDmaTable.rows(id).data()[0];
title = '{{ "Edit DMA"|trans }}';
method = 'dmaEdit';
} else {
title = '{{ "Add DMA"|trans }}';
method = 'dmaAdd';
}
bootbox.hideAll();
const $dialog = bootbox.dialog({
message: dmaTemplate({
connectorId: $table.data('connectorId'),
method: method,
dma: dma,
impressionSources: $table.data('impressionSources'),
}),
title: title,
animate: false,
size: 'large',
buttons: {
back: {
label: '{{ "Back"|trans }}',
className: 'btn-white',
callback: function() {
// Open up the connector config form.
bootbox.hideAll();
$('[data-connector-class-name-last="XiboAudienceReportingConnector"]').find('.btn-primary').click();
}
},
save: {
label: '{{ "Save"|trans }}',
className: 'btn-success',
callback: function() {
// Submit the form in the usual way, and then open up the connector config form.
const $form = $dialog.find('form');
const button = $dialog.find('.btn-success');
button.addClass('disabled').append('<span class="saving fa fa-cog fa-spin p-1"></span>');
XiboFormSubmit($form, null, function(xhr) {
if (xhr.success === false) {
button.removeClass('disabled').find('.saving').remove();
} else {
bootbox.hideAll();
$('[data-connector-class-name-last="XiboAudienceReportingConnector"]').find('.btn-primary').click();
}
});
return false;
}
},
},
onShown: function(e) {
const $form = $(e.target).find('form');
// Before we initialise we work out if we need to set the initial key/value on the displayGroupId
// select list.
const $displayGroupId = $form.find('select[name=displayGroupId]');
if (dma && dma.displayGroupId) {
$displayGroupId.attr('data-initial-key', 'displayGroupId');
$displayGroupId.attr('data-initial-value', dma.displayGroupId);
}
// Initialise general fields.
XiboInitialise('#' + $form.attr('id'));
const $daysOfWeek = $form.find('select[name="daysOfWeek[]"]');
if (dma && dma.daysOfWeek) {
$.each(dma.daysOfWeek, function(index, element) {
$daysOfWeek.find('option[value=' + element + ']')
.attr('selected', 'selected');
});
}
$daysOfWeek.select2({width: '100%'}).val();
// Map
// ---
const getDataProperty = function($element, property, defaultValue = null) {
const value = $element.data(property);
if (value) {
return value;
} else {
return defaultValue;
}
};
const $containerSelector = $form.find('#audience-dma-map');
const $geoFenceField = $form.find('input[name="geoFence"]');
if (dma && dma.geoFence) {
$geoFenceField.val(JSON.stringify(dma.geoFence));
}
let map = null;
$(e.target).find('a[data-toggle="tab"]').on('shown.bs.tab', function (event) {
if ($(event.target).attr('href') === '#tab-geo' && map === null) {
map = L.map('audience-dma-map').setView(
[
getDataProperty($containerSelector, 'mapLat', '51'),
getDataProperty($containerSelector, 'mapLong', '0.4'),
],
getDataProperty($containerSelector, 'mapZoom', 13),
);
L.tileLayer(getDataProperty($containerSelector, 'mapTileServer'), {
attribution: getDataProperty(
$containerSelector,
'mapAttribution',
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
),
subdomains: ['a', 'b', 'c'],
}).addTo(map);
// Add a layer for drawn items
const drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
// Add draw control (toolbar)
const drawControl = new L.Control.Draw({
position: 'topright',
draw: {
polyline: false,
circle: false,
marker: false,
circlemarker: false,
},
edit: {
featureGroup: drawnItems,
},
});
map.addControl(drawControl);
// add search Control - allows searching by country/city and automatically
// moves map to that location
const searchControl = new L.Control.Search({
url: 'https://nominatim.openstreetmap.org/search?format=json&q={s}',
jsonpParam: 'json_callback',
propertyName: 'display_name',
propertyLoc: ['lat', 'lon'],
marker: L.circleMarker([0, 0], {radius: 30}),
autoCollapse: true,
autoType: false,
minLength: 2,
hideMarkerOnCollapse: true,
firstTipSubmit: true,
});
map.addControl(searchControl);
// Draw events
map.on('draw:created', function(e) {
drawnItems.addLayer(e.layer);
$geoFenceField.val(JSON.stringify(drawnItems.toGeoJSON()));
});
map.on('draw:edited', function(e) {
$geoFenceField.val(JSON.stringify(drawnItems.toGeoJSON()));
});
map.on('draw:deleted', function(e) {
e.layers.eachLayer(function(layer) {
drawnItems.removeLayer(layer);
});
$geoFenceField.val(JSON.stringify(drawnItems.toGeoJSON()));
});
// Load existing geoJSON
if (dma && dma.geoFence) {
L.geoJSON(dma.geoFence, {
onEachFeature: function(feature, layer) {
drawnItems.addLayer(layer);
map.fitBounds(drawnItems.getBounds());
},
});
}
}
});
}
});
}
window.deleteDmaForm = function(id) {
const dma = audienceDmaTable.rows(id).data()[0];
bootbox.hideAll();
bootbox.confirm({
message: '{{ "Are you sure you want to delete this DMA?"|trans }}',
buttons: {
confirm: {
label: '{{ "Yes"|trans }}',
className: 'btn-success',
},
cancel: {
label: '{{ "No"|trans }}',
className: 'btn-danger',
},
},
callback: function (result) {
if (result) {
$.ajax({
method: 'POST',
url: $table.data('proxyUrl').replace(':method', 'dmaDelete') + '?_id=' + dma._id,
success: function(xhr) {
if (xhr && !xhr.success) {
toastr.error(xhr.message);
} else {
SystemMessage('{{ "DMA deleted"|trans }}', true);
}
},
error: function(xhr, textStatus, errorThrown) {
toastr.error(xhr.message);
},
complete: function() {
$('[data-connector-class-name-last="XiboAudienceReportingConnector"]').find('.btn-primary').click();
}
});
} else {
$('[data-connector-class-name-last="XiboAudienceReportingConnector"]').find('.btn-primary').click();
}
}
});
}
};
</script>
<script type="text/x-handlebars-template" id="xibo-audience-connector-dma-form">
<div class="row">
<div class="col-md-12">
<ul class="nav nav-tabs" role="tablist">
<li class="nav-item"><a class="nav-link active" href="#tab-general" role="tab" data-toggle="tab"><span>{% trans "General" %}</span></a></li>
<li class="nav-item"><a class="nav-link" href="#tab-dates" role="tab" data-toggle="tab"><span>{% trans "Date/Time" %}</span></a></li>
<li class="nav-item"><a class="nav-link" href="#tab-geo" role="tab" data-toggle="tab"><span>{% trans "Geofence" %}</span></a></li>
</ul>
<form id="{{ random() }}" class="form-horizontal" action="{{ url_for("connector.edit.form.proxy", {id: "{{connectorId}}", method: "{{method}}"}) }}" method="POST">
<input type="hidden" name="_id" value="{% verbatim %}{{ dma._id }}{% endverbatim %}">
<div class="tab-content">
<div class="tab-pane active" id="tab-general">
{% set title %}{% trans "Name" %}{% endset %}
{% set helpText %}{% trans "The Name of this DMA - (1 - 50 characters)" %}{% endset %}
{{ forms.input("name", title, "{{dma.name}}", helpText) }}
{% set title %}{% trans "Priority" %}{% endset %}
{% set helpText %}{% trans "Set a priority for this DMA. Higher priorities take precedence." %}{% endset %}
{{ forms.number("priority", title, "{{dma.priority}}", helpText) }}
{% set title %}{% trans "Display Group" %}{% endset %}
{% set helpText %}{% trans "Which displays would you like this DMA to apply to?" %}{% endset %}
{% set attributes = [
{ name: "data-width", value: "200px" },
{ name: "data-allow-clear", value: "true" },
{ name: "data-placeholder--id", value: null },
{ name: "data-placeholder--value", value: "" },
{ name: "data-search-url", value: url_for("displayGroup.search") },
{ name: "data-search-term", value: "displayGroup" },
{ name: "data-search-term-tags", value: "tags" },
{ name: "data-id-property", value: "displayGroupId" },
{ name: "data-text-property", value: "displayGroup" },
] %}
{{ forms.dropdown("displayGroupId", "single", title, null, null, "displayGroupId", "displayGroup", helpText, "pagedSelect", "", "d", "", attributes) }}
{% set title %}{% trans "Cost per Play" %}{% endset %}
{% set helpText %}{% trans "The cost per play" %}{% endset %}
{{ forms.input("costPerPlay", title, "{{dma.costPerPlay}}", helpText) }}
<div class="form-group row">
<label class="col-sm-2 control-label" for="field-impression-source">
{{ "Impression Source"|trans }}
</label>
<div class="col-sm-10">
<select class="form-control" name="impressionSource" id="field-impression-source">{% verbatim %}
{{#each impressionSources}}
<option name="{{value}}"{{#eq value dma.impressionSource }} selected{{/eq}}>{{name}}</option>
{{/each}}
{% endverbatim %}</select>
<small class="form-text text-muted">{{ "What is the source of this impression figure?"|trans }}</small>
</div>
</div>
{% set title %}{% trans "Impressions per play" %}{% endset %}
{% set helpText %}{% trans "The impressions per play" %}{% endset %}
{{ forms.number("impressionsPerPlay", title, "{{dma.impressionsPerPlay}}", helpText) }}
</div>
<div class="tab-pane" id="tab-dates">
{% set title %}{% trans "Start Date" %}{% endset %}
{% set helpText %}{% trans "Select the start date for this DMA" %}{% endset %}
{{ forms.date("startDate", title, "{{dma.startDate}}", helpText, "starttime-control", "", "") }}
{% set title %}{% trans "End Date" %}{% endset %}
{% set helpText %}{% trans "Select the end date for this DMA" %}{% endset %}
{{ forms.date("endDate", title, "{{dma.endDate}}", helpText, "endtime-control", "", "") }}
{% set title %}{% trans "Days of the week" %}{% endset %}
{% set helpText %}{% trans "Which days of the week should the DMA be active?" %}{% endset %}
{% set options = [
{ id: 1, name: "Monday"|trans },
{ id: 2, name: "Tuesday"|trans },
{ id: 3, name: "Wednesday"|trans },
{ id: 4, name: "Thursday"|trans },
{ id: 5, name: "Friday"|trans },
{ id: 6, name: "Saturday"|trans },
{ id: 7, name: "Sunday"|trans },
] %}
{{ forms.dropdown("daysOfWeek[]", "dropdownmulti", title, null, options, "id", "name", helpText) }}
{% set title %}{% trans "Start Time" %}{% endset %}
{% set helpText %}{% trans "Select the start time for this DMA" %}{% endset %}
{{ forms.time("startTime", title, "{{dma.startTime}}", helpText, "starttime-control", "", "") }}
{% set title %}{% trans "End Time" %}{% endset %}
{% set helpText %}{% trans "Select the end time for this DMA" %}{% endset %}
{{ forms.time("endTime", title, "{{dma.endTime}}", helpText, "endtime-control", "", "") }}
</div>
<div class="tab-pane" id="tab-geo">
{{ forms.hidden("geoFence", "{{dma.geoFence}}") }}
{{ forms.message("Draw areas on the map where you want this DMA to be active."|trans) }}
<div id="audience-dma-map"
data-map-tile-server="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
data-map-lat="{{ settings.DEFAULT_LAT }}"
data-map-long="{{ settings.DEFAULT_LONG }}"
data-map-zoom="13"
style="height: 500px; width: 100%"></div>
</div>
</div>
</form>
</div>
</div>
</script>