Initial Upload

This commit is contained in:
Matt Batchelder
2025-12-02 10:32:59 -05:00
commit 05ce0da296
2240 changed files with 467811 additions and 0 deletions

View File

@@ -0,0 +1,196 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
use GuzzleHttp\Psr7\Stream;
use Illuminate\Support\Str;
use Intervention\Image\ImageManagerStatic as Img;
use Psr\Http\Message\ResponseInterface;
use Slim\Http\Response;
use Slim\Http\ServerRequest;
use Xibo\Support\Exception\GeneralException;
use Xibo\Support\Exception\NotFoundException;
use Xibo\Xmds\Entity\Dependency;
/**
* An asset
*/
class Asset implements \JsonSerializable
{
public $id;
public $type;
public $alias;
public $path;
public $mimeType;
/** @var bool */
public $autoInclude;
/** @var bool */
public $cmsOnly;
public $assetNo;
private $fileSize;
private $md5;
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'alias' => $this->alias,
'type' => $this->type,
'path' => $this->path,
'mimeType' => $this->mimeType,
'cmsOnly' => $this->cmsOnly,
'autoInclude' => $this->autoInclude,
];
}
/**
* Should this asset be sent to the player?
* @return bool
*/
public function isSendToPlayer(): bool
{
return !($this->cmsOnly ?? false);
}
/**
* Should this asset be auto included in the HTML sent to the player
* @return bool
*/
public function isAutoInclude(): bool
{
return $this->autoInclude && $this->isSendToPlayer();
}
/**
* @param string $libraryLocation
* @param bool $forceUpdate
* @return $this
* @throws GeneralException
*/
public function updateAssetCache(string $libraryLocation, bool $forceUpdate = false): Asset
{
// Verify the asset is cached and update its path.
$assetPath = $libraryLocation . 'assets/' . $this->getFilename();
if (!file_exists($assetPath) || $forceUpdate) {
$result = @copy(PROJECT_ROOT . $this->path, $assetPath);
if (!$result) {
throw new GeneralException('Unable to copy asset');
}
$forceUpdate = true;
}
// Get the bundle MD5
$assetMd5CachePath = $assetPath . '.md5';
if (!file_exists($assetMd5CachePath) || $forceUpdate) {
$assetMd5 = md5_file($assetPath);
file_put_contents($assetMd5CachePath, $assetMd5);
} else {
$assetMd5 = file_get_contents($assetPath . '.md5');
}
$this->path = $assetPath;
$this->md5 = $assetMd5;
$this->fileSize = filesize($assetPath);
return $this;
}
/**
* Get this asset as a dependency.
* @return \Xibo\Xmds\Entity\Dependency
* @throws \Xibo\Support\Exception\NotFoundException
*/
public function getDependency(): Dependency
{
// Check that this asset is valid.
if (!file_exists($this->path)) {
throw new NotFoundException(sprintf(__('Asset %s not found'), $this->path));
}
// Return a dependency
return new Dependency(
'asset',
$this->id,
$this->getLegacyId(),
$this->path,
$this->fileSize,
$this->md5,
true
);
}
/**
* Get the file name for this asset
* @return string
*/
public function getFilename(): string
{
return basename($this->path);
}
/**
* Generate a PSR response for this asset.
* @throws \Xibo\Support\Exception\NotFoundException
*/
public function psrResponse(ServerRequest $request, Response $response, string $sendFileMode): ResponseInterface
{
// Make sure this asset exists
if (!file_exists($this->path)) {
throw new NotFoundException(__('Asset file does not exist'));
}
$response = $response->withHeader('Content-Length', $this->fileSize);
$response = $response->withHeader('Content-Type', $this->mimeType);
// Output the file
if ($sendFileMode === 'Apache') {
// Send via Apache X-Sendfile header?
$response = $response->withHeader('X-Sendfile', $this->path);
} else if ($sendFileMode === 'Nginx') {
// Send via Nginx X-Accel-Redirect?
$response = $response->withHeader('X-Accel-Redirect', '/download/assets/' . $this->getFilename());
} else if (Str::startsWith('image', $this->mimeType)) {
$response = Img::make('/' . $this->path)->psrResponse();
} else {
// Set the right content type.
$response = $response->withBody(new Stream(fopen($this->path, 'r')));
}
return $response;
}
/**
* Get Legacy ID for this asset on older players
* there is a risk that this ID will change as modules/templates with assets are added/removed in the system
* however, we have mitigated by ensuring that only one instance of any required file is added to rf return
* @return int
*/
private function getLegacyId(): int
{
return (Dependency::LEGACY_ID_OFFSET_ASSET + $this->assetNo) * -1;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* Represents a condition for a test
*/
class Condition implements \JsonSerializable
{
public $field;
public $type;
public $value;
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'field' => $this->field,
'type' => $this->type,
'value' => $this->value
];
}
}

View File

@@ -0,0 +1,56 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* A module data type
*/
class DataType implements \JsonSerializable
{
public $id;
public $name;
/** @var \Xibo\Widget\Definition\Field[] */
public $fields = [];
public function addField(string $id, string $title, string $type, bool $isRequired = false): DataType
{
$field = new Field();
$field->id = $id;
$field->type = $type;
$field->title = $title;
$field->isRequired = $isRequired;
$this->fields[] = $field;
return $this;
}
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'name' => $this->name,
'fields' => $this->fields,
];
}
}

View File

@@ -0,0 +1,56 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* @SWG\Definition()
* A class representing an instance of an element template
*/
class Element implements \JsonSerializable
{
public $id;
public $top;
public $left;
public $width;
public $height;
public $rotation;
public $layer;
public $elementGroupId;
public $properties = [];
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'top' => $this->top,
'left' => $this->left,
'width' => $this->width,
'height' => $this->height,
'rotation' => $this->rotation,
'layer' => $this->layer,
'elementGroupId' => $this->elementGroupId,
'properties' => $this->properties
];
}
}

View File

@@ -0,0 +1,56 @@
<?php
/*
* Copyright (C) 2024 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/>.
*/
namespace Xibo\Widget\Definition;
/**
* A class representing an instance of a group of elements
* @SWG\Definition()
*/
class ElementGroup implements \JsonSerializable
{
public $id;
public $top;
public $left;
public $width;
public $height;
public $layer;
public $title;
public $slot;
public $pinSlot;
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'top' => $this->top,
'left' => $this->left,
'width' => $this->width,
'height' => $this->height,
'layer' => $this->layer,
'title' => $this->title,
'slot' => $this->slot,
'pinSlot' => $this->pinSlot
];
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* @SWG\Definition()
* A class representing one template extending another
*/
class Extend implements \JsonSerializable
{
public $template;
public $override;
public $with;
public $escapeHtml;
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'template' => $this->template,
'override' => $this->override,
'with' => $this->with,
'escapeHtml' => $this->escapeHtml,
];
}
}

View File

@@ -0,0 +1,45 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* Class representing a data type field
*/
class Field implements \JsonSerializable
{
public $id;
public $type;
public $title;
public $isRequired;
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'type' => $this->type,
'title' => $this->title,
'isRequired' => $this->isRequired,
];
}
}

View File

@@ -0,0 +1,42 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* A Legacy Type
* @SWG\Definition()
*/
class LegacyType implements \JsonSerializable
{
public $name;
public $condition;
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'name' => $this->name,
'condition' => $this->condition,
];
}
}

View File

@@ -0,0 +1,67 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* Option: typically used when paired with a dropdown
* @SWG\Definition()
*/
class Option implements \JsonSerializable
{
/**
* @SWG\Property(description="Name")
* @var string
*/
public $name;
/**
* @SWG\Property(description="Image: optional image asset")
* @var string
*/
public $image;
/**
* @SWG\Property(description="Set")
* @var string[]
*/
public $set = [];
/**
* * @SWG\Property(description="Title: shown in the dropdown/select")
* @var string
*/
public $title;
/**
* @inheritDoc
*/
public function jsonSerialize(): array
{
return [
'name' => $this->name,
'image' => $this->image,
'set' => $this->set,
'title' => $this->title
];
}
}

View File

@@ -0,0 +1,53 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* Player compatibility
*/
class PlayerCompatibility implements \JsonSerializable
{
public $windows;
public $linux;
public $android;
public $webos;
public $tizen;
public $chromeos;
public $message;
/**
* @inheritDoc
*/
public function jsonSerialize(): array
{
return [
'windows' => $this->windows,
'linux' => $this->linux,
'android' => $this->android,
'webos' => $this->webos,
'tizen' => $this->tizen,
'chromeos' => $this->chromeos,
'message' => $this->message,
];
}
}

View File

@@ -0,0 +1,554 @@
<?php
/*
* Copyright (C) 2025 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/>.
*/
namespace Xibo\Widget\Definition;
use Carbon\Carbon;
use Carbon\CarbonInterval;
use Illuminate\Support\Str;
use Respect\Validation\Validator as v;
use Xibo\Support\Exception\InvalidArgumentException;
use Xibo\Support\Exception\ValueTooLargeException;
use Xibo\Support\Sanitizer\SanitizerInterface;
/**
* A Property
* @SWG\Definition()
*/
class Property implements \JsonSerializable
{
/**
* @SWG\Property(description="ID, saved as a widget option")
* @var string
*/
public $id;
/**
* @SWG\Property(description="Type, determines the field type")
* @var string
*/
public $type;
/**
* @SWG\Property(description="Title: shown in the property panel")
* @var string
*/
public $title;
/**
* @SWG\Property(description="Help Text: shown in the property panel")
* @var string
*/
public $helpText;
/** @var \Xibo\Widget\Definition\Rule */
public $validation;
/**
* @SWG\Property()
* @var string An optional default value
*/
public $default;
/** @var \Xibo\Widget\Definition\Option[] */
public $options;
/** @var \Xibo\Widget\Definition\Test[] */
public $visibility = [];
/** @var string The element variant */
public $variant;
/** @var string The data format */
public $format;
/** @var bool Should library refs be permitted in the value? */
public $allowLibraryRefs = false;
/** @var bool Should asset refs be permitted in the value? */
public $allowAssetRefs = false;
/** @var bool Should translations be parsed in the value? */
public $parseTranslations = false;
/** @var bool Should the property be included in the XLF? */
public $includeInXlf = false;
/** @var bool Should the property be sent into Elements */
public $sendToElements = false;
/** @var bool Should the default value be written out to widget options */
public $saveDefault = false;
/** @var \Xibo\Widget\Definition\PlayerCompatibility */
public $playerCompatibility;
/** @var string HTML to populate a custom popover to be shown next to the input */
public $customPopOver;
/** @var string HTML selector of the element that this property depends on */
public $dependsOn;
/** @var string ID of the target element */
public $target;
/** @var string The mode of the property */
public $mode;
/** @var string The group ID of the property */
public $propertyGroupId;
/** @var mixed The value assigned to this property. This is set from widget options, or settings, never via XML */
public $value;
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'value' => $this->value,
'type' => $this->type,
'variant' => $this->variant,
'format' => $this->format,
'title' => $this->title,
'mode' => $this->mode,
'target' => $this->target,
'propertyGroupId' => $this->propertyGroupId,
'helpText' => $this->helpText,
'validation' => $this->validation,
'default' => $this->default,
'options' => $this->options,
'customPopOver' => $this->customPopOver,
'playerCompatibility' => $this->playerCompatibility,
'visibility' => $this->visibility,
'allowLibraryRefs' => $this->allowLibraryRefs,
'allowAssetRefs' => $this->allowAssetRefs,
'parseTranslations' => $this->parseTranslations,
'saveDefault' => $this->saveDefault,
'dependsOn' => $this->dependsOn,
'sendToElements' => $this->sendToElements,
];
}
/**
* Add an option
* @param string $name
* @param string $image
* @param array $set
* @param string $title
* @return $this
*/
public function addOption(string $name, string $image, array $set, string $title): Property
{
$option = new Option();
$option->name = $name;
$option->image = $image;
$option->set = $set;
$option->title = __($title);
$this->options[] = $option;
return $this;
}
/**
* Add a visibility test
* @param string $type
* @param string|null $message
* @param array $conditions
* @return $this
*/
public function addVisibilityTest(string $type, ?string $message, array $conditions): Property
{
$this->visibility[] = $this->parseTest($type, $message, $conditions);
return $this;
}
/**
* @param \Xibo\Support\Sanitizer\SanitizerInterface $params
* @param string|null $key
* @return \Xibo\Widget\Definition\Property
* @throws \Xibo\Support\Exception\InvalidArgumentException
*/
public function setDefaultByType(SanitizerInterface $params, ?string $key = null): Property
{
$this->default = $this->getByType($params, $key);
return $this;
}
/**
* @param SanitizerInterface $params
* @param string|null $key
* @param bool $ignoreDefault
* @return Property
* @throws InvalidArgumentException
*/
public function setValueByType(
SanitizerInterface $params,
?string $key = null,
bool $ignoreDefault = false
): Property {
$value = $this->getByType($params, $key);
if ($value !== $this->default || $ignoreDefault || $this->saveDefault) {
$this->value = $value;
}
return $this;
}
/**
* @param array $properties A key/value array of all properties for this entity (be it module or template)
* @param string $stage What stage are we at?
* @return Property
* @throws InvalidArgumentException
* @throws ValueTooLargeException
*/
public function validate(array $properties, string $stage): Property
{
if (!empty($this->value) && strlen($this->value) > 67108864) {
throw new ValueTooLargeException(sprintf(__('Value too large for %s'), $this->title), $this->id);
}
// Skip if no validation.
if ($this->validation === null
|| ($stage === 'save' && !$this->validation->onSave)
|| ($stage === 'status' && !$this->validation->onStatus)
) {
return $this;
}
foreach ($this->validation->tests as $test) {
// We have a test, evaulate its conditions.
$exceptions = [];
foreach ($test->conditions as $condition) {
try {
// Assume we're testing the field we belong to, and if that's empty use the default value
$testValue = $this->value ?? $this->default;
// What value are we testing against (only used by certain types)
if (empty($condition->field)) {
$valueToTestAgainst = $condition->value;
} else {
// If a field and a condition value is provided, test against those, ignoring my own field value
if (!empty($condition->value)) {
$testValue = $condition->value;
}
$valueToTestAgainst = $properties[$condition->field] ?? null;
}
// Do we have a message
$message = empty($test->message) ? null : __($test->message);
switch ($condition->type) {
case 'required':
// We will accept the default value here
if (empty($testValue) && empty($this->default)) {
throw new InvalidArgumentException(
$message ?? sprintf(__('Missing required property %s'), $this->title),
$this->id
);
}
break;
case 'uri':
if (!empty($testValue)
&& !v::url()->validate($testValue)
) {
throw new InvalidArgumentException(
$message ?? sprintf(__('%s must be a valid URI'), $this->title),
$this->id
);
}
break;
case 'windowsPath':
// Ensure the path is a valid Windows file path ending in a file, not a directory
$windowsPathRegex = '/^(?P<Root>[A-Za-z]:)(?P<Relative>(?:\\\\[^<>:"\/\\\\|?*\r\n]+)+)(?P<File>\\\\[^<>:"\/\\\\|?*\r\n]+)$/';
// Check if the test value is not empty and does not match the regular expression
if (!empty($testValue)
&& !preg_match($windowsPathRegex, $testValue)
) {
// Throw an InvalidArgumentException if the test value is not a valid Windows path
throw new InvalidArgumentException(
$message ?? sprintf(__('%s must be a valid Windows path'), $this->title),
$this->id
);
}
break;
case 'interval':
if (!empty($testValue)) {
// Try to create a date interval from it
$dateInterval = CarbonInterval::createFromDateString($testValue);
if ($dateInterval === false) {
throw new InvalidArgumentException(
// phpcs:ignore Generic.Files.LineLength
__('That is not a valid date interval, please use natural language such as 1 week'),
'customInterval'
);
}
// Use now and add the date interval to it
$now = Carbon::now();
$check = $now->copy()->add($dateInterval);
if ($now->equalTo($check)) {
throw new InvalidArgumentException(
// phpcs:ignore Generic.Files.LineLength
$message ?? __('That is not a valid date interval, please use natural language such as 1 week'),
$this->id
);
}
}
break;
case 'eq':
if ($testValue != $valueToTestAgainst) {
throw new InvalidArgumentException(
$message ?? sprintf(__('%s must equal %s'), $this->title, $valueToTestAgainst),
$this->id,
);
}
break;
case 'neq':
if ($testValue == $valueToTestAgainst) {
throw new InvalidArgumentException(
$message ?? sprintf(__('%s must not equal %s'), $this->title, $valueToTestAgainst),
$this->id,
);
}
break;
case 'contains':
if (!empty($testValue) && !Str::contains($testValue, $valueToTestAgainst)) {
throw new InvalidArgumentException(
$message ?? sprintf(__('%s must contain %s'), $this->title, $valueToTestAgainst),
$this->id,
);
}
break;
case 'ncontains':
if (!empty($testValue) && Str::contains($testValue, $valueToTestAgainst)) {
throw new InvalidArgumentException(
// phpcs:ignore Generic.Files.LineLength
$message ?? sprintf(__('%s must not contain %s'), $this->title, $valueToTestAgainst),
$this->id,
);
}
break;
case 'lt':
// Value must be < to the condition value, or field value
if (!($testValue < $valueToTestAgainst)) {
throw new InvalidArgumentException(
// phpcs:ignore Generic.Files.LineLength
$message ?? sprintf(__('%s must be less than %s'), $this->title, $valueToTestAgainst),
$this->id
);
}
break;
case 'lte':
// Value must be <= to the condition value, or field value
if (!($testValue <= $valueToTestAgainst)) {
throw new InvalidArgumentException(
// phpcs:ignore Generic.Files.LineLength
$message ?? sprintf(__('%s must be less than or equal to %s'), $this->title, $valueToTestAgainst),
$this->id
);
}
break;
case 'gte':
// Value must be >= to the condition value, or field value
if (!($testValue >= $valueToTestAgainst)) {
throw new InvalidArgumentException(
// phpcs:ignore Generic.Files.LineLength
$message ?? sprintf(__('%s must be greater than or equal to %s'), $this->title, $valueToTestAgainst),
$this->id
);
}
break;
case 'gt':
// Value must be > to the condition value, or field value
if (!($testValue > $valueToTestAgainst)) {
throw new InvalidArgumentException(
// phpcs:ignore Generic.Files.LineLength
$message ?? sprintf(__('%s must be greater than %s'), $this->title, $valueToTestAgainst),
$this->id
);
}
break;
default:
// Nothing to validate
}
} catch (InvalidArgumentException $invalidArgumentException) {
// If we are an AND test, all conditions must pass, so we know already to exception here.
if ($test->type === 'and') {
throw $invalidArgumentException;
}
// We're an OR
$exceptions[] = $invalidArgumentException;
}
}
// If we are an OR then make sure all conditions have failed.
$countOfFailures = count($exceptions);
if ($test->type === 'or' && $countOfFailures === count($test->conditions)) {
throw $exceptions[0];
}
}
return $this;
}
/**
* @param \Xibo\Support\Sanitizer\SanitizerInterface $params
* @param string|null $key
* @return bool|float|int|string|null
* @throws \Xibo\Support\Exception\InvalidArgumentException
*/
private function getByType(SanitizerInterface $params, ?string $key = null)
{
$key = $key ?: $this->id;
if (!$params->hasParam($key) && $this->type !== 'checkbox') {
// Clear the stored value and therefore use the default
return null;
}
// Parse according to the type of field we're expecting
switch ($this->type) {
case 'checkbox':
return $params->getCheckbox($key);
case 'integer':
return $params->getInt($key);
case 'number':
return $params->getDouble($key);
case 'dropdown':
$value = $params->getString($key);
if ($value === null) {
return null;
}
$found = false;
foreach ($this->options as $option) {
if ($option->name === $value) {
$found = true;
break;
}
}
if ($found) {
return $value;
} else {
throw new InvalidArgumentException(
sprintf(__('%s is not a valid option'), $value),
$key
);
}
case 'code':
case 'richText':
return $params->getParam($key);
case 'text':
if ($this->variant === 'sql') {
// Handle raw SQL clauses
return str_ireplace(Sql::DISALLOWED_KEYWORDS, '', $params->getParam($key));
} else {
return $params->getString($key);
}
default:
return $params->getString($key);
}
}
/**
* Apply any filters on the data.
* @return void
*/
public function applyFilters(): void
{
if ($this->variant === 'uri' || $this->type === 'commandBuilder') {
$this->value = urlencode($this->value);
}
}
/**
* Reverse filters
* @return void
*/
public function reverseFilters(): void
{
$this->value = $this->reverseFiltersOnValue($this->value);
}
/**
* @param mixed $value
* @return mixed|string
*/
public function reverseFiltersOnValue(mixed $value): mixed
{
if (($this->variant === 'uri' || $this->type === 'commandBuilder') && !empty($value)) {
$value = urldecode($value);
}
return $value;
}
/**
* Should this property be represented with CData
* @return bool
*/
public function isCData(): bool
{
return $this->type === 'code' || $this->type === 'richText';
}
/**
* @param string $type
* @param string $message
* @param array $conditions
* @return Test
*/
public function parseTest(string $type, string $message, array $conditions): Test
{
$test = new Test();
$test->type = $type ?: 'and';
$test->message = $message;
foreach ($conditions as $item) {
$condition = new Condition();
$condition->type = $item['type'];
$condition->field = $item['field'];
$condition->value = $item['value'];
$test->conditions[] = $condition;
}
return $test;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* A class representing an instance of a group property to put a property in assigned Tab
* @SWG\Definition()
*/
class PropertyGroup implements \JsonSerializable
{
public $id;
public $expanded;
public $title;
public $helpText;
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'id' => $this->id,
'expanded' => $this->expanded,
'title' => $this->title,
'helpText' => $this->helpText
];
}
}

View File

@@ -0,0 +1,52 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* A rule to apply to a property
* @SWG\Definition()
*/
class Rule implements \JsonSerializable
{
public $onSave = true;
public $onStatus = true;
/** @var Test[] */
public $tests;
public function addRuleTest(Test $test): Rule
{
$this->tests[] = $test;
return $this;
}
public function jsonSerialize(): array
{
return [
'onSave' => $this->onSave,
'onStatus' => $this->onStatus,
'tests' => $this->tests,
];
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* Copyright (C) 2024 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/>.
*/
namespace Xibo\Widget\Definition;
/**
* SQL definitions
*/
class Sql
{
const DISALLOWED_KEYWORDS = [
';',
'INSERT',
'UPDATE',
'SELECT',
'FROM',
'WHERE',
'DELETE',
'TRUNCATE',
'TABLE',
'ALTER',
'GRANT',
'REVOKE',
'CREATE',
'DROP',
];
}

View File

@@ -0,0 +1,80 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* @SWG\Definition()
* A Stencil is a template which is rendered in the server and/or client
* it can optionally have properties and/or elements
*/
class Stencil implements \JsonSerializable
{
/** @var \Xibo\Widget\Definition\Element[] */
public $elements = [];
/** @var string|null */
public $twig;
/** @var string|null */
public $hbs;
/** @var string|null */
public $head;
/** @var string|null */
public $style;
/** @var string|null */
public $hbsId;
/** @var double Optional positional information if contained as part of an element group */
public $width;
/** @var double Optional positional information if contained as part of an element group */
public $height;
/** @var double Optional positional information if contained as part of an element group */
public $gapBetweenHbs;
/**
* @SWG\Property(description="An array of element groups")
* @var \Xibo\Widget\Definition\ElementGroup[]
*/
public $elementGroups = [];
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'hbsId' => $this->hbsId,
'hbs' => $this->hbs,
'head' => $this->head,
'style' => $this->style,
'width' => $this->width,
'height' => $this->height,
'gapBetweenHbs' => $this->gapBetweenHbs,
'elements' => $this->elements,
'elementGroups' => $this->elementGroups
];
}
}

View File

@@ -0,0 +1,49 @@
<?php
/*
* 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 <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Widget\Definition;
/**
* Represents a test/group of conditions
* @SWG\Definition()
*/
class Test implements \JsonSerializable
{
/** @var string */
public $type;
/** @var Condition[] */
public $conditions;
/** @var string|null */
public $message;
/** @inheritDoc */
public function jsonSerialize(): array
{
return [
'type' => $this->type,
'message' => $this->message,
'conditions' => $this->conditions,
];
}
}