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 %}
2026-02-11 20:47:09 -05:00
<div class="ots-static-page ots-displays-page">
2026-02-04 15:26:44 -05:00
<div class="page-header ots-page-header">
<h1> {% trans "Display Groups" %} </h1>
<p class="text-muted"> {% trans "Organize Displays into logical groups." %} </p>
</div>
Refactor dashboard card classes to use 'content-card' instead of 'dashboard-card'
- Updated various views to replace 'dashboard-card' with 'content-card' for consistency in styling.
- Modified filter and table card classes across multiple pages including applications, campaigns, commands, datasets, dayparts, displays, display groups, display profiles, fonts, layouts, libraries, menu boards, modules, player software, playlists, resolutions, schedules, settings, sync groups, tags, tasks, templates, transitions, users, and user groups.
2026-02-11 09:17:45 -05:00
<div class="widget content-card ots-displays-card">
2026-02-04 15:26:44 -05:00
<div class="widget-body ots-displays-body">
<div class="XiboGrid" id=" {{ random ( ) }} " data-grid-name="displayGroupGridView">
Refactor dashboard card classes to use 'content-card' instead of 'dashboard-card'
- Updated various views to replace 'dashboard-card' with 'content-card' for consistency in styling.
- Modified filter and table card classes across multiple pages including applications, campaigns, commands, datasets, dayparts, displays, display groups, display profiles, fonts, layouts, libraries, menu boards, modules, player software, playlists, resolutions, schedules, settings, sync groups, tags, tasks, templates, transitions, users, and user groups.
2026-02-11 09:17:45 -05:00
<div class="XiboFilter card mb-3 bg-light content-card ots-filter-card">
2026-02-04 15:26:44 -05:00
<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">
Refactor dashboard card classes to use 'content-card' instead of 'dashboard-card'
- Updated various views to replace 'dashboard-card' with 'content-card' for consistency in styling.
- Modified filter and table card classes across multiple pages including applications, campaigns, commands, datasets, dayparts, displays, display groups, display profiles, fonts, layouts, libraries, menu boards, modules, player software, playlists, resolutions, schedules, settings, sync groups, tags, tasks, templates, transitions, users, and user groups.
2026-02-11 09:17:45 -05:00
<div class="grid-folder-tree-container p-3 content-card ots-folder-tree" id="grid-folder-filter">
2026-02-04 15:26:44 -05:00
<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">
Refactor dashboard card classes to use 'content-card' instead of 'dashboard-card'
- Updated various views to replace 'dashboard-card' with 'content-card' for consistency in styling.
- Modified filter and table card classes across multiple pages including applications, campaigns, commands, datasets, dayparts, displays, display groups, display profiles, fonts, layouts, libraries, menu boards, modules, player software, playlists, resolutions, schedules, settings, sync groups, tags, tasks, templates, transitions, users, and user groups.
2026-02-11 09:17:45 -05:00
<div class="XiboData card py-3 content-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 %}