2026-02-04 15:26:44 -05:00
{ #
/**
* Copyright (C) 2020-2023 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* 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/>.
*/
#}
{% extends "authed.twig" %}
{% import "inline.twig" as inline %}
{% block title %} {{ "Display Groups" | trans }} | {% endblock %}
Add new pages for managing tags, tasks, transitions, users, user groups, and their respective JavaScript functionalities
- Implemented tag management page with filtering, data table, and AJAX functionality.
- Created task management page with task listing, filtering, and AJAX data loading.
- Developed transition management page with a data table for transitions.
- Added user management page with comprehensive user details, filtering options, and AJAX support.
- Introduced user group management page with filtering and data table for user groups.
- Enhanced JavaScript for data tables, including state saving, filtering, and AJAX data fetching for all new pages.
2026-02-06 23:54:21 -05:00
{% block actionMenu %} {% endblock %}
2026-02-04 15:26:44 -05:00
{% block pageContent %}
<div class="ots-displays-page">
<div class="page-header ots-page-header">
<h1> {% trans "Display Groups" %} </h1>
<p class="text-muted"> {% trans "Organize Displays into logical groups." %} </p>
</div>
<div class="widget dashboard-card ots-displays-card">
<div class="widget-body ots-displays-body">
<div class="XiboGrid" id=" {{ random ( ) }} " data-grid-name="displayGroupGridView">
<div class="XiboFilter card mb-3 bg-light dashboard-card ots-filter-card">
<div class="ots-filter-header">
<h3 class="ots-filter-title"> {% trans "Filter Display Groups" %} </h3>
<button type="button" class="ots-filter-toggle" id="ots-filter-collapse-btn" title=" {% trans 'Toggle filter panel' %} ">
<i class="fa fa-chevron-up"></i>
</button>
</div>
<div class="ots-filter-content" id="ots-filter-content">
<div class="FilterDiv card-body" id="Filter">
<form class="form-inline">
{% set title %} {% trans "ID" %} {% endset %}
{{ inline .input ( "displayGroupId" , title ) }}
{% set title %} {% trans "Name" %} {% endset %}
{{ inline .inputNameGrid ( 'displayGroup' , title ) }}
{% set title %} {% trans "Display" %} {% endset %}
{% set attributes = [
Add new pages for managing tags, tasks, transitions, users, user groups, and their respective JavaScript functionalities
- Implemented tag management page with filtering, data table, and AJAX functionality.
- Created task management page with task listing, filtering, and AJAX data loading.
- Developed transition management page with a data table for transitions.
- Added user management page with comprehensive user details, filtering options, and AJAX support.
- Introduced user group management page with filtering and data table for user groups.
- Enhanced JavaScript for data tables, including state saving, filtering, and AJAX data fetching for all new pages.
2026-02-06 23:54:21 -05:00
{ name: "data-width", value: "100%" },
2026-02-04 15:26:44 -05:00
{ name: "data-allow-clear", value: "true" },
{ name: "data-placeholder--id", value: null },
{ name: "data-placeholder--value", value: "" },
{ name: "data-search-url", value: url_for("display.search") },
{ name: "data-search-term", value: "display" },
{ name: "data-search-term-tags", value: "tags" },
{ name: "data-id-property", value: "displayId" },
{ name: "data-text-property", value: "display" },
{ name: "data-initial-key", value: "displayId" },
] %}
{% set helpText %} {% trans "Return Display Groups that directly contain the selected Display." %} {% endset %}
{{ inline .dropdown ( "displayId" , "single" , title , "" , null , "displayId" , "display" , helpText , "pagedSelect" , "" , "" , "" , attributes ) }}
{% set title %} {% trans "Nested Display" %} {% endset %}
{% set helpText %} {% trans "Return Display Groups that contain the selected Display somewhere in the nested Display Group relationship tree." %} {% endset %}
{{ inline .dropdown ( "nestedDisplayId" , "single" , title , "" , null , "displayId" , "display" , helpText , "pagedSelect" , "" , "" , "" , attributes ) }}
{% set title %} {% trans "Dynamic Criteria" %} {% endset %}
{{ inline .input ( "dynamicCriteria" , title ) }}
{% if currentUser .featureEnabled ( "tag.tagging" ) %}
{% set title %} {% trans "Tags" %} {% endset %}
{% set exactTagTitle %} {% trans "Exact match?" %} {% endset %}
{% set logicalOperatorTitle %} {% trans "When filtering by multiple Tags, which logical operator should be used?" %} {% endset %}
{% set helpText %} {% trans "A comma separated list of tags to filter by. Enter a tag|tag value to filter tags with values. Enter --no-tag to filter all items without tags. Enter - before a tag or tag value to exclude from results." %} {% endset %}
{{ inline .inputWithTags ( "tags" , title , null , helpText , null , null , null , "exactTags" , exactTagTitle , logicalOperatorTitle ) }}
{% endif %}
{{ inline .hidden ( "folderId" ) }}
</form>
</div>
</div>
</div>
<div class="grid-with-folders-container ots-grid-with-folders">
<div class="grid-folder-tree-container p-3 dashboard-card ots-folder-tree" id="grid-folder-filter">
<input id="jstree-search" class="form-control" type="text" placeholder=" {% trans "Search" %} ">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="folder-tree-clear-selection-button">
<label class="form-check-label" for="folder-tree-clear-selection-button" title=" {% trans "Search in all folders" %} "> {% trans "All Folders" %} </label>
</div>
<div class="folder-search-no-results d-none">
<p> {% trans 'No Folders matching the search term' %} </p>
</div>
<div id="container-folder-tree"></div>
</div>
<div id="datatable-container">
<div class="XiboData card py-3 dashboard-card ots-table-card">
Add new pages for managing tags, tasks, transitions, users, user groups, and their respective JavaScript functionalities
- Implemented tag management page with filtering, data table, and AJAX functionality.
- Created task management page with task listing, filtering, and AJAX data loading.
- Developed transition management page with a data table for transitions.
- Added user management page with comprehensive user details, filtering options, and AJAX support.
- Introduced user group management page with filtering and data table for user groups.
- Enhanced JavaScript for data tables, including state saving, filtering, and AJAX data fetching for all new pages.
2026-02-06 23:54:21 -05:00
<div class="ots-table-toolbar">
Refactor toolbar buttons across various pages to unify styling
- Updated button classes for consistency in the playersoftware-page, playlist-page, resolution-page, schedule-page, settings-page, syncgroup-page, tag-page, task-page, template-page, transition-page, user-page, and usergroup-page.
- Removed unnecessary text from button titles and ensured all buttons have the 'ots-toolbar-btn' class for uniformity.
- Cleaned up the code by removing commented-out sections and ensuring proper indentation.
2026-02-07 14:50:40 -05:00
<button type="button" id="folder-tree-select-folder-button" class="btn btn-sm btn-outline-secondary ots-toolbar-btn" title=" {% trans "Open / Close Folder Search options" %} "><i class="fas fa-folder"></i></button>
<div id="breadcrumbs"></div>
Add new pages for managing tags, tasks, transitions, users, user groups, and their respective JavaScript functionalities
- Implemented tag management page with filtering, data table, and AJAX functionality.
- Created task management page with task listing, filtering, and AJAX data loading.
- Developed transition management page with a data table for transitions.
- Added user management page with comprehensive user details, filtering options, and AJAX support.
- Introduced user group management page with filtering and data table for user groups.
- Enhanced JavaScript for data tables, including state saving, filtering, and AJAX data fetching for all new pages.
2026-02-06 23:54:21 -05:00
{% if currentUser .featureEnabled ( "displaygroup.add" ) %}
Refactor toolbar buttons across various pages to unify styling
- Updated button classes for consistency in the playersoftware-page, playlist-page, resolution-page, schedule-page, settings-page, syncgroup-page, tag-page, task-page, template-page, transition-page, user-page, and usergroup-page.
- Removed unnecessary text from button titles and ensured all buttons have the 'ots-toolbar-btn' class for uniformity.
- Cleaned up the code by removing commented-out sections and ensuring proper indentation.
2026-02-07 14:50:40 -05:00
<button class="btn btn-sm btn-success ots-toolbar-btn XiboFormButton" title=" {% trans "Add Display Group" %} " href=" {{ url_for ( "displayGroup.add.form" ) }} "><i class="fa fa-plus-circle" aria-hidden="true"></i></button>
Add new pages for managing tags, tasks, transitions, users, user groups, and their respective JavaScript functionalities
- Implemented tag management page with filtering, data table, and AJAX functionality.
- Created task management page with task listing, filtering, and AJAX data loading.
- Developed transition management page with a data table for transitions.
- Added user management page with comprehensive user details, filtering options, and AJAX support.
- Introduced user group management page with filtering and data table for user groups.
- Enhanced JavaScript for data tables, including state saving, filtering, and AJAX data fetching for all new pages.
2026-02-06 23:54:21 -05:00
{% endif %}
Refactor toolbar buttons across various pages to unify styling
- Updated button classes for consistency in the playersoftware-page, playlist-page, resolution-page, schedule-page, settings-page, syncgroup-page, tag-page, task-page, template-page, transition-page, user-page, and usergroup-page.
- Removed unnecessary text from button titles and ensured all buttons have the 'ots-toolbar-btn' class for uniformity.
- Cleaned up the code by removing commented-out sections and ensuring proper indentation.
2026-02-07 14:50:40 -05:00
<button class="btn btn-sm btn-primary ots-toolbar-btn" id="refreshGrid" title=" {% trans "Refresh the Table" %} " href="#"><i class="fa fa-refresh" aria-hidden="true"></i></button>
Add new pages for managing tags, tasks, transitions, users, user groups, and their respective JavaScript functionalities
- Implemented tag management page with filtering, data table, and AJAX functionality.
- Created task management page with task listing, filtering, and AJAX data loading.
- Developed transition management page with a data table for transitions.
- Added user management page with comprehensive user details, filtering options, and AJAX support.
- Introduced user group management page with filtering and data table for user groups.
- Enhanced JavaScript for data tables, including state saving, filtering, and AJAX data fetching for all new pages.
2026-02-06 23:54:21 -05:00
</div>
2026-02-04 15:26:44 -05:00
<table id="displaygroups" class="table table-striped" data-content-type="displayGroup" data-content-id-name="displayGroupId" data-state-preference-name="displayGroupGrid" style="width: 100%;">
<thead>
<tr>
<th> {% trans "ID" %} </th>
<th> {% trans "Name" %} </th>
<th> {% trans "Description" %} </th>
<th> {% trans "Is Dynamic?" %} </th>
<th> {% trans "Criteria" %} </th>
{% if currentUser .featureEnabled ( "tag.tagging" ) %}
<th> {% trans "Criteria Tags" %} </th>
<th> {% trans "Tags" %} </th>
{% endif %}
<th> {% trans "Sharing" %} </th>
<th> {% trans "Reference 1" %} </th>
<th> {% trans "Reference 2" %} </th>
<th> {% trans "Reference 3" %} </th>
<th> {% trans "Reference 4" %} </th>
<th> {% trans "Reference 5" %} </th>
<th> {% trans "Created Date" %} </th>
<th> {% trans "Modified Date" %} </th>
<th class="rowMenu"></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block javaScript %}
<script type="text/javascript" nonce=" {{ cspNonce }} ">
var displayGroupTable;
var displayTable;
var criteria;
var criteriaTag;
var useRegexForName;
var exactTags;
var logicalOperator;
var logicalOperatorName;
$(document).ready(function() {
{% if not currentUser .featureEnabled ( "folder.view" ) %}
disableFolders();
{% endif %}
displayGroupTable = $("#displaygroups").DataTable( {
"language": dataTablesLanguage,
dom: dataTablesTemplate,
serverSide: true,
stateSave: true,
stateDuration: 0,
responsive: true,
stateLoadCallback: dataTableStateLoadCallback,
stateSaveCallback: dataTableStateSaveCallback,
"filter": false,
searchDelay: 3000,
"order": [[ 1, "asc"]],
ajax: {
"url": " {{ url_for ( "displayGroup.search" ) }} ",
"data": function(d) {
$.extend(d, $("#displaygroups").closest(".XiboGrid").find(".FilterDiv form").serializeObject());
}
},
"columns": [
{ "data": "displayGroupId", responsivePriority: 2},
{ "data": "displayGroup", "render": dataTableSpacingPreformatted, responsivePriority: 2 },
{ "data": "description", responsivePriority: 3 },
{ "data": "isDynamic", "render": dataTableTickCrossColumn, responsivePriority: 3 },
{ "data": "dynamicCriteria", responsivePriority: 4 },
{% if currentUser .featureEnabled ( "tag.tagging" ) %}
{ "data": "dynamicCriteriaTags", responsivePriority: 4},
{
"name": "tags",
"sortable": false,
responsivePriority: 3,
"data": dataTableCreateTags
},
{% endif %}
{
"data": "groupsWithPermissions",
visible: false,
responsivePriority: 10,
"render": dataTableCreatePermissions
},
{ "data": "ref1", "visible": false, responsivePriority: 5},
{ "data": "ref2", "visible": false, responsivePriority: 5},
{ "data": "ref3", "visible": false, responsivePriority: 5},
{ "data": "ref4", "visible": false, responsivePriority: 5},
{ "data": "ref5", "visible": false, responsivePriority: 5},
{ "data": "createdDt", "visible": false, responsivePriority: 5 },
{ "data": "modifiedDt", "visible": false, responsivePriority: 5 },
{
"orderable": false,
responsivePriority: 1,
"data": dataTableButtonsColumn
}
]
});
$("#refreshGrid").click(function () {
displayGroupTable.ajax.reload();
});
displayGroupTable.on('draw', dataTableDraw);
displayGroupTable.on('draw', { form: $("#displaygroups").closest(".XiboGrid").find(".FilterDiv form") }, dataTableCreateTagEvents);
displayGroupTable.on('processing.dt', dataTableProcessing);
dataTableAddButtons(displayGroupTable, $('#displaygroups_wrapper').find('.dataTables_buttons'));
$("#refreshGrid").click(function () {
displayGroupTable.ajax.reload();
});
});
function setDeleteMultiSelectFormOpen(dialog) {
$(dialog).find('.save-button').prop('disabled', false);
var template = Handlebars.compile($('#template-display-group-multi-delete-checkbox').html());
var $input = $(template());
$input.find('input').on('change', function() {
$(dialog).find('.save-button').prop('disabled', !$(this).is(':checked'));
});
$(dialog).find('.modal-body').append($input);
}
function displayGroupAddFormNext() {
// Get form
var $form = $("#displayGroupAddForm");
// Set apply and apply reset data
$form.data("apply", true);
$form.data("applyCallback", 'applyResetCallback');
// Submit form
$form.submit();
}
function applyResetCallback(form) {
// Reset form fields
$(form).find('#displayGroup').val("");
}
function displayGroupFormOpen(dialog) {
displayTable = null;
$(dialog).find("input[name=dynamicCriteria]").on("keyup", _.debounce(function() {
displayGroupQueryDynamicMembers(dialog);
}, 500));
$(dialog).find("input[name=dynamicCriteriaTags], input[name=exactTags], select[name=logicalOperator], select[name=logicalOperatorName]").change(function() {
displayGroupQueryDynamicMembers(dialog);
});
var $form = $('#displayGroupAddForm');
// First time in there
displayGroupQueryDynamicMembers(dialog);
}
function displayGroupQueryDynamicMembers(dialog) {
if ($(dialog).find("input[name=isDynamic]")[0].checked) {
criteria = $(dialog).find("input[name=dynamicCriteria]").val();
criteriaTag = $(dialog).find("input[name=dynamicCriteriaTags]").val();
useRegexForName = $(dialog).find("input[name=useRegexForName]").val();
exactTags = $(dialog).find("input[name=exactTags]").is(':checked');
logicalOperator = $(dialog).find("select[name=logicalOperator]").val();
logicalOperatorName = $(dialog).find("select[name=logicalOperatorName]").val();
if (criteria === "" && criteriaTag === "") {
if (displayTable != null) {
displayTable.destroy();
displayTable = null;
$("#displayGroupDisplays tbody").empty();
}
return;
}
if (displayTable != null) {
displayTable.ajax.reload();
} else {
displayTable = $("#displayGroupDisplays").DataTable( {
"language": dataTablesLanguage,
serverSide: true,
stateSave: true, stateDuration: 0,
filter: false,
searchDelay: 3000,
"order": [[1, "asc"]],
ajax: {
"url": " {{ url_for ( "display.search" ) }} ",
"data": function (d) {
$.extend(
d,
{
display: criteria,
tags: criteriaTag,
useRegexForName: useRegexForName,
exactTags: exactTags,
logicalOperator: logicalOperator,
logicalOperatorName: logicalOperatorName
}
);
}
},
"columns": [
{ "data": "displayId"},
{ "data": "display"},
{ "data": dataTableCreateTags},
{
"data": "mediaInventoryStatus",
"render": function (data, type, row) {
if (type != "display")
return data;
var icon = "";
if (data == 1)
icon = "fa-check";
else if (data == 0)
icon = "fa-times";
else
icon = "fa-cloud-download";
return "<span class='fa " + icon + "'></span>";
}
},
{ "data": "licensed", "render": dataTableTickCrossColumn}
]
});
displayTable.on('processing.dt', dataTableProcessing);
displayTable.on('draw', { form: $(".displayGroupForm") }, dataTableCreateTagEvents);
}
}
}
</script>
{% endblock %}
{% block javaScriptTemplates %}
{{ parent ( ) }}
{% verbatim %}
<script type="text/x-handlebars-template" id="template-display-group-multi-delete-checkbox">
<div class="form-group row">
<div class="offset-sm-2 col-sm-10 mt-4">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="checkbox-confirmDelete" name="confirmDelete">
<label class="form-check-label" for="checkbox-confirmDelete">
{% endverbatim %} {{ "Are you sure you want to delete?" | trans }} {% verbatim %}
</label>
</div>
<small class="form-text text-muted"> {% endverbatim %} {{ "Check to confirm deletion of the selected records." | trans }} {% verbatim %} </small>
</div>
</div>
</script>
{% endverbatim %}
{% endblock %}