Skip to content

Commit

Permalink
Bugfix/4.0.8 issues (#2382)
Browse files Browse the repository at this point in the history
* Sync Schedule : Fix duplicate button. Fixes xibosignage/xibo#3337
* Required Files : Fix issue with emptyLayout modifiedDt. Fixes xibosignage/xibo#3346
* Commands : Fix name and code filtering. Fixes xibosignage/xibo#3335
* Schedule : Additional validation when changing event type on edit form. Fixes xibosignage/xibo#3334
* Schedule : Fix deprecation notice on Carbon createFromTimestamp with null value.
  • Loading branch information
PeterMis committed Feb 23, 2024
1 parent 36db3fc commit 3456050
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 58 deletions.
46 changes: 36 additions & 10 deletions lib/Controller/Command.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
* Copyright (C) 2024 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
Expand Down Expand Up @@ -84,24 +84,47 @@ public function displayPage(Request $request, Response $response)
* @SWG\Parameter(
* name="command",
* in="query",
* description="Filter by Command Name, exact match",
* description="Filter by Command Name",
* type="string",
* required=false
* ),
@SWG\Parameter(
* name="commandLike",
* in="query",
* description="Filter by Command Name, LIKE match",
* type="string",
* required=false
* ),
* @SWG\Parameter(
* name="code",
* in="query",
* description="Filter by Command Code",
* type="string",
* required=false
* ),
* @SWG\Parameter(
* name="useRegexForName",
* in="query",
* description="Flag (0,1). When filtering by multiple commands in command filter, should we use regex?,
* type="integer",
* required=false
* ),
* @SWG\Parameter(
* name="useRegexForCode",
* in="query",
* description="Flag (0,1). When filtering by multiple codes in code filter, should we use regex?",
* type="integer",
* required=false
* ),
* @SWG\Parameter(
* name="logicalOperatorName",
* in="query",
* description="When filtering by multiple commands in command filter,
* which logical operator should be used? AND|OR",
* type="string",
* required=false
* ),
* @SWG\Parameter(
* name="logicalOperatorCode",
* in="query",
* description="When filtering by multiple codes in code filter,
* which logical operator should be used? AND|OR",
* type="string",
* required=false
* ),
* @SWG\Response(
* response=200,
* description="successful operation",
Expand All @@ -126,7 +149,10 @@ public function grid(Request $request, Response $response)
'commandId' => $sanitizedParams->getInt('commandId'),
'command' => $sanitizedParams->getString('command'),
'code' => $sanitizedParams->getString('code'),
'commandLike' => $sanitizedParams->getString('commandLike'),
'useRegexForName' => $sanitizedParams->getCheckbox('useRegexForName'),
'useRegexForCode' => $sanitizedParams->getCheckbox('useRegexForCode'),
'logicalOperatorName' => $sanitizedParams->getString('logicalOperatorName'),
'logicalOperatorCode' => $sanitizedParams->getString('logicalOperatorCode'),
];

$commands = $this->commandFactory->query(
Expand Down
61 changes: 42 additions & 19 deletions lib/Controller/Schedule.php
Original file line number Diff line number Diff line change
Expand Up @@ -1720,7 +1720,6 @@ public function edit(Request $request, Response $response, $id)
'loadScheduleReminders' => in_array('scheduleReminders', $embed),
]);


if (!$this->isEventEditable($schedule)) {
throw new AccessDeniedException();
}
Expand All @@ -1729,7 +1728,6 @@ public function edit(Request $request, Response $response, $id)
$schedule->campaignId = $this->isFullScreenSchedule($schedule->eventTypeId)
? $sanitizedParams->getInt('fullScreenCampaignId')
: $sanitizedParams->getInt('campaignId');
$schedule->commandId = $sanitizedParams->getInt('commandId');
$schedule->displayOrder = $sanitizedParams->getInt('displayOrder', ['default' => $schedule->displayOrder]);
$schedule->isPriority = $sanitizedParams->getInt('isPriority', ['default' => $schedule->isPriority]);
$schedule->dayPartId = $sanitizedParams->getInt('dayPartId', ['default' => $schedule->dayPartId]);
Expand All @@ -1745,14 +1743,35 @@ public function edit(Request $request, Response $response, $id)
);
$schedule->displayGroups = [];
$schedule->isGeoAware = $sanitizedParams->getCheckbox('isGeoAware');
$schedule->actionType = $sanitizedParams->getString('actionType');
$schedule->actionTriggerCode = $sanitizedParams->getString('actionTriggerCode');
$schedule->actionLayoutCode = $sanitizedParams->getString('actionLayoutCode');
$schedule->maxPlaysPerHour = $sanitizedParams->getInt('maxPlaysPerHour', ['default' => 0]);
$schedule->name = $sanitizedParams->getString('name');
$schedule->modifiedBy = $this->getUser()->getId();

// collect action event relevant properties only on action event
// null these properties otherwise
if ($schedule->eventTypeId === \Xibo\Entity\Schedule::$ACTION_EVENT) {
$schedule->actionType = $sanitizedParams->getString('actionType');
$schedule->actionTriggerCode = $sanitizedParams->getString('actionTriggerCode');
$schedule->actionLayoutCode = $sanitizedParams->getString('actionLayoutCode');
$schedule->campaignId = null;
} else {
$schedule->actionType = null;
$schedule->actionTriggerCode = null;
$schedule->actionLayoutCode = null;
}

// collect commandId only on Command event
// null commandId otherwise
if ($schedule->eventTypeId === \Xibo\Entity\Schedule::$COMMAND_EVENT) {
$schedule->commandId = $sanitizedParams->getInt('commandId');
$schedule->campaignId = null;
} else {
$schedule->commandId = null;
}

// Set the parentCampaignId for campaign events
// null parentCampaignId on other events
// make sure correct Layout/Campaign is selected for relevant event.
if ($schedule->eventTypeId === \Xibo\Entity\Schedule::$CAMPAIGN_EVENT) {
$schedule->parentCampaignId = $schedule->campaignId;

Expand All @@ -1764,6 +1783,24 @@ public function edit(Request $request, Response $response, $id)
'campaignId'
);
}

if ($campaign->isLayoutSpecific === 1) {
throw new InvalidArgumentException(
__('Cannot schedule Layout as a Campaign, please select a Campaign instead.'),
'campaignId'
);
}
} else {
$schedule->parentCampaignId = null;
if (!empty($schedule->campaignId)) {
$campaign = $this->campaignFactory->getById($schedule->campaignId);
if ($campaign->isLayoutSpecific === 0) {
throw new InvalidArgumentException(
__('Cannot schedule Campaign in selected event type, please select a Layout instead.'),
'campaignId'
);
}
}
}

// Fields only collected for interrupt events
Expand All @@ -1776,20 +1813,6 @@ public function edit(Request $request, Response $response, $id)
);
}
]);

// if this was originally Campaign event, now changed to interrupt,
// check if the selected Campaign is Layout specific
// set parentCampaignId to null, otherwise the event will not be editable.
if ($schedule->getOriginalValue('eventTypeId') === \Xibo\Entity\Schedule::$CAMPAIGN_EVENT) {
$campaign = $this->campaignFactory->getById($schedule->campaignId);
if ($campaign->isLayoutSpecific === 0) {
throw new InvalidArgumentException(
__('Cannot schedule campaign as an interrupt, please select a Layout instead.'),
'campaignId'
);
}
$schedule->parentCampaignId = null;
}
} else {
$schedule->shareOfVoice = null;
}
Expand Down
12 changes: 9 additions & 3 deletions lib/Entity/Schedule.php
Original file line number Diff line number Diff line change
Expand Up @@ -1908,9 +1908,15 @@ public function getChangedProperties($jsonEncodeArrays = false)
&& $this->hasPropertyChanged($key)
) {
if (in_array($key, $this->datesToFormat)) {
$changedProperties[$key] = Carbon::createFromTimestamp($this->getOriginalValue($key))
->format(DateFormatHelper::getSystemFormat())
. ' > ' . Carbon::createFromTimestamp($value)->format(DateFormatHelper::getSystemFormat());
$original = empty($this->getOriginalValue($key))
? $this->getOriginalValue($key)
: Carbon::createFromTimestamp($this->getOriginalValue($key))
->format(DateFormatHelper::getSystemFormat());
$new = empty($value)
? $value
: Carbon::createFromTimestamp($value)
->format(DateFormatHelper::getSystemFormat());
$changedProperties[$key] = $original . ' > ' . $new;
} else {
$changedProperties[$key] = $this->getOriginalValue($key) . ' > ' . $value;

Expand Down
33 changes: 23 additions & 10 deletions lib/Factory/CommandFactory.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/*
* Copyright (C) 2023 Xibo Signage Ltd
* Copyright (C) 2024 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
Expand Down Expand Up @@ -158,18 +158,31 @@ public function query($sortOrder = null, $filterBy = [])
}

if ($sanitizedFilter->getString('command') != null) {
$body .= ' AND `command`.command = :command ';
$params['command'] = $sanitizedFilter->getString('command');
}

if ($sanitizedFilter->getString('commandLike') != null) {
$body .= ' AND `command`.command LIKE :commandLike ';
$params['commandLike'] = '%' . $sanitizedFilter->getString('commandLike') . '%';
$terms = explode(',', $sanitizedFilter->getString('command'));
$logicalOperator = $sanitizedFilter->getString('logicalOperatorName', ['default' => 'OR']);
$this->nameFilter(
'command',
'command',
$terms,
$body,
$params,
($sanitizedFilter->getCheckbox('useRegexForName') == 1),
$logicalOperator
);
}

if ($sanitizedFilter->getString('code') != null) {
$body .= ' AND `command`.code = :code ';
$params['code'] = $sanitizedFilter->getString('code');
$terms = explode(',', $sanitizedFilter->getString('code'));
$logicalOperator = $sanitizedFilter->getString('logicalOperatorCode', ['default' => 'OR']);
$this->nameFilter(
'command',
'code',
$terms,
$body,
$params,
($sanitizedFilter->getCheckbox('useRegexForCode') == 1),
$logicalOperator
);
}

if ($sanitizedFilter->getString('type') != null) {
Expand Down
7 changes: 6 additions & 1 deletion lib/Xmds/Soap.php
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,12 @@ protected function doRequiredFiles(
}

// Get the Layout Modified Date
$layoutModifiedDt = Carbon::createFromFormat(DateFormatHelper::getSystemFormat(), $layout->modifiedDt);
// To cover for instances where modifiedDt isn't correctly recorded
// use the createdDt instead.
$layoutModifiedDt = Carbon::createFromFormat(
DateFormatHelper::getSystemFormat(),
$layout->modifiedDt ?? $layout->createdDt
);

// merge regions and drawers
/** @var Region[] $allRegions */
Expand Down
5 changes: 2 additions & 3 deletions ui/src/core/xibo-calendar.js
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ var setupScheduleForm = function(dialog) {
.attr("id", "scheduleDuplateButton")
.html(translations.duplicate)
.on("click", function() {
duplicateScheduledEvent();
duplicateScheduledEvent($scheduleEditForm);
});

$(dialog).find('.modal-footer').prepend($button);
Expand Down Expand Up @@ -1161,9 +1161,8 @@ var processScheduleFormElements = function(el) {
}
};

var duplicateScheduledEvent = function() {
var duplicateScheduledEvent = function($scheduleForm) {
// Set the edit form URL to that of the add form
var $scheduleForm = $("#scheduleEditForm");
$scheduleForm.attr("action", $scheduleForm.data().addUrl).attr("method", "post");

// Remove the duplicate button
Expand Down
8 changes: 4 additions & 4 deletions views/authed.twig
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{#
/**
* Copyright (C) 2020 Xibo Signage Ltd
* Copyright (C) 2020-2024 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
Expand Down Expand Up @@ -837,12 +837,12 @@
}
function sendCommandMultiSelectFormOpen(dialog) {
// Inject a list of layouts into the form, in a drop down.
// Inject a list of commands into the form, in a drop down.
const $commandSelect = $(
'<div class="form-group form-horizontal row mt-4">' +
'<label class="col-sm-2 control-label" for="commandId" accesskey="">{% trans "Command" %}</label>' +
'<div class="col-sm-10">' +
'<select name="commandId" class="form-control" data-search-url="{{ url_for("command.search") }}" data-search-term="commandLike" data-id-property="commandId" data-text-property="command">' +
'<select name="commandId" class="form-control" data-search-url="{{ url_for("command.search") }}" data-search-term="command" data-id-property="commandId" data-text-property="command">' +
'<span class="help-block">{% trans "Pick a command to send to the Player. If the CMS has XMR enabled this will be sent immediately, otherwise it will show an error."%}</span>' +
'</div>' +
'</div>'
Expand Down
8 changes: 4 additions & 4 deletions views/command-page.twig
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{#
/**
* Copyright (C) 2020 Xibo Signage Ltd
* Copyright (C) 2020-2024 Xibo Signage Ltd
*
* Xibo - Digital Signage - http://www.xibo.org.uk
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
Expand Down Expand Up @@ -43,10 +43,10 @@
<div class="FilterDiv card-body" id="Filter">
<form class="form-inline">
{% set title %}{% trans "Command" %}{% endset %}
{{ inline.input("command", title) }}
{{ inline.inputNameGrid('command', title) }}

{% set title %}{% trans "Code" %}{% endset %}
{{ inline.input("code", title) }}
{{ inline.inputNameGrid('code', title, null, 'useRegexForCode', 'logicalOperatorCode') }}
</form>
</div>
</div>
Expand Down
7 changes: 4 additions & 3 deletions views/inline.twig
Original file line number Diff line number Diff line change
Expand Up @@ -243,17 +243,18 @@
</div>
{% endmacro %}

{% macro inputNameGrid(name, title, groupClass) %}
{% macro inputNameGrid(name, title, groupClass, useRegexName, logicalOperatorName) %}
<div class="form-group mr-1 mb-1 {{ groupClass }}">
<label class="control-label mr-1" title="" for="{{ name }}" accesskey="">{{ title }}</label>
<div>
<div class="input-group">
<input class="form-control" name="{{ name }}" type="text" id="{{ name }}" value="">
<div class="input-group-append input-group-addon">
<div class="input-group-text">
<input title="{% trans "Use Regex?" %}" type="checkbox" id="useRegexForName" name="useRegexForName">
<input title="{% trans "Use Regex?" %}" type="checkbox" {% if useRegexName %} id="{{ useRegexName }}" name="{{ useRegexName }}" {% else %} id="useRegexForName" name="useRegexForName"{% endif %}>
</div>
<select class="custom-select" id="logicalOperatorName" name="logicalOperatorName" title="{% trans "When filtering by multiple names, which logical operator should be used?" %}" style="min-width:auto!important">
<select class="custom-select" {% if logicalOperatorName %} id="{{ logicalOperatorName }}" name="{{ logicalOperatorName }}" {% else %} id="logicalOperatorName" name="logicalOperatorName"{% endif %}
title="{% trans "When filtering by multiple names, which logical operator should be used?" %}" style="min-width:auto!important">
<option value="OR" selected>OR</option>
<option value="AND">AND</option>
</select>
Expand Down
4 changes: 3 additions & 1 deletion views/schedule-form-sync-edit.twig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{#
/**
* Copyright (C) 2023 Xibo Signage Ltd
* Copyright (C) 2024 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
Expand Down Expand Up @@ -49,6 +49,8 @@
data-full-screen-url="{{ url_for('layout.add.full.screen.schedule') }}" data-fetch-sync-displays="{{ url_for('syncgroup.fetch.displays', {id:':id'}) }}"
data-event-sync-group-id="{{ event.syncGroupId }}" data-event-id="{{ event.eventId }}" data-daypart-message="{{ dayPartMessage }}" data-not-daypart-message="{{ notDayPartMessage }}" data-default-lat="{{ defaultLat }}"
data-default-long = "{{ defaultLong }}" data-event-start="{{ eventStart }}" data-event-end="{{ eventEnd }}"
data-add-url="{{ url_for("schedule.add") }}"
data-duplicated-message="{% trans "Duplicate form loaded, make adjustments and press save." %}"
>
<div class="tab-content">
<div class="tab-pane active" id="general">
Expand Down

0 comments on commit 3456050

Please sign in to comment.