From 50ae7f467b7e316d0defee32f53e2000b172b6bb Mon Sep 17 00:00:00 2001 From: Dan Garner Date: Thu, 1 Jun 2023 15:09:25 +0100 Subject: [PATCH 1/4] 3.3.6 issues (#1835) * Widgets: add installName to dashboard widget so that it can be uninstalled xibosignage/xibo#3059 * Library: swagger documentation for add method xibosignage/xibo#3057 * Schedule: validate repeating events do not repeat more often than the interval between their start/end xibosignage/xibo#3050 --- lib/Controller/Library.php | 15 ++++++++--- lib/Entity/Schedule.php | 54 +++++++++++++++++++++++++++++++++++--- lib/Widget/Dashboard.php | 4 +-- 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/lib/Controller/Library.php b/lib/Controller/Library.php index 370ec64add..fe7f3666d7 100644 --- a/lib/Controller/Library.php +++ b/lib/Controller/Library.php @@ -1,6 +1,6 @@ isPriority < 0) { throw new InvalidArgumentException(__('Priority must be 0 or a positive number'), 'isPriority'); } - // Check recurrenceDetail every is positive - if ($this->recurrenceType != '' && ($this->recurrenceDetail === null || $this->recurrenceDetail <= 0)) { - throw new InvalidArgumentException(__('Repeat every must be a positive number'), 'recurrenceDetail'); + + // Run some additional validation if we have a recurrence type set. + if (!empty($this->recurrenceType)) { + // Check recurrenceDetail every is positive + if ($this->recurrenceDetail === null || $this->recurrenceDetail <= 0) { + throw new InvalidArgumentException(__('Repeat every must be a positive number'), 'recurrenceDetail'); + } + + // Make sure that we don't repeat more frequently than the duration of the event as this is a common + // misconfiguration which results in overlapping repeats + if ($this->eventTypeId !== Schedule::$COMMAND_EVENT) { + $eventDuration = $this->toDt - $this->fromDt; + + // Determine the number of seconds our repeat type/interval represents + switch ($this->recurrenceType) { + case 'Minute': + $repeatDuration = $this->recurrenceDetail * 60; + break; + + case 'Hour': + $repeatDuration = $this->recurrenceDetail * 3600; + break; + + case 'Day': + $repeatDuration = $this->recurrenceDetail * 86400; + break; + + case 'Week': + $repeatDuration = $this->recurrenceDetail * 86400 * 7; + break; + + case 'Month': + $repeatDuration = $this->recurrenceDetail * 86400 * 30; + break; + + case 'Year': + $repeatDuration = $this->recurrenceDetail * 86400 * 365; + break; + + default: + throw new InvalidArgumentException(__('Unknown repeat type'), 'recurrenceType'); + } + + if ($repeatDuration < $eventDuration) { + throw new InvalidArgumentException( + __('An event cannot repeat more often than the interval between its start and end date'), + 'recurrenceDetail', + $eventDuration . ' seconds' + ); + } + } } } diff --git a/lib/Widget/Dashboard.php b/lib/Widget/Dashboard.php index 73d13beb30..a75b4bc1b6 100644 --- a/lib/Widget/Dashboard.php +++ b/lib/Widget/Dashboard.php @@ -1,6 +1,6 @@ defaultDuration = 60; $module->settings = []; $module->viewPath = '../modules'; + $module->installName = 'dashboard'; // Set the newly created module and then call install $this->setModule($module); From 8a0bbcf020023bd9e80760d1795621f25e098a33 Mon Sep 17 00:00:00 2001 From: Peter Date: Fri, 2 Jun 2023 10:29:57 +0100 Subject: [PATCH 2/4] 3.3.6 issues (#1840) * Folders : Fix incorrectly selected initial folder in forms in certain cases. * Playlist : Fix dynamic playlist tag logical operator on edit form * Player Software : if we have a name from upload form set it on player software record. * Media upload : check pixel size of an image and show a warning if needed. --- lib/Controller/Module.php | 6 +-- lib/Middleware/Theme.php | 8 ++-- lib/Widget/PlayerSoftware.php | 14 ++++--- ui/src/core/file-upload.js | 63 ++++++++++++++++++++++++++++-- ui/src/core/xibo-cms.js | 4 +- ui/src/editor-core/toolbar.js | 21 ++++++++++ ui/src/helpers/form-helpers.js | 24 +++++++++++- ui/src/layout-editor/main.js | 4 +- ui/src/playlist-editor/playlist.js | 23 ++++++++++- views/base.twig | 1 + views/include-file-upload.twig | 5 +++ views/layout-designer-page.twig | 2 +- views/library-page.twig | 4 +- views/media-manager-page.twig | 4 +- views/playlist-form-edit.twig | 2 +- 15 files changed, 156 insertions(+), 29 deletions(-) diff --git a/lib/Controller/Module.php b/lib/Controller/Module.php index e58a518190..11e478d126 100644 --- a/lib/Controller/Module.php +++ b/lib/Controller/Module.php @@ -1,8 +1,8 @@ str_replace(',', '|', $module->getModule()->validExtensions), 'templatesAvailable' => $templates, 'options' => $options, - 'isTopLevel' => $this->playlistFactory->getById($module->widget->playlistId)->isRegionPlaylist() + 'isTopLevel' => $this->playlistFactory->getById($module->widget->playlistId)->isRegionPlaylist(), ])); return $this->render($request, $response); diff --git a/lib/Middleware/Theme.php b/lib/Middleware/Theme.php index cace9a31d4..95fbc0e2f1 100644 --- a/lib/Middleware/Theme.php +++ b/lib/Middleware/Theme.php @@ -1,8 +1,8 @@ ByteFormatter::toBytes(Environment::getMaxUploadSize()), 'maxSizeMessage' => sprintf(__('This form accepts files up to a maximum size of %s'), Environment::getMaxUploadSize()), 'validExt' => implode('|', $container->get('moduleFactory')->getValidExtensions()), - 'validImageExt' => implode('|', $container->get('moduleFactory')->getValidExtensions(['type' => 'image'])) + 'validImageExt' => implode('|', $container->get('moduleFactory')->getValidExtensions(['type' => 'image'])), ]; $view['ckeditorConfig'] = $container->get('mediaService')->setUser($container->get('user'))->fontCKEditorConfig(RouteContext::fromRequest($request)->getRouteParser()); $view['version'] = Environment::$WEBSITE_VERSION_NAME; diff --git a/lib/Widget/PlayerSoftware.php b/lib/Widget/PlayerSoftware.php index 93326d8dff..4943a947ce 100644 --- a/lib/Widget/PlayerSoftware.php +++ b/lib/Widget/PlayerSoftware.php @@ -1,8 +1,8 @@ name !== $media->fileName) { + $playerShowVersion = $media->name; + } + return $factory->create($type, $version, $code, $media->mediaId, $playerShowVersion); } @@ -159,5 +164,4 @@ public function getValidExtensions() { return $this->module->validExtensions; } - -} \ No newline at end of file +} diff --git a/ui/src/core/file-upload.js b/ui/src/core/file-upload.js index cdf182ba5b..82fac54dab 100644 --- a/ui/src/core/file-upload.js +++ b/ui/src/core/file-upload.js @@ -1,7 +1,7 @@ /* - * Copyright (C) 2020 Xibo Signage Ltd + * Copyright (C) 2023 Xibo Signage Ltd * - * Xibo - Digital Signage - http://www.xibo.org.uk + * Xibo - Digital Signage - https://xibosignage.com * * This file is part of Xibo. * @@ -85,6 +85,10 @@ function openUploadForm(options) { $(dialog).find('#files').on('change', handleVideoCoverImage); } + if (maxImagePixelSize > 0) { + $(dialog).find('#files').on('change', checkImagePixelSize); + } + // If we are not a multi-upload, then limit to 1 if (!options.templateOptions.multi) { uploadOptions = $.extend({}, uploadOptions, { @@ -185,7 +189,15 @@ function openUploadForm(options) { $button.attr('disabled', 'disabled'); } }) - .bind('fileuploaddrop', handleVideoCoverImage); + .bind('fileuploaddrop', function (e, data) { + if (options.videoImageCovers) { + handleVideoCoverImage(e, data) + } + + if (maxImagePixelSize > 0) { + checkImagePixelSize(e, data) + } + }) if (options.templateOptions.folderSelector) { // Handle creating a folder selector @@ -319,4 +331,47 @@ function saveVideoCoverImage(data) { data: thumbnailData }); } -} \ No newline at end of file +} + +function checkImagePixelSize(e, data) { + let files = data === undefined ? this.files : data.files; + + let $existingFiles = $('.template-upload canvas') + .closest('tr') + .find('td span.info') + + const checkExist = setInterval(function() { + if ($('.preview').find('canvas').length) { + // iterate through our files + Array.from(files).forEach(function(file, index) { + if (!file.error && file.type.includes('image')) { + // if we have existing files, adjust index + // to ensure we put the warning in the right place + if ($existingFiles.length > 0) { + if (index === 0) { + index = $existingFiles.length + } else { + index += $existingFiles.length + } + } + img = new Image(); + let objectUrl = URL.createObjectURL(file); + img.onload = function() { + if (this.width > maxImagePixelSize || this.height > maxImagePixelSize) { + const helpText = translations.imagePixelSizeTooLarge; + const $helpTextSelector = $('.template-upload canvas') + .closest('tr') + .find('td span.info')[index] + .append(helpText) + } + + URL.revokeObjectURL(objectUrl); + }; + img.src = objectUrl; + } + }); + + clearInterval(checkExist); + } + }, 300); +} diff --git a/ui/src/core/xibo-cms.js b/ui/src/core/xibo-cms.js index 4c419f3c39..e37c72217a 100644 --- a/ui/src/core/xibo-cms.js +++ b/ui/src/core/xibo-cms.js @@ -3648,7 +3648,7 @@ function initJsTreeAjax(container, id, isForm, ttl, onReady = null, onSelected = // check state let currentState = localStorage.getItem(id+'_folder_tree') // if we have no state saved, select the homeFolderId in the tree. - if (currentState === undefined || currentState === null) { + if ((currentState === undefined || currentState === null) && !isForm) { $(container).jstree(true).select_node(homeNodeId) } } @@ -3664,7 +3664,7 @@ function initJsTreeAjax(container, id, isForm, ttl, onReady = null, onSelected = folderIdInputSelector = '#formFolderId'; } - var selectedFolder = $(folderIdInputSelector).val(); + var selectedFolder = !$(folderIdInputSelector).val() ? homeNodeId : $(folderIdInputSelector).val(); if (selectedFolder !== undefined && selectedFolder !== '') { $(this).jstree('select_node', selectedFolder); diff --git a/ui/src/editor-core/toolbar.js b/ui/src/editor-core/toolbar.js index 0b7e31a2a5..b79be94ea8 100644 --- a/ui/src/editor-core/toolbar.js +++ b/ui/src/editor-core/toolbar.js @@ -1,3 +1,24 @@ +/* + * Copyright (C) 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 . + */ + // NAVIGATOR Module // Load templates diff --git a/ui/src/helpers/form-helpers.js b/ui/src/helpers/form-helpers.js index 193c5d920b..08ece38aaa 100644 --- a/ui/src/helpers/form-helpers.js +++ b/ui/src/helpers/form-helpers.js @@ -1,3 +1,24 @@ +/* + * Copyright (C) 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 . + */ + // Include templates const templates = { dataSetOrderClauseTemplate: require("../templates/form-helpers-data-set-order-clause.hbs"), @@ -890,7 +911,6 @@ let formHelpers = function() { var mediaId = dialog.find('form').data().mediaId; var widgetId = dialog.find('form').data().widgetId; var validExtensions = dialog.find('form').data().validExtensions; - // Append var replaceButton = $('