init commit
This commit is contained in:
300
lib/Entity/Action.php
Normal file
300
lib/Entity/Action.php
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class Action
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Action implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Action Id")
|
||||
* @var int
|
||||
*/
|
||||
public $actionId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Owner Id")
|
||||
* @var int
|
||||
*/
|
||||
public $ownerId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Action trigger type")
|
||||
* @var string
|
||||
*/
|
||||
public $triggerType;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Action trigger code")
|
||||
* @var string
|
||||
*/
|
||||
public $triggerCode;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Action type")
|
||||
* @var string
|
||||
*/
|
||||
public $actionType;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Action source (layout, region or widget)")
|
||||
* @var string
|
||||
*/
|
||||
public $source;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Action source Id (layoutId, regionId or widgetId)")
|
||||
* @var int
|
||||
*/
|
||||
public $sourceId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Action target (region)")
|
||||
* @var string
|
||||
*/
|
||||
public $target;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Action target Id (regionId)")
|
||||
* @var int
|
||||
*/
|
||||
public $targetId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Widget ID that will be loaded as a result of navigate to Widget Action type")
|
||||
* @var int
|
||||
*/
|
||||
public $widgetId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Layout Code identifier")
|
||||
* @var string
|
||||
*/
|
||||
public $layoutCode;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Layout Id associated with this Action")
|
||||
* @var int
|
||||
*/
|
||||
public $layoutId;
|
||||
|
||||
/** @var \Xibo\Factory\PermissionFactory */
|
||||
private $permissionFactory;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->hash = null;
|
||||
$this->actionId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->actionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OwnerId
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return $this->ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Owner
|
||||
* @param int $ownerId
|
||||
*/
|
||||
public function setOwner($ownerId)
|
||||
{
|
||||
$this->ownerId = $ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('ActionId %d, Trigger Type %s, Trigger Code %s, Action Type %s, Source %s, SourceId %s, Target %s, TargetId %d', $this->actionId, $this->triggerType, $this->triggerCode, $this->actionType, $this->source, $this->sourceId, $this->target, $this->targetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
// on add we expect only layoutId, actionType, target and targetId
|
||||
if ($this->layoutId == null) {
|
||||
throw new InvalidArgumentException(__('No layoutId specified'), 'layoutId');
|
||||
}
|
||||
|
||||
if (!in_array($this->actionType, ['next', 'previous', 'navLayout', 'navWidget'])) {
|
||||
throw new InvalidArgumentException(__('Invalid action type'), 'actionType');
|
||||
}
|
||||
|
||||
if (!in_array(strtolower($this->source), ['layout', 'region', 'widget'])) {
|
||||
throw new InvalidArgumentException(__('Invalid source'), 'source');
|
||||
}
|
||||
|
||||
if (!in_array(strtolower($this->target), ['region', 'screen'])) {
|
||||
throw new InvalidArgumentException(__('Invalid target'), 'target');
|
||||
}
|
||||
|
||||
if ($this->target == 'region' && $this->targetId == null) {
|
||||
throw new InvalidArgumentException(__('Please select a Region'), 'targetId');
|
||||
}
|
||||
|
||||
if ($this->triggerType === 'webhook' && $this->triggerCode === null) {
|
||||
throw new InvalidArgumentException(__('Please provide trigger code'), 'triggerCode');
|
||||
}
|
||||
|
||||
if ($this->triggerType === 'keyPress' && $this->triggerCode === null) {
|
||||
throw new InvalidArgumentException(__('Please provide trigger key'), 'triggerKey');
|
||||
}
|
||||
|
||||
if (!in_array($this->triggerType, ['touch', 'webhook', 'keyPress'])) {
|
||||
throw new InvalidArgumentException(__('Invalid trigger type'), 'triggerType');
|
||||
}
|
||||
|
||||
if ($this->actionType === 'navLayout' && $this->layoutCode == '') {
|
||||
throw new InvalidArgumentException(__('Please enter Layout code'), 'layoutCode');
|
||||
}
|
||||
|
||||
if ($this->actionType === 'navWidget' && $this->widgetId == null) {
|
||||
throw new InvalidArgumentException(__('Please create a Widget to be loaded'), 'widgetId');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true,
|
||||
'notifyLayout' => false
|
||||
], $options);
|
||||
|
||||
$this->getLog()->debug('Saving ' . $this);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($this->actionId == null || $this->actionId == 0) {
|
||||
$this->add();
|
||||
$this->loaded = true;
|
||||
} else {
|
||||
$this->update();
|
||||
}
|
||||
|
||||
if ($options['notifyLayout'] && $this->layoutId != null) {
|
||||
$this->notifyLayout($this->layoutId);
|
||||
}
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
$this->actionId = $this->getStore()->insert('INSERT INTO `action` (ownerId, triggerType, triggerCode, actionType, source, sourceId, target, targetId, widgetId, layoutCode, layoutId) VALUES (:ownerId, :triggerType, :triggerCode, :actionType, :source, :sourceId, :target, :targetId, :widgetId, :layoutCode, :layoutId)', [
|
||||
'ownerId' => $this->ownerId,
|
||||
'triggerType' => $this->triggerType,
|
||||
'triggerCode' => $this->triggerCode,
|
||||
'actionType' => $this->actionType,
|
||||
'source' => $this->source,
|
||||
'sourceId' => $this->sourceId,
|
||||
'target' => $this->target,
|
||||
'targetId' => $this->targetId,
|
||||
'widgetId' => $this->widgetId,
|
||||
'layoutCode' => $this->layoutCode,
|
||||
'layoutId' => $this->layoutId
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
public function update()
|
||||
{
|
||||
$this->getStore()->update('UPDATE `action` SET ownerId = :ownerId, triggerType = :triggerType, triggerCode = :triggerCode, actionType = :actionType, source = :source, sourceId = :sourceId, target = :target, targetId = :targetId, widgetId = :widgetId, layoutCode = :layoutCode, layoutId = :layoutId WHERE actionId = :actionId', [
|
||||
'ownerId' => $this->ownerId,
|
||||
'triggerType' => $this->triggerType,
|
||||
'triggerCode' => $this->triggerCode,
|
||||
'actionType' => $this->actionType,
|
||||
'source' => $this->source,
|
||||
'sourceId' => $this->sourceId,
|
||||
'target' => $this->target,
|
||||
'targetId' => $this->targetId,
|
||||
'actionId' => $this->actionId,
|
||||
'widgetId' => $this->widgetId,
|
||||
'layoutCode' => $this->layoutCode,
|
||||
'layoutId' => $this->layoutId
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `action` WHERE actionId = :actionId', ['actionId' => $this->actionId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the Layout (set to building)
|
||||
* @param $layoutId
|
||||
*/
|
||||
public function notifyLayout($layoutId)
|
||||
{
|
||||
$this->getLog()->debug(sprintf('Saving Interactive Action ID %d triggered layout ID %d build', $this->actionId, $layoutId));
|
||||
|
||||
$this->getStore()->update('
|
||||
UPDATE `layout` SET `status` = 3, `modifiedDT` = :modifiedDt WHERE layoutId = :layoutId
|
||||
', [
|
||||
'layoutId' => $layoutId,
|
||||
'modifiedDt' => Carbon::now()->format(DateFormatHelper::getSystemFormat())
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
458
lib/Entity/Application.php
Normal file
458
lib/Entity/Application.php
Normal file
@@ -0,0 +1,458 @@
|
||||
<?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\Entity;
|
||||
|
||||
use League\OAuth2\Server\Entities\ClientEntityInterface;
|
||||
use Xibo\Factory\ApplicationRedirectUriFactory;
|
||||
use Xibo\Factory\ApplicationScopeFactory;
|
||||
use Xibo\Helper\Random;
|
||||
use Xibo\OAuth\ScopeEntity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class Application
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition
|
||||
*/
|
||||
class Application implements \JsonSerializable, ClientEntityInterface
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Application Key"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $key;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Private Secret Key"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $secret;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Application Name"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Application Owner"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $owner;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Application Session Expiry"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $expires;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="The Owner of this Application"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag indicating whether to allow the authorizationCode Grant Type")
|
||||
* @var int
|
||||
*/
|
||||
public $authCode = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag indicating whether to allow the clientCredentials Grant Type")
|
||||
* @var int
|
||||
*/
|
||||
public $clientCredentials = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag indicating whether this Application will be confidential or not (can it keep a secret?)")
|
||||
* @var int
|
||||
*/
|
||||
public $isConfidential = 1;
|
||||
|
||||
/** * @var ApplicationRedirectUri[] */
|
||||
public $redirectUris = [];
|
||||
|
||||
/** * @var ApplicationScope[] */
|
||||
public $scopes = [];
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Application description")
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
/**
|
||||
* @SWG\Property(description="Path to Application logo")
|
||||
* @var string
|
||||
*/
|
||||
public $logo;
|
||||
/**
|
||||
* @SWG\Property(description="Path to Application Cover Image")
|
||||
* @var string
|
||||
*/
|
||||
public $coverImage;
|
||||
/**
|
||||
* @SWG\Property(description="Company name associated with this Application")
|
||||
* @var string
|
||||
*/
|
||||
public $companyName;
|
||||
/**
|
||||
* @SWG\Property(description="URL to Application terms")
|
||||
* @var string
|
||||
*/
|
||||
public $termsUrl;
|
||||
/**
|
||||
* @SWG\Property(description="URL to Application privacy policy")
|
||||
* @var string
|
||||
*/
|
||||
public $privacyUrl;
|
||||
|
||||
/** @var ApplicationRedirectUriFactory */
|
||||
private $applicationRedirectUriFactory;
|
||||
|
||||
/** @var ApplicationScopeFactory */
|
||||
private $applicationScopeFactory;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param ApplicationRedirectUriFactory $applicationRedirectUriFactory
|
||||
* @param ApplicationScopeFactory $applicationScopeFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $applicationRedirectUriFactory, $applicationScopeFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
|
||||
$this->applicationRedirectUriFactory = $applicationRedirectUriFactory;
|
||||
$this->applicationScopeFactory = $applicationScopeFactory;
|
||||
}
|
||||
|
||||
public function __serialize(): array
|
||||
{
|
||||
return $this->jsonSerialize();
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
foreach ($data as $key => $value) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ApplicationRedirectUri $redirectUri
|
||||
*/
|
||||
public function assignRedirectUri($redirectUri)
|
||||
{
|
||||
$this->load();
|
||||
|
||||
// Assert client id
|
||||
$redirectUri->clientId = $this->key;
|
||||
|
||||
if (!in_array($redirectUri, $this->redirectUris)) {
|
||||
$this->redirectUris[] = $redirectUri;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unassign RedirectUri
|
||||
* @param ApplicationRedirectUri $redirectUri
|
||||
*/
|
||||
public function unassignRedirectUri($redirectUri)
|
||||
{
|
||||
$this->load();
|
||||
|
||||
$this->redirectUris = array_udiff($this->redirectUris, [$redirectUri], function($a, $b) {
|
||||
/**
|
||||
* @var ApplicationRedirectUri $a
|
||||
* @var ApplicationRedirectUri $b
|
||||
*/
|
||||
return $a->getId() - $b->getId();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ApplicationScope $scope
|
||||
*/
|
||||
public function assignScope($scope)
|
||||
{
|
||||
if (!in_array($scope, $this->scopes)) {
|
||||
$this->scopes[] = $scope;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ApplicationScope $scope
|
||||
*/
|
||||
public function unassignScope($scope)
|
||||
{
|
||||
$this->scopes = array_udiff($this->scopes, [$scope], function ($a, $b) {
|
||||
/**
|
||||
* @var ApplicationScope $a
|
||||
* @var ApplicationScope $b
|
||||
*/
|
||||
return $a->getId() !== $b->getId();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hash for password verify
|
||||
* @return string
|
||||
*/
|
||||
public function getHash()
|
||||
{
|
||||
return password_hash($this->secret, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load
|
||||
* @return $this
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
if ($this->loaded || empty($this->key)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Redirects
|
||||
$this->redirectUris = $this->applicationRedirectUriFactory->getByClientId($this->key);
|
||||
|
||||
// Get scopes
|
||||
$this->scopes = $this->applicationScopeFactory->getByClientId($this->key);
|
||||
|
||||
$this->loaded = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if ($this->key == null || $this->key == '') {
|
||||
// Make a new secret.
|
||||
$this->resetSecret();
|
||||
|
||||
// Add
|
||||
$this->add();
|
||||
} else {
|
||||
// Edit
|
||||
$this->edit();
|
||||
}
|
||||
|
||||
$this->getLog()->debug('Saving redirect uris: ' . json_encode($this->redirectUris));
|
||||
|
||||
foreach ($this->redirectUris as $redirectUri) {
|
||||
$redirectUri->save();
|
||||
}
|
||||
|
||||
$this->manageScopeAssignments();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->load();
|
||||
|
||||
foreach ($this->redirectUris as $redirectUri) {
|
||||
$redirectUri->delete();
|
||||
}
|
||||
|
||||
// Clear link table for this Application
|
||||
$this->getStore()->update('DELETE FROM `oauth_lkclientuser` WHERE clientId = :id', ['id' => $this->key]);
|
||||
|
||||
// Clear out everything owned by this client
|
||||
$this->getStore()->update('DELETE FROM `oauth_client_scopes` WHERE `clientId` = :id', ['id' => $this->key]);
|
||||
$this->getStore()->update('DELETE FROM `oauth_clients` WHERE `id` = :id', ['id' => $this->key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset Secret
|
||||
*/
|
||||
public function resetSecret()
|
||||
{
|
||||
$this->secret = Random::generateString(254);
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
// Make an ID
|
||||
$this->key = Random::generateString(40);
|
||||
|
||||
// Simple Insert for now
|
||||
$this->getStore()->insert('
|
||||
INSERT INTO `oauth_clients` (`id`, `secret`, `name`, `userId`, `authCode`, `clientCredentials`, `isConfidential`, `description`, `logo`, `coverImage`, `companyName`, `termsUrl`, `privacyUrl`)
|
||||
VALUES (:id, :secret, :name, :userId, :authCode, :clientCredentials, :isConfidential, :description, :logo, :coverImage, :companyName, :termsUrl, :privacyUrl)
|
||||
', [
|
||||
'id' => $this->key,
|
||||
'secret' => $this->secret,
|
||||
'name' => $this->name,
|
||||
'userId' => $this->userId,
|
||||
'authCode' => $this->authCode,
|
||||
'clientCredentials' => $this->clientCredentials,
|
||||
'isConfidential' => $this->isConfidential,
|
||||
'description' => $this->description,
|
||||
'logo' => $this->logo,
|
||||
'coverImage' => $this->coverImage,
|
||||
'companyName' => $this->companyName,
|
||||
'termsUrl' => $this->termsUrl,
|
||||
'privacyUrl' => $this->privacyUrl
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `oauth_clients` SET
|
||||
`id` = :id,
|
||||
`secret` = :secret,
|
||||
`name` = :name,
|
||||
`userId` = :userId,
|
||||
`authCode` = :authCode,
|
||||
`clientCredentials` = :clientCredentials,
|
||||
`isConfidential` = :isConfidential,
|
||||
`description` = :description,
|
||||
`logo` = :logo,
|
||||
`coverImage` = :coverImage,
|
||||
`companyName` = :companyName,
|
||||
`termsUrl` = :termsUrl,
|
||||
`privacyUrl` = :privacyUrl
|
||||
WHERE `id` = :id
|
||||
', [
|
||||
'id' => $this->key,
|
||||
'secret' => $this->secret,
|
||||
'name' => $this->name,
|
||||
'userId' => $this->userId,
|
||||
'authCode' => $this->authCode,
|
||||
'clientCredentials' => $this->clientCredentials,
|
||||
'isConfidential' => $this->isConfidential,
|
||||
'description' => $this->description,
|
||||
'logo' => $this->logo,
|
||||
'coverImage' => $this->coverImage,
|
||||
'companyName' => $this->companyName,
|
||||
'termsUrl' => $this->termsUrl,
|
||||
'privacyUrl' => $this->privacyUrl
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the original assignments with the current assignments and delete any that are missing, add any new ones
|
||||
*/
|
||||
private function manageScopeAssignments()
|
||||
{
|
||||
$i = 0;
|
||||
$params = ['clientId' => $this->key];
|
||||
$unassignIn = '';
|
||||
|
||||
foreach ($this->scopes as $link) {
|
||||
$this->getStore()->update('
|
||||
INSERT INTO `oauth_client_scopes` (clientId, scopeId) VALUES (:clientId, :scopeId)
|
||||
ON DUPLICATE KEY UPDATE scopeId = scopeId', [
|
||||
'clientId' => $this->key,
|
||||
'scopeId' => $link->id
|
||||
]);
|
||||
|
||||
$i++;
|
||||
$unassignIn .= ',:scopeId' . $i;
|
||||
$params['scopeId' . $i] = $link->id;
|
||||
}
|
||||
|
||||
// Unlink any NOT in the collection
|
||||
$sql = 'DELETE FROM `oauth_client_scopes` WHERE clientId = :clientId AND scopeId NOT IN (\'0\'' . $unassignIn . ')';
|
||||
|
||||
$this->getStore()->update($sql, $params);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function getRedirectUri()
|
||||
{
|
||||
$count = count($this->redirectUris);
|
||||
|
||||
if ($count <= 0) {
|
||||
return null;
|
||||
} else if (count($this->redirectUris) == 1) {
|
||||
return $this->redirectUris[0]->redirectUri;
|
||||
} else {
|
||||
return array_map(function($el) {
|
||||
return $el->redirectUri;
|
||||
}, $this->redirectUris);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \League\OAuth2\Server\Entities\ScopeEntityInterface[]
|
||||
*/
|
||||
public function getScopes()
|
||||
{
|
||||
$scopes = [];
|
||||
foreach ($this->scopes as $applicationScope) {
|
||||
$scope = new ScopeEntity();
|
||||
$scope->setIdentifier($applicationScope->getId());
|
||||
$scopes[] = $scope;
|
||||
}
|
||||
return $scopes;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function isConfidential()
|
||||
{
|
||||
return $this->isConfidential === 1;
|
||||
}
|
||||
}
|
||||
123
lib/Entity/ApplicationRedirectUri.php
Normal file
123
lib/Entity/ApplicationRedirectUri.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?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\Entity;
|
||||
|
||||
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class ApplicationRedirectUri
|
||||
* @package Xibo\Entity
|
||||
*/
|
||||
class ApplicationRedirectUri implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $clientId;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $redirectUri;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
public function __serialize(): array
|
||||
{
|
||||
return $this->jsonSerialize();
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
foreach ($data as $key => $value) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if ($this->id == null)
|
||||
$this->add();
|
||||
else
|
||||
$this->edit();
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `oauth_client_redirect_uris` WHERE `id` = :id', ['id' => $this->id]);
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->id = $this->getStore()->insert('
|
||||
INSERT INTO `oauth_client_redirect_uris` (`client_id`, `redirect_uri`)
|
||||
VALUES (:clientId, :redirectUri)
|
||||
', [
|
||||
'clientId' => $this->clientId,
|
||||
'redirectUri' => $this->redirectUri
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `oauth_client_redirect_uris`
|
||||
SET `redirect_uri` = :redirectUri
|
||||
WHERE `id` = :id
|
||||
',[
|
||||
'id' => $this->id,
|
||||
'redirectUri' => $this->redirectUri
|
||||
]);
|
||||
}
|
||||
}
|
||||
98
lib/Entity/ApplicationRequest.php
Normal file
98
lib/Entity/ApplicationRequest.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Application Request
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class ApplicationRequest implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The request ID")
|
||||
* @var int
|
||||
*/
|
||||
public $requestId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The user ID")
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The application ID")
|
||||
* @var string
|
||||
*/
|
||||
public $applicationId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The request route")
|
||||
* @var string
|
||||
*/
|
||||
public $url;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The request method")
|
||||
* @var string
|
||||
*/
|
||||
public $method;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The request start time")
|
||||
* @var string
|
||||
*/
|
||||
public $startTime;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The request end time")
|
||||
* @var string
|
||||
*/
|
||||
public $endTime;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The request duration")
|
||||
* @var int
|
||||
*/
|
||||
public $duration;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct(
|
||||
StorageServiceInterface $store,
|
||||
LogServiceInterface $log,
|
||||
EventDispatcherInterface $dispatcher
|
||||
) {
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
}
|
||||
111
lib/Entity/ApplicationScope.php
Normal file
111
lib/Entity/ApplicationScope.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class ApplicationScope
|
||||
* @package Xibo\Entity
|
||||
*/
|
||||
class ApplicationScope implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
public function __serialize(): array
|
||||
{
|
||||
return $this->jsonSerialize();
|
||||
}
|
||||
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
foreach ($data as $key => $value) {
|
||||
$this->{$key} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id
|
||||
* @return string
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this scope has permission for this route
|
||||
* @param string $method
|
||||
* @param string $requestedRoute
|
||||
* @return bool
|
||||
*/
|
||||
public function checkRoute(string $method, string $requestedRoute): bool
|
||||
{
|
||||
$routes = $this->getStore()->select('
|
||||
SELECT `route`
|
||||
FROM `oauth_scope_routes`
|
||||
WHERE `scopeId` = :scope
|
||||
AND `method` LIKE :method
|
||||
', [
|
||||
'scope' => $this->getId(),
|
||||
'method' => '%' . $method . '%',
|
||||
]);
|
||||
|
||||
$this->getLog()->debug('checkRoute: there are ' . count($routes) . ' potential routes for the scope '
|
||||
. $this->getId() . ' with ' . $method);
|
||||
|
||||
// We need to look through each route and run the regex against our requested route.
|
||||
$grantAccess = false;
|
||||
foreach ($routes as $route) {
|
||||
$regexResult = preg_match($route['route'], $requestedRoute);
|
||||
if ($regexResult === 1) {
|
||||
$grantAccess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $grantAccess;
|
||||
}
|
||||
}
|
||||
108
lib/Entity/AuditLog.php
Normal file
108
lib/Entity/AuditLog.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2022-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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class AuditLog
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class AuditLog implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Log Id")
|
||||
* @var int
|
||||
*/
|
||||
public $logId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Log Date")
|
||||
* @var int
|
||||
*/
|
||||
public $logDate;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The userId of the User that took this action")
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Message describing the action taken")
|
||||
* @var string
|
||||
*/
|
||||
public $message;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The effected entity")
|
||||
* @var string
|
||||
*/
|
||||
public $entity;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The effected entityId")
|
||||
* @var int
|
||||
*/
|
||||
public $entityId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A JSON representation of the object after it was changed")
|
||||
* @var string
|
||||
*/
|
||||
public $objectAfter;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The User Name of the User that took this action")
|
||||
* @var string
|
||||
*/
|
||||
public $userName;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The IP Address of the User that took this action")
|
||||
* @var string
|
||||
*/
|
||||
public $ipAddress;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Session history id.")
|
||||
* @var int
|
||||
*/
|
||||
public $sessionHistoryId;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
}
|
||||
89
lib/Entity/Bandwidth.php
Normal file
89
lib/Entity/Bandwidth.php
Normal file
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\DeadlockException;
|
||||
|
||||
|
||||
/**
|
||||
* Class Bandwidth
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
*/
|
||||
class Bandwidth
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public static $REGISTER = 1;
|
||||
public static $RF = 2;
|
||||
public static $SCHEDULE = 3;
|
||||
public static $GETFILE = 4;
|
||||
public static $GETRESOURCE = 5;
|
||||
public static $MEDIAINVENTORY = 6;
|
||||
public static $NOTIFYSTATUS = 7;
|
||||
public static $SUBMITSTATS = 8;
|
||||
public static $SUBMITLOG = 9;
|
||||
public static $REPORTFAULT = 10;
|
||||
public static $SCREENSHOT = 11;
|
||||
public static $GET_DATA = 12;
|
||||
public static $GET_DEPENDENCY = 13;
|
||||
|
||||
public $displayId;
|
||||
public $type;
|
||||
public $size;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
try {
|
||||
// This runs on the "isolated" connection because we do not want a failure here to impact the
|
||||
// main transaction we've just completed (we log bandwidth at the end).
|
||||
// Running on a separate transaction is cleaner than committing what we already have (debatable)
|
||||
$this->getStore()->updateWithDeadlockLoop('
|
||||
INSERT INTO `bandwidth` (Month, Type, DisplayID, Size)
|
||||
VALUES (:month, :type, :displayId, :size)
|
||||
ON DUPLICATE KEY UPDATE Size = Size + :size2
|
||||
', [
|
||||
'month' => strtotime(date('m') . '/02/' . date('Y') . ' 00:00:00'),
|
||||
'type' => $this->type,
|
||||
'displayId' => $this->displayId,
|
||||
'size' => $this->size,
|
||||
'size2' => $this->size
|
||||
], 'isolated', false, true);
|
||||
} catch (DeadlockException $deadlockException) {
|
||||
$this->getLog()->error('Deadlocked inserting bandwidth');
|
||||
}
|
||||
}
|
||||
}
|
||||
1031
lib/Entity/Campaign.php
Normal file
1031
lib/Entity/Campaign.php
Normal file
File diff suppressed because it is too large
Load Diff
56
lib/Entity/CampaignProgress.php
Normal file
56
lib/Entity/CampaignProgress.php
Normal 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\Entity;
|
||||
|
||||
/**
|
||||
* Campaign Progress
|
||||
*/
|
||||
class CampaignProgress implements \JsonSerializable
|
||||
{
|
||||
/** @var int */
|
||||
public $daysIn = 0;
|
||||
|
||||
/** @var int */
|
||||
public $daysTotal = 0;
|
||||
|
||||
/** @var float */
|
||||
public $targetPerDay = 0.0;
|
||||
|
||||
/** @var float */
|
||||
public $progressTime = 0.0;
|
||||
|
||||
/** @var float */
|
||||
public $progressTarget = 0.0;
|
||||
|
||||
/** @inheritDoc */
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'daysIn' => $this->daysIn,
|
||||
'daysTotal' => $this->daysTotal,
|
||||
'targetPerDay' => $this->targetPerDay,
|
||||
'progressTime' => $this->progressTime,
|
||||
'progressTarget' => $this->progressTarget,
|
||||
];
|
||||
}
|
||||
}
|
||||
353
lib/Entity/Command.php
Normal file
353
lib/Entity/Command.php
Normal file
@@ -0,0 +1,353 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class Command
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Command implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Command Id"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $commandId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Command Name"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $command;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Unique Code"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $code;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Description"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="User Id"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Command String"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $commandString;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Validation String"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $validationString;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="DisplayProfileId if specific to a Display Profile"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $displayProfileId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Command String specific to the provided DisplayProfile"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $commandStringDisplayProfile;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Validation String specific to the provided DisplayProfile"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $validationStringDisplayProfile;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="A comma separated list of player types this command is available on"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $availableOn;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Define if execution of this command should create an alert on success, failure, always or never."
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $createAlertOn;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Create Alert On specific to the provided DisplayProfile."
|
||||
* )
|
||||
*/
|
||||
public $createAlertOnDisplayProfile;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A comma separated list of groups/users with permissions to this Command")
|
||||
* @var string
|
||||
*/
|
||||
public $groupsWithPermissions;
|
||||
|
||||
/**
|
||||
* Command constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->commandId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OwnerId
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCommandString()
|
||||
{
|
||||
return empty($this->commandStringDisplayProfile) ? $this->commandString : $this->commandStringDisplayProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getValidationString()
|
||||
{
|
||||
return empty($this->validationStringDisplayProfile)
|
||||
? $this->validationString
|
||||
: $this->validationStringDisplayProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCreateAlertOn(): string
|
||||
{
|
||||
return empty($this->createAlertOnDisplayProfile)
|
||||
? $this->createAlertOn
|
||||
: $this->createAlertOnDisplayProfile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableOn()
|
||||
{
|
||||
return empty($this->availableOn) ? [] : explode(',', $this->availableOn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type Player Type
|
||||
* @return bool
|
||||
*/
|
||||
public function isAvailableOn($type)
|
||||
{
|
||||
$availableOn = $this->getAvailableOn();
|
||||
return count($availableOn) <= 0 || in_array($type, $availableOn);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isReady()
|
||||
{
|
||||
return !empty($this->getCommandString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!v::stringType()->notEmpty()->length(1, 254)->validate($this->command)) {
|
||||
throw new InvalidArgumentException(
|
||||
__('Please enter a command name between 1 and 254 characters'),
|
||||
'command'
|
||||
);
|
||||
}
|
||||
|
||||
if (!v::alpha('_')->NoWhitespace()->notEmpty()->length(1, 50)->validate($this->code)) {
|
||||
throw new InvalidArgumentException(
|
||||
__('Please enter a code between 1 and 50 characters containing only alpha characters and no spaces'),
|
||||
'code'
|
||||
);
|
||||
}
|
||||
|
||||
if (!v::stringType()->length(0, 1000)->validate($this->description)) {
|
||||
throw new InvalidArgumentException(
|
||||
__('Please enter a description between 1 and 1000 characters'),
|
||||
'description'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
* @param array $options
|
||||
*
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge($options, ['validate' => true]);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($this->commandId == null) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update(
|
||||
'DELETE FROM `command` WHERE `commandId` = :commandId',
|
||||
['commandId' => $this->commandId]
|
||||
);
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->commandId = $this->getStore()->insert('
|
||||
INSERT INTO `command` (
|
||||
`command`,
|
||||
`code`,
|
||||
`description`,
|
||||
`userId`,
|
||||
`commandString`,
|
||||
`validationString`,
|
||||
`availableOn`,
|
||||
`createAlertOn`
|
||||
)
|
||||
VALUES (
|
||||
:command,
|
||||
:code,
|
||||
:description,
|
||||
:userId,
|
||||
:commandString,
|
||||
:validationString,
|
||||
:availableOn,
|
||||
:createAlertOn
|
||||
)
|
||||
', [
|
||||
'command' => $this->command,
|
||||
'code' => $this->code,
|
||||
'description' => $this->description,
|
||||
'userId' => $this->userId,
|
||||
'commandString' => $this->commandString,
|
||||
'validationString' => $this->validationString,
|
||||
'availableOn' => $this->availableOn,
|
||||
'createAlertOn' => $this->createAlertOn
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `command` SET
|
||||
`command` = :command,
|
||||
`code` = :code,
|
||||
`description` = :description,
|
||||
`userId` = :userId,
|
||||
`commandString` = :commandString,
|
||||
`validationString` = :validationString,
|
||||
`availableOn` = :availableOn,
|
||||
`createAlertOn` = :createAlertOn
|
||||
WHERE `commandId` = :commandId
|
||||
', [
|
||||
'command' => $this->command,
|
||||
'code' => $this->code,
|
||||
'description' => $this->description,
|
||||
'userId' => $this->userId,
|
||||
'commandId' => $this->commandId,
|
||||
'commandString' => $this->commandString,
|
||||
'validationString' => $this->validationString,
|
||||
'availableOn' => $this->availableOn,
|
||||
'createAlertOn' => $this->createAlertOn
|
||||
]);
|
||||
}
|
||||
}
|
||||
135
lib/Entity/Connector.php
Normal file
135
lib/Entity/Connector.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
use Xibo\Connector\ConnectorInterface;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Represents the database object for a Connector
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Connector implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
// Status properties
|
||||
public $isInstalled = true;
|
||||
public $isSystem = true;
|
||||
|
||||
// Database properties
|
||||
public $connectorId;
|
||||
public $className;
|
||||
public $settings;
|
||||
public $isEnabled;
|
||||
public $isVisible;
|
||||
|
||||
// Decorated properties
|
||||
public $title;
|
||||
public $description;
|
||||
public $thumbnail;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Xibo\Connector\ConnectorInterface $connector
|
||||
* @return $this
|
||||
*/
|
||||
public function decorate(ConnectorInterface $connector): Connector
|
||||
{
|
||||
$this->title = $connector->getTitle();
|
||||
$this->description = $connector->getDescription();
|
||||
$this->thumbnail = $connector->getThumbnail();
|
||||
if (empty($this->thumbnail)) {
|
||||
$this->thumbnail = 'theme/default/img/connectors/placeholder.png';
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
if ($this->connectorId == null || $this->connectorId == 0) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->connectorId = $this->getStore()->insert('
|
||||
INSERT INTO `connectors` (`className`, `isEnabled`, `isVisible`, `settings`)
|
||||
VALUES (:className, :isEnabled, :isVisible, :settings)
|
||||
', [
|
||||
'className' => $this->className,
|
||||
'isEnabled' => $this->isEnabled,
|
||||
'isVisible' => $this->isVisible,
|
||||
'settings' => json_encode($this->settings)
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `connectors` SET
|
||||
`className` = :className,
|
||||
`isEnabled` = :isEnabled,
|
||||
`isVisible` = :isVisible,
|
||||
`settings` = :settings
|
||||
WHERE connectorId = :connectorId
|
||||
', [
|
||||
'connectorId' => $this->connectorId,
|
||||
'className' => $this->className,
|
||||
'isEnabled' => $this->isEnabled,
|
||||
'isVisible' => $this->isVisible,
|
||||
'settings' => json_encode($this->settings)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws \Xibo\Support\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if ($this->isSystem) {
|
||||
throw new InvalidArgumentException(__('Sorry we cannot delete a system connector.'), 'isSystem');
|
||||
}
|
||||
|
||||
$this->getStore()->update('DELETE FROM `connectors` WHERE connectorId = :connectorId', [
|
||||
'connectorId' => $this->connectorId
|
||||
]);
|
||||
}
|
||||
}
|
||||
1412
lib/Entity/DataSet.php
Normal file
1412
lib/Entity/DataSet.php
Normal file
File diff suppressed because it is too large
Load Diff
521
lib/Entity/DataSetColumn.php
Normal file
521
lib/Entity/DataSetColumn.php
Normal file
@@ -0,0 +1,521 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Factory\DataSetColumnFactory;
|
||||
use Xibo\Factory\DataSetColumnTypeFactory;
|
||||
use Xibo\Factory\DataTypeFactory;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
use Xibo\Widget\Definition\Sql;
|
||||
|
||||
/**
|
||||
* Class DataSetColumn
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class DataSetColumn implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of this DataSetColumn")
|
||||
* @var int
|
||||
*/
|
||||
public $dataSetColumnId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of the DataSet that this Column belongs to")
|
||||
* @var int
|
||||
*/
|
||||
public $dataSetId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Column Heading")
|
||||
* @var string
|
||||
*/
|
||||
public $heading;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of the DataType for this Column")
|
||||
* @var int
|
||||
*/
|
||||
public $dataTypeId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of the ColumnType for this Column")
|
||||
* @var int
|
||||
*/
|
||||
public $dataSetColumnTypeId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Comma separated list of valid content for drop down columns")
|
||||
* @var string
|
||||
*/
|
||||
public $listContent;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The order this column should be displayed")
|
||||
* @var int
|
||||
*/
|
||||
public $columnOrder;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A MySQL formula for this column")
|
||||
* @var string
|
||||
*/
|
||||
public $formula;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The data type for this Column")
|
||||
* @var string
|
||||
*/
|
||||
public $dataType;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The data field of the remote DataSet as a JSON-String")
|
||||
* @var string
|
||||
*/
|
||||
public $remoteField;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Does this column show a filter on the data entry page?")
|
||||
* @var string
|
||||
*/
|
||||
public $showFilter = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Does this column allow a sorting on the data entry page?")
|
||||
* @var string
|
||||
*/
|
||||
public $showSort = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The column type for this Column")
|
||||
* @var string
|
||||
*/
|
||||
public $dataSetColumnType;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Help text that should be displayed when entering data for this Column.")
|
||||
* @var string
|
||||
*/
|
||||
public $tooltip;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag indicating whether value must be provided for this Column.")
|
||||
* @var int
|
||||
*/
|
||||
public $isRequired = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Date format of dates in the source for remote DataSet.")
|
||||
* @var string
|
||||
*/
|
||||
public $dateFormat;
|
||||
|
||||
/** @var DataSetColumnFactory */
|
||||
private $dataSetColumnFactory;
|
||||
|
||||
/** @var DataTypeFactory */
|
||||
private $dataTypeFactory;
|
||||
|
||||
/** @var DataSetColumnTypeFactory */
|
||||
private $dataSetColumnTypeFactory;
|
||||
|
||||
/**
|
||||
* The prior dataset column id, when cloning
|
||||
* @var int
|
||||
*/
|
||||
public $priorDatasetColumnId;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param DataSetColumnFactory $dataSetColumnFactory
|
||||
* @param DataTypeFactory $dataTypeFactory
|
||||
* @param DataSetColumnTypeFactory $dataSetColumnTypeFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $dataSetColumnFactory, $dataTypeFactory, $dataSetColumnTypeFactory)
|
||||
{
|
||||
$this->excludeProperty('priorDatasetColumnId');
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
|
||||
$this->dataSetColumnFactory = $dataSetColumnFactory;
|
||||
$this->dataTypeFactory = $dataTypeFactory;
|
||||
$this->dataSetColumnTypeFactory = $dataSetColumnTypeFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->priorDatasetColumnId = $this->dataSetColumnId;
|
||||
$this->dataSetColumnId = null;
|
||||
$this->dataSetId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* List Content Array
|
||||
* @return array
|
||||
*/
|
||||
public function listContentArray()
|
||||
{
|
||||
return explode(',', $this->listContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate($options = []): void
|
||||
{
|
||||
$options = array_merge([
|
||||
'testFormulas' => true,
|
||||
'allowSpacesInHeading' => false,
|
||||
], $options);
|
||||
|
||||
if ($this->dataSetId == 0 || $this->dataSetId == '') {
|
||||
throw new InvalidArgumentException(__('Missing dataSetId'), 'dataSetId');
|
||||
}
|
||||
|
||||
if ($this->dataTypeId == 0 || $this->dataTypeId == '') {
|
||||
throw new InvalidArgumentException(__('Missing dataTypeId'), 'dataTypeId');
|
||||
}
|
||||
|
||||
if ($this->dataSetColumnTypeId == 0 || $this->dataSetColumnTypeId == '') {
|
||||
throw new InvalidArgumentException(__('Missing dataSetColumnTypeId'), 'dataSetColumnTypeId');
|
||||
}
|
||||
|
||||
if ($this->heading == '') {
|
||||
throw new InvalidArgumentException(__('Please provide a column heading.'), 'heading');
|
||||
}
|
||||
|
||||
// Column heading should not allow reserved/disallowed SQL words
|
||||
if (Str::contains($this->heading, Sql::DISALLOWED_KEYWORDS, true)) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
__('Headings cannot contain reserved words, such as %s'),
|
||||
implode(', ', Sql::DISALLOWED_KEYWORDS),
|
||||
),
|
||||
'heading',
|
||||
);
|
||||
}
|
||||
|
||||
// We allow spaces here for backwards compatibility, but only on import and edit.
|
||||
$additionalCharacters = $options['allowSpacesInHeading'] ? ' ' : '';
|
||||
if (!v::stringType()->alnum($additionalCharacters)->validate($this->heading)
|
||||
|| strtolower($this->heading) == 'id'
|
||||
) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
__('Please provide an alternative column heading %s can not be used.'),
|
||||
$this->heading
|
||||
), 'heading');
|
||||
}
|
||||
|
||||
if ($this->dataSetColumnTypeId == 2 && $this->formula == '') {
|
||||
throw new InvalidArgumentException(__('Please enter a valid formula'), 'formula');
|
||||
}
|
||||
|
||||
// Make sure this column name is unique
|
||||
$columns = $this->dataSetColumnFactory->getByDataSetId($this->dataSetId);
|
||||
|
||||
foreach ($columns as $column) {
|
||||
if ($column->heading == $this->heading
|
||||
&& ($this->dataSetColumnId == null || $column->dataSetColumnId != $this->dataSetColumnId)
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
__('A column already exists with this name, please choose another'),
|
||||
'heading',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the actual values
|
||||
try {
|
||||
$this->dataTypeFactory->getById($this->dataTypeId);
|
||||
} catch (NotFoundException $e) {
|
||||
throw new InvalidArgumentException(__('Provided Data Type doesn\'t exist'), 'datatype');
|
||||
}
|
||||
|
||||
try {
|
||||
$dataSetColumnType = $this->dataSetColumnTypeFactory->getById($this->dataSetColumnTypeId);
|
||||
|
||||
// If we are a remote column, validate we have a field
|
||||
if (strtolower($dataSetColumnType->dataSetColumnType) === 'remote'
|
||||
&& ($this->remoteField === '' || $this->remoteField === null)) {
|
||||
throw new InvalidArgumentException(
|
||||
__('Remote field is required when the column type is set to Remote'),
|
||||
'remoteField',
|
||||
);
|
||||
}
|
||||
} catch (NotFoundException) {
|
||||
throw new InvalidArgumentException(
|
||||
__('Provided DataSet Column Type doesn\'t exist'),
|
||||
'dataSetColumnTypeId',
|
||||
);
|
||||
}
|
||||
|
||||
// Should we validate the list content?
|
||||
if ($this->dataSetColumnId != 0 && $this->listContent != '') {
|
||||
// Look up all DataSet data in this table to make sure that the existing data is covered by the list content
|
||||
$list = $this->listContentArray();
|
||||
|
||||
// Add an empty field
|
||||
$list[] = '';
|
||||
|
||||
// We can check this is valid by building up a NOT IN sql statement, if we get results we know it's not good
|
||||
$select = '';
|
||||
|
||||
$dbh = $this->getStore()->getConnection('isolated');
|
||||
|
||||
for ($i=0; $i < count($list); $i++) {
|
||||
if (!empty($list[$i])) {
|
||||
$list_val = $dbh->quote($list[$i]);
|
||||
$select .= $list_val . ',';
|
||||
}
|
||||
}
|
||||
|
||||
$select = rtrim($select, ',');
|
||||
|
||||
// $select has been quoted in the for loop
|
||||
// always test the original value of the column (we won't have changed the actualised table yet)
|
||||
$SQL = 'SELECT id FROM `dataset_' . $this->dataSetId . '` WHERE `' . $this->getOriginalValue('heading') . '` NOT IN (' . $select . ')';//phpcs:ignore
|
||||
|
||||
$sth = $dbh->prepare($SQL);
|
||||
$sth->execute();
|
||||
|
||||
if ($sth->fetch()) {
|
||||
throw new InvalidArgumentException(
|
||||
__('New list content value is invalid as it does not include values for existing data'),
|
||||
'listcontent'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// if formula dataSetType is set and formula is not empty, try to execute the SQL to validate it - we're
|
||||
// ignoring client side formulas here.
|
||||
if ($options['testFormulas']
|
||||
&& $this->dataSetColumnTypeId == 2
|
||||
&& $this->formula != ''
|
||||
&& !str_starts_with($this->formula, '$')
|
||||
) {
|
||||
try {
|
||||
$count = 0;
|
||||
$formula = str_ireplace(
|
||||
Sql::DISALLOWED_KEYWORDS,
|
||||
'',
|
||||
htmlspecialchars_decode($this->formula, ENT_QUOTES),
|
||||
$count
|
||||
);
|
||||
|
||||
if ($count > 0) {
|
||||
throw new InvalidArgumentException(__('Formula contains disallowed keywords.'));
|
||||
}
|
||||
|
||||
$formula = str_replace('[DisplayId]', 0, $formula);
|
||||
$formula = str_replace('[DisplayGeoLocation]', "ST_GEOMFROMTEXT('POINT(51.504 -0.104)')", $formula);
|
||||
|
||||
$this->getStore()->select('
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT `id`, ' . $formula . ' AS `' . $this->heading . '`
|
||||
FROM `dataset_' . $this->dataSetId . '`
|
||||
) dataset
|
||||
', [], 'isolated');
|
||||
} catch (\Exception $e) {
|
||||
$this->getLog()->debug('Formula validation failed with following message ' . $e->getMessage());
|
||||
throw new InvalidArgumentException(__('Provided formula is invalid'), 'formula');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
* @param array $options
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true,
|
||||
'rebuilding' => false,
|
||||
], $options);
|
||||
|
||||
if ($options['validate'] && !$options['rebuilding']) {
|
||||
$this->validate($options);
|
||||
}
|
||||
|
||||
if ($this->dataSetColumnId == 0) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit($options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*/
|
||||
public function delete(bool $isDeletingDataset = false): void
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `datasetcolumn` WHERE DataSetColumnID = :dataSetColumnId', ['dataSetColumnId' => $this->dataSetColumnId]);
|
||||
|
||||
// Delete column (unless remote, or dropping the whole dataset)
|
||||
if (!$isDeletingDataset && $this->dataSetColumnTypeId !== 2) {
|
||||
$this->getStore()->update('ALTER TABLE `dataset_' . $this->dataSetId . '` DROP `' . $this->heading . '`', []);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add
|
||||
*/
|
||||
private function add()
|
||||
{
|
||||
$this->dataSetColumnId = $this->getStore()->insert('
|
||||
INSERT INTO `datasetcolumn` (DataSetID, Heading, DataTypeID, ListContent, ColumnOrder, DataSetColumnTypeID, Formula, RemoteField, `showFilter`, `showSort`, `tooltip`, `isRequired`, `dateFormat`)
|
||||
VALUES (:dataSetId, :heading, :dataTypeId, :listContent, :columnOrder, :dataSetColumnTypeId, :formula, :remoteField, :showFilter, :showSort, :tooltip, :isRequired, :dateFormat)
|
||||
', [
|
||||
'dataSetId' => $this->dataSetId,
|
||||
'heading' => $this->heading,
|
||||
'dataTypeId' => $this->dataTypeId,
|
||||
'listContent' => $this->listContent,
|
||||
'columnOrder' => $this->columnOrder,
|
||||
'dataSetColumnTypeId' => $this->dataSetColumnTypeId,
|
||||
'formula' => $this->formula,
|
||||
'remoteField' => $this->remoteField,
|
||||
'showFilter' => $this->showFilter,
|
||||
'showSort' => $this->showSort,
|
||||
'tooltip' => $this->tooltip,
|
||||
'isRequired' => $this->isRequired,
|
||||
'dateFormat' => $this->dateFormat
|
||||
]);
|
||||
|
||||
// Add Column to Underlying Table
|
||||
if (($this->dataSetColumnTypeId == 1) || ($this->dataSetColumnTypeId == 3)) {
|
||||
// Use a separate connection for DDL (it operates outside transactions)
|
||||
$this->getStore()->update('ALTER TABLE `dataset_' . $this->dataSetId . '` ADD `' . $this->heading . '` ' . $this->sqlDataType() . ' NULL', [], 'isolated', false, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
* @param array $options
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function edit($options)
|
||||
{
|
||||
$params = [
|
||||
'dataSetId' => $this->dataSetId,
|
||||
'heading' => $this->heading,
|
||||
'dataTypeId' => $this->dataTypeId,
|
||||
'listContent' => $this->listContent,
|
||||
'columnOrder' => $this->columnOrder,
|
||||
'dataSetColumnTypeId' => $this->dataSetColumnTypeId,
|
||||
'formula' => $this->formula,
|
||||
'dataSetColumnId' => $this->dataSetColumnId,
|
||||
'remoteField' => $this->remoteField,
|
||||
'showFilter' => $this->showFilter,
|
||||
'showSort' => $this->showSort,
|
||||
'tooltip' => $this->tooltip,
|
||||
'isRequired' => $this->isRequired,
|
||||
'dateFormat' => $this->dateFormat
|
||||
];
|
||||
|
||||
$sql = '
|
||||
UPDATE `datasetcolumn` SET
|
||||
dataSetId = :dataSetId,
|
||||
Heading = :heading,
|
||||
ListContent = :listContent,
|
||||
ColumnOrder = :columnOrder,
|
||||
DataTypeID = :dataTypeId,
|
||||
DataSetColumnTypeID = :dataSetColumnTypeId,
|
||||
Formula = :formula,
|
||||
RemoteField = :remoteField,
|
||||
`showFilter` = :showFilter,
|
||||
`showSort` = :showSort,
|
||||
`tooltip` = :tooltip,
|
||||
`isRequired` = :isRequired,
|
||||
`dateFormat` = :dateFormat
|
||||
WHERE dataSetColumnId = :dataSetColumnId
|
||||
';
|
||||
|
||||
$this->getStore()->update($sql, $params);
|
||||
|
||||
try {
|
||||
if ($options['rebuilding'] && ($this->dataSetColumnTypeId == 1 || $this->dataSetColumnTypeId == 3)) {
|
||||
$this->getStore()->update('ALTER TABLE `dataset_' . $this->dataSetId . '` ADD `' . $this->heading . '` ' . $this->sqlDataType() . ' NULL', [], 'isolated', false, false);
|
||||
|
||||
} else if (($this->dataSetColumnTypeId == 1 || $this->dataSetColumnTypeId == 3)
|
||||
&& ($this->hasPropertyChanged('heading') || $this->hasPropertyChanged('dataTypeId'))) {
|
||||
$sql = 'ALTER TABLE `dataset_' . $this->dataSetId . '` CHANGE `' . $this->getOriginalValue('heading') . '` `' . $this->heading . '` ' . $this->sqlDataType() . ' NULL DEFAULT NULL';
|
||||
$this->getStore()->update($sql, [], 'isolated', false, false);
|
||||
}
|
||||
} catch (\PDOException $PDOException) {
|
||||
$this->getLog()->error('Unable to change DataSetColumn because ' . $PDOException->getMessage());
|
||||
throw new InvalidArgumentException(__('Existing data is incompatible with your new configuration'), 'dataSetData');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQL Data Type for this Column Definition
|
||||
* @return string
|
||||
*/
|
||||
private function sqlDataType()
|
||||
{
|
||||
$dataType = null;
|
||||
|
||||
switch ($this->dataTypeId) {
|
||||
|
||||
case 2:
|
||||
$dataType = 'DOUBLE';
|
||||
break;
|
||||
|
||||
case 3:
|
||||
$dataType = 'DATETIME';
|
||||
break;
|
||||
|
||||
case 5:
|
||||
$dataType = 'INT';
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 6:
|
||||
$dataType = 'TEXT';
|
||||
break;
|
||||
|
||||
case 4:
|
||||
default:
|
||||
$dataType = 'VARCHAR(1000)';
|
||||
}
|
||||
|
||||
return $dataType;
|
||||
}
|
||||
}
|
||||
59
lib/Entity/DataSetColumnType.php
Normal file
59
lib/Entity/DataSetColumnType.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class DataSetColumnType
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class DataSetColumnType implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID for this DataSetColumnType")
|
||||
* @var int
|
||||
*/
|
||||
public $dataSetColumnTypeId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The name for this DataSetColumnType")
|
||||
* @var string
|
||||
*/
|
||||
public $dataSetColumnType;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
}
|
||||
164
lib/Entity/DataSetRss.php
Normal file
164
lib/Entity/DataSetRss.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
|
||||
use Xibo\Helper\Random;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class DataSetRss
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class DataSetRss implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public $id;
|
||||
public $dataSetId;
|
||||
public $titleColumnId;
|
||||
public $summaryColumnId;
|
||||
public $contentColumnId;
|
||||
public $publishedDateColumnId;
|
||||
|
||||
public $psk;
|
||||
public $title;
|
||||
public $author;
|
||||
|
||||
public $sort;
|
||||
public $filter;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function getFilter()
|
||||
{
|
||||
return ($this->filter == '') ? ['filter' => '', 'useFilteringClause' => 0, 'filterClauses' => []] : json_decode($this->filter, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function getSort()
|
||||
{
|
||||
return ($this->sort == '') ? ['sort' => '', 'useOrderingClause' => 0, 'orderClauses' => []] : json_decode($this->sort, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if ($this->id == null) {
|
||||
$this->add();
|
||||
|
||||
$this->audit($this->id, 'Added', []);
|
||||
} else {
|
||||
$this->edit();
|
||||
|
||||
$this->audit($this->id, 'Saved');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setNewPsk()
|
||||
{
|
||||
$this->psk = Random::generateString(12);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `datasetrss` WHERE id = :id', ['id' => $this->id]);
|
||||
|
||||
$this->audit($this->id, 'Deleted');
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->id = $this->getStore()->insert('
|
||||
INSERT INTO datasetrss (dataSetId, psk, title, author, titleColumnId, summaryColumnId, contentColumnId, publishedDateColumnId, sort, filter) VALUES
|
||||
(:dataSetId, :psk, :title, :author, :titleColumnId, :summaryColumnId, :contentColumnId, :publishedDateColumnId, :sort, :filter)
|
||||
', [
|
||||
'dataSetId' => $this->dataSetId,
|
||||
'psk' => $this->psk,
|
||||
'title' => $this->title,
|
||||
'author' => $this->author,
|
||||
'titleColumnId' => $this->titleColumnId,
|
||||
'summaryColumnId' => $this->summaryColumnId,
|
||||
'contentColumnId' => $this->contentColumnId,
|
||||
'publishedDateColumnId' => $this->publishedDateColumnId,
|
||||
'sort' => $this->sort,
|
||||
'filter' => $this->filter
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `datasetrss` SET
|
||||
psk = :psk,
|
||||
title = :title,
|
||||
author = :author,
|
||||
titleColumnId = :titleColumnId,
|
||||
summaryColumnId = :summaryColumnId,
|
||||
contentColumnId = :contentColumnId,
|
||||
publishedDateColumnId = :publishedDateColumnId,
|
||||
sort = :sort,
|
||||
filter = :filter
|
||||
WHERE id = :id
|
||||
', [
|
||||
'id' => $this->id,
|
||||
'psk' => $this->psk,
|
||||
'title' => $this->title,
|
||||
'author' => $this->author,
|
||||
'titleColumnId' => $this->titleColumnId,
|
||||
'summaryColumnId' => $this->summaryColumnId,
|
||||
'contentColumnId' => $this->contentColumnId,
|
||||
'publishedDateColumnId' => $this->publishedDateColumnId,
|
||||
'sort' => $this->sort,
|
||||
'filter' => $this->filter
|
||||
]);
|
||||
}
|
||||
}
|
||||
61
lib/Entity/DataType.php
Normal file
61
lib/Entity/DataType.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Class DataType
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class DataType implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID for this DataType")
|
||||
* @var int
|
||||
*/
|
||||
public $dataTypeId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Name for this DataType")
|
||||
* @var string
|
||||
*/
|
||||
public $dataType;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
}
|
||||
357
lib/Entity/DayPart.php
Normal file
357
lib/Entity/DayPart.php
Normal file
@@ -0,0 +1,357 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Event\DayPartDeleteEvent;
|
||||
use Xibo\Factory\ScheduleFactory;
|
||||
use Xibo\Service\DisplayNotifyServiceInterface;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\GeneralException;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class DayPart
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class DayPart implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of this Daypart")
|
||||
* @var int
|
||||
*/
|
||||
public $dayPartId;
|
||||
public $name;
|
||||
public $description;
|
||||
public $isRetired;
|
||||
public $userId;
|
||||
|
||||
public $startTime;
|
||||
public $endTime;
|
||||
public $exceptions;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A readonly flag determining whether this DayPart is always")
|
||||
* @var int
|
||||
*/
|
||||
public $isAlways = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A readonly flag determining whether this DayPart is custom")
|
||||
* @var int
|
||||
*/
|
||||
public $isCustom = 0;
|
||||
|
||||
/** @var Carbon $adjustedStart Adjusted start datetime */
|
||||
public $adjustedStart;
|
||||
|
||||
/** @var Carbon Adjusted end datetime */
|
||||
public $adjustedEnd;
|
||||
|
||||
private $timeHash;
|
||||
|
||||
/** @var ScheduleFactory */
|
||||
private $scheduleFactory;
|
||||
|
||||
/** @var DisplayNotifyServiceInterface */
|
||||
private $displayNotifyService;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ScheduleFactory $scheduleFactory
|
||||
* @param \Xibo\Service\DisplayNotifyServiceInterface $displayNotifyService
|
||||
* @return $this
|
||||
*/
|
||||
public function setScheduleFactory($scheduleFactory, DisplayNotifyServiceInterface $displayNotifyService)
|
||||
{
|
||||
$this->scheduleFactory = $scheduleFactory;
|
||||
$this->displayNotifyService = $displayNotifyService;
|
||||
return $this;
|
||||
}
|
||||
/**
|
||||
* Calculate time hash
|
||||
* @return string
|
||||
*/
|
||||
private function calculateTimeHash()
|
||||
{
|
||||
$hash = $this->startTime . $this->endTime;
|
||||
|
||||
foreach ($this->exceptions as $exception) {
|
||||
$hash .= $exception['day'] . $exception['start'] . $exception['end'];
|
||||
}
|
||||
|
||||
return md5($hash);
|
||||
}
|
||||
|
||||
public function isSystemDayPart(): bool
|
||||
{
|
||||
return ($this->isAlways || $this->isCustom);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->dayPartId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Owner
|
||||
* @param int $ownerId
|
||||
*/
|
||||
public function setOwner($ownerId)
|
||||
{
|
||||
$this->userId = $ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
$this->getLog()->debug('Validating daypart ' . $this->name);
|
||||
|
||||
if (!v::stringType()->notEmpty()->validate($this->name))
|
||||
throw new InvalidArgumentException(__('Name cannot be empty'), 'name');
|
||||
|
||||
// Check the start/end times are in the correct format (H:i)
|
||||
if ((strlen($this->startTime) != 8 && strlen($this->startTime) != 5) || (strlen($this->endTime) != 8 && strlen($this->endTime) != 5))
|
||||
throw new InvalidArgumentException(__('Start/End time are empty or in an incorrect format'), 'start/end time');
|
||||
|
||||
foreach ($this->exceptions as $exception) {
|
||||
if ((strlen($exception['start']) != 8 && strlen($exception['start']) != 5) || (strlen($exception['end']) != 8 && strlen($exception['end']) != 5))
|
||||
throw new InvalidArgumentException(sprintf(__('Exception Start/End time for %s are empty or in an incorrect format'), $exception['day']), 'exception start/end time');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load
|
||||
* @return $this
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
$this->timeHash = $this->calculateTimeHash();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Carbon\Carbon $date
|
||||
* @return void
|
||||
*/
|
||||
public function adjustForDate(Carbon $date)
|
||||
{
|
||||
// Matching exceptions?
|
||||
// we use a lookup because the form control uses the below date abbreviations
|
||||
$dayOfWeekLookup = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
|
||||
foreach ($this->exceptions as $exception) {
|
||||
if ($exception['day'] === $dayOfWeekLookup[$date->dayOfWeekIso]) {
|
||||
$this->adjustedStart = $date->copy()->setTimeFromTimeString($exception['start']);
|
||||
$this->adjustedEnd = $date->copy()->setTimeFromTimeString($exception['end']);
|
||||
if ($this->adjustedStart >= $this->adjustedEnd) {
|
||||
$this->adjustedEnd->addDay();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No matching exceptions.
|
||||
$this->adjustedStart = $date->copy()->setTimeFromTimeString($this->startTime);
|
||||
$this->adjustedEnd = $date->copy()->setTimeFromTimeString($this->endTime);
|
||||
if ($this->adjustedStart >= $this->adjustedEnd) {
|
||||
$this->adjustedEnd->addDay();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
* @param array $options
|
||||
* @throws InvalidArgumentException
|
||||
* @throws GeneralException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true,
|
||||
'recalculateHash' => true
|
||||
], $options);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($this->dayPartId == 0) {
|
||||
$this->add();
|
||||
} else {
|
||||
// Update
|
||||
$this->update();
|
||||
|
||||
// When we change user on reassignAllTo, we do save dayPart,
|
||||
// however it will not have required childObjectDependencies to run the below checks
|
||||
// it is also not needed to run them when we just changed the owner.
|
||||
if ($options['recalculateHash']) {
|
||||
// Compare the time hash with a new time hash to see if we need to update associated schedules
|
||||
if ($this->timeHash != $this->calculateTimeHash()) {
|
||||
$this->handleEffectedSchedules();
|
||||
} else {
|
||||
$this->getLog()->debug('Daypart hash identical, no need to update schedules. ' . $this->timeHash . ' vs ' . $this->calculateTimeHash());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
if ($this->isSystemDayPart()) {
|
||||
throw new InvalidArgumentException('Cannot delete system dayParts');
|
||||
}
|
||||
|
||||
$this->getDispatcher()->dispatch(new DayPartDeleteEvent($this), DayPartDeleteEvent::$NAME);
|
||||
|
||||
// Delete all events using this daypart
|
||||
$schedules = $this->scheduleFactory->getByDayPartId($this->dayPartId);
|
||||
|
||||
foreach ($schedules as $schedule) {
|
||||
$schedule->delete();
|
||||
}
|
||||
|
||||
// Delete the daypart
|
||||
$this->getStore()->update('DELETE FROM `daypart` WHERE dayPartId = :dayPartId', ['dayPartId' => $this->dayPartId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add
|
||||
*/
|
||||
private function add()
|
||||
{
|
||||
$this->dayPartId = $this->getStore()->insert('
|
||||
INSERT INTO `daypart` (`name`, `description`, `isRetired`, `userId`, `startTime`, `endTime`, `exceptions`)
|
||||
VALUES (:name, :description, :isRetired, :userId, :startTime, :endTime, :exceptions)
|
||||
', [
|
||||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
'isRetired' => $this->isRetired,
|
||||
'userId' => $this->userId,
|
||||
'startTime' => $this->startTime,
|
||||
'endTime' => $this->endTime,
|
||||
'exceptions' => json_encode(is_array($this->exceptions) ? $this->exceptions : [])
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update
|
||||
*/
|
||||
private function update()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `daypart`
|
||||
SET `name` = :name,
|
||||
`description` = :description,
|
||||
`isRetired` = :isRetired,
|
||||
`userId` = :userId,
|
||||
`startTime` = :startTime,
|
||||
`endTime` = :endTime,
|
||||
`exceptions` = :exceptions
|
||||
WHERE `daypart`.dayPartId = :dayPartId
|
||||
', [
|
||||
'dayPartId' => $this->dayPartId,
|
||||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
'isRetired' => $this->isRetired,
|
||||
'userId' => $this->userId,
|
||||
'startTime' => $this->startTime,
|
||||
'endTime' => $this->endTime,
|
||||
'exceptions' => json_encode(is_array($this->exceptions) ? $this->exceptions : [])
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles schedules effected by an update
|
||||
* @throws NotFoundException
|
||||
* @throws GeneralException
|
||||
*/
|
||||
private function handleEffectedSchedules()
|
||||
{
|
||||
$now = Carbon::now()->format('U');
|
||||
|
||||
// Get all schedules that use this dayPart and exist after the current time.
|
||||
$schedules = $this->scheduleFactory->query(null, ['dayPartId' => $this->dayPartId, 'futureSchedulesFrom' => $now]);
|
||||
|
||||
$this->getLog()->debug('Daypart update effects ' . count($schedules) . ' schedules.');
|
||||
|
||||
foreach ($schedules as $schedule) {
|
||||
/** @var Schedule $schedule */
|
||||
$schedule
|
||||
->setDisplayNotifyService($this->displayNotifyService)
|
||||
->load();
|
||||
|
||||
// Is this schedule a recurring event?
|
||||
if ($schedule->recurrenceType != '' && $schedule->fromDt < $now) {
|
||||
$this->getLog()->debug('Schedule is for a recurring event which has already recurred');
|
||||
|
||||
// Split the scheduled event, adjusting only the recurring end date on the original event
|
||||
$newSchedule = clone $schedule;
|
||||
$schedule->recurrenceRange = $now;
|
||||
$schedule->save();
|
||||
|
||||
// Adjusting the fromdt on the new event
|
||||
$newSchedule->fromDt = Carbon::now()->addDay()->format('U');
|
||||
$newSchedule->save();
|
||||
} else {
|
||||
$this->getLog()->debug('Schedule is for a single event');
|
||||
|
||||
// Update just this single event to have the new date/time
|
||||
$schedule->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1576
lib/Entity/Display.php
Normal file
1576
lib/Entity/Display.php
Normal file
File diff suppressed because it is too large
Load Diff
225
lib/Entity/DisplayEvent.php
Normal file
225
lib/Entity/DisplayEvent.php
Normal file
@@ -0,0 +1,225 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class DisplayEvent
|
||||
* @package Xibo\Entity
|
||||
*/
|
||||
class DisplayEvent implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public $displayEventId;
|
||||
public $displayId;
|
||||
public $eventDate;
|
||||
public $start;
|
||||
public $end;
|
||||
public $eventTypeId;
|
||||
public $refId;
|
||||
public $detail;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct(
|
||||
StorageServiceInterface $store,
|
||||
LogServiceInterface $log,
|
||||
EventDispatcherInterface $dispatcher
|
||||
) {
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save displayevent
|
||||
* @return void
|
||||
*/
|
||||
public function save(): void
|
||||
{
|
||||
if ($this->displayEventId == null) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new displayevent
|
||||
* @return void
|
||||
*/
|
||||
private function add(): void
|
||||
{
|
||||
$this->displayEventId = $this->getStore()->insert('
|
||||
INSERT INTO `displayevent` (eventDate, start, end, displayID, eventTypeId, refId, detail)
|
||||
VALUES (:eventDate, :start, :end, :displayId, :eventTypeId, :refId, :detail)
|
||||
', [
|
||||
'eventDate' => Carbon::now()->format('U'),
|
||||
'start' => $this->start,
|
||||
'end' => $this->end,
|
||||
'displayId' => $this->displayId,
|
||||
'eventTypeId' => $this->eventTypeId,
|
||||
'refId' => $this->refId,
|
||||
'detail' => $this->detail,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit displayevent
|
||||
* @return void
|
||||
*/
|
||||
private function edit(): void
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE displayevent
|
||||
SET end = :end,
|
||||
displayId = :displayId,
|
||||
eventTypeId = :eventTypeId,
|
||||
refId = :refId,
|
||||
detail = :detail
|
||||
WHERE displayEventId = :displayEventId
|
||||
', [
|
||||
'displayEventId' => $this->displayEventId,
|
||||
'end' => $this->end,
|
||||
'displayId' => $this->displayId,
|
||||
'eventTypeId' => $this->eventTypeId,
|
||||
'refId' => $this->refId,
|
||||
'detail' => $this->detail,
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Record end date for specified display and event type.
|
||||
* @param int $displayId
|
||||
* @param int|null $date
|
||||
* @param int $eventTypeId
|
||||
* @return void
|
||||
*/
|
||||
public function eventEnd(int $displayId, int $eventTypeId = 1, string $detail = null, ?int $date = null): void
|
||||
{
|
||||
$this->getLog()->debug(
|
||||
sprintf(
|
||||
'displayEvent : end display alert for eventType %s and displayId %d',
|
||||
$this->getEventNameFromId($eventTypeId),
|
||||
$displayId
|
||||
)
|
||||
);
|
||||
|
||||
$this->getStore()->update(
|
||||
"UPDATE `displayevent` SET `end` = :toDt, `detail` = CONCAT_WS('. ', NULLIF(`detail`, ''), :detail)
|
||||
WHERE displayId = :displayId
|
||||
AND `end` IS NULL
|
||||
AND eventTypeId = :eventTypeId",
|
||||
[
|
||||
'toDt' => $date ?? Carbon::now()->format('U'),
|
||||
'displayId' => $displayId,
|
||||
'eventTypeId' => $eventTypeId,
|
||||
'detail' => $detail,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record end date for specified display, event type and refId
|
||||
* @param int $displayId
|
||||
* @param int $eventTypeId
|
||||
* @param int $refId
|
||||
* @param int|null $date
|
||||
* @return void
|
||||
*/
|
||||
public function eventEndByReference(int $displayId, int $eventTypeId, int $refId, string $detail = null, ?int $date = null): void
|
||||
{
|
||||
$this->getLog()->debug(
|
||||
sprintf(
|
||||
'displayEvent : end display alert for refId %d, displayId %d and eventType %s',
|
||||
$refId,
|
||||
$displayId,
|
||||
$this->getEventNameFromId($eventTypeId),
|
||||
)
|
||||
);
|
||||
|
||||
// When updating the event end, concatenate the end message to the current message
|
||||
$this->getStore()->update(
|
||||
"UPDATE `displayevent` SET
|
||||
`end` = :toDt,
|
||||
`detail` = CONCAT_WS('. ', NULLIF(`detail`, ''), :detail)
|
||||
WHERE displayId = :displayId
|
||||
AND `end` IS NULL
|
||||
AND eventTypeId = :eventTypeId
|
||||
AND refId = :refId",
|
||||
[
|
||||
'toDt' => $date ?? Carbon::now()->format('U'),
|
||||
'displayId' => $displayId,
|
||||
'eventTypeId' => $eventTypeId,
|
||||
'refId' => $refId,
|
||||
'detail' => $detail,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match event type string from log to eventTypeId in database.
|
||||
* @param string $eventType
|
||||
* @return int
|
||||
*/
|
||||
public function getEventIdFromString(string $eventType): int
|
||||
{
|
||||
return match ($eventType) {
|
||||
'Display Up/down' => 1,
|
||||
'App Start' => 2,
|
||||
'Power Cycle' => 3,
|
||||
'Network Cycle' => 4,
|
||||
'TV Monitoring' => 5,
|
||||
'Player Fault' => 6,
|
||||
'Command' => 7,
|
||||
default => 8
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Match eventTypeId from database to string event name.
|
||||
* @param int $eventTypeId
|
||||
* @return string
|
||||
*/
|
||||
public function getEventNameFromId(int $eventTypeId): string
|
||||
{
|
||||
return match ($eventTypeId) {
|
||||
1 => __('Display Up/down'),
|
||||
2 => __('App Start'),
|
||||
3 => __('Power Cycle'),
|
||||
4 => __('Network Cycle'),
|
||||
5 => __('TV Monitoring'),
|
||||
6 => __('Player Fault'),
|
||||
7 => __('Command'),
|
||||
default => __('Other')
|
||||
};
|
||||
}
|
||||
}
|
||||
1115
lib/Entity/DisplayGroup.php
Normal file
1115
lib/Entity/DisplayGroup.php
Normal file
File diff suppressed because it is too large
Load Diff
611
lib/Entity/DisplayProfile.php
Normal file
611
lib/Entity/DisplayProfile.php
Normal file
@@ -0,0 +1,611 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Factory\CommandFactory;
|
||||
use Xibo\Factory\DisplayProfileFactory;
|
||||
use Xibo\Service\ConfigServiceInterface;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
|
||||
|
||||
/**
|
||||
* Class DisplayProfile
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class DisplayProfile implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of this Display Profile")
|
||||
* @var int
|
||||
*/
|
||||
public $displayProfileId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The name of this Display Profile")
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The player type that this Display Profile is for")
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The configuration options for this Profile")
|
||||
* @var string[]
|
||||
*/
|
||||
public $config;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating if this profile should be used as the Default for the client type")
|
||||
* @var int
|
||||
*/
|
||||
public $isDefault;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The userId of the User that owns this profile")
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The default configuration options for this Profile")
|
||||
* @var string[]
|
||||
*/
|
||||
public $configDefault;
|
||||
|
||||
/**
|
||||
* Commands associated with this profile.
|
||||
* @var Command[]
|
||||
*/
|
||||
public $commands = [];
|
||||
|
||||
public $isCustom;
|
||||
|
||||
/** @var string the client type */
|
||||
private $clientType;
|
||||
|
||||
/** @var array Combined configuration */
|
||||
private $configCombined = [];
|
||||
|
||||
/**
|
||||
* @var ConfigServiceInterface
|
||||
*/
|
||||
private $configService;
|
||||
|
||||
/**
|
||||
* @var CommandFactory
|
||||
*/
|
||||
private $commandFactory;
|
||||
/**
|
||||
* @var DisplayProfileFactory
|
||||
*/
|
||||
private $displayProfileFactory;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param ConfigServiceInterface $config
|
||||
* @param CommandFactory $commandFactory
|
||||
* @param DisplayProfileFactory $displayProfileFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $config, $commandFactory, $displayProfileFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
|
||||
$this->configService = $config;
|
||||
$this->commandFactory = $commandFactory;
|
||||
$this->displayProfileFactory = $displayProfileFactory;
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->displayProfileId = null;
|
||||
$this->commands = [];
|
||||
$this->isDefault = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->displayProfileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Setting
|
||||
* @param $setting
|
||||
* @param null $default
|
||||
* @param bool $fromDefault
|
||||
* @return mixed
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function getSetting($setting, $default = null, $fromDefault = false): mixed
|
||||
{
|
||||
$this->load();
|
||||
|
||||
$configs = ($fromDefault) ? $this->configDefault : $this->getProfileConfig();
|
||||
|
||||
foreach ($configs as $config) {
|
||||
if ($config['name'] == $setting || $config['name'] == ucfirst($setting)) {
|
||||
$default = $config['value'] ?? ($config['default'] ?? $default);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set setting
|
||||
* @param $setting
|
||||
* @param $value
|
||||
* @param boolean $ownConfig if provided will set the values on this object and not on the member config object
|
||||
* @param array|null $config
|
||||
* @return $this
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function setSetting($setting, $value, $ownConfig = true, &$config = null)
|
||||
{
|
||||
$this->load();
|
||||
|
||||
$found = false;
|
||||
|
||||
// Get the setting from default
|
||||
// Which object do we operate on.
|
||||
if ($ownConfig) {
|
||||
$config = $this->config;
|
||||
$default = $this->getSetting($setting, null, true);
|
||||
} else {
|
||||
// we are editing Display object, as such we want the $default to come from display profile assigned to our display
|
||||
$default = $this->getSetting($setting, null, false);
|
||||
}
|
||||
|
||||
// Check to see if we have this setting already
|
||||
for ($i = 0; $i < count($config); $i++) {
|
||||
if ($config[$i]['name'] == $setting || $config[$i]['name'] == ucfirst($setting)) {
|
||||
// We found the setting - is the value different to the default?
|
||||
if ($value !== $default) {
|
||||
$config[$i]['value'] = $value;
|
||||
$config[$i]['name'] = lcfirst($setting);
|
||||
} else {
|
||||
// the value is the same as the default - unset it
|
||||
$this->getLog()->debug('Setting [' . $setting . '] identical to the default, unsetting.');
|
||||
unset($config[$i]);
|
||||
$config = array_values($config);
|
||||
}
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found && $value !== $default) {
|
||||
$this->getLog()->debug('Setting [' . $setting . '] not yet in the profile config, and different to the default. ' . var_export($value, true) . ' --- ' . var_export($default, true));
|
||||
// The config option isn't in our array yet, so add it
|
||||
$config[] = [
|
||||
'name' => lcfirst($setting),
|
||||
'value' => $value
|
||||
];
|
||||
}
|
||||
|
||||
if ($ownConfig) {
|
||||
// Reset our object
|
||||
$this->config = $config;
|
||||
|
||||
// Reload our combined array
|
||||
$this->configCombined = $this->mergeConfigs($this->configDefault, $this->config);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two configs
|
||||
* @param $default
|
||||
* @param $override
|
||||
* @return array
|
||||
*/
|
||||
private function mergeConfigs($default, $override): array
|
||||
{
|
||||
foreach ($default as &$defaultItem) {
|
||||
for ($i = 0; $i < count($override); $i++) {
|
||||
if ($defaultItem['name'] == $override[$i]['name']) {
|
||||
// merge
|
||||
$defaultItem = array_merge($defaultItem, $override[$i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the remainder
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $clientType
|
||||
*/
|
||||
public function setClientType($clientType)
|
||||
{
|
||||
$this->clientType = $clientType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isCustom(): bool
|
||||
{
|
||||
return $this->isCustom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client type
|
||||
* @return string
|
||||
*/
|
||||
public function getClientType()
|
||||
{
|
||||
return (empty($this->clientType)) ? $this->type : $this->clientType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign Command
|
||||
* @param Command $command
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function assignCommand($command)
|
||||
{
|
||||
$this->load([]);
|
||||
|
||||
$assigned = false;
|
||||
|
||||
foreach ($this->commands as $alreadyAssigned) {
|
||||
/* @var Command $alreadyAssigned */
|
||||
if ($alreadyAssigned->getId() == $command->getId()) {
|
||||
$alreadyAssigned->commandString = $command->commandString;
|
||||
$alreadyAssigned->validationString = $command->validationString;
|
||||
$alreadyAssigned->createAlertOn = $command->createAlertOn;
|
||||
$assigned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$assigned) {
|
||||
$this->commands[] = $command;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unassign Command
|
||||
* @param Command $command
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function unassignCommand($command)
|
||||
{
|
||||
$this->load([]);
|
||||
|
||||
$this->commands = array_udiff($this->commands, [$command], function ($a, $b) {
|
||||
/**
|
||||
* @var Command $a
|
||||
* @var Command $b
|
||||
*/
|
||||
return $a->getId() - $b->getId();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Owner
|
||||
* @param int $ownerId
|
||||
*/
|
||||
public function setOwner($ownerId)
|
||||
{
|
||||
$this->userId = $ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load
|
||||
* @param array $options
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function load($options = []): void
|
||||
{
|
||||
$this->getLog()->debug('load: Loading display profile, type: ' . $this->clientType
|
||||
. ' id: ' . $this->displayProfileId);
|
||||
|
||||
$options = array_merge([
|
||||
'loadConfig' => true,
|
||||
'loadCommands' => true
|
||||
], $options);
|
||||
|
||||
if ($this->loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load in our default config from this class, based on the client type we are
|
||||
$this->configDefault = $this->displayProfileFactory->loadForType($this->getClientType());
|
||||
|
||||
// Get our combined config
|
||||
$this->configCombined = [];
|
||||
|
||||
if ($options['loadConfig']) {
|
||||
if (!is_array($this->config) && !empty($this->config)) {
|
||||
$this->config = json_decode($this->config, true);
|
||||
}
|
||||
|
||||
// handle cases when config is empty
|
||||
if (empty($this->config)) {
|
||||
$this->config = [];
|
||||
}
|
||||
|
||||
$this->getLog()->debug('Config loaded: ' . json_encode($this->config, JSON_PRETTY_PRINT));
|
||||
|
||||
// Populate our combined config accordingly
|
||||
$this->configCombined = $this->mergeConfigs($this->configDefault, $this->config);
|
||||
}
|
||||
|
||||
$this->getLog()->debug('Config Combined is: ' . json_encode($this->configCombined, JSON_PRETTY_PRINT));
|
||||
|
||||
// Load any commands
|
||||
if ($options['loadCommands']) {
|
||||
$this->commands = $this->commandFactory->getByDisplayProfileId($this->displayProfileId, $this->type);
|
||||
}
|
||||
|
||||
// We are loaded
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!v::stringType()->notEmpty()->validate($this->name))
|
||||
throw new InvalidArgumentException(__('Missing name'), 'name');
|
||||
|
||||
if (!v::stringType()->notEmpty()->validate($this->type))
|
||||
throw new InvalidArgumentException(__('Missing type'), 'type');
|
||||
|
||||
for ($j = 0; $j < count($this->config); $j++) {
|
||||
if ($this->config[$j]['name'] == 'MaxConcurrentDownloads' && $this->config[$j]['value'] <= 0 && $this->type = 'windows') {
|
||||
throw new InvalidArgumentException(__('Concurrent downloads must be a positive number'), 'MaxConcurrentDownloads');
|
||||
}
|
||||
|
||||
if ($this->config[$j]['name'] == 'maxRegionCount' && !v::intType()->min(0)->validate($this->config[$j]['value'])) {
|
||||
throw new InvalidArgumentException(__('Maximum Region Count must be a positive number'), 'maxRegionCount');
|
||||
}
|
||||
}
|
||||
// Check there is only 1 default (including this one)
|
||||
$sql = '
|
||||
SELECT COUNT(*) AS cnt
|
||||
FROM `displayprofile`
|
||||
WHERE `type` = :type
|
||||
AND isdefault = 1
|
||||
';
|
||||
|
||||
$params = ['type' => $this->type];
|
||||
|
||||
if ($this->displayProfileId != 0) {
|
||||
$sql .= ' AND displayprofileid <> :displayProfileId ';
|
||||
$params['displayProfileId'] = $this->displayProfileId;
|
||||
}
|
||||
|
||||
$count = $this->getStore()->select($sql, $params);
|
||||
|
||||
if ($count[0]['cnt'] + $this->isDefault > 1) {
|
||||
throw new InvalidArgumentException(__('Only 1 default per display type is allowed.'), 'isDefault');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
* @param bool $validate
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($validate = true)
|
||||
{
|
||||
if ($validate)
|
||||
$this->validate();
|
||||
|
||||
if ($this->displayProfileId == null || $this->displayProfileId == 0)
|
||||
$this->add();
|
||||
else
|
||||
$this->edit();
|
||||
|
||||
$this->manageAssignments();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->commands = [];
|
||||
$this->manageAssignments();
|
||||
|
||||
if ($this->getStore()->exists('SELECT displayId FROM display WHERE displayProfileId = :displayProfileId', ['displayProfileId' => $this->displayProfileId]) ) {
|
||||
throw new InvalidArgumentException(__('This Display Profile is currently assigned to one or more Displays'), 'displayProfileId');
|
||||
}
|
||||
|
||||
if ($this->isDefault === 1) {
|
||||
throw new InvalidArgumentException(__('Cannot delete default Display Profile.'), 'isDefault');
|
||||
}
|
||||
|
||||
$this->getStore()->update('DELETE FROM `displayprofile` WHERE displayprofileid = :displayProfileId', ['displayProfileId' => $this->displayProfileId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage Assignments
|
||||
*/
|
||||
private function manageAssignments()
|
||||
{
|
||||
$this->getLog()->debug('Managing Assignment for Display Profile: %d. %d commands.', $this->displayProfileId, count($this->commands));
|
||||
|
||||
// Link
|
||||
foreach ($this->commands as $command) {
|
||||
/* @var Command $command */
|
||||
$this->getStore()->update('
|
||||
INSERT INTO `lkcommanddisplayprofile` (
|
||||
`commandId`,
|
||||
`displayProfileId`,
|
||||
`commandString`,
|
||||
`validationString`,
|
||||
`createAlertOn`
|
||||
)
|
||||
VALUES (
|
||||
:commandId,
|
||||
:displayProfileId,
|
||||
:commandString,
|
||||
:validationString,
|
||||
:createAlertOn
|
||||
)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
commandString = :commandString2,
|
||||
validationString = :validationString2,
|
||||
createAlertOn = :createAlertOn2
|
||||
', [
|
||||
'commandId' => $command->commandId,
|
||||
'displayProfileId' => $this->displayProfileId,
|
||||
'commandString' => $command->commandString,
|
||||
'validationString' => $command->validationString,
|
||||
'createAlertOn' => $command->createAlertOn,
|
||||
'commandString2' => $command->commandString,
|
||||
'validationString2' => $command->validationString,
|
||||
'createAlertOn2' => $command->createAlertOn
|
||||
]);
|
||||
}
|
||||
|
||||
// Unlink
|
||||
$params = ['displayProfileId' => $this->displayProfileId];
|
||||
|
||||
$sql = 'DELETE FROM `lkcommanddisplayprofile`
|
||||
WHERE `displayProfileId` = :displayProfileId AND `commandId` NOT IN (0';
|
||||
|
||||
$i = 0;
|
||||
foreach ($this->commands as $command) {
|
||||
/* @var Command $command */
|
||||
$i++;
|
||||
$sql .= ',:commandId' . $i;
|
||||
$params['commandId' . $i] = $command->commandId;
|
||||
}
|
||||
|
||||
$sql .= ')';
|
||||
|
||||
$this->getStore()->update($sql, $params);
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->displayProfileId = $this->getStore()->insert('
|
||||
INSERT INTO `displayprofile` (`name`, type, config, isdefault, userid, isCustom)
|
||||
VALUES (:name, :type, :config, :isDefault, :userId, :isCustom)
|
||||
', [
|
||||
'name' => $this->name,
|
||||
'type' => $this->type,
|
||||
'config' => ($this->config == '') ? '[]' : json_encode($this->config),
|
||||
'isDefault' => $this->isDefault,
|
||||
'userId' => $this->userId,
|
||||
'isCustom' => $this->isCustom ?? 0
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `displayprofile`
|
||||
SET `name` = :name, type = :type, config = :config, isdefault = :isDefault, isCustom = :isCustom
|
||||
WHERE displayprofileid = :displayProfileId', [
|
||||
'name' => $this->name,
|
||||
'type' => $this->type,
|
||||
'config' => ($this->config == '') ? '[]' : json_encode($this->config),
|
||||
'isDefault' => $this->isDefault,
|
||||
'isCustom' => $this->isCustom ?? 0,
|
||||
'displayProfileId' => $this->displayProfileId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getProfileConfig(): array
|
||||
{
|
||||
return $this->configCombined;
|
||||
}
|
||||
|
||||
public function getCustomEditTemplate()
|
||||
{
|
||||
if ($this->isCustom()) {
|
||||
return $this->displayProfileFactory->getCustomEditTemplate($this->getClientType());
|
||||
} else {
|
||||
$this->getLog()->error(
|
||||
'Attempting to get Custom Edit template for Display Profile ' .
|
||||
$this->getClientType() . ' that is not custom'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function handleCustomFields($sanitizedParams, $config = null, $display = null)
|
||||
{
|
||||
return $this->displayProfileFactory->handleCustomFields($this, $sanitizedParams, $config, $display);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this display profile has elevated log level?
|
||||
* @return bool
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function isElevatedLogging(): bool
|
||||
{
|
||||
$elevatedUntil = $this->getSetting('elevateLogsUntil', 0);
|
||||
|
||||
$this->getLog()->debug(sprintf(
|
||||
'Testing whether this display profile has elevated log level. %d vs %d.',
|
||||
$elevatedUntil,
|
||||
Carbon::now()->format('U')
|
||||
));
|
||||
|
||||
return (!empty($elevatedUntil) && $elevatedUntil >= Carbon::now()->format('U'));
|
||||
}
|
||||
}
|
||||
61
lib/Entity/DisplayType.php
Normal file
61
lib/Entity/DisplayType.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class DisplayType
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class DisplayType implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID for this DisplayType")
|
||||
* @var int
|
||||
*/
|
||||
public $displayTypeId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Name for this DisplayType")
|
||||
* @var string
|
||||
*/
|
||||
public $displayType;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
}
|
||||
437
lib/Entity/EntityTrait.php
Normal file
437
lib/Entity/EntityTrait.php
Normal file
@@ -0,0 +1,437 @@
|
||||
<?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\Entity;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\ObjectVars;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class EntityTrait
|
||||
* used by all entities
|
||||
* @package Xibo\Entity
|
||||
*/
|
||||
trait EntityTrait
|
||||
{
|
||||
private $hash = null;
|
||||
private $loaded = false;
|
||||
private $permissionsClass = null;
|
||||
private $canChangeOwner = true;
|
||||
|
||||
public $buttons = [];
|
||||
private $jsonExclude = ['buttons', 'jsonExclude', 'originalValues', 'jsonInclude', 'datesToFormat'];
|
||||
|
||||
/** @var array Original values hydrated */
|
||||
protected $originalValues = [];
|
||||
|
||||
/** @var array Unmatched properties */
|
||||
private $unmatchedProperties = [];
|
||||
|
||||
/**
|
||||
* @var StorageServiceInterface
|
||||
*/
|
||||
private $store;
|
||||
|
||||
/**
|
||||
* @var LogServiceInterface
|
||||
*/
|
||||
private $log;
|
||||
|
||||
/** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */
|
||||
private $dispatcher;
|
||||
|
||||
/**
|
||||
* Set common dependencies.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
* @return $this
|
||||
*/
|
||||
protected function setCommonDependencies($store, $log, $dispatcher)
|
||||
{
|
||||
$this->store = $store;
|
||||
$this->log = $log;
|
||||
$this->dispatcher = $dispatcher;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Store
|
||||
* @return StorageServiceInterface
|
||||
*/
|
||||
protected function getStore()
|
||||
{
|
||||
return $this->store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Log
|
||||
* @return LogServiceInterface
|
||||
*/
|
||||
protected function getLog()
|
||||
{
|
||||
return $this->log;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Symfony\Component\EventDispatcher\EventDispatcherInterface
|
||||
*/
|
||||
public function getDispatcher(): EventDispatcherInterface
|
||||
{
|
||||
if ($this->dispatcher === null) {
|
||||
$this->getLog()->error('getDispatcher: [entity] No dispatcher found, returning an empty one');
|
||||
$this->dispatcher = new EventDispatcher();
|
||||
}
|
||||
|
||||
return $this->dispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrate an entity with properties
|
||||
*
|
||||
* @param array $properties
|
||||
* @param array $options
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function hydrate(array $properties, $options = [])
|
||||
{
|
||||
$intProperties = (array_key_exists('intProperties', $options)) ? $options['intProperties'] : [];
|
||||
$doubleProperties = (array_key_exists('doubleProperties', $options)) ? $options['doubleProperties'] : [];
|
||||
$stringProperties = (array_key_exists('stringProperties', $options)) ? $options['stringProperties'] : [];
|
||||
$htmlStringProperties = (array_key_exists('htmlStringProperties', $options))
|
||||
? $options['htmlStringProperties'] : [];
|
||||
|
||||
foreach ($properties as $prop => $val) {
|
||||
// Parse the property
|
||||
if ((stripos(strrev($prop), 'dI') === 0 || in_array($prop, $intProperties))
|
||||
&& !in_array($prop, $stringProperties)
|
||||
) {
|
||||
$val = intval($val);
|
||||
} else if (in_array($prop, $doubleProperties)) {
|
||||
$val = doubleval($val);
|
||||
} else if (in_array($prop, $stringProperties) && $val !== null) {
|
||||
$val = htmlspecialchars($val);
|
||||
} else if (in_array($prop, $htmlStringProperties)) {
|
||||
$val = htmlentities($val);
|
||||
}
|
||||
|
||||
if (property_exists($this, $prop)) {
|
||||
$this->{$prop} = $val;
|
||||
$this->originalValues[$prop] = $val;
|
||||
} else {
|
||||
$this->unmatchedProperties[$prop] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset originals to current values
|
||||
*/
|
||||
public function setOriginals()
|
||||
{
|
||||
foreach ($this->jsonSerialize() as $key => $value) {
|
||||
$this->originalValues[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original value of a property
|
||||
* @param string $property
|
||||
* @return null|mixed
|
||||
*/
|
||||
public function getOriginalValue($property)
|
||||
{
|
||||
return (isset($this->originalValues[$property])) ? $this->originalValues[$property] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $property
|
||||
* @param mixed $value
|
||||
* @return $this
|
||||
*/
|
||||
public function setOriginalValue(string $property, $value)
|
||||
{
|
||||
$this->originalValues[$property] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the provided property been changed from its original value
|
||||
* @param string $property
|
||||
* @return bool
|
||||
*/
|
||||
public function hasPropertyChanged($property)
|
||||
{
|
||||
if (!property_exists($this, $property))
|
||||
return true;
|
||||
|
||||
return $this->getOriginalValue($property) != $this->{$property};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $property
|
||||
* @return bool
|
||||
*/
|
||||
public function propertyOriginallyExisted($property)
|
||||
{
|
||||
return array_key_exists($property, $this->originalValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all changed properties for this entity
|
||||
* @param bool $jsonEncodeArrays
|
||||
* @return array
|
||||
*/
|
||||
public function getChangedProperties($jsonEncodeArrays = false)
|
||||
{
|
||||
$changedProperties = [];
|
||||
|
||||
foreach ($this->jsonSerialize() as $key => $value) {
|
||||
if (!is_array($value) && !is_object($value) && $this->propertyOriginallyExisted($key) && $this->hasPropertyChanged($key)) {
|
||||
if (isset($this->datesToFormat) && in_array($key, $this->datesToFormat)) {
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_array($value) && $jsonEncodeArrays && $this->propertyOriginallyExisted($key) && $this->hasPropertyChanged($key)) {
|
||||
$changedProperties[$key] = json_encode($this->getOriginalValue($key)) . ' > ' . json_encode($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $changedProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unmatched property
|
||||
* @param string $property
|
||||
* @param mixed $default The default value to return if the unmatched property doesn't exist.
|
||||
* @return null|mixed
|
||||
*/
|
||||
public function getUnmatchedProperty(string $property, mixed $default = null): mixed
|
||||
{
|
||||
return $this->unmatchedProperties[$property] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $property
|
||||
* @param mixed $value
|
||||
* @return $this
|
||||
*/
|
||||
public function setUnmatchedProperty(string $property, mixed $value)
|
||||
{
|
||||
$this->unmatchedProperties[$property] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Json Serialize
|
||||
*/
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
$properties = ObjectVars::getObjectVars($this);
|
||||
$json = [];
|
||||
foreach ($properties as $key => $value) {
|
||||
if (!in_array($key, $this->jsonExclude)) {
|
||||
$json[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// Output unmatched properties too?
|
||||
if (!in_array('unmatchedProperties', $this->jsonExclude)) {
|
||||
foreach ($this->unmatchedProperties as $key => $value) {
|
||||
if (!in_array($key, $this->jsonExclude)) {
|
||||
$json[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
public function jsonForAudit(): array
|
||||
{
|
||||
$properties = ObjectVars::getObjectVars($this);
|
||||
$json = [];
|
||||
foreach ($properties as $key => $value) {
|
||||
if (in_array($key, $this->jsonInclude)) {
|
||||
$json[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
|
||||
/**
|
||||
* To Array
|
||||
* @param bool $jsonEncodeArrays
|
||||
* @return array
|
||||
*/
|
||||
public function toArray($jsonEncodeArrays = false)
|
||||
{
|
||||
$objectAsJson = $this->jsonSerialize();
|
||||
|
||||
foreach ($objectAsJson as $key => $value) {
|
||||
if (isset($this->datesToFormat) && in_array($key, $this->datesToFormat)) {
|
||||
$objectAsJson[$key] = Carbon::createFromTimestamp($value)->format(DateFormatHelper::getSystemFormat());
|
||||
}
|
||||
|
||||
if ($jsonEncodeArrays) {
|
||||
if (is_array($value)) {
|
||||
$objectAsJson[$key] = json_encode($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $objectAsJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a property to the excluded list
|
||||
* @param string $property
|
||||
*/
|
||||
public function excludeProperty($property)
|
||||
{
|
||||
$this->jsonExclude[] = $property;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a property from the excluded list
|
||||
* @param string $property
|
||||
*/
|
||||
public function includeProperty($property)
|
||||
{
|
||||
$this->jsonExclude = array_diff($this->jsonExclude, [$property]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Permissions Class
|
||||
* @return string
|
||||
*/
|
||||
public function permissionsClass()
|
||||
{
|
||||
return ($this->permissionsClass == null) ? get_class($this) : $this->permissionsClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Permissions Class
|
||||
* @param string $class
|
||||
*/
|
||||
protected function setPermissionsClass($class)
|
||||
{
|
||||
$this->permissionsClass = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the owner change?
|
||||
* @return bool
|
||||
*/
|
||||
public function canChangeOwner()
|
||||
{
|
||||
return $this->canChangeOwner && method_exists($this, 'setOwner');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bool Can the owner be changed?
|
||||
*/
|
||||
protected function setCanChangeOwner($bool)
|
||||
{
|
||||
$this->canChangeOwner = $bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $entityId
|
||||
* @param $message
|
||||
* @param null $changedProperties
|
||||
* @param bool $jsonEncodeArrays
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
protected function audit($entityId, $message, $changedProperties = null, $jsonEncodeArrays = false)
|
||||
{
|
||||
$class = substr(get_class($this), strrpos(get_class($this), '\\') + 1);
|
||||
|
||||
if ($changedProperties === null) {
|
||||
// No properties provided, so we should work them out
|
||||
// If we have originals, then get changed, otherwise get the current object state
|
||||
$changedProperties = (count($this->originalValues) <= 0)
|
||||
? $this->toArray($jsonEncodeArrays)
|
||||
: $this->getChangedProperties($jsonEncodeArrays);
|
||||
} else if ($changedProperties !== false && count($changedProperties) <= 0) {
|
||||
// Only audit if properties have been provided
|
||||
return;
|
||||
}
|
||||
|
||||
$this->getLog()->audit($class, $entityId, $message, $changedProperties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two arrays, both keys and values.
|
||||
*
|
||||
* @param $array1
|
||||
* @param $array2
|
||||
* @param bool $compareValues
|
||||
* @return array
|
||||
*/
|
||||
public function compareMultidimensionalArrays($array1, $array2, $compareValues = true)
|
||||
{
|
||||
$result = [];
|
||||
|
||||
// go through arrays, compare keys and values
|
||||
// the compareValues flag is there for tag unlink - we're interested only in array keys
|
||||
foreach ($array1 as $key => $value) {
|
||||
|
||||
if (!is_array($array2) || !array_key_exists($key, $array2)) {
|
||||
$result[$key] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($value != $array2[$key] && $compareValues) {
|
||||
$result[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function updateFolders($table)
|
||||
{
|
||||
$this->getStore()->update('UPDATE `'. $table .'` SET permissionsFolderId = :permissionsFolderId, folderId = :folderId WHERE folderId = :oldFolderId', [
|
||||
'permissionsFolderId' => $this->permissionsFolderId,
|
||||
'folderId' => $this->folderId,
|
||||
'oldFolderId' => $this->getOriginalValue('folderId')
|
||||
]);
|
||||
}
|
||||
}
|
||||
471
lib/Entity/Folder.php
Normal file
471
lib/Entity/Folder.php
Normal file
@@ -0,0 +1,471 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Factory\FolderFactory;
|
||||
use Xibo\Factory\PermissionFactory;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class Folder
|
||||
* @package Xibo\Entity
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Folder implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of this Folder")
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The type of folder (home or root)")
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The name of this Folder")
|
||||
* @var string
|
||||
*/
|
||||
public $text;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The folderId of the parent of this Folder")
|
||||
* @var int
|
||||
*/
|
||||
public $parentId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag indicating whether this is root Folder")
|
||||
* @var int
|
||||
*/
|
||||
public $isRoot;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="An array of children folderIds")
|
||||
* @var string
|
||||
*/
|
||||
public $children;
|
||||
|
||||
public $permissionsFolderId;
|
||||
|
||||
/** @var FolderFactory */
|
||||
private $folderFactory;
|
||||
|
||||
/**
|
||||
* @var PermissionFactory
|
||||
*/
|
||||
private $permissionFactory;
|
||||
|
||||
private $permissions = [];
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param FolderFactory $folderFactory
|
||||
* @param PermissionFactory $permissionFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $folderFactory, $permissionFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
$this->setPermissionsClass('Xibo\Entity\Folder');
|
||||
$this->folderFactory = $folderFactory;
|
||||
$this->permissionFactory = $permissionFactory;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getPermissionFolderId()
|
||||
{
|
||||
return $this->permissionsFolderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* When you set ACL on a folder the permissionsFolderId on the folder record is set to null
|
||||
* any objects inside this folder get the permissionsFolderId set to this folderId
|
||||
* @return int
|
||||
*/
|
||||
public function getPermissionFolderIdOrThis(): int
|
||||
{
|
||||
return $this->permissionsFolderId == null ? $this->id : $this->permissionsFolderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Owner Id
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function getParentId()
|
||||
{
|
||||
return $this->parentId;
|
||||
}
|
||||
|
||||
public function isRoot(): bool
|
||||
{
|
||||
return $this->isRoot === 1;
|
||||
}
|
||||
|
||||
public function getChildren()
|
||||
{
|
||||
return explode(',', $this->children);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!v::stringType()->notEmpty()->length(1, 254)->validate($this->text)) {
|
||||
throw new InvalidArgumentException(__('Folder needs to have a name, between 1 and 254 characters.'), 'folderName');
|
||||
}
|
||||
|
||||
if (empty($this->parentId)) {
|
||||
throw new InvalidArgumentException(__('Folder needs a specified parent Folder id'), 'parentId');
|
||||
}
|
||||
}
|
||||
|
||||
public function load()
|
||||
{
|
||||
if ($this->loaded || $this->id == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Permissions
|
||||
$this->permissions = $this->permissionFactory->getByObjectId(get_class($this), $this->id);
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $validate
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($validate = true)
|
||||
{
|
||||
if ($validate) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($this->id == null || $this->id == 0) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
foreach ($this->permissions as $permission) {
|
||||
/* @var Permission $permission */
|
||||
$permission->delete();
|
||||
}
|
||||
|
||||
$this->manageChildren('delete');
|
||||
|
||||
$this->getStore()->update('DELETE FROM `folder` WHERE folderId = :folderId', [
|
||||
'folderId' => $this->id
|
||||
]);
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$parent = $this->folderFactory->getById($this->parentId);
|
||||
|
||||
$this->id = $this->getStore()->insert('INSERT INTO `folder` (folderName, parentId, isRoot, permissionsFolderId) VALUES (:folderName, :parentId, :isRoot, :permissionsFolderId)',
|
||||
[
|
||||
'folderName' => $this->text,
|
||||
'parentId' => $this->parentId,
|
||||
'isRoot' => 0,
|
||||
'permissionsFolderId' => ($parent->permissionsFolderId == null) ? $this->parentId : $parent->permissionsFolderId
|
||||
]);
|
||||
|
||||
$this->manageChildren('add');
|
||||
}
|
||||
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('UPDATE `folder` SET folderName = :folderName, parentId = :parentId WHERE folderId = :folderId',
|
||||
[
|
||||
'folderId' => $this->id,
|
||||
'folderName' => $this->text,
|
||||
'parentId' => $this->parentId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages folder tree structure
|
||||
*
|
||||
* If mode delete is passed then it will remove selected folder and all its children down the tree
|
||||
* Then update children property on parent accordingly
|
||||
*
|
||||
* On add mode we just add this folder id to parent children property
|
||||
*
|
||||
* @param $mode
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
private function manageChildren($mode)
|
||||
{
|
||||
$parent = $this->folderFactory->getById($this->parentId);
|
||||
$parentChildren = array_filter(explode(',', $parent->children ?? ''));
|
||||
$children = array_filter(explode(',', $this->children ?? ''));
|
||||
|
||||
if ($mode === 'delete') {
|
||||
// remove this folder from children of the parent
|
||||
foreach ($parentChildren as $index => $child) {
|
||||
if ((int)$child === (int)$this->id) {
|
||||
unset($parentChildren[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
// remove this folder children
|
||||
foreach ($children as $child) {
|
||||
$childObject = $this->folderFactory->getById($child);
|
||||
$childObject->manageChildren('delete');
|
||||
$this->getStore()->update('DELETE FROM `folder` WHERE folderId = :folderId', [
|
||||
'folderId' => $childObject->id
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$parentChildren[] = $this->id;
|
||||
}
|
||||
|
||||
$updatedChildren = implode(',', array_filter($parentChildren));
|
||||
|
||||
$this->getStore()->update('UPDATE `folder` SET children = :children WHERE folderId = :folderId', [
|
||||
'folderId' => $this->parentId,
|
||||
'children' => $updatedChildren
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages folder permissions
|
||||
*
|
||||
* When permissions are added on folder, this starts new ACL from that folder and is cascaded down to all folders under this folder
|
||||
* permissionsFolderId is also updated on all relevant objects that are in this folder or under this folder in folder tree structure
|
||||
*
|
||||
* When permissions are removed from a folder, this sets the permissionsFolderId to parent folderId (or parent permissionsFolderId)
|
||||
* same is cascaded down the folder tree and all relevant objects
|
||||
*
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function managePermissions()
|
||||
{
|
||||
// this function happens after permissions are inserted into permission table
|
||||
// with that we can look up if there are any permissions for edited folder and act accordingly.
|
||||
$permissionExists = $this->getStore()->exists('SELECT permissionId FROM permission INNER JOIN permissionentity ON permission.entityId = permissionentity.entityId WHERE objectId = :folderId AND permissionentity.entity = :folderEntity', [
|
||||
'folderId' => $this->id,
|
||||
'folderEntity' => 'Xibo\Entity\Folder'
|
||||
]);
|
||||
|
||||
if ($permissionExists) {
|
||||
// if we added/edited permission on this folder, then new ACL starts here, cascade this folderId as permissionFolderId to all children
|
||||
$this->getStore()->update('UPDATE `folder` SET permissionsFolderId = NULL WHERE folderId = :folderId', [
|
||||
'folderId' => $this->id
|
||||
]);
|
||||
$permissionFolderId = $this->id;
|
||||
} else {
|
||||
// if there are no permissions for this folder, basically reset the permissions on this folder and its children
|
||||
if ($this->id === 1 && $this->isRoot()) {
|
||||
$permissionFolderId = 1;
|
||||
} else {
|
||||
$parent = $this->folderFactory->getById($this->parentId);
|
||||
$permissionFolderId = ($parent->permissionsFolderId == null) ? $parent->id : $parent->permissionsFolderId;
|
||||
}
|
||||
|
||||
$this->getStore()->update('UPDATE `folder` SET permissionsFolderId = :permissionsFolderId WHERE folderId = :folderId', [
|
||||
'folderId' => $this->id,
|
||||
'permissionsFolderId' => $permissionFolderId
|
||||
]);
|
||||
}
|
||||
|
||||
$this->updateChildObjects($permissionFolderId, $this->id);
|
||||
|
||||
|
||||
$this->manageChildPermissions($permissionFolderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper recursive function to make sure all folders under the edited parent folder have correct permissionsFolderId set on them
|
||||
* along with all relevant objects in those folders.
|
||||
*
|
||||
*
|
||||
* @param $permissionFolderId
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
private function manageChildPermissions($permissionFolderId)
|
||||
{
|
||||
$children = array_filter(explode(',', $this->children ?? ''));
|
||||
|
||||
foreach ($children as $child) {
|
||||
|
||||
$this->updateChildObjects($permissionFolderId, $child);
|
||||
|
||||
$childObject = $this->folderFactory->getById($child);
|
||||
$childObject->manageChildPermissions($permissionFolderId);
|
||||
}
|
||||
}
|
||||
|
||||
private function updateChildObjects($permissionFolderId, $folderId)
|
||||
{
|
||||
$this->getStore()->update('UPDATE `folder` SET permissionsFolderId = :permissionsFolderId WHERE parentId = :folderId', [
|
||||
'permissionsFolderId' => $permissionFolderId,
|
||||
'folderId' => $folderId
|
||||
]);
|
||||
|
||||
$this->getStore()->update('UPDATE `media` SET permissionsFolderId = :permissionsFolderId WHERE folderId = :folderId', [
|
||||
'permissionsFolderId' => $permissionFolderId,
|
||||
'folderId' => $folderId
|
||||
]);
|
||||
|
||||
$this->getStore()->update('UPDATE `campaign` SET permissionsFolderId = :permissionsFolderId WHERE folderId = :folderId', [
|
||||
'permissionsFolderId' => $permissionFolderId,
|
||||
'folderId' => $folderId
|
||||
]);
|
||||
|
||||
$this->getStore()->update('UPDATE `displaygroup` SET permissionsFolderId = :permissionsFolderId WHERE folderId = :folderId', [
|
||||
'permissionsFolderId' => $permissionFolderId,
|
||||
'folderId' => $folderId
|
||||
]);
|
||||
|
||||
$this->getStore()->update('UPDATE `dataset` SET permissionsFolderId = :permissionsFolderId WHERE folderId = :folderId', [
|
||||
'permissionsFolderId' => $permissionFolderId,
|
||||
'folderId' => $folderId
|
||||
]);
|
||||
|
||||
$this->getStore()->update('UPDATE `playlist` SET permissionsFolderId = :permissionsFolderId WHERE folderId = :folderId', [
|
||||
'permissionsFolderId' => $permissionFolderId,
|
||||
'folderId' => $folderId
|
||||
]);
|
||||
|
||||
$this->getStore()->update('UPDATE `menu_board` SET permissionsFolderId = :permissionsFolderId WHERE folderId = :folderId', [
|
||||
'permissionsFolderId' => $permissionFolderId,
|
||||
'folderId' => $folderId
|
||||
]);
|
||||
|
||||
$this->getStore()->update('UPDATE `syncgroup` SET permissionsFolderId = :permissionsFolderId WHERE folderId = :folderId', [
|
||||
'permissionsFolderId' => $permissionFolderId,
|
||||
'folderId' => $folderId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update old parent, new parent records with adjusted children
|
||||
* Update current folders records with new parent, permissionsFolderId
|
||||
* Recursively go through the current folder's children folder and objects and adjust permissionsFolderId if needed.
|
||||
* @param int $oldParentFolder
|
||||
* @param int $newParentFolder
|
||||
*/
|
||||
public function updateFoldersAfterMove(int $oldParentFolderId, int $newParentFolderId)
|
||||
{
|
||||
$oldParentFolder = $this->folderFactory->getById($oldParentFolderId, 0);
|
||||
$newParentFolder = $this->folderFactory->getById($newParentFolderId, 0);
|
||||
|
||||
// new parent folder that adopted this folder, adjust children
|
||||
$newParentChildren = array_filter(explode(',', $newParentFolder->children));
|
||||
$newParentChildren[] = $this->id;
|
||||
$newParentUpdatedChildren = implode(',', array_filter($newParentChildren));
|
||||
$this->getStore()->update('UPDATE `folder` SET children = :children WHERE folderId = :folderId', [
|
||||
'folderId' => $newParentFolder->id,
|
||||
'children' => $newParentUpdatedChildren
|
||||
]);
|
||||
|
||||
// old parent that gave this folder for adoption, adjust children
|
||||
$oldParentChildren = array_filter(explode(',', $oldParentFolder->children));
|
||||
foreach ($oldParentChildren as $index => $child) {
|
||||
if ((int)$child === $this->id) {
|
||||
unset($oldParentChildren[$index]);
|
||||
}
|
||||
}
|
||||
|
||||
$oldParentUpdatedChildren = implode(',', array_filter($oldParentChildren));
|
||||
|
||||
$this->getStore()->update('UPDATE `folder` SET children = :children WHERE folderId = :folderId', [
|
||||
'folderId' => $oldParentFolder->id,
|
||||
'children' => $oldParentUpdatedChildren
|
||||
]);
|
||||
|
||||
// if we had permissions set on this folder, then permissionsFolderId stays as it was
|
||||
if ($this->getPermissionFolderId() !== null) {
|
||||
$this->permissionsFolderId = $newParentFolder->getPermissionFolderIdOrThis();
|
||||
$this->manageChildPermissions($this->permissionsFolderId);
|
||||
}
|
||||
|
||||
$this->getStore()->update('UPDATE `folder` SET parentId = :parentId, permissionsFolderId = :permissionsFolderId WHERE folderId = :folderId', [
|
||||
'parentId' => $newParentFolder->id,
|
||||
'permissionsFolderId' => $this->permissionsFolderId,
|
||||
'folderId' => $this->id
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* We do not allow moving a parent Folder inside of one of its sub-folders
|
||||
* If that's what was requested, throw an error
|
||||
* @param int $newParentFolderId
|
||||
* @return bool
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function isTheSameBranch(int $newParentFolderId): bool
|
||||
{
|
||||
$children = array_filter(explode(',', $this->children ?? ''));
|
||||
$found = false;
|
||||
|
||||
foreach ($children as $child) {
|
||||
if ((int)$child === $newParentFolderId) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
$childObject = $this->folderFactory->getById($child);
|
||||
$childObject->isTheSameBranch($newParentFolderId);
|
||||
}
|
||||
|
||||
return $found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this folder is used as Home Folder for any existing Users
|
||||
* @return bool
|
||||
*/
|
||||
public function isHome(): bool
|
||||
{
|
||||
$userIds = $this->getStore()->select('SELECT userId FROM `user` WHERE `user`.homeFolderId = :folderId', [
|
||||
'folderId' => $this->id
|
||||
]);
|
||||
|
||||
return count($userIds) > 0;
|
||||
}
|
||||
}
|
||||
208
lib/Entity/Font.php
Normal file
208
lib/Entity/Font.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Service\ConfigServiceInterface;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Factory\FontFactory;
|
||||
use Xibo\Support\Exception\DuplicateEntityException;
|
||||
|
||||
/**
|
||||
* Class Font
|
||||
* @package Xibo\Entity
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Font
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Font ID")
|
||||
* @var int
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Font created date")
|
||||
* @var string
|
||||
*/
|
||||
public $createdAt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Font modified date")
|
||||
* @var string
|
||||
*/
|
||||
public $modifiedAt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The name of the user that modified this font last")
|
||||
* @var string
|
||||
*/
|
||||
public $modifiedBy;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Font name")
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Font file name")
|
||||
* @var string
|
||||
*/
|
||||
public $fileName;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Font family name")
|
||||
* @var string
|
||||
*/
|
||||
public $familyName;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Font file size in bytes")
|
||||
* @var int
|
||||
*/
|
||||
public $size;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A MD5 checksum of the stored font file")
|
||||
* @var string
|
||||
*/
|
||||
public $md5;
|
||||
|
||||
/** @var ConfigServiceInterface */
|
||||
private $config;
|
||||
|
||||
/** @var FontFactory */
|
||||
private $fontFactory;
|
||||
|
||||
/**
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $config, $fontFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
$this->config = $config;
|
||||
$this->fontFactory = $fontFactory;
|
||||
}
|
||||
|
||||
public function getFilePath()
|
||||
{
|
||||
return $this->config->getSetting('LIBRARY_LOCATION') . 'fonts/' . $this->fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DuplicateEntityException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge($options, ['validate' => true]);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($this->id === null || $this->id === 0) {
|
||||
$this->add();
|
||||
|
||||
$this->audit($this->id, 'Added font', [
|
||||
'mediaId' => $this->id,
|
||||
'name' => $this->name,
|
||||
'fileName' => $this->fileName,
|
||||
]);
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->id = $this->getStore()->insert('
|
||||
INSERT INTO `fonts` (`createdAt`, `modifiedAt`, modifiedBy, name, fileName, familyName, size, md5)
|
||||
VALUES (:createdAt, :modifiedAt, :modifiedBy, :name, :fileName, :familyName, :size, :md5)
|
||||
', [
|
||||
'createdAt' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
'modifiedAt' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
'modifiedBy' => $this->modifiedBy,
|
||||
'name' => $this->name,
|
||||
'fileName' => $this->fileName,
|
||||
'familyName' => $this->familyName,
|
||||
'size' => $this->size,
|
||||
'md5' => $this->md5
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('UPDATE `fonts` SET modifiedAt = :modifiedAt, modifiedBy = :modifiedBy, name = :name WHERE id = :id', [
|
||||
'modifiedAt' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
'modifiedBy' => $this->modifiedBy,
|
||||
'name' => $this->name,
|
||||
'id' => $this->id
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
// delete record
|
||||
$this->getStore()->update('DELETE FROM `fonts` WHERE id = :id', [
|
||||
'id' => $this->id
|
||||
]);
|
||||
|
||||
// delete file
|
||||
$libraryLocation = $this->config->getSetting('LIBRARY_LOCATION');
|
||||
|
||||
if (file_exists($libraryLocation . 'fonts/' . $this->fileName)) {
|
||||
unlink($libraryLocation . 'fonts/' . $this->fileName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures no duplicate fonts are created
|
||||
*
|
||||
* @throws DuplicateEntityException
|
||||
*/
|
||||
private function validate(): void
|
||||
{
|
||||
// Prevents uploading the same file under a different name
|
||||
if ($this->fontFactory->query(null, ['md5' => $this->md5])) {
|
||||
throw new DuplicateEntityException(__('This font file already exists in the library.'));
|
||||
}
|
||||
|
||||
// Keeps unique file storage
|
||||
if ($this->fontFactory->query(null, ['fileName' => $this->fileName])) {
|
||||
throw new DuplicateEntityException(__('Similar font filename already exists in the library.'));
|
||||
}
|
||||
|
||||
// Prevents multiple fonts with same name in UI
|
||||
if ($this->fontFactory->query(null, ['name' => $this->name])) {
|
||||
throw new DuplicateEntityException(__('Similar font name already exists in the library.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
55
lib/Entity/HelpLink.php
Normal file
55
lib/Entity/HelpLink.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?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\Entity;
|
||||
|
||||
/**
|
||||
* A simple help link, used by the help service.
|
||||
*/
|
||||
class HelpLink implements \JsonSerializable
|
||||
{
|
||||
public $title;
|
||||
public $summary;
|
||||
public $url;
|
||||
public $isAllowWhiteLabel;
|
||||
|
||||
/**
|
||||
* @param $array
|
||||
*/
|
||||
public function __construct($array)
|
||||
{
|
||||
$this->title = $array['title'] ?? '';
|
||||
$this->summary = $array['summary'] ?? '';
|
||||
$this->url = $array['url'] ?? '';
|
||||
$this->isAllowWhiteLabel = $array['isAllowWhiteLabel'] ?? true;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'title' => $this->title,
|
||||
'summary' => $this->summary,
|
||||
'url' => $this->url,
|
||||
'isAllowWhiteLabel' => $this->isAllowWhiteLabel,
|
||||
];
|
||||
}
|
||||
}
|
||||
52
lib/Entity/Homepage.php
Normal file
52
lib/Entity/Homepage.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2020 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
/**
|
||||
* Class Homepage
|
||||
* @package Xibo\Entity
|
||||
*/
|
||||
class Homepage implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public $homepage;
|
||||
public $feature;
|
||||
public $title;
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* Homepage constructor.
|
||||
* @param $homepage
|
||||
* @param $feature
|
||||
* @param $title
|
||||
* @param $description
|
||||
*/
|
||||
public function __construct($homepage, $feature, $title, $description)
|
||||
{
|
||||
$this->homepage = $homepage;
|
||||
$this->feature = $feature;
|
||||
$this->title = $title;
|
||||
$this->description = $description;
|
||||
}
|
||||
}
|
||||
3132
lib/Entity/Layout.php
Normal file
3132
lib/Entity/Layout.php
Normal file
File diff suppressed because it is too large
Load Diff
70
lib/Entity/LayoutOnCampaign.php
Normal file
70
lib/Entity/LayoutOnCampaign.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?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\Entity;
|
||||
|
||||
/**
|
||||
* @SWG\Definition("Layout linked to a Campaign")
|
||||
*/
|
||||
class LayoutOnCampaign implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public $lkCampaignLayoutId;
|
||||
public $campaignId;
|
||||
public $layoutId;
|
||||
public $displayOrder;
|
||||
|
||||
public $dayPartId;
|
||||
public $daysOfWeek;
|
||||
public $geoFence;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Layout name (readonly)")
|
||||
* @var string
|
||||
*/
|
||||
public $layout;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Layout campaignId (readonly)")
|
||||
* @var string
|
||||
*/
|
||||
public $layoutCampaignId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The owner id (readonly))")
|
||||
* @var integer
|
||||
*/
|
||||
public $ownerId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The duration (readonly))")
|
||||
* @var integer
|
||||
*/
|
||||
public $duration;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The dayPart (readonly)")
|
||||
* @var string
|
||||
*/
|
||||
public $dayPart;
|
||||
}
|
||||
120
lib/Entity/LogEntry.php
Normal file
120
lib/Entity/LogEntry.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class LogEntry
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class LogEntry implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Log ID")
|
||||
* @var int
|
||||
*/
|
||||
public $logId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A unique run number for a set of Log Messages.")
|
||||
* @var string
|
||||
*/
|
||||
public $runNo;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A timestamp representing the CMS date this log message occured")
|
||||
* @var int
|
||||
*/
|
||||
public $logDate;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Channel that generated this message. WEB/API/MAINT/TEST")
|
||||
* @var string
|
||||
*/
|
||||
public $channel;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The requested route")
|
||||
* @var string
|
||||
*/
|
||||
public $page;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The request method, GET/POST/PUT/DELETE")
|
||||
* @var string
|
||||
*/
|
||||
public $function;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The log message")
|
||||
* @var string
|
||||
*/
|
||||
public $message;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The display ID this message relates to or NULL for CMS")
|
||||
* @var int
|
||||
*/
|
||||
public $displayId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Log Level")
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The display this message relates to or CMS for CMS.")
|
||||
* @var string
|
||||
*/
|
||||
public $display;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Session history id.")
|
||||
* @var int
|
||||
*/
|
||||
public $sessionHistoryId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="User id.")
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
}
|
||||
1040
lib/Entity/Media.php
Normal file
1040
lib/Entity/Media.php
Normal file
File diff suppressed because it is too large
Load Diff
388
lib/Entity/MenuBoard.php
Normal file
388
lib/Entity/MenuBoard.php
Normal file
@@ -0,0 +1,388 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Respect\Validation\Validator as v;
|
||||
use Stash\Interfaces\PoolInterface;
|
||||
use Xibo\Factory\MenuBoardCategoryFactory;
|
||||
use Xibo\Factory\PermissionFactory;
|
||||
use Xibo\Helper\SanitizerService;
|
||||
use Xibo\Service\ConfigServiceInterface;
|
||||
use Xibo\Service\DisplayNotifyServiceInterface;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class MenuBoard implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Id")
|
||||
* @var int
|
||||
*/
|
||||
public $menuId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board name")
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board description")
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board code identifier")
|
||||
* @var string
|
||||
*/
|
||||
public $code;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board owner Id")
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
public $owner;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board last modified date")
|
||||
* @var int
|
||||
*/
|
||||
public $modifiedDt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Id of the Folder this Menu Board belongs to")
|
||||
* @var string
|
||||
*/
|
||||
public $folderId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The id of the Folder responsible for providing permissions for this Menu Board")
|
||||
* @var int
|
||||
*/
|
||||
public $permissionsFolderId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A comma separated list of Groups/Users that have permission to this menu Board")
|
||||
* @var string
|
||||
*/
|
||||
public $groupsWithPermissions;
|
||||
|
||||
/** @var SanitizerService */
|
||||
private $sanitizerService;
|
||||
|
||||
/** @var PoolInterface */
|
||||
private $pool;
|
||||
|
||||
/** @var ConfigServiceInterface */
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var Permission[]
|
||||
*/
|
||||
private $permissions = [];
|
||||
private $categories;
|
||||
|
||||
/** @var PermissionFactory */
|
||||
private $permissionFactory;
|
||||
|
||||
/** @var MenuBoardCategoryFactory */
|
||||
private $menuBoardCategoryFactory;
|
||||
|
||||
/** @var DisplayNotifyServiceInterface */
|
||||
private $displayNotifyService;
|
||||
|
||||
private $datesToFormat = ['modifiedDt'];
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param SanitizerService $sanitizerService
|
||||
* @param PoolInterface $pool
|
||||
* @param ConfigServiceInterface $config
|
||||
* @param PermissionFactory $permissionFactory
|
||||
* @param MenuBoardCategoryFactory $menuBoardCategoryFactory
|
||||
* @param DisplayNotifyServiceInterface $displayNotifyService
|
||||
*/
|
||||
public function __construct(
|
||||
$store,
|
||||
$log,
|
||||
$dispatcher,
|
||||
$sanitizerService,
|
||||
$pool,
|
||||
$config,
|
||||
$permissionFactory,
|
||||
$menuBoardCategoryFactory,
|
||||
$displayNotifyService
|
||||
) {
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
$this->sanitizerService = $sanitizerService;
|
||||
$this->config = $config;
|
||||
$this->pool = $pool;
|
||||
$this->permissionFactory = $permissionFactory;
|
||||
$this->menuBoardCategoryFactory = $menuBoardCategoryFactory;
|
||||
$this->displayNotifyService = $displayNotifyService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $array
|
||||
* @return \Xibo\Support\Sanitizer\SanitizerInterface
|
||||
*/
|
||||
protected function getSanitizer($array)
|
||||
{
|
||||
return $this->sanitizerService->getSanitizer($array);
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->menuId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('MenuId %d, Name %s, Description %s, Code %s', $this->menuId, $this->name, $this->description, $this->code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->menuId;
|
||||
}
|
||||
|
||||
public function getPermissionFolderId()
|
||||
{
|
||||
return $this->permissionsFolderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OwnerId
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Owner
|
||||
* @param int $ownerId
|
||||
*/
|
||||
public function setOwner($ownerId)
|
||||
{
|
||||
$this->userId = $ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return MenuBoard
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function load($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'loadPermissions' => true,
|
||||
'loadCategories' => false
|
||||
], $options);
|
||||
|
||||
// If we are already loaded, then don't do it again
|
||||
if ($this->menuId == null || $this->loaded) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Permissions
|
||||
if ($options['loadPermissions']) {
|
||||
$this->permissions = $this->permissionFactory->getByObjectId('MenuBoard', $this->menuId);
|
||||
}
|
||||
|
||||
if ($options['loadCategories']) {
|
||||
$this->categories = $this->menuBoardCategoryFactory->getByMenuId($this->menuId);
|
||||
}
|
||||
|
||||
$this->loaded = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!v::stringType()->notEmpty()->validate($this->name)) {
|
||||
throw new InvalidArgumentException(__('Name cannot be empty'), 'name');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save this Menu Board
|
||||
* @param array $options
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true,
|
||||
'audit' => true
|
||||
], $options);
|
||||
|
||||
if ($options['audit']) {
|
||||
$this->getLog()->debug('Saving ' . $this);
|
||||
}
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($this->menuId == null || $this->menuId == 0) {
|
||||
$this->add();
|
||||
$this->loaded = true;
|
||||
} else {
|
||||
$this->update();
|
||||
}
|
||||
|
||||
// We've been touched
|
||||
$this->setActive();
|
||||
|
||||
// Notify Displays?
|
||||
$this->notify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this MenuBoard active currently
|
||||
* @return bool
|
||||
*/
|
||||
public function isActive()
|
||||
{
|
||||
$cache = $this->pool->getItem('/menuboard/accessed/' . $this->menuId);
|
||||
return $cache->isHit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that this MenuBoard has been accessed recently
|
||||
* @return $this
|
||||
*/
|
||||
public function setActive()
|
||||
{
|
||||
$this->getLog()->debug('Setting ' . $this->menuId . ' as active');
|
||||
|
||||
$cache = $this->pool->getItem('/menuboard/accessed/' . $this->menuId);
|
||||
$cache->set('true');
|
||||
$cache->expiresAfter(intval($this->config->getSetting('REQUIRED_FILES_LOOKAHEAD')) * 1.5);
|
||||
$this->pool->saveDeferred($cache);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Display Notify Service
|
||||
* @return DisplayNotifyServiceInterface
|
||||
*/
|
||||
public function getDisplayNotifyService(): DisplayNotifyServiceInterface
|
||||
{
|
||||
return $this->displayNotifyService->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify displays of this campaign change
|
||||
*/
|
||||
public function notify()
|
||||
{
|
||||
$this->getLog()->debug('MenuBoard ' . $this->menuId . ' wants to notify');
|
||||
|
||||
$this->getDisplayNotifyService()->collectNow()->notifyByMenuBoardId($this->menuId);
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->menuId = $this->getStore()->insert(
|
||||
'INSERT INTO `menu_board` (name, description, code, userId, modifiedDt, folderId, permissionsFolderId) VALUES (:name, :description, :code, :userId, :modifiedDt, :folderId, :permissionsFolderId)',
|
||||
[
|
||||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
'code' => $this->code,
|
||||
'userId' => $this->userId,
|
||||
'modifiedDt' => Carbon::now()->format('U'),
|
||||
'folderId' => ($this->folderId == null) ? 1 : $this->folderId,
|
||||
'permissionsFolderId' => ($this->permissionsFolderId == null) ? 1 : $this->permissionsFolderId
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function update()
|
||||
{
|
||||
$this->getStore()->update(
|
||||
'UPDATE `menu_board` SET name = :name, description = :description, code = :code, userId = :userId, modifiedDt = :modifiedDt, folderId = :folderId, permissionsFolderId = :permissionsFolderId WHERE menuId = :menuId',
|
||||
[
|
||||
'menuId' => $this->menuId,
|
||||
'name' => $this->name,
|
||||
'description' => $this->description,
|
||||
'code' => $this->code,
|
||||
'userId' => $this->userId,
|
||||
'modifiedDt' => Carbon::now()->format('U'),
|
||||
'folderId' => $this->folderId,
|
||||
'permissionsFolderId' => $this->permissionsFolderId
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Menu Board
|
||||
* @throws InvalidArgumentException
|
||||
* @throws NotFoundException
|
||||
* @throws \Xibo\Support\Exception\DuplicateEntityException
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->load(['loadCategories' => true]);
|
||||
|
||||
// Delete all permissions
|
||||
foreach ($this->permissions as $permission) {
|
||||
/* @var Permission $permission */
|
||||
$permission->delete();
|
||||
}
|
||||
|
||||
// Delete all
|
||||
/** @var MenuBoardCategory $category */
|
||||
foreach ($this->categories as $category) {
|
||||
$category->delete();
|
||||
}
|
||||
|
||||
$this->getStore()->update('DELETE FROM `menu_board` WHERE menuId = :menuId', ['menuId' => $this->menuId]);
|
||||
}
|
||||
}
|
||||
263
lib/Entity/MenuBoardCategory.php
Normal file
263
lib/Entity/MenuBoardCategory.php
Normal file
@@ -0,0 +1,263 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Factory\MenuBoardCategoryFactory;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
use Xibo\Widget\DataType\ProductCategory;
|
||||
|
||||
/**
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class MenuBoardCategory implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Category Id")
|
||||
* @var int
|
||||
*/
|
||||
public $menuCategoryId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Id")
|
||||
* @var int
|
||||
*/
|
||||
public $menuId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Category name")
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Category description")
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Category code identifier")
|
||||
* @var string
|
||||
*/
|
||||
public $code;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Category associated mediaId")
|
||||
* @var int
|
||||
*/
|
||||
public $mediaId;
|
||||
|
||||
private $products;
|
||||
|
||||
/** @var MenuBoardCategoryFactory */
|
||||
private $menuCategoryFactory;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param MenuBoardCategoryFactory $menuCategoryFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $menuCategoryFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
$this->menuCategoryFactory = $menuCategoryFactory;
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->menuCategoryId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf(
|
||||
'MenuCategoryId %d MenuId %d, Name %s, Media %d, Code %s',
|
||||
$this->menuCategoryId,
|
||||
$this->menuId,
|
||||
$this->name,
|
||||
$this->mediaId,
|
||||
$this->code
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this to a product category
|
||||
* @return ProductCategory
|
||||
*/
|
||||
public function toProductCategory(): ProductCategory
|
||||
{
|
||||
$productCategory = new ProductCategory();
|
||||
$productCategory->name = $this->name;
|
||||
$productCategory->description = $this->description;
|
||||
$productCategory->image = $this->mediaId;
|
||||
return $productCategory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->menuCategoryId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return MenuBoardCategory
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function load($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'loadProducts' => false
|
||||
], $options);
|
||||
|
||||
// If we are already loaded, then don't do it again
|
||||
if ($this->menuId == null || $this->loaded) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($options['loadProducts']) {
|
||||
$this->products = $this->getProducts();
|
||||
}
|
||||
|
||||
$this->loaded = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!v::stringType()->notEmpty()->validate($this->name)) {
|
||||
throw new InvalidArgumentException(__('Name cannot be empty'), 'name');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|null $sort The sort order to be applied
|
||||
* @return MenuBoardProduct[]
|
||||
*/
|
||||
public function getProducts($sort = null): array
|
||||
{
|
||||
return $this->menuCategoryFactory->getProductData($sort, [
|
||||
'menuCategoryId' => $this->menuCategoryId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array|null $sort The sort order to be applied
|
||||
* @return MenuBoardProduct[]
|
||||
*/
|
||||
public function getAvailableProducts($sort = null): array
|
||||
{
|
||||
return $this->menuCategoryFactory->getProductData($sort, [
|
||||
'menuCategoryId' => $this->menuCategoryId,
|
||||
'availability' => 1
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save this Menu Board Category
|
||||
* @param array $options
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true,
|
||||
], $options);
|
||||
|
||||
$this->getLog()->debug('Saving ' . $this);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($this->menuCategoryId == null || $this->menuCategoryId == 0) {
|
||||
$this->add();
|
||||
$this->loaded = true;
|
||||
} else {
|
||||
$this->update();
|
||||
}
|
||||
}
|
||||
|
||||
private function add(): void
|
||||
{
|
||||
$this->menuCategoryId = $this->getStore()->insert('
|
||||
INSERT INTO `menu_category` (`name`, `menuId`, `mediaId`, `code`, `description`)
|
||||
VALUES (:name, :menuId, :mediaId, :code, :description)
|
||||
', [
|
||||
'name' => $this->name,
|
||||
'mediaId' => $this->mediaId,
|
||||
'menuId' => $this->menuId,
|
||||
'code' => $this->code,
|
||||
'description' => $this->description,
|
||||
]);
|
||||
}
|
||||
|
||||
private function update(): void
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `menu_category`
|
||||
SET `name` = :name, `mediaId` = :mediaId, `code` = :code, `description` = :description
|
||||
WHERE `menuCategoryId` = :menuCategoryId
|
||||
', [
|
||||
'menuCategoryId' => $this->menuCategoryId,
|
||||
'name' => $this->name,
|
||||
'mediaId' => $this->mediaId,
|
||||
'code' => $this->code,
|
||||
'description' => $this->description,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Menu Board
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->load(['loadProducts' => true]);
|
||||
|
||||
/** @var MenuBoardProduct $product */
|
||||
foreach ($this->products as $product) {
|
||||
$product->delete();
|
||||
}
|
||||
|
||||
$this->getStore()->update('DELETE FROM `menu_category` WHERE menuCategoryId = :menuCategoryId', ['menuCategoryId' => $this->menuCategoryId]);
|
||||
}
|
||||
}
|
||||
327
lib/Entity/MenuBoardProduct.php
Normal file
327
lib/Entity/MenuBoardProduct.php
Normal file
@@ -0,0 +1,327 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Factory\MenuBoardProductOptionFactory;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Widget\DataType\Product;
|
||||
|
||||
/**
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class MenuBoardProduct implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Product Id")
|
||||
* @var int
|
||||
*/
|
||||
public $menuProductId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Category Id")
|
||||
* @var int
|
||||
*/
|
||||
public $menuCategoryId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Id")
|
||||
* @var int
|
||||
*/
|
||||
public $menuId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Category name")
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Product price")
|
||||
* @var double
|
||||
*/
|
||||
public $price;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Product description")
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Product code identifier")
|
||||
* @var string
|
||||
*/
|
||||
public $code;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Product display order, used for sorting")
|
||||
* @var int
|
||||
*/
|
||||
public $displayOrder;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Product availability")
|
||||
* @var int
|
||||
*/
|
||||
public $availability;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Product allergy information")
|
||||
* @var string
|
||||
*/
|
||||
public $allergyInfo;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Product allergy information")
|
||||
* @var int
|
||||
*/
|
||||
public $calories;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Product associated mediaId")
|
||||
* @var int
|
||||
*/
|
||||
public $mediaId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Board Product array of options", @SWG\Items(type="string"))
|
||||
* @var MenuBoardProductOption[]
|
||||
*/
|
||||
public $productOptions;
|
||||
|
||||
/**
|
||||
* @var MenuBoardProductOptionFactory
|
||||
*/
|
||||
private $menuBoardProductOptionFactory;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param MenuBoardProductOptionFactory $menuBoardProductOptionFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $menuBoardProductOptionFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
$this->menuBoardProductOptionFactory = $menuBoardProductOptionFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->menuProductId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(): void
|
||||
{
|
||||
if (!v::stringType()->notEmpty()->validate($this->name)) {
|
||||
throw new InvalidArgumentException(__('Name cannot be empty'), 'name');
|
||||
}
|
||||
|
||||
if (!empty($this->calories) && !v::intType()->min(0)->max(32767)->validate($this->calories)) {
|
||||
throw new InvalidArgumentException(
|
||||
__('Calories must be a whole number between 0 and 32767'),
|
||||
'calories'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf(
|
||||
'MenuProductId %d, MenuCategoryId %d, MenuId %d, Name %s, Price %s, Media %d, Code %s',
|
||||
$this->menuProductId,
|
||||
$this->menuCategoryId,
|
||||
$this->menuId,
|
||||
$this->name,
|
||||
$this->price,
|
||||
$this->mediaId,
|
||||
$this->code
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this to a Product
|
||||
* @return Product
|
||||
*/
|
||||
public function toProduct(): Product
|
||||
{
|
||||
$product = new Product();
|
||||
$product->name = $this->name;
|
||||
$product->price = $this->price;
|
||||
$product->description = $this->description;
|
||||
$product->availability = $this->availability;
|
||||
$product->allergyInfo = $this->allergyInfo;
|
||||
$product->calories = $this->calories;
|
||||
$product->image = $this->mediaId;
|
||||
foreach (($this->productOptions ?? []) as $productOption) {
|
||||
$product->productOptions[] = [
|
||||
'name' => $productOption->option,
|
||||
'value' => $productOption->value,
|
||||
];
|
||||
}
|
||||
return $product;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save this Menu Board Product
|
||||
* @param array $options
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true,
|
||||
], $options);
|
||||
|
||||
$this->getLog()->debug('Saving ' . $this);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($this->menuProductId == null || $this->menuProductId == 0) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Menu Board Product
|
||||
*/
|
||||
private function add(): void
|
||||
{
|
||||
$this->menuProductId = $this->getStore()->insert('
|
||||
INSERT INTO `menu_product` (
|
||||
`menuCategoryId`,
|
||||
`menuId`,
|
||||
`name`,
|
||||
`price`,
|
||||
`description`,
|
||||
`mediaId`,
|
||||
`displayOrder`,
|
||||
`availability`,
|
||||
`allergyInfo`,
|
||||
`calories`,
|
||||
`code`
|
||||
)
|
||||
VALUES (
|
||||
:menuCategoryId,
|
||||
:menuId,
|
||||
:name,
|
||||
:price,
|
||||
:description,
|
||||
:mediaId,
|
||||
:displayOrder,
|
||||
:availability,
|
||||
:allergyInfo,
|
||||
:calories,
|
||||
:code
|
||||
)
|
||||
', [
|
||||
'menuCategoryId' => $this->menuCategoryId,
|
||||
'menuId' => $this->menuId,
|
||||
'name' => $this->name,
|
||||
'price' => $this->price,
|
||||
'description' => $this->description,
|
||||
'mediaId' => $this->mediaId,
|
||||
'displayOrder' => $this->displayOrder,
|
||||
'availability' => $this->availability,
|
||||
'allergyInfo' => $this->allergyInfo,
|
||||
'calories' => $this->calories,
|
||||
'code' => $this->code,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update Menu Board Product
|
||||
*/
|
||||
private function update(): void
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `menu_product` SET
|
||||
`name` = :name,
|
||||
`price` = :price,
|
||||
`description` = :description,
|
||||
`mediaId` = :mediaId,
|
||||
`displayOrder` = :displayOrder,
|
||||
`availability` = :availability,
|
||||
`allergyInfo` = :allergyInfo,
|
||||
`calories` = :calories,
|
||||
`code` = :code
|
||||
WHERE `menuProductId` = :menuProductId
|
||||
', [
|
||||
'name' => $this->name,
|
||||
'price' => $this->price,
|
||||
'description' => $this->description,
|
||||
'mediaId' => $this->mediaId,
|
||||
'displayOrder' => $this->displayOrder,
|
||||
'availability' => $this->availability,
|
||||
'allergyInfo' => $this->allergyInfo,
|
||||
'calories' => $this->calories,
|
||||
'code' => $this->code,
|
||||
'menuProductId' => $this->menuProductId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Menu Board Product
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->removeOptions();
|
||||
$this->getStore()->update('DELETE FROM `menu_product` WHERE menuProductId = :menuProductId', ['menuProductId' => $this->menuProductId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return MenuBoardProductOption[]
|
||||
*/
|
||||
public function getOptions()
|
||||
{
|
||||
$options = $this->menuBoardProductOptionFactory->getByMenuProductId($this->menuProductId);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function removeOptions()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `menu_product_options` WHERE menuProductId = :menuProductId', ['menuProductId' => $this->menuProductId]);
|
||||
}
|
||||
}
|
||||
130
lib/Entity/MenuBoardProductOption.php
Normal file
130
lib/Entity/MenuBoardProductOption.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class MenuBoardProductOption implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Menu Product ID that this Option belongs to")
|
||||
* @var int
|
||||
*/
|
||||
public $menuProductId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The option name")
|
||||
* @var string
|
||||
*/
|
||||
public $option;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The option value")
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->menuProductId = null;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('ProductOption %s with value %s', $this->option, $this->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!v::stringType()->notEmpty()->validate($this->option)
|
||||
&& v::floatType()->notEmpty()->validate($this->value)
|
||||
) {
|
||||
throw new InvalidArgumentException(__('Each value needs a corresponding option'), 'option');
|
||||
}
|
||||
|
||||
if (!v::floatType()->notEmpty()->validate($this->value)
|
||||
&& v::stringType()->notEmpty()->validate($this->option)
|
||||
) {
|
||||
throw new InvalidArgumentException(__('Each option needs a corresponding value'), 'value');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true,
|
||||
], $options);
|
||||
|
||||
$this->getLog()->debug('Saving ' . $this);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
$this->getStore()->insert(
|
||||
'INSERT INTO `menu_product_options` (`menuProductId`, `option`, `value`) VALUES (:menuProductId, :option, :value) ON DUPLICATE KEY UPDATE `value` = :value2',
|
||||
[
|
||||
'menuProductId' => $this->menuProductId,
|
||||
'option' => $this->option,
|
||||
'value' => $this->value,
|
||||
'value2' => $this->value
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update(
|
||||
'DELETE FROM `menu_product_options` WHERE `menuProductId` = :menuProductId AND `option` = :option',
|
||||
[
|
||||
'menuProductId' => $this->menuProductId,
|
||||
'option' => $this->option
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
649
lib/Entity/Module.php
Normal file
649
lib/Entity/Module.php
Normal file
@@ -0,0 +1,649 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Xibo\Factory\ModuleFactory;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Widget\Definition\LegacyType;
|
||||
use Xibo\Widget\Provider\DataProvider;
|
||||
use Xibo\Widget\Provider\WidgetCompatibilityInterface;
|
||||
use Xibo\Widget\Provider\WidgetProviderInterface;
|
||||
use Xibo\Widget\Provider\WidgetValidatorInterface;
|
||||
|
||||
/**
|
||||
* Class Module
|
||||
* @package Xibo\Entity
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Module implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
use ModulePropertyTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of this Module")
|
||||
* @var int
|
||||
*/
|
||||
public $moduleId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Module Name")
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Module Author")
|
||||
* @var string
|
||||
*/
|
||||
public $author;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Description of the Module")
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="An icon to use in the toolbar")
|
||||
* @var string
|
||||
*/
|
||||
public $icon;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The type code for this module")
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Legacy type codes for this module")
|
||||
* @var LegacyType[]
|
||||
*/
|
||||
public $legacyTypes;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The data type of the data expected to be returned by this modules data provider")
|
||||
* @var string
|
||||
*/
|
||||
public $dataType;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The group details for this module")
|
||||
* @var string[]
|
||||
*/
|
||||
public $group;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The cache key used when requesting data")
|
||||
* @var string
|
||||
*/
|
||||
public $dataCacheKey;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Is fallback data allowed for this module? Only applicable for a Data Widget")
|
||||
* @var int
|
||||
*/
|
||||
public $fallbackData;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Is specific to a Layout or can be uploaded to the Library?")
|
||||
* @var int
|
||||
*/
|
||||
public $regionSpecific;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The schema version of the module")
|
||||
* @var int
|
||||
*/
|
||||
public $schemaVersion;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The compatibility class of the module")
|
||||
* @var string
|
||||
*/
|
||||
public $compatibilityClass = null;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether the module should be excluded from the Layout Editor")
|
||||
* @var string
|
||||
*/
|
||||
public $showIn = 'both';
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether the module is assignable to a Layout")
|
||||
* @var int
|
||||
*/
|
||||
public $assignable;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Does this module have a thumbnail to render?")
|
||||
* @var int
|
||||
*/
|
||||
public $hasThumbnail;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="This is the location to a module's thumbnail")
|
||||
* @var string
|
||||
*/
|
||||
public $thumbnail;
|
||||
|
||||
/** @var int The width of the zone */
|
||||
public $startWidth;
|
||||
|
||||
/** @var int The height of the zone */
|
||||
public $startHeight;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Should be rendered natively by the Player or via the CMS (native|html)")
|
||||
* @var string
|
||||
*/
|
||||
public $renderAs;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Class Name including namespace")
|
||||
* @var string
|
||||
*/
|
||||
public $class;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Validator class name including namespace")
|
||||
* @var string[]
|
||||
*/
|
||||
public $validatorClass = [];
|
||||
|
||||
/** @var \Xibo\Widget\Definition\Stencil|null Stencil for this modules preview */
|
||||
public $preview;
|
||||
|
||||
/** @var \Xibo\Widget\Definition\Stencil|null Stencil for this modules HTML cache */
|
||||
public $stencil;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Properties to display in the property panel and supply to stencils")
|
||||
* @var \Xibo\Widget\Definition\Property[]|null
|
||||
*/
|
||||
public $properties;
|
||||
|
||||
/** @var \Xibo\Widget\Definition\Asset[]|null */
|
||||
public $assets;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="JavaScript function run when a module is initialised, before data is returned")
|
||||
* @var string
|
||||
*/
|
||||
public $onInitialize;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Data Parser run against each data item applicable when a dataType is present")
|
||||
* @var string
|
||||
*/
|
||||
public $onParseData;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A load function to run when the widget first fetches data")
|
||||
* @var string
|
||||
*/
|
||||
public $onDataLoad;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="JavaScript function run when a module is rendered, after data has been returned")
|
||||
* @var string
|
||||
*/
|
||||
public $onRender;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="JavaScript function run when a module becomes visible")
|
||||
* @var string
|
||||
*/
|
||||
public $onVisible;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Optional sample data item, only applicable when a dataType is present")
|
||||
* @var string
|
||||
*/
|
||||
public $sampleData;
|
||||
|
||||
// <editor-fold desc="Properties recorded in the database">
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether this module is enabled")
|
||||
* @var int
|
||||
*/
|
||||
public $enabled;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether the Layout designer should render a preview of this module")
|
||||
* @var int
|
||||
*/
|
||||
public $previewEnabled;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="The default duration for Widgets of this Module when the user has not set a duration."
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $defaultDuration;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="An array of additional module specific settings")
|
||||
* @var \Xibo\Widget\Definition\Property[]
|
||||
*/
|
||||
public $settings = [];
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="An array of additional module specific group properties")
|
||||
* @var \Xibo\Widget\Definition\PropertyGroup[]
|
||||
*/
|
||||
public $propertyGroups = [];
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="An array of required elements",
|
||||
* type="array",
|
||||
* @SWG\Items(type="string")
|
||||
* )
|
||||
* @var string[]
|
||||
*/
|
||||
public $requiredElements = [];
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var bool $isInstalled Is this module installed?
|
||||
*/
|
||||
public $isInstalled;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var bool $isError Does this module have any errors?
|
||||
*/
|
||||
public $isError;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var string[] $errors An array of errors this module has.
|
||||
*/
|
||||
public $errors;
|
||||
|
||||
// </editor-fold>
|
||||
public $allowPreview;
|
||||
|
||||
/** @var ModuleFactory */
|
||||
private $moduleFactory;
|
||||
|
||||
/** @var WidgetProviderInterface */
|
||||
private $widgetProvider;
|
||||
|
||||
/** @var WidgetCompatibilityInterface */
|
||||
private $widgetCompatibility;
|
||||
|
||||
/** @var WidgetValidatorInterface[] */
|
||||
private $widgetValidators = [];
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param \Xibo\Factory\ModuleFactory $moduleFactory
|
||||
*/
|
||||
public function __construct(
|
||||
StorageServiceInterface $store,
|
||||
LogServiceInterface $log,
|
||||
EventDispatcherInterface $dispatcher,
|
||||
ModuleFactory $moduleFactory
|
||||
) {
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
$this->moduleFactory = $moduleFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('%s - %s', $this->type, $this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a template expected?
|
||||
* @return bool
|
||||
*/
|
||||
public function isTemplateExpected(): bool
|
||||
{
|
||||
return (!empty($this->dataType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a template expected?
|
||||
* @return bool
|
||||
*/
|
||||
public function isDataProviderExpected(): bool
|
||||
{
|
||||
return (!empty($this->dataType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this module have required elements?
|
||||
* @return bool
|
||||
*/
|
||||
public function hasRequiredElements(): bool
|
||||
{
|
||||
return count($this->requiredElements) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this module's widget provider, or null if there isn't one
|
||||
* @return \Xibo\Widget\Provider\WidgetProviderInterface|null
|
||||
*/
|
||||
public function getWidgetProviderOrNull(): ?WidgetProviderInterface
|
||||
{
|
||||
return $this->widgetProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Xibo\Entity\Widget $widget
|
||||
* @return \Xibo\Widget\Provider\DataProvider
|
||||
*/
|
||||
public function createDataProvider(Widget $widget): DataProvider
|
||||
{
|
||||
return $this->moduleFactory->createDataProvider($this, $widget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch duration of a file.
|
||||
* @param string $file
|
||||
* @return int
|
||||
*/
|
||||
public function fetchDurationOrDefaultFromFile(string $file): int
|
||||
{
|
||||
$this->getLog()->debug('fetchDurationOrDefaultFromFile: fetchDuration with file: ' . $file);
|
||||
|
||||
// If we don't have a file name, then we use the default duration of 0 (end-detect)
|
||||
if (empty($file)) {
|
||||
return 0;
|
||||
} else {
|
||||
$info = new \getID3();
|
||||
$file = $info->analyze($file);
|
||||
|
||||
// Log error if duration is missing
|
||||
if (!isset($file['playtime_seconds'])) {
|
||||
$errorMessage = isset($file['error'])
|
||||
? implode('; ', $file['error'])
|
||||
: 'Unknown';
|
||||
$this->getLog()->error('fetchDurationOrDefaultFromFile; Missing playtime_seconds in analyzed
|
||||
file. Error: ' . $errorMessage);
|
||||
}
|
||||
|
||||
return intval($file['playtime_seconds'] ?? $this->defaultDuration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the duration of this Widget.
|
||||
* @param Widget $widget
|
||||
* @return int|null
|
||||
*/
|
||||
public function calculateDuration(Widget $widget): ?int
|
||||
{
|
||||
if ($this->widgetProvider === null && $this->regionSpecific === 1) {
|
||||
// Take some default action to cover the majourity of region specific widgets
|
||||
// Duration can depend on the number of items per page for some widgets
|
||||
// this is a legacy way of working, and our preference is to use elements
|
||||
$numItems = $widget->getOptionValue('numItems', 15);
|
||||
|
||||
if ($widget->getOptionValue('durationIsPerItem', 0) == 1 && $numItems > 1) {
|
||||
// If we have paging involved then work out the page count.
|
||||
$itemsPerPage = $widget->getOptionValue('itemsPerPage', 0);
|
||||
if ($itemsPerPage > 0) {
|
||||
$numItems = ceil($numItems / $itemsPerPage);
|
||||
}
|
||||
|
||||
return $widget->calculatedDuration * $numItems;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if ($this->widgetProvider === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->getLog()->debug('calculateDuration: using widget provider');
|
||||
|
||||
$durationProvider = $this->moduleFactory->createDurationProvider($this, $widget);
|
||||
$this->widgetProvider->fetchDuration($durationProvider);
|
||||
|
||||
return $durationProvider->isDurationSet() ? $durationProvider->getDuration() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the widget provider for this module
|
||||
* @param \Xibo\Widget\Provider\WidgetProviderInterface $widgetProvider
|
||||
* @return $this
|
||||
*/
|
||||
public function setWidgetProvider(WidgetProviderInterface $widgetProvider): Module
|
||||
{
|
||||
$this->widgetProvider = $widgetProvider;
|
||||
$this->widgetProvider
|
||||
->setLog($this->getLog()->getLoggerInterface())
|
||||
->setDispatcher($this->getDispatcher());
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a widget compatibility available
|
||||
* @return bool
|
||||
*/
|
||||
public function isWidgetCompatibilityAvailable(): bool
|
||||
{
|
||||
return $this->widgetCompatibility !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this module's widget compatibility, or null if there isn't one
|
||||
* @return \Xibo\Widget\Provider\WidgetCompatibilityInterface|null
|
||||
*/
|
||||
public function getWidgetCompatibilityOrNull(): ?WidgetCompatibilityInterface
|
||||
{
|
||||
return $this->widgetCompatibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the widget compatibility for this module
|
||||
* @param WidgetCompatibilityInterface $widgetCompatibility
|
||||
* @return $this
|
||||
*/
|
||||
public function setWidgetCompatibility(WidgetCompatibilityInterface $widgetCompatibility): Module
|
||||
{
|
||||
$this->widgetCompatibility = $widgetCompatibility;
|
||||
$this->widgetCompatibility->setLog($this->getLog()->getLoggerInterface());
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addWidgetValidator(WidgetValidatorInterface $widgetValidator): Module
|
||||
{
|
||||
$this->widgetValidators[] = $widgetValidator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this module's widget validators
|
||||
* @return \Xibo\Widget\Provider\WidgetValidatorInterface[]
|
||||
*/
|
||||
public function getWidgetValidators(): array
|
||||
{
|
||||
return $this->widgetValidators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all properties which allow library references.
|
||||
* @return \Xibo\Widget\Definition\Property[]
|
||||
*/
|
||||
public function getPropertiesAllowingLibraryRefs(): array
|
||||
{
|
||||
$props = [];
|
||||
foreach ($this->properties as $property) {
|
||||
if ($property->allowLibraryRefs) {
|
||||
$props[] = $property;
|
||||
}
|
||||
}
|
||||
|
||||
return $props;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assets
|
||||
* @return \Xibo\Widget\Definition\Asset[]
|
||||
*/
|
||||
public function getAssets(): array
|
||||
{
|
||||
return $this->assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a module setting
|
||||
* If the setting does not exist, $default will be returned.
|
||||
* If the setting exists, but is not set, the default value from the setting will be returned
|
||||
* @param string $setting The setting
|
||||
* @param mixed|null $default A default value if the setting does not exist
|
||||
* @return mixed
|
||||
*/
|
||||
public function getSetting(string $setting, $default = null)
|
||||
{
|
||||
foreach ($this->settings as $property) {
|
||||
if ($property->id === $setting) {
|
||||
return $property->value ?? $property->default;
|
||||
}
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Xibo\Support\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!v::intType()->validate($this->defaultDuration)) {
|
||||
throw new InvalidArgumentException(__('Default Duration is a required field.'), 'defaultDuration');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Xibo\Support\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
if (!$this->isInstalled) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->moduleId = $this->getStore()->insert('
|
||||
INSERT INTO `module` (
|
||||
`moduleId`,
|
||||
`enabled`,
|
||||
`previewEnabled`,
|
||||
`defaultDuration`,
|
||||
`settings`
|
||||
)
|
||||
VALUES (
|
||||
:moduleId,
|
||||
:enabled,
|
||||
:previewEnabled,
|
||||
:defaultDuration,
|
||||
:settings
|
||||
)
|
||||
', [
|
||||
'moduleId' => $this->moduleId,
|
||||
'enabled' => $this->enabled,
|
||||
'previewEnabled' => $this->previewEnabled,
|
||||
'defaultDuration' => $this->defaultDuration,
|
||||
'settings' => $this->getSettingsForSaving()
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `module` SET
|
||||
enabled = :enabled,
|
||||
previewEnabled = :previewEnabled,
|
||||
defaultDuration = :defaultDuration,
|
||||
settings = :settings
|
||||
WHERE moduleid = :moduleId
|
||||
', [
|
||||
'moduleId' => $this->moduleId,
|
||||
'enabled' => $this->enabled,
|
||||
'previewEnabled' => $this->previewEnabled,
|
||||
'defaultDuration' => $this->defaultDuration,
|
||||
'settings' => $this->getSettingsForSaving()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function getSettingsForSaving(): string
|
||||
{
|
||||
$settings = [];
|
||||
foreach ($this->settings as $setting) {
|
||||
if ($setting->value !== null) {
|
||||
$settings[$setting->id] = $setting->value;
|
||||
}
|
||||
}
|
||||
return count($settings) > 0 ? json_encode($settings) : '[]';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getSettingsForOutput(): array
|
||||
{
|
||||
$settings = [];
|
||||
foreach ($this->settings as $setting) {
|
||||
$settings[$setting->id] = $setting->value ?? $setting->default;
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this module
|
||||
* @return void
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `module` WHERE moduleId = :id', [
|
||||
'id' => $this->moduleId
|
||||
]);
|
||||
}
|
||||
}
|
||||
208
lib/Entity/ModulePropertyTrait.php
Normal file
208
lib/Entity/ModulePropertyTrait.php
Normal file
@@ -0,0 +1,208 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
|
||||
/**
|
||||
* A trait for common functionality in regard to properties on modules/module templates
|
||||
*/
|
||||
trait ModulePropertyTrait
|
||||
{
|
||||
/**
|
||||
* @param Widget $widget
|
||||
* @param bool $includeDefaults
|
||||
* @param bool $reverseFilters Reverse filters?
|
||||
* @return $this
|
||||
*/
|
||||
public function decorateProperties(Widget $widget, bool $includeDefaults = false, bool $reverseFilters = true)
|
||||
{
|
||||
foreach ($this->properties as $property) {
|
||||
$property->value = $widget->getOptionValue($property->id, null);
|
||||
|
||||
// Should we include defaults?
|
||||
if ($includeDefaults && $property->value === null) {
|
||||
$property->value = $property->default;
|
||||
}
|
||||
|
||||
if ($property->value !== null) {
|
||||
if ($property->type === 'integer') {
|
||||
$property->value = intval($property->value);
|
||||
} else if ($property->type === 'double' || $property->type === 'number') {
|
||||
$property->value = doubleval($property->value);
|
||||
} else if ($property->type === 'checkbox') {
|
||||
$property->value = intval($property->value);
|
||||
}
|
||||
}
|
||||
|
||||
if ($reverseFilters) {
|
||||
$property->reverseFilters();
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $properties
|
||||
* @param bool $includeDefaults
|
||||
* @return array
|
||||
*/
|
||||
public function decoratePropertiesByArray(array $properties, bool $includeDefaults = false): array
|
||||
{
|
||||
// Flatten the properties array so that we can reference it by key.
|
||||
$keyedProperties = [];
|
||||
foreach ($properties as $property) {
|
||||
$keyedProperties[$property['id']] = $property['value'] ?? null;
|
||||
}
|
||||
|
||||
$decoratedProperties = [];
|
||||
foreach ($this->properties as $property) {
|
||||
$decoratedProperty = $keyedProperties[$property->id] ?? null;
|
||||
|
||||
// Should we include defaults?
|
||||
if ($includeDefaults && $decoratedProperty === null) {
|
||||
$decoratedProperty = $property->default;
|
||||
}
|
||||
|
||||
if ($decoratedProperty !== null) {
|
||||
if ($property->type === 'integer') {
|
||||
$decoratedProperty = intval($decoratedProperty);
|
||||
} else if ($property->type === 'double' || $property->type === 'number') {
|
||||
$decoratedProperty = doubleval($decoratedProperty);
|
||||
} else if ($property->type === 'checkbox') {
|
||||
$decoratedProperty = intval($decoratedProperty);
|
||||
}
|
||||
}
|
||||
|
||||
$decoratedProperty = $property->reverseFiltersOnValue($decoratedProperty);
|
||||
|
||||
// Add our decorated property
|
||||
$decoratedProperties[$property->id] = $decoratedProperty;
|
||||
}
|
||||
return $decoratedProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $decorateForOutput true if we should decorate for output to either the preview or player
|
||||
* @param array|null $overrideValues a key/value array of values to use instead the stored property values
|
||||
* @param bool $includeDefaults include default values
|
||||
* @param bool $skipNullProperties skip null properties
|
||||
* @return array
|
||||
*/
|
||||
public function getPropertyValues(
|
||||
bool $decorateForOutput = true,
|
||||
?array $overrideValues = null,
|
||||
bool $includeDefaults = false,
|
||||
bool $skipNullProperties = false,
|
||||
): array {
|
||||
$properties = [];
|
||||
foreach ($this->properties as $property) {
|
||||
$value = $overrideValues !== null ? ($overrideValues[$property->id] ?? null) : $property->value;
|
||||
|
||||
if ($includeDefaults && $value === null) {
|
||||
$value = $property->default ?? null;
|
||||
}
|
||||
|
||||
if ($skipNullProperties && $value === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: should we cast values to their appropriate field formats.
|
||||
if ($decorateForOutput) {
|
||||
// Does this property have library references?
|
||||
if ($property->allowLibraryRefs && !empty($value)) {
|
||||
// Parse them out and replace for our special syntax.
|
||||
// TODO: Can we improve this regex to ignore things we suspect are JavaScript array access?
|
||||
$matches = [];
|
||||
preg_match_all('/\[(.*?)\]/', $value, $matches);
|
||||
foreach ($matches[1] as $match) {
|
||||
// We ignore non-numbers and zero/negative integers
|
||||
if (is_numeric($match) && intval($match) > 0) {
|
||||
$value = str_replace(
|
||||
'[' . $match . ']',
|
||||
'[[mediaId=' . $match . ']]',
|
||||
$value
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do we need to parse out any translations? We only do this on output.
|
||||
if ($property->parseTranslations && !empty($value)) {
|
||||
$matches = [];
|
||||
preg_match_all('/\|\|.*?\|\|/', $value, $matches);
|
||||
|
||||
foreach ($matches[0] as $sub) {
|
||||
// Parse out the translatable string and substitute
|
||||
$value = str_replace($sub, __(str_replace('||', '', $sub)), $value);
|
||||
}
|
||||
}
|
||||
|
||||
// Date format
|
||||
if ($property->variant === 'dateFormat' && !empty($value)) {
|
||||
$value = DateFormatHelper::convertPhpToMomentFormat($value);
|
||||
}
|
||||
|
||||
// Media selector
|
||||
if ($property->type === 'mediaSelector') {
|
||||
$value = (!$value) ? '' : '[[mediaId=' . $value . ']]';
|
||||
}
|
||||
}
|
||||
$properties[$property->id] = $value;
|
||||
}
|
||||
return $properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default value for a property
|
||||
* @param string $id
|
||||
* @return mixed
|
||||
*/
|
||||
public function getPropertyDefault(string $id): mixed
|
||||
{
|
||||
foreach ($this->properties as $property) {
|
||||
if ($property->id === $id) {
|
||||
return $property->default;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Xibo\Support\Exception\InvalidArgumentException|\Xibo\Support\Exception\ValueTooLargeException
|
||||
*/
|
||||
public function validateProperties(string $stage, $additionalProperties = []): void
|
||||
{
|
||||
// Go through all of our required properties, and validate that they are as they should be.
|
||||
// provide a key/value state of all current properties
|
||||
$properties = array_merge(
|
||||
$this->getPropertyValues(false, null, true),
|
||||
$additionalProperties,
|
||||
);
|
||||
|
||||
foreach ($this->properties as $property) {
|
||||
$property->validate($properties, $stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
380
lib/Entity/ModuleTemplate.php
Normal file
380
lib/Entity/ModuleTemplate.php
Normal file
@@ -0,0 +1,380 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Xibo\Factory\ModuleTemplateFactory;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Widget\Definition\Asset;
|
||||
|
||||
/**
|
||||
* Represents a module template
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class ModuleTemplate implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
use ModulePropertyTrait;
|
||||
|
||||
/** @var int The database ID */
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var string The templateId
|
||||
*/
|
||||
public $templateId;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var string Type of template (static|element|stencil)
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var \Xibo\Widget\Definition\Extend|null If this template extends another
|
||||
*/
|
||||
public $extends;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var string The datatype of this template
|
||||
*/
|
||||
public $dataType;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var string The title
|
||||
*/
|
||||
public $title;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Description of the Module Template")
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var string Icon
|
||||
*/
|
||||
public $icon;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* Thumbnail
|
||||
* this is the location to a module template's thumbnail, which should be added to the installation
|
||||
* relative to the module class file.
|
||||
* @var string
|
||||
*/
|
||||
public $thumbnail;
|
||||
|
||||
/** @var int The width of the zone */
|
||||
public $startWidth;
|
||||
|
||||
/** @var int The height of the zone */
|
||||
public $startHeight;
|
||||
|
||||
/** @var bool Does this template have dimensions? */
|
||||
public $hasDimensions;
|
||||
|
||||
/** @var bool Can this template be rotated? */
|
||||
public $canRotate;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether the template should be excluded from the Layout Editor")
|
||||
* @var string
|
||||
*/
|
||||
public $showIn = 'both';
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var \Xibo\Widget\Definition\Property[]|null Properties
|
||||
*/
|
||||
public $properties;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var bool Is Visible?
|
||||
*/
|
||||
public $isVisible = true;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var bool Is Enabled?
|
||||
*/
|
||||
public $isEnabled = true;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="An array of additional module specific group properties")
|
||||
* @var \Xibo\Widget\Definition\PropertyGroup[]
|
||||
*/
|
||||
public $propertyGroups = [];
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var \Xibo\Widget\Definition\Stencil|null A stencil, if needed
|
||||
*/
|
||||
public $stencil;
|
||||
|
||||
/**
|
||||
* @SWG\Property()
|
||||
* @var Asset[]
|
||||
*/
|
||||
public $assets;
|
||||
|
||||
/** @var string A Renderer to run if custom rendering is required. */
|
||||
public $onTemplateRender;
|
||||
|
||||
/** @var string JavaScript function run when the template becomes visible. */
|
||||
public $onTemplateVisible;
|
||||
|
||||
/** @var string A data parser for elements */
|
||||
public $onElementParseData;
|
||||
|
||||
/** @var bool $isError Does this module have any errors? */
|
||||
public $isError;
|
||||
|
||||
/** @var string[] $errors An array of errors this module has. */
|
||||
public $errors;
|
||||
|
||||
/** @var string $ownership Who owns this file? system|custom|user */
|
||||
public $ownership;
|
||||
|
||||
/** @var int $ownerId User ID of the owner of this template */
|
||||
public $ownerId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A comma separated list of groups/users with permissions to this template")
|
||||
* @var string
|
||||
*/
|
||||
public $groupsWithPermissions;
|
||||
/** @var string $xml The XML used to build this template */
|
||||
|
||||
private $xml;
|
||||
|
||||
/** @var \DOMDocument The DOM Document for this templates XML */
|
||||
private $document;
|
||||
|
||||
/** @var \Xibo\Factory\ModuleTemplateFactory */
|
||||
private $moduleTemplateFactory;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param \Xibo\Storage\StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param \Xibo\Factory\ModuleTemplateFactory $moduleTemplateFactory
|
||||
* @param string $file The file this template resides in
|
||||
*/
|
||||
public function __construct(
|
||||
StorageServiceInterface $store,
|
||||
LogServiceInterface $log,
|
||||
EventDispatcherInterface $dispatcher,
|
||||
ModuleTemplateFactory $moduleTemplateFactory,
|
||||
private readonly string $file
|
||||
) {
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
$this->setPermissionsClass('Xibo\Entity\ModuleTemplate');
|
||||
$this->moduleTemplateFactory = $moduleTemplateFactory;
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getOwnerId()
|
||||
{
|
||||
return $this->ownerId;
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->id = null;
|
||||
$this->templateId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get assets
|
||||
* @return \Xibo\Widget\Definition\Asset[]
|
||||
*/
|
||||
public function getAssets(): array
|
||||
{
|
||||
return $this->assets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set XML for this Module Template
|
||||
* @param string $xml
|
||||
* @return void
|
||||
*/
|
||||
public function setXml(string $xml): void
|
||||
{
|
||||
$this->xml = $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get XML for this Module Template
|
||||
* @return string
|
||||
*/
|
||||
public function getXml(): string
|
||||
{
|
||||
// for system templates
|
||||
if ($this->file !== 'database') {
|
||||
$xml = new \DOMDocument();
|
||||
// load whole file to document
|
||||
$xml->loadXML(file_get_contents($this->file));
|
||||
// go through template tags
|
||||
foreach ($xml->getElementsByTagName('template') as $templateXml) {
|
||||
if ($templateXml instanceof \DOMElement) {
|
||||
foreach ($templateXml->childNodes as $childNode) {
|
||||
if ($childNode instanceof \DOMElement) {
|
||||
// match the template to what was requested
|
||||
// set the xml and return it.
|
||||
if ($childNode->nodeName === 'id' && $childNode->nodeValue == $this->templateId) {
|
||||
$this->setXml($xml->saveXML($templateXml));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Document
|
||||
* @param \DOMDocument $document
|
||||
* @return void
|
||||
*/
|
||||
public function setDocument(\DOMDocument $document): void
|
||||
{
|
||||
$this->document = $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this templates DOM document
|
||||
* @return \DOMDocument
|
||||
*/
|
||||
public function getDocument(): \DOMDocument
|
||||
{
|
||||
if ($this->document === null) {
|
||||
$this->document = new \DOMDocument();
|
||||
$this->document->load($this->getXml());
|
||||
}
|
||||
return $this->document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
* @return void
|
||||
*/
|
||||
public function save(): void
|
||||
{
|
||||
if ($this->file === 'database') {
|
||||
if ($this->id === null) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
* @return void
|
||||
*/
|
||||
public function delete(): void
|
||||
{
|
||||
if ($this->file === 'database') {
|
||||
$this->getStore()->update('DELETE FROM module_templates WHERE id = :id', [
|
||||
'id' => $this->id
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidate this module template for any widgets that use it
|
||||
* @return void
|
||||
*/
|
||||
public function invalidate(): void
|
||||
{
|
||||
// TODO: can we improve this via the event mechanism instead?
|
||||
$this->getStore()->update('
|
||||
UPDATE `widget` SET modifiedDt = :now
|
||||
WHERE widgetId IN (
|
||||
SELECT widgetId
|
||||
FROM widgetoption
|
||||
WHERE `option` = \'templateId\'
|
||||
AND `value` = :templateId
|
||||
)
|
||||
', [
|
||||
'now' => time(),
|
||||
'templateId' => $this->templateId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add
|
||||
* @return void
|
||||
*/
|
||||
private function add(): void
|
||||
{
|
||||
$this->id = $this->getStore()->insert('
|
||||
INSERT INTO `module_templates` (`templateId`, `dataType`, `xml`, `ownerId`)
|
||||
VALUES (:templateId, :dataType, :xml, :ownerId)
|
||||
', [
|
||||
'templateId' => $this->templateId,
|
||||
'dataType' => $this->dataType,
|
||||
'xml' => $this->xml,
|
||||
'ownerId' => $this->ownerId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
* @return void
|
||||
*/
|
||||
private function edit(): void
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `module_templates` SET
|
||||
`templateId` = :templateId,
|
||||
`dataType`= :dataType,
|
||||
`enabled` = :enabled,
|
||||
`xml` = :xml,
|
||||
`ownerId` = :ownerId
|
||||
WHERE `id` = :id
|
||||
', [
|
||||
'templateId' => $this->templateId,
|
||||
'dataType' => $this->dataType,
|
||||
'xml' => $this->xml,
|
||||
'enabled' => $this->isEnabled ? 1 : 0,
|
||||
'ownerId' => $this->ownerId,
|
||||
'id' => $this->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
533
lib/Entity/Notification.php
Normal file
533
lib/Entity/Notification.php
Normal file
@@ -0,0 +1,533 @@
|
||||
<?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\Entity;
|
||||
use Xibo\Factory\DisplayGroupFactory;
|
||||
use Xibo\Factory\UserGroupFactory;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class Notification
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Notification implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="The Notifcation ID"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $notificationId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Create Date as Unix Timestamp"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $createDt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Release Date as Unix Timestamp"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $releaseDt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="The subject line"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $subject;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="The Notification type"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="The HTML body of the notification"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $body;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Should the notification interrupt the CMS UI on navigate/login"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $isInterrupt = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Flag for system notification"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $isSystem = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="The Owner User Id"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Attachment filename"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $filename;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Attachment originalFileName"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $originalFileName;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Additional email addresses to which a saved report will be sent"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $nonusers;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="User Group Notifications associated with this notification"
|
||||
* )
|
||||
* @var UserGroup[]
|
||||
*/
|
||||
public $userGroups = [];
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Display Groups associated with this notification"
|
||||
* )
|
||||
* @var DisplayGroup[]
|
||||
*/
|
||||
public $displayGroups = [];
|
||||
|
||||
/** @var UserGroupFactory */
|
||||
private $userGroupFactory;
|
||||
|
||||
/** @var DisplayGroupFactory */
|
||||
private $displayGroupFactory;
|
||||
|
||||
/**
|
||||
* Command constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param UserGroupFactory $userGroupFactory
|
||||
* @param DisplayGroupFactory $displayGroupFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $userGroupFactory, $displayGroupFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
|
||||
$this->userGroupFactory = $userGroupFactory;
|
||||
$this->displayGroupFactory = $displayGroupFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->notificationId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Owner
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add User Group Notification
|
||||
* @param UserGroup $userGroup
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function assignUserGroup($userGroup)
|
||||
{
|
||||
$this->load();
|
||||
|
||||
if (!in_array($userGroup, $this->userGroups)) {
|
||||
$this->userGroups[] = $userGroup;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Display Group
|
||||
* @param DisplayGroup $displayGroup
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function assignDisplayGroup($displayGroup)
|
||||
{
|
||||
$this->load();
|
||||
|
||||
if (!in_array($displayGroup, $this->displayGroups)) {
|
||||
$this->displayGroups[] = $displayGroup;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (empty($this->subject)) {
|
||||
throw new InvalidArgumentException(__('Please provide a subject'), 'subject');
|
||||
}
|
||||
|
||||
if (empty($this->body)) {
|
||||
throw new InvalidArgumentException(__('Please provide a body'), 'body');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load
|
||||
* @param array $options
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function load($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'loadUserGroups' => true,
|
||||
'loadDisplayGroups' => true,
|
||||
], $options);
|
||||
|
||||
if ($this->loaded || $this->notificationId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the Display Groups and User Group Notifications
|
||||
if ($options['loadUserGroups']) {
|
||||
$this->userGroups = $this->userGroupFactory->getByNotificationId($this->notificationId);
|
||||
}
|
||||
|
||||
if ($options['loadDisplayGroups']) {
|
||||
$this->displayGroups = $this->displayGroupFactory->getByNotificationId($this->notificationId);
|
||||
}
|
||||
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save Notification
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save(): void
|
||||
{
|
||||
$this->validate();
|
||||
|
||||
$isNewRecord = false;
|
||||
if ($this->notificationId == null) {
|
||||
$isNewRecord = true;
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
|
||||
$this->manageAssignments($isNewRecord);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Notification
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
// Remove all links
|
||||
$this->getStore()->update(
|
||||
'DELETE FROM `lknotificationuser` WHERE `notificationId` = :notificationId',
|
||||
['notificationId' => $this->notificationId]
|
||||
);
|
||||
|
||||
$this->getStore()->update(
|
||||
'DELETE FROM `lknotificationgroup` WHERE `notificationId` = :notificationId',
|
||||
['notificationId' => $this->notificationId]
|
||||
);
|
||||
|
||||
$this->getStore()->update(
|
||||
'DELETE FROM `lknotificationdg` WHERE `notificationId` = :notificationId',
|
||||
['notificationId' => $this->notificationId]
|
||||
);
|
||||
|
||||
// Remove the notification
|
||||
$this->getStore()->update(
|
||||
'DELETE FROM `notification` WHERE `notificationId` = :notificationId',
|
||||
['notificationId' => $this->notificationId]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add to DB
|
||||
*/
|
||||
private function add()
|
||||
{
|
||||
$this->notificationId = $this->getStore()->insert('
|
||||
INSERT INTO `notification` (
|
||||
`subject`,
|
||||
`body`,
|
||||
`createDt`,
|
||||
`releaseDt`,
|
||||
`isInterrupt`,
|
||||
`isSystem`,
|
||||
`userId`,
|
||||
`filename`,
|
||||
`originalFileName`,
|
||||
`nonusers`,
|
||||
`type`
|
||||
)
|
||||
VALUES (
|
||||
:subject,
|
||||
:body,
|
||||
:createDt,
|
||||
:releaseDt,
|
||||
:isInterrupt,
|
||||
:isSystem,
|
||||
:userId,
|
||||
:filename,
|
||||
:originalFileName,
|
||||
:nonusers,
|
||||
:type
|
||||
)
|
||||
', [
|
||||
'subject' => $this->subject,
|
||||
'body' => $this->body,
|
||||
'createDt' => $this->createDt,
|
||||
'releaseDt' => $this->releaseDt,
|
||||
'isInterrupt' => $this->isInterrupt,
|
||||
'isSystem' => $this->isSystem,
|
||||
'userId' => $this->userId,
|
||||
'filename' => $this->filename,
|
||||
'originalFileName' => $this->originalFileName,
|
||||
'nonusers' => $this->nonusers,
|
||||
'type' => $this->type ?? 'custom'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update in DB
|
||||
*/
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `notification` SET `subject` = :subject,
|
||||
`body` = :body,
|
||||
`createDt` = :createDt,
|
||||
`releaseDt` = :releaseDt,
|
||||
`isInterrupt` = :isInterrupt,
|
||||
`isSystem` = :isSystem,
|
||||
`userId` = :userId,
|
||||
`filename` = :filename,
|
||||
`originalFileName` = :originalFileName,
|
||||
`nonusers` = :nonusers,
|
||||
`type` = :type
|
||||
WHERE `notificationId` = :notificationId
|
||||
', [
|
||||
'subject' => $this->subject,
|
||||
'body' => $this->body,
|
||||
'createDt' => $this->createDt,
|
||||
'releaseDt' => $this->releaseDt,
|
||||
'isInterrupt' => $this->isInterrupt,
|
||||
'isSystem' => $this->isSystem,
|
||||
'userId' => $this->userId,
|
||||
'filename' => $this->filename,
|
||||
'originalFileName' => $this->originalFileName,
|
||||
'nonusers' => $this->nonusers,
|
||||
'type' => $this->type ?? 'custom',
|
||||
'notificationId' => $this->notificationId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage assignements in DB
|
||||
*/
|
||||
private function manageAssignments(bool $isNewRecord): void
|
||||
{
|
||||
$this->linkUserGroups();
|
||||
|
||||
// Only unlink if we're not new (otherwise there is no point as we can't have any links yet)
|
||||
if (!$isNewRecord) {
|
||||
$this->unlinkUserGroups();
|
||||
}
|
||||
|
||||
$this->linkDisplayGroups();
|
||||
|
||||
if (!$isNewRecord) {
|
||||
$this->unlinkDisplayGroups();
|
||||
}
|
||||
|
||||
$this->manageRealisedUserLinks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage the links in the User notification table
|
||||
*/
|
||||
private function manageRealisedUserLinks(bool $isNewRecord = false): void
|
||||
{
|
||||
if (!$isNewRecord) {
|
||||
// Delete links that no longer exist
|
||||
$this->getStore()->update('
|
||||
DELETE FROM `lknotificationuser`
|
||||
WHERE `notificationId` = :notificationId AND `userId` NOT IN (
|
||||
SELECT `userId`
|
||||
FROM `lkusergroup`
|
||||
INNER JOIN `lknotificationgroup`
|
||||
ON `lknotificationgroup`.groupId = `lkusergroup`.groupId
|
||||
WHERE `lknotificationgroup`.notificationId = :notificationId2
|
||||
) AND userId <> 0
|
||||
', [
|
||||
'notificationId' => $this->notificationId,
|
||||
'notificationId2' => $this->notificationId
|
||||
]);
|
||||
}
|
||||
|
||||
// Pop in new links following from this adjustment
|
||||
$this->getStore()->update('
|
||||
INSERT INTO `lknotificationuser` (`notificationId`, `userId`, `read`, `readDt`, `emailDt`)
|
||||
SELECT DISTINCT :notificationId, `userId`, 0, 0, 0
|
||||
FROM `lkusergroup`
|
||||
INNER JOIN `lknotificationgroup`
|
||||
ON `lknotificationgroup`.groupId = `lkusergroup`.groupId
|
||||
WHERE `lknotificationgroup`.notificationId = :notificationId2
|
||||
ON DUPLICATE KEY UPDATE userId = `lknotificationuser`.userId
|
||||
', [
|
||||
'notificationId' => $this->notificationId,
|
||||
'notificationId2' => $this->notificationId
|
||||
]);
|
||||
|
||||
if ($this->isSystem) {
|
||||
$this->getStore()->insert('
|
||||
INSERT INTO `lknotificationuser` (`notificationId`, `userId`, `read`, `readDt`, `emailDt`)
|
||||
VALUES (:notificationId, :userId, 0, 0, 0)
|
||||
ON DUPLICATE KEY UPDATE userId = `lknotificationuser`.userId
|
||||
', [
|
||||
'notificationId' => $this->notificationId,
|
||||
'userId' => $this->userId
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link User Groups
|
||||
*/
|
||||
private function linkUserGroups()
|
||||
{
|
||||
foreach ($this->userGroups as $userGroup) {
|
||||
/* @var UserGroup $userGroup */
|
||||
$this->getStore()->update('INSERT INTO `lknotificationgroup` (notificationId, groupId) VALUES (:notificationId, :userGroupId) ON DUPLICATE KEY UPDATE groupId = groupId', [
|
||||
'notificationId' => $this->notificationId,
|
||||
'userGroupId' => $userGroup->groupId
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink User Groups
|
||||
*/
|
||||
private function unlinkUserGroups()
|
||||
{
|
||||
// Unlink any userGroup that is NOT in the collection
|
||||
$params = ['notificationId' => $this->notificationId];
|
||||
|
||||
$sql = 'DELETE FROM `lknotificationgroup` WHERE notificationId = :notificationId AND groupId NOT IN (0';
|
||||
|
||||
$i = 0;
|
||||
foreach ($this->userGroups as $userGroup) {
|
||||
/* @var UserGroup $userGroup */
|
||||
$i++;
|
||||
$sql .= ',:userGroupId' . $i;
|
||||
$params['userGroupId' . $i] = $userGroup->groupId;
|
||||
}
|
||||
|
||||
$sql .= ')';
|
||||
|
||||
$this->getStore()->update($sql, $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link Display Groups
|
||||
*/
|
||||
private function linkDisplayGroups()
|
||||
{
|
||||
foreach ($this->displayGroups as $displayGroup) {
|
||||
/* @var DisplayGroup $displayGroup */
|
||||
$this->getStore()->update('INSERT INTO `lknotificationdg` (notificationId, displayGroupId) VALUES (:notificationId, :displayGroupId) ON DUPLICATE KEY UPDATE displayGroupId = displayGroupId', [
|
||||
'notificationId' => $this->notificationId,
|
||||
'displayGroupId' => $displayGroup->displayGroupId
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink Display Groups
|
||||
*/
|
||||
private function unlinkDisplayGroups()
|
||||
{
|
||||
// Unlink any displayGroup that is NOT in the collection
|
||||
$params = ['notificationId' => $this->notificationId];
|
||||
|
||||
$sql = 'DELETE FROM `lknotificationdg` WHERE notificationId = :notificationId AND displayGroupId NOT IN (0';
|
||||
|
||||
$i = 0;
|
||||
foreach ($this->displayGroups as $displayGroup) {
|
||||
/* @var DisplayGroup $displayGroup */
|
||||
$i++;
|
||||
$sql .= ',:displayGroupId' . $i;
|
||||
$params['displayGroupId' . $i] = $displayGroup->displayGroupId;
|
||||
}
|
||||
|
||||
$sql .= ')';
|
||||
|
||||
$this->getStore()->update($sql, $params);
|
||||
}
|
||||
}
|
||||
209
lib/Entity/Permission.php
Normal file
209
lib/Entity/Permission.php
Normal file
@@ -0,0 +1,209 @@
|
||||
<?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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class Permission
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Permission implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of this Permission Record")
|
||||
* @var int
|
||||
*/
|
||||
public $permissionId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Entity ID that this Permission refers to")
|
||||
* @var int
|
||||
*/
|
||||
public $entityId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The User Group ID that this permission refers to")
|
||||
* @var int
|
||||
*/
|
||||
public $groupId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The object ID that this permission refers to")
|
||||
* @var int
|
||||
*/
|
||||
public $objectId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether the groupId refers to a user specific group")
|
||||
* @var int
|
||||
*/
|
||||
public $isUser;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The entity name that this refers to")
|
||||
* @var string
|
||||
*/
|
||||
public $entity;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Legacy for when the Object ID is a string")
|
||||
* @var string
|
||||
*/
|
||||
public $objectIdString;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The group name that this refers to")
|
||||
* @var string
|
||||
*/
|
||||
public $group;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether view permission is granted")
|
||||
* @var int
|
||||
*/
|
||||
public $view;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether edit permission is granted")
|
||||
* @var int
|
||||
*/
|
||||
public $edit;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether delete permission is granted")
|
||||
* @var int
|
||||
*/
|
||||
public $delete;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether modify permission permission is granted.")
|
||||
* @var int
|
||||
*/
|
||||
public $modifyPermissions;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->permissionId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save this permission
|
||||
* @return void
|
||||
*/
|
||||
public function save(): void
|
||||
{
|
||||
if ($this->permissionId == 0) {
|
||||
// Check there is something to add
|
||||
if ($this->view != 0 || $this->edit != 0 || $this->delete != 0) {
|
||||
$this->getLog()->debug(sprintf(
|
||||
'save: Adding Permission for %s, %d. GroupId: %d - View = %d, Edit = %d, Delete = %d',
|
||||
$this->entity,
|
||||
$this->objectId,
|
||||
$this->groupId,
|
||||
$this->view,
|
||||
$this->edit,
|
||||
$this->delete,
|
||||
));
|
||||
|
||||
$this->add();
|
||||
}
|
||||
} else {
|
||||
$this->getLog()->debug(sprintf(
|
||||
'save: Editing Permission for %s, %d. GroupId: %d - View = %d, Edit = %d, Delete = %d',
|
||||
$this->entity,
|
||||
$this->objectId,
|
||||
$this->groupId,
|
||||
$this->view,
|
||||
$this->edit,
|
||||
$this->delete,
|
||||
));
|
||||
|
||||
// If all permissions are set to 0, then we delete the record to tidy up
|
||||
if ($this->view == 0 && $this->edit == 0 && $this->delete == 0) {
|
||||
$this->delete();
|
||||
} else if (count($this->getChangedProperties()) > 0) {
|
||||
// Something has changed, so run the update.
|
||||
$this->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->permissionId = $this->getStore()->insert('INSERT INTO `permission` (`entityId`, `groupId`, `objectId`, `view`, `edit`, `delete`) VALUES (:entityId, :groupId, :objectId, :view, :edit, :delete)', array(
|
||||
'entityId' => $this->entityId,
|
||||
'objectId' => $this->objectId,
|
||||
'groupId' => $this->groupId,
|
||||
'view' => $this->view,
|
||||
'edit' => $this->edit,
|
||||
'delete' => $this->delete,
|
||||
));
|
||||
}
|
||||
|
||||
private function update()
|
||||
{
|
||||
$this->getStore()->update('UPDATE `permission` SET `view` = :view, `edit` = :edit, `delete` = :delete WHERE `entityId` = :entityId AND `groupId` = :groupId AND `objectId` = :objectId', array(
|
||||
'entityId' => $this->entityId,
|
||||
'objectId' => $this->objectId,
|
||||
'groupId' => $this->groupId,
|
||||
'view' => $this->view,
|
||||
'edit' => $this->edit,
|
||||
'delete' => $this->delete,
|
||||
));
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$this->getLog()->debug(sprintf('Deleting Permission for %s, %d', $this->entity, $this->objectId));
|
||||
$this->getStore()->update('DELETE FROM `permission` WHERE entityId = :entityId AND objectId = :objectId AND groupId = :groupId', array(
|
||||
'entityId' => $this->entityId,
|
||||
'objectId' => $this->objectId,
|
||||
'groupId' => $this->groupId
|
||||
));
|
||||
}
|
||||
|
||||
public function deleteAll()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `permission` WHERE entityId = :entityId AND objectId = :objectId', array(
|
||||
'entityId' => $this->entityId,
|
||||
'objectId' => $this->objectId,
|
||||
));
|
||||
}
|
||||
}
|
||||
119
lib/Entity/PlayerFault.php
Normal file
119
lib/Entity/PlayerFault.php
Normal file
@@ -0,0 +1,119 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class PlayerFault implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Fault Id")
|
||||
* @var int
|
||||
*/
|
||||
public $playerFaultId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Display Id")
|
||||
* @var int
|
||||
*/
|
||||
public $displayId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Date the error occured")
|
||||
* @var string
|
||||
*/
|
||||
public $incidentDt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Date the error expires")
|
||||
* @var string
|
||||
*/
|
||||
public $expires;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Code associated with the fault")
|
||||
* @var int
|
||||
*/
|
||||
public $code;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Reason for the fault")
|
||||
* @var string
|
||||
*/
|
||||
public $reason;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Layout Id")
|
||||
* @var int
|
||||
*/
|
||||
public $layoutId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Region Id")
|
||||
* @var int
|
||||
*/
|
||||
public $regionId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Schedule Id")
|
||||
* @var int
|
||||
*/
|
||||
public $scheduleId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Widget Id")
|
||||
* @var int
|
||||
*/
|
||||
public $widgetId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Media Id")
|
||||
* @var int
|
||||
*/
|
||||
public $mediaId;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('Player Fault Id %d, Code %d, Reason %s, Date %s', $this->playerFaultId, $this->code, $this->reason, $this->incidentDt);
|
||||
}
|
||||
}
|
||||
466
lib/Entity/PlayerVersion.php
Normal file
466
lib/Entity/PlayerVersion.php
Normal file
@@ -0,0 +1,466 @@
|
||||
<?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\Entity;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Slim\Http\ServerRequest;
|
||||
use Symfony\Component\Filesystem\Filesystem;
|
||||
use Xibo\Factory\MediaFactory;
|
||||
use Xibo\Factory\PlayerVersionFactory;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Helper\HttpsDetect;
|
||||
use Xibo\Service\ConfigServiceInterface;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\DuplicateEntityException;
|
||||
use Xibo\Support\Exception\GeneralException;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class PlayerVersion
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class PlayerVersion implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Version ID")
|
||||
* @var int
|
||||
*/
|
||||
public $versionId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Player type")
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Version number")
|
||||
* @var string
|
||||
*/
|
||||
public $version;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Code number")
|
||||
* @var int
|
||||
*/
|
||||
public $code;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Player version to show")
|
||||
* @var string
|
||||
*/
|
||||
public $playerShowVersion;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Player Version created date")
|
||||
* @var string
|
||||
*/
|
||||
public $createdAt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Player Version modified date")
|
||||
* @var string
|
||||
*/
|
||||
public $modifiedAt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The name of the user that modified this Player Version last")
|
||||
* @var string
|
||||
*/
|
||||
public $modifiedBy;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Player Version file name")
|
||||
* @var string
|
||||
*/
|
||||
public $fileName;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Player Version file size in bytes")
|
||||
* @var int
|
||||
*/
|
||||
public $size;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A MD5 checksum of the stored Player Version file")
|
||||
* @var string
|
||||
*/
|
||||
public $md5;
|
||||
|
||||
/**
|
||||
* @var ConfigServiceInterface
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var PlayerVersionFactory
|
||||
*/
|
||||
private $playerVersionFactory;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param ConfigServiceInterface $config
|
||||
* @param MediaFactory $mediaFactory
|
||||
* @param PlayerVersionFactory $playerVersionFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $config, $playerVersionFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
|
||||
$this->config = $config;
|
||||
$this->playerVersionFactory = $playerVersionFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add
|
||||
*/
|
||||
private function add()
|
||||
{
|
||||
$this->versionId = $this->getStore()->insert('
|
||||
INSERT INTO `player_software` (`player_type`, `player_version`, `player_code`, `playerShowVersion`,`createdAt`, `modifiedAt`, `modifiedBy`, `fileName`, `size`, `md5`)
|
||||
VALUES (:type, :version, :code, :playerShowVersion, :createdAt, :modifiedAt, :modifiedBy, :fileName, :size, :md5)
|
||||
', [
|
||||
'type' => $this->type,
|
||||
'version' => $this->version,
|
||||
'code' => $this->code,
|
||||
'playerShowVersion' => $this->playerShowVersion,
|
||||
'createdAt' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
'modifiedAt' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
'modifiedBy' => $this->modifiedBy,
|
||||
'fileName' => $this->fileName,
|
||||
'size' => $this->size,
|
||||
'md5' => $this->md5
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
*/
|
||||
private function edit()
|
||||
{
|
||||
$sql = '
|
||||
UPDATE `player_software`
|
||||
SET `player_version` = :version,
|
||||
`player_code` = :code,
|
||||
`playerShowVersion` = :playerShowVersion,
|
||||
`modifiedAt` = :modifiedAt,
|
||||
`modifiedBy` = :modifiedBy
|
||||
WHERE versionId = :versionId
|
||||
';
|
||||
|
||||
$params = [
|
||||
'version' => $this->version,
|
||||
'code' => $this->code,
|
||||
'playerShowVersion' => $this->playerShowVersion,
|
||||
'modifiedAt' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
'modifiedBy' => $this->modifiedBy,
|
||||
'versionId' => $this->versionId
|
||||
];
|
||||
|
||||
$this->getStore()->update($sql, $params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->load();
|
||||
|
||||
// delete record
|
||||
$this->getStore()->update('DELETE FROM `player_software` WHERE `versionId` = :versionId', [
|
||||
'versionId' => $this->versionId
|
||||
]);
|
||||
|
||||
// Library location
|
||||
$libraryLocation = $this->config->getSetting('LIBRARY_LOCATION');
|
||||
|
||||
// delete file
|
||||
if (file_exists($libraryLocation . 'playersoftware/' . $this->fileName)) {
|
||||
unlink($libraryLocation . 'playersoftware/' . $this->fileName);
|
||||
}
|
||||
|
||||
// delete unpacked file
|
||||
if (is_dir($libraryLocation . 'playersoftware/chromeos/' . $this->versionId)) {
|
||||
(new Filesystem())->remove($libraryLocation . 'playersoftware/chromeos/' . $this->versionId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Xibo\Support\Exception\InvalidArgumentException
|
||||
*/
|
||||
public function unpack(string $libraryFolder, ServerRequest $request): static
|
||||
{
|
||||
// ChromeOS
|
||||
// Unpack the `.chrome` file as a tar/gz, validate its signature, extract it into the library folder
|
||||
if ($this->type === 'chromeOS') {
|
||||
$this->getLog()->debug('add: handling chromeOS upload');
|
||||
|
||||
$fullFileName = $libraryFolder . 'playersoftware/' . $this->fileName;
|
||||
|
||||
// Check the signature of the file to make sure it comes from a verified source.
|
||||
try {
|
||||
$this->getLog()->debug('unpack: loading gnupg to verify the signature');
|
||||
|
||||
$gpg = new \gnupg();
|
||||
$gpg->seterrormode(\gnupg::ERROR_EXCEPTION);
|
||||
$info = $gpg->verify(
|
||||
file_get_contents($fullFileName),
|
||||
false,
|
||||
);
|
||||
|
||||
if ($info === false
|
||||
|| $info[0]['fingerprint'] !== '10415C506BE63E70BAF1D58BC1EF165A0F880F75'
|
||||
|| $info[0]['status'] !== 0
|
||||
|| $info[0]['summary'] !== 0
|
||||
) {
|
||||
$this->getLog()->error('unpack: unable to verify GPG. file = ' . $this->fileName);
|
||||
throw new GeneralException();
|
||||
}
|
||||
|
||||
$this->getLog()->debug('unpack: signature verified');
|
||||
|
||||
// Signature verified, move the file, so we can decrypt it.
|
||||
rename($fullFileName, $libraryFolder . 'playersoftware/' . $this->versionId . '.gpg');
|
||||
|
||||
$this->getLog()->debug('unpack: using the shell to decrypt the file');
|
||||
|
||||
// Go to the shell to decrypt it.
|
||||
shell_exec('gpg --decrypt --output ' . $libraryFolder . 'playersoftware/' . $this->versionId
|
||||
. ' ' . $libraryFolder . 'playersoftware/' . $this->versionId . '.gpg');
|
||||
|
||||
// Was this successful?
|
||||
if (!file_exists($libraryFolder . 'playersoftware/' . $this->versionId)) {
|
||||
throw new NotFoundException('Not found after decryption');
|
||||
}
|
||||
|
||||
// Rename the GPG file back to its original name.
|
||||
rename($libraryFolder . 'playersoftware/' . $this->versionId . '.gpg', $fullFileName);
|
||||
} catch (\Exception $e) {
|
||||
$this->getLog()->error('unpack: ' . $e->getMessage());
|
||||
throw new InvalidArgumentException(__('Package file unsupported or invalid'));
|
||||
}
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
if (!$zip->open($libraryFolder . 'playersoftware/' . $this->versionId)) {
|
||||
throw new InvalidArgumentException(__('Unable to open ZIP'));
|
||||
}
|
||||
|
||||
// Make sure the ZIP file contains a manifest.json file.
|
||||
if ($zip->locateName('manifest.json') === false) {
|
||||
throw new InvalidArgumentException(__('Software package does not contain a manifest'));
|
||||
}
|
||||
|
||||
// Make a folder for this
|
||||
$folder = $libraryFolder . 'playersoftware/chromeos/' . $this->versionId;
|
||||
if (is_dir($folder)) {
|
||||
unlink($folder);
|
||||
}
|
||||
mkdir($folder);
|
||||
|
||||
// Extract to that folder
|
||||
$zip->extractTo($folder);
|
||||
$zip->close();
|
||||
|
||||
// Update manifest.json
|
||||
$manifest = json_decode(file_get_contents($folder . '/manifest.json'), true);
|
||||
|
||||
$isXiboThemed = $this->config->getThemeConfig('app_name', 'Xibo') === 'Xibo';
|
||||
if (!$isXiboThemed) {
|
||||
$manifest['id'] = $this->config->getThemeConfig('theme_url');
|
||||
$manifest['name'] = $this->config->getThemeConfig('theme_name');
|
||||
$manifest['description'] = $this->config->getThemeConfig('theme_title');
|
||||
$manifest['short_name'] = $this->config->getThemeConfig('app_name') . '-chromeos';
|
||||
}
|
||||
|
||||
// Start URL if we're running in a sub-folder.
|
||||
$manifest['start_url'] = (new HttpsDetect())->getBaseUrl($request) . '/pwa';
|
||||
|
||||
// Update asset URLs
|
||||
for ($i = 0; $i < count($manifest['icons']); $i++) {
|
||||
if ($manifest['icons'][$i]['sizes'] == '512x512') {
|
||||
$manifest['icons'][$i]['src'] = $this->config->uri('img/512x512.png');
|
||||
} else {
|
||||
$manifest['icons'][$i]['src'] = $this->config->uri('img/192x192.png');
|
||||
}
|
||||
}
|
||||
|
||||
file_put_contents($folder . '/manifest.json', json_encode($manifest));
|
||||
|
||||
// Unlink our decrypted file
|
||||
unlink($libraryFolder . 'playersoftware/' . $this->versionId);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setActive(): static
|
||||
{
|
||||
if ($this->type === 'chromeOS') {
|
||||
$this->getLog()->debug('setActive: set this version to be the latest');
|
||||
|
||||
$chromeLocation = $this->config->getSetting('LIBRARY_LOCATION') . 'playersoftware/chromeos';
|
||||
if (is_link($chromeLocation . '/latest')) {
|
||||
unlink($chromeLocation . '/latest');
|
||||
}
|
||||
symlink($chromeLocation . '/' . $this->versionId, $chromeLocation . '/latest');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
if ($this->loaded || $this->versionId == null)
|
||||
return;
|
||||
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save this media
|
||||
* @param array $options
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true
|
||||
], $options);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($this->versionId == null || $this->versionId == 0) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
|
||||
public function validate() {
|
||||
// do we already have a file with the same exact name?
|
||||
$params = [];
|
||||
$checkSQL = 'SELECT `fileName` FROM `player_software` WHERE `fileName` = :fileName';
|
||||
|
||||
if ($this->versionId != null) {
|
||||
$checkSQL .= ' AND `versionId` <> :versionId ';
|
||||
$params['versionId'] = $this->versionId;
|
||||
}
|
||||
|
||||
$params['fileName'] = $this->fileName;
|
||||
|
||||
$result = $this->getStore()->select($checkSQL, $params);
|
||||
|
||||
if (count($result) > 0) {
|
||||
throw new DuplicateEntityException(__('You already own Player Version file with this name.'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function decorateRecord(): static
|
||||
{
|
||||
$version = '';
|
||||
$code = null;
|
||||
$type = '';
|
||||
$explode = explode('_', $this->fileName);
|
||||
$explodeExt = explode('.', $this->fileName);
|
||||
$playerShowVersion = $explodeExt[0];
|
||||
|
||||
// standard releases
|
||||
if (count($explode) === 5) {
|
||||
if (str_contains($explode[4], '.')) {
|
||||
$explodeExtension = explode('.', $explode[4]);
|
||||
$explode[4] = $explodeExtension[0];
|
||||
}
|
||||
|
||||
if (str_contains($explode[3], 'v')) {
|
||||
$version = strtolower(substr(strrchr($explode[3], 'v'), 1, 3)) ;
|
||||
}
|
||||
if (str_contains($explode[4], 'R')) {
|
||||
$code = strtolower(substr(strrchr($explode[4], 'R'), 1, 3)) ;
|
||||
}
|
||||
$playerShowVersion = $version . ' Revision ' . $code;
|
||||
// for DSDevices specific apk
|
||||
} elseif (count($explode) === 6) {
|
||||
if (str_contains($explode[5], '.')) {
|
||||
$explodeExtension = explode('.', $explode[5]);
|
||||
$explode[5] = $explodeExtension[0];
|
||||
}
|
||||
if (str_contains($explode[3], 'v')) {
|
||||
$version = strtolower(substr(strrchr($explode[3], 'v'), 1, 3)) ;
|
||||
}
|
||||
if (str_contains($explode[4], 'R')) {
|
||||
$code = strtolower(substr(strrchr($explode[4], 'R'), 1, 3)) ;
|
||||
}
|
||||
$playerShowVersion = $version . ' Revision ' . $code . ' ' . $explode[5];
|
||||
// for white labels
|
||||
} elseif (count($explode) === 3) {
|
||||
if (str_contains($explode[2], '.')) {
|
||||
$explodeExtension = explode('.', $explode[2]);
|
||||
$explode[2] = $explodeExtension[0];
|
||||
}
|
||||
if (str_contains($explode[1], 'v')) {
|
||||
$version = strtolower(substr(strrchr($explode[1], 'v'), 1, 3)) ;
|
||||
}
|
||||
if (str_contains($explode[2], 'R')) {
|
||||
$code = strtolower(substr(strrchr($explode[2], 'R'), 1, 3)) ;
|
||||
}
|
||||
$playerShowVersion = $version . ' Revision ' . $code . ' ' . $explode[0];
|
||||
}
|
||||
|
||||
$extension = strtolower(substr(strrchr($this->fileName, '.'), 1));
|
||||
|
||||
if ($extension == 'apk') {
|
||||
$type = 'android';
|
||||
} else if ($extension == 'ipk') {
|
||||
$type = 'lg';
|
||||
} else if ($extension == 'wgt') {
|
||||
$type = 'sssp';
|
||||
} else if ($extension == 'chrome') {
|
||||
$type = 'chromeOS';
|
||||
}
|
||||
|
||||
$this->version = $version;
|
||||
$this->code = $code;
|
||||
$this->playerShowVersion = $playerShowVersion;
|
||||
$this->type = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
1190
lib/Entity/Playlist.php
Normal file
1190
lib/Entity/Playlist.php
Normal file
File diff suppressed because it is too large
Load Diff
654
lib/Entity/Region.php
Normal file
654
lib/Entity/Region.php
Normal file
@@ -0,0 +1,654 @@
|
||||
<?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\Entity;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Xibo\Factory\ActionFactory;
|
||||
use Xibo\Factory\CampaignFactory;
|
||||
use Xibo\Factory\PermissionFactory;
|
||||
use Xibo\Factory\PlaylistFactory;
|
||||
use Xibo\Factory\RegionFactory;
|
||||
use Xibo\Factory\RegionOptionFactory;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\GeneralException;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class Region
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Region implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of this region")
|
||||
* @var int
|
||||
*/
|
||||
public $regionId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Layout ID this region belongs to")
|
||||
* @var int
|
||||
*/
|
||||
public $layoutId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The userId of the User that owns this Region")
|
||||
* @var int
|
||||
*/
|
||||
public $ownerId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Region Type, zone, playlist, frame or canvas")
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The name of this Region")
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Width of the region")
|
||||
* @var double
|
||||
*/
|
||||
public $width;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Height of the Region")
|
||||
* @var double
|
||||
*/
|
||||
public $height;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The top coordinate of the Region")
|
||||
* @var double
|
||||
*/
|
||||
public $top;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The left coordinate of the Region")
|
||||
* @var double
|
||||
*/
|
||||
public $left;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The z-index of the Region to control Layering")
|
||||
* @var int
|
||||
*/
|
||||
public $zIndex;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The syncKey of this Region")
|
||||
* @var string
|
||||
*/
|
||||
public $syncKey;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="An array of Region Options")
|
||||
* @var RegionOption[]
|
||||
*/
|
||||
public $regionOptions = [];
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="An array of Permissions")
|
||||
* @var Permission[]
|
||||
*/
|
||||
public $permissions = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
* @SWG\Property(
|
||||
* description="A read-only estimate of this Regions's total duration in seconds. This is valid when the parent layout status is 1 or 2."
|
||||
* )
|
||||
*/
|
||||
public $duration;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag, whether this region is used as an interactive drawer attached to a layout.")
|
||||
* @var int
|
||||
*/
|
||||
public $isDrawer = 0;
|
||||
|
||||
/** @var Action[] */
|
||||
public $actions = [];
|
||||
|
||||
/**
|
||||
* Temporary Id used during import/upgrade
|
||||
* @var string read only string
|
||||
*/
|
||||
public $tempId = null;
|
||||
|
||||
/**
|
||||
* @var Playlist|null
|
||||
* @SWG\Property(
|
||||
* description="This Regions Playlist - null if getPlaylist() has not been called."
|
||||
* )
|
||||
*/
|
||||
public $regionPlaylist = null;
|
||||
|
||||
//<editor-fold desc="Factories and Dependencies">
|
||||
|
||||
/**
|
||||
* @var RegionFactory
|
||||
*/
|
||||
private $regionFactory;
|
||||
|
||||
/**
|
||||
* @var RegionOptionFactory
|
||||
*/
|
||||
private $regionOptionFactory;
|
||||
|
||||
/**
|
||||
* @var PermissionFactory
|
||||
*/
|
||||
private $permissionFactory;
|
||||
|
||||
/**
|
||||
* @var PlaylistFactory
|
||||
*/
|
||||
private $playlistFactory;
|
||||
|
||||
/** @var ActionFactory */
|
||||
private $actionFactory;
|
||||
|
||||
/** @var CampaignFactory */
|
||||
private $campaignFactory;
|
||||
|
||||
//</editor-fold>
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param RegionFactory $regionFactory
|
||||
* @param PermissionFactory $permissionFactory
|
||||
* @param RegionOptionFactory $regionOptionFactory
|
||||
* @param PlaylistFactory $playlistFactory
|
||||
* @param ActionFactory $actionFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $regionFactory, $permissionFactory, $regionOptionFactory, $playlistFactory, $actionFactory, $campaignFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
$this->regionFactory = $regionFactory;
|
||||
$this->permissionFactory = $permissionFactory;
|
||||
$this->regionOptionFactory = $regionOptionFactory;
|
||||
$this->playlistFactory = $playlistFactory;
|
||||
$this->actionFactory = $actionFactory;
|
||||
$this->campaignFactory = $campaignFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone this object
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
// Clear the regionId, clone the Playlist
|
||||
$this->regionId = null;
|
||||
$this->hash = null;
|
||||
$this->permissions = [];
|
||||
|
||||
$this->regionPlaylist = clone $this->regionPlaylist;
|
||||
|
||||
$this->regionOptions = array_map(function ($object) { return clone $object; }, $this->regionOptions);
|
||||
// Clone actions
|
||||
$this->actions = array_map(function ($object) { return clone $object; }, $this->actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('Region %s - %d x %d (%d, %d). RegionId = %d, LayoutId = %d. OwnerId = %d. Duration = %d', $this->name, $this->width, $this->height, $this->top, $this->left, $this->regionId, $this->layoutId, $this->ownerId, $this->duration);
|
||||
}
|
||||
|
||||
public function getPermissionFolderId()
|
||||
{
|
||||
return $this->getPlaylist()->permissionsFolderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private function hash()
|
||||
{
|
||||
return md5($this->name
|
||||
. $this->type
|
||||
. $this->ownerId
|
||||
. $this->width
|
||||
. $this->height
|
||||
. $this->top
|
||||
. $this->left
|
||||
. $this->regionId
|
||||
. $this->zIndex
|
||||
. $this->duration
|
||||
. $this->syncKey
|
||||
. json_encode($this->actions));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->regionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OwnerId
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return $this->ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the Owner
|
||||
* @param int $ownerId
|
||||
* @param bool $cascade Cascade ownership change down to Playlist records
|
||||
* @throws GeneralException
|
||||
*/
|
||||
public function setOwner($ownerId, $cascade = false)
|
||||
{
|
||||
$this->load();
|
||||
|
||||
$this->ownerId = $ownerId;
|
||||
|
||||
if ($cascade) {
|
||||
$playlist = $this->getPlaylist();
|
||||
$playlist->setOwner($ownerId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Option
|
||||
* @param string $option
|
||||
* @return RegionOption
|
||||
* @throws GeneralException
|
||||
*/
|
||||
public function getOption($option)
|
||||
{
|
||||
$this->load();
|
||||
|
||||
foreach ($this->regionOptions as $regionOption) {
|
||||
/* @var RegionOption $regionOption */
|
||||
if ($regionOption->option == $option)
|
||||
return $regionOption;
|
||||
}
|
||||
|
||||
$this->getLog()->debug('RegionOption ' . $option . ' not found');
|
||||
|
||||
throw new NotFoundException(__('Region Option not found'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Region Option Value
|
||||
* @param string $option
|
||||
* @param mixed $default
|
||||
* @return mixed
|
||||
* @throws GeneralException
|
||||
*/
|
||||
public function getOptionValue($option, $default = null)
|
||||
{
|
||||
$this->load();
|
||||
|
||||
try {
|
||||
$regionOption = $this->getOption($option);
|
||||
return $regionOption->value;
|
||||
}
|
||||
catch (NotFoundException $e) {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Region Option Value
|
||||
* @param string $option
|
||||
* @param mixed $value
|
||||
* @throws GeneralException
|
||||
*/
|
||||
public function setOptionValue($option, $value)
|
||||
{
|
||||
try {
|
||||
$this->getOption($option)->value = $value;
|
||||
}
|
||||
catch (NotFoundException $e) {
|
||||
$this->regionOptions[] = $this->regionOptionFactory->create($this->regionId, $option, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return Playlist
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function getPlaylist($options = [])
|
||||
{
|
||||
if ($this->regionPlaylist === null) {
|
||||
try {
|
||||
$this->regionPlaylist = $this->playlistFactory->getByRegionId($this->regionId)->load($options);
|
||||
} catch (NotFoundException $exception) {
|
||||
$this->regionPlaylist = $this->playlistFactory->create($this->name, $this->ownerId, $this->regionId);
|
||||
$this->regionPlaylist->save();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->regionPlaylist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load
|
||||
* @param array $options
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function load($options = [])
|
||||
{
|
||||
if ($this->loaded || $this->regionId == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$options = array_merge([
|
||||
'loadPlaylists' => false,
|
||||
'loadActions' => true
|
||||
], $options);
|
||||
|
||||
$this->getLog()->debug('Load Region with ' . json_encode($options));
|
||||
|
||||
// Load permissions
|
||||
$this->permissions = $this->permissionFactory->getByObjectId(get_class($this), $this->regionId);
|
||||
|
||||
// Get region options
|
||||
$this->regionOptions = $this->regionOptionFactory->getByRegionId($this->regionId);
|
||||
|
||||
// Get Region Actions?
|
||||
if ($options['loadActions']) {
|
||||
$this->actions = $this->actionFactory->getBySourceAndSourceId('region', $this->regionId);
|
||||
}
|
||||
|
||||
// Load the Playlist?
|
||||
if ($options['loadPlaylists']) {
|
||||
$this->getPlaylist($options);
|
||||
}
|
||||
|
||||
$this->hash = $this->hash();
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the region
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if ($this->width <= 0 || $this->height <= 0) {
|
||||
throw new InvalidArgumentException(__('The Region dimensions cannot be empty or negative'), 'width/height');
|
||||
}
|
||||
|
||||
// Check zindex is positive
|
||||
if ($this->zIndex < 0) {
|
||||
throw new InvalidArgumentException(__('Layer must be 0 or a positive number'), 'zIndex');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
* @param array $options
|
||||
* @throws GeneralException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'saveRegionOptions' => true,
|
||||
'validate' => true,
|
||||
'audit' => true,
|
||||
'notify' => true
|
||||
], $options);
|
||||
|
||||
$this->getLog()->debug('Saving ' . $this . '. Options = ' . json_encode($options, JSON_PRETTY_PRINT));
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($options['audit']) {
|
||||
// get the layout specific campaignId
|
||||
$campaignId = $this->campaignFactory->getCampaignIdForLayout($this->layoutId);
|
||||
}
|
||||
|
||||
if ($this->regionId == null || $this->regionId == 0) {
|
||||
// We are adding
|
||||
$this->add();
|
||||
|
||||
// Add and save a region specific playlist
|
||||
if ($this->regionPlaylist === null) {
|
||||
$this->regionPlaylist = $this->playlistFactory->create($this->name, $this->ownerId, $this->regionId);
|
||||
} else {
|
||||
// assert the region id
|
||||
$this->regionPlaylist->regionId = $this->regionId;
|
||||
$this->regionPlaylist->setOwner($this->ownerId);
|
||||
}
|
||||
|
||||
// TODO: this is strange, campaignId will only be set if we are configured to Audit.
|
||||
if (isset($campaignId)) {
|
||||
$campaign = $this->campaignFactory->getById($campaignId);
|
||||
$this->regionPlaylist->folderId = $campaign->folderId;
|
||||
$this->regionPlaylist->permissionsFolderId = $campaign->permissionsFolderId;
|
||||
}
|
||||
|
||||
$this->regionPlaylist->save($options);
|
||||
|
||||
// Audit
|
||||
if ($options['audit']) {
|
||||
$this->audit(
|
||||
$this->regionId,
|
||||
'Added',
|
||||
[
|
||||
'regionId' => $this->regionId,
|
||||
'campaignId' => $campaignId,
|
||||
'details' => (string)$this,
|
||||
]
|
||||
);
|
||||
}
|
||||
} else if ($this->hash != $this->hash()) {
|
||||
$this->update();
|
||||
|
||||
// There are 3 cases that we need to consider
|
||||
// 1 - Saving direct edit of region properties, $this->regionPlaylist will be null, as such we load it from database.
|
||||
// 2 - Saving whole Layout without changing ownership, $this->regionPlaylist will be populated including widgets property, we do not need to save widgets, load from database,
|
||||
// 3 - Saving whole Layout and changing the ownership (reassignAll or setOwner on Layout), in this case, we need to save widgets to cascade the ownerId change, don't load from database
|
||||
// case 3 due to - https://github.com/xibosignage/xibo/issues/2061
|
||||
$regionPlaylist = $this->playlistFactory->getByRegionId($this->regionId);
|
||||
|
||||
if ($this->regionPlaylist == null || $this->ownerId == $regionPlaylist->ownerId) {
|
||||
$this->regionPlaylist = $regionPlaylist;
|
||||
}
|
||||
|
||||
$this->regionPlaylist->name = $this->name;
|
||||
|
||||
if (isset($campaignId)) {
|
||||
$campaign = $this->campaignFactory->getById($campaignId);
|
||||
$this->regionPlaylist->folderId = $campaign->folderId;
|
||||
$this->regionPlaylist->permissionsFolderId = $campaign->permissionsFolderId;
|
||||
}
|
||||
|
||||
$this->regionPlaylist->save($options);
|
||||
|
||||
if ($options['audit'] && count($this->getChangedProperties()) > 0) {
|
||||
$change = $this->getChangedProperties();
|
||||
$change['campaignId'][] = $campaignId;
|
||||
$this->audit($this->regionId, 'Saved', $change);
|
||||
}
|
||||
}
|
||||
|
||||
if ($options['saveRegionOptions']) {
|
||||
// Save all Options
|
||||
foreach ($this->regionOptions as $regionOption) {
|
||||
/* @var RegionOption $regionOption */
|
||||
// Assert the regionId
|
||||
$regionOption->regionId = $this->regionId;
|
||||
$regionOption->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Region
|
||||
* @param array $options
|
||||
* @throws GeneralException
|
||||
*/
|
||||
public function delete($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'notify' => true
|
||||
], $options);
|
||||
|
||||
// We must ensure everything is loaded before we delete
|
||||
if ($this->hash == null) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
$this->getLog()->debug('Deleting ' . $this);
|
||||
|
||||
// Delete Permissions
|
||||
foreach ($this->permissions as $permission) {
|
||||
/* @var Permission $permission */
|
||||
$permission->deleteAll();
|
||||
}
|
||||
|
||||
// Delete all region options
|
||||
foreach ($this->regionOptions as $regionOption) {
|
||||
/* @var RegionOption $regionOption */
|
||||
$regionOption->delete();
|
||||
}
|
||||
|
||||
foreach ($this->actions as $action) {
|
||||
$action->delete();
|
||||
}
|
||||
|
||||
// Delete any actions that had this Region id as targetId, to avoid orphaned records in action table.
|
||||
$this->getStore()->update('DELETE FROM `action` WHERE targetId = :targetId', ['targetId' => $this->regionId]);
|
||||
|
||||
// Delete the region specific playlist
|
||||
$this->getPlaylist()->delete(['regionDelete' => true]);
|
||||
|
||||
// Delete this region
|
||||
$this->getStore()->update('DELETE FROM `region` WHERE regionId = :regionId', array('regionId' => $this->regionId));
|
||||
|
||||
$this->getLog()->audit('Region', $this->regionId, 'Region Deleted', ['regionId' => $this->regionId, 'layoutId' => $this->layoutId]);
|
||||
|
||||
// Notify Layout
|
||||
if ($options['notify'])
|
||||
$this->notifyLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add
|
||||
*/
|
||||
private function add()
|
||||
{
|
||||
$this->getLog()->debug('Adding region to LayoutId ' . $this->layoutId);
|
||||
|
||||
$sql = '
|
||||
INSERT INTO `region` (`layoutId`, `ownerId`, `name`, `width`, `height`, `top`, `left`, `zIndex`, `isDrawer`, `type`, `syncKey`)
|
||||
VALUES (:layoutId, :ownerId, :name, :width, :height, :top, :left, :zIndex, :isDrawer, :type, :syncKey)
|
||||
';
|
||||
|
||||
$this->regionId = $this->getStore()->insert($sql, array(
|
||||
'layoutId' => $this->layoutId,
|
||||
'ownerId' => $this->ownerId,
|
||||
'name' => $this->name,
|
||||
'width' => $this->width,
|
||||
'height' => $this->height,
|
||||
'top' => $this->top,
|
||||
'left' => $this->left,
|
||||
'zIndex' => $this->zIndex,
|
||||
'isDrawer' => $this->isDrawer,
|
||||
'type' => $this->type,
|
||||
'syncKey' => $this->syncKey
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update
|
||||
*/
|
||||
private function update()
|
||||
{
|
||||
$this->getLog()->debug('Editing ' . $this);
|
||||
|
||||
$sql = '
|
||||
UPDATE `region` SET
|
||||
`ownerId` = :ownerId,
|
||||
`name` = :name,
|
||||
`width` = :width,
|
||||
`height` = :height,
|
||||
`top` = :top,
|
||||
`left` = :left,
|
||||
`zIndex` = :zIndex,
|
||||
`duration` = :duration,
|
||||
`isDrawer` = :isDrawer,
|
||||
`type` = :type,
|
||||
`syncKey` = :syncKey
|
||||
WHERE `regionId` = :regionId
|
||||
';
|
||||
|
||||
$this->getStore()->update($sql, array(
|
||||
'ownerId' => $this->ownerId,
|
||||
'name' => $this->name,
|
||||
'width' => $this->width,
|
||||
'height' => $this->height,
|
||||
'top' => $this->top,
|
||||
'left' => $this->left,
|
||||
'zIndex' => $this->zIndex,
|
||||
'duration' => $this->duration,
|
||||
'isDrawer' => $this->isDrawer,
|
||||
'type' => $this->type,
|
||||
'syncKey' => $this->syncKey,
|
||||
'regionId' => $this->regionId
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the Layout (set to building)
|
||||
*/
|
||||
public function notifyLayout()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `layout` SET `status` = 3, `modifiedDT` = :modifiedDt WHERE layoutId = :layoutId
|
||||
', [
|
||||
'layoutId' => $this->layoutId,
|
||||
'modifiedDt' => Carbon::now()->format(DateFormatHelper::getSystemFormat())
|
||||
]);
|
||||
}
|
||||
}
|
||||
91
lib/Entity/RegionOption.php
Normal file
91
lib/Entity/RegionOption.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class RegionOption
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class RegionOption implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The regionId that this Option applies to")
|
||||
* @var int
|
||||
*/
|
||||
public $regionId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The option name")
|
||||
* @var string
|
||||
*/
|
||||
public $option;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The option value")
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
$this->regionId = null;
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$sql = 'INSERT INTO `regionoption` (`regionId`, `option`, `value`) VALUES (:regionId, :option, :value) ON DUPLICATE KEY UPDATE `value` = :value2';
|
||||
$this->getStore()->insert($sql, array(
|
||||
'regionId' => $this->regionId,
|
||||
'option' => $this->option,
|
||||
'value' => $this->value,
|
||||
'value2' => $this->value,
|
||||
));
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$sql = 'DELETE FROM `regionoption` WHERE `regionId` = :regionId AND `option` = :option';
|
||||
$this->getStore()->update($sql, array('regionId' => $this->regionId, 'option' => $this->option));
|
||||
}
|
||||
}
|
||||
85
lib/Entity/ReportForm.php
Normal file
85
lib/Entity/ReportForm.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2021 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
/**
|
||||
* Class ReportForm
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
*/
|
||||
class ReportForm
|
||||
{
|
||||
/**
|
||||
* On demand report form template
|
||||
* @var string
|
||||
*/
|
||||
public $template;
|
||||
|
||||
/**
|
||||
* Report name is the string that is defined in .report file
|
||||
* @var string
|
||||
*/
|
||||
public $reportName;
|
||||
|
||||
/**
|
||||
* Report category is the string that is defined in .report file
|
||||
* @var string|null
|
||||
*/
|
||||
public $reportCategory;
|
||||
|
||||
/**
|
||||
* The defaults that is used in report form twig file
|
||||
* @var array
|
||||
*/
|
||||
public $defaults;
|
||||
|
||||
/**
|
||||
* The string that is displayed when we popover the Schedule button
|
||||
* @var string
|
||||
*/
|
||||
public $reportAddBtnTitle;
|
||||
|
||||
/**
|
||||
* ReportForm constructor.
|
||||
* @param string $template
|
||||
* @param string $reportName
|
||||
* @param string $reportCategory
|
||||
* @param array $defaults
|
||||
* @param string|null $reportAddBtnTitle
|
||||
*/
|
||||
public function __construct(
|
||||
string $template,
|
||||
string $reportName,
|
||||
string $reportCategory,
|
||||
array $defaults = [],
|
||||
string $reportAddBtnTitle = 'Schedule'
|
||||
) {
|
||||
$this->template = $template;
|
||||
$this->reportName = $reportName;
|
||||
$this->reportCategory = $reportCategory;
|
||||
$this->defaults = $defaults;
|
||||
$this->reportAddBtnTitle = $reportAddBtnTitle;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
111
lib/Entity/ReportResult.php
Normal file
111
lib/Entity/ReportResult.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?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\Entity;
|
||||
|
||||
/**
|
||||
* Class ReportResult
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
*/
|
||||
class ReportResult implements \JsonSerializable
|
||||
{
|
||||
/**
|
||||
* Number of total records
|
||||
* @var int
|
||||
*/
|
||||
public $recordsTotal;
|
||||
|
||||
/**
|
||||
* Chart data points
|
||||
* @var array|null
|
||||
*/
|
||||
public $chart;
|
||||
|
||||
/**
|
||||
* Error message
|
||||
* @var null|string
|
||||
*/
|
||||
public $error;
|
||||
|
||||
/**
|
||||
* Metadata that is used in the report preview or in the email template
|
||||
* @var array
|
||||
*/
|
||||
public $metadata;
|
||||
|
||||
/**
|
||||
* Datatable Records
|
||||
* @var array
|
||||
*/
|
||||
public $table;
|
||||
|
||||
/**
|
||||
* ReportResult constructor.
|
||||
* @param array $metadata
|
||||
* @param array $table
|
||||
* @param int $recordsTotal
|
||||
* @param array $chart
|
||||
* @param null|string $error
|
||||
*/
|
||||
public function __construct(
|
||||
array $metadata = [],
|
||||
array $table = [],
|
||||
int $recordsTotal = 0,
|
||||
array $chart = [],
|
||||
?string $error = null
|
||||
) {
|
||||
$this->metadata = $metadata;
|
||||
$this->table = $table;
|
||||
$this->recordsTotal = $recordsTotal;
|
||||
$this->chart = $chart;
|
||||
$this->error = $error;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMetaData(): array
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
public function getRows(): array
|
||||
{
|
||||
return $this->table;
|
||||
}
|
||||
|
||||
public function countLast(): int
|
||||
{
|
||||
return $this->recordsTotal;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'metadata' => $this->metadata,
|
||||
'table' => $this->table,
|
||||
'recordsTotal' => $this->recordsTotal,
|
||||
'chart' => $this->chart,
|
||||
'error' => $this->error
|
||||
];
|
||||
}
|
||||
}
|
||||
211
lib/Entity/ReportSchedule.php
Normal file
211
lib/Entity/ReportSchedule.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class ReportSchedule
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class ReportSchedule implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public static $SCHEDULE_DAILY = '0 0 * * *';
|
||||
public static $SCHEDULE_WEEKLY = '0 0 * * 1';
|
||||
public static $SCHEDULE_MONTHLY = '0 0 1 * *';
|
||||
public static $SCHEDULE_YEARLY = '0 0 1 1 *';
|
||||
|
||||
public $reportScheduleId;
|
||||
public $lastSavedReportId;
|
||||
public $name;
|
||||
public $reportName;
|
||||
public $filterCriteria;
|
||||
public $schedule;
|
||||
public $lastRunDt = 0;
|
||||
public $previousRunDt;
|
||||
public $createdDt;
|
||||
public $isActive = 1;
|
||||
public $fromDt = 0;
|
||||
public $toDt = 0;
|
||||
|
||||
public $message;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The username of the User that owns this report schedule")
|
||||
* @var string
|
||||
*/
|
||||
public $owner;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of the User that owns this report schedule")
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* Command constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
* @param array $options
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true
|
||||
], $options);
|
||||
|
||||
if ($options['validate'])
|
||||
$this->validate();
|
||||
|
||||
if ($this->reportScheduleId == null) {
|
||||
$this->add();
|
||||
$this->getLog()->debug('Adding report schedule');
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->edit();
|
||||
$this->getLog()->debug('Editing a report schedule');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!v::stringType()->notEmpty()->validate($this->name))
|
||||
throw new InvalidArgumentException(__('Missing name'), 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `reportschedule` WHERE `reportScheduleId` = :reportScheduleId', ['reportScheduleId' => $this->reportScheduleId]);
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->reportScheduleId = $this->getStore()->insert('
|
||||
INSERT INTO `reportschedule` (`name`, `lastSavedReportId`, `reportName`, `schedule`, `lastRunDt`, `previousRunDt`, `filterCriteria`, `userId`, `isActive`, `fromDt`, `toDt`, `message`, `createdDt`) VALUES
|
||||
(:name, :lastSavedReportId, :reportName, :schedule, :lastRunDt, :previousRunDt, :filterCriteria, :userId, :isActive, :fromDt, :toDt, :message, :createdDt)
|
||||
', [
|
||||
'name' => $this->name,
|
||||
'lastSavedReportId' => $this->lastSavedReportId,
|
||||
'reportName' => $this->reportName,
|
||||
'schedule' => $this->schedule,
|
||||
'lastRunDt' => $this->lastRunDt,
|
||||
'previousRunDt' => $this->previousRunDt,
|
||||
'filterCriteria' => $this->filterCriteria,
|
||||
'userId' => $this->userId,
|
||||
'isActive' => $this->isActive,
|
||||
'fromDt' => $this->fromDt,
|
||||
'toDt' => $this->toDt,
|
||||
'message' => $this->message,
|
||||
'createdDt' => $this->createdDt,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
*/
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `reportschedule`
|
||||
SET `name` = :name,
|
||||
`lastSavedReportId` = :lastSavedReportId,
|
||||
`reportName` = :reportName,
|
||||
`schedule` = :schedule,
|
||||
`lastRunDt` = :lastRunDt,
|
||||
`previousRunDt` = :previousRunDt,
|
||||
`filterCriteria` = :filterCriteria,
|
||||
`userId` = :userId,
|
||||
`isActive` = :isActive,
|
||||
`fromDt` = :fromDt,
|
||||
`toDt` = :toDt,
|
||||
`message` = :message,
|
||||
`createdDt` = :createdDt
|
||||
WHERE reportScheduleId = :reportScheduleId', [
|
||||
'reportScheduleId' => $this->reportScheduleId,
|
||||
'lastSavedReportId' => $this->lastSavedReportId,
|
||||
'name' => $this->name,
|
||||
'reportName' => $this->reportName,
|
||||
'schedule' => $this->schedule,
|
||||
'lastRunDt' => $this->lastRunDt,
|
||||
'previousRunDt' => $this->previousRunDt,
|
||||
'filterCriteria' => $this->filterCriteria,
|
||||
'userId' => $this->userId,
|
||||
'isActive' => $this->isActive,
|
||||
'fromDt' => $this->fromDt,
|
||||
'toDt' => $this->toDt,
|
||||
'message' => $this->message,
|
||||
'createdDt' => $this->createdDt
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->reportScheduleId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Owner Id
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last saved report id
|
||||
* @return integer
|
||||
*/
|
||||
public function getLastSavedReportId()
|
||||
{
|
||||
return $this->lastSavedReportId;
|
||||
}
|
||||
}
|
||||
127
lib/Entity/RequiredFile.php
Normal file
127
lib/Entity/RequiredFile.php
Normal file
@@ -0,0 +1,127 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\DeadlockException;
|
||||
|
||||
/**
|
||||
* Class RequiredFile
|
||||
* @package Xibo\Entity
|
||||
*/
|
||||
class RequiredFile implements \JsonSerializable
|
||||
{
|
||||
public static $TYPE_DEPENDENCY = 'P';
|
||||
public static $TYPE_LAYOUT = 'L';
|
||||
public static $TYPE_MEDIA = 'M';
|
||||
public static $TYPE_WIDGET_DATA = 'D';
|
||||
|
||||
use EntityTrait;
|
||||
public $rfId;
|
||||
public $displayId;
|
||||
public $type;
|
||||
public $itemId;
|
||||
public $size = 0;
|
||||
public $path;
|
||||
public $bytesRequested = 0;
|
||||
public $complete = 0;
|
||||
public $released = 1;
|
||||
public $fileType;
|
||||
|
||||
/** @var string The realId of a dependency which we will use to resolve it */
|
||||
public $realId;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
* @return $this
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
if ($this->rfId == null) {
|
||||
$this->add();
|
||||
} else if ($this->hasPropertyChanged('bytesRequested') || $this->hasPropertyChanged('complete')) {
|
||||
$this->edit($options);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add
|
||||
*/
|
||||
private function add()
|
||||
{
|
||||
$this->rfId = $this->store->insert('
|
||||
INSERT INTO `requiredfile` (`displayId`, `type`, `itemId`, `bytesRequested`, `complete`, `size`, `path`, `released`, `fileType`, `realId`)
|
||||
VALUES (:displayId, :type, :itemId, :bytesRequested, :complete, :size, :path, :released, :fileType, :realId)
|
||||
', [
|
||||
'displayId' => $this->displayId,
|
||||
'type' => $this->type,
|
||||
'itemId' => $this->itemId,
|
||||
'bytesRequested' => $this->bytesRequested,
|
||||
'complete' => $this->complete,
|
||||
'size' => $this->size,
|
||||
'path' => $this->path,
|
||||
'released' => $this->released,
|
||||
'fileType' => $this->fileType,
|
||||
'realId' => $this->realId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
*/
|
||||
private function edit($options)
|
||||
{
|
||||
$options = array_merge([
|
||||
'connection' => 'default',
|
||||
'useTransaction' => true,
|
||||
], $options);
|
||||
|
||||
try {
|
||||
$this->store->updateWithDeadlockLoop('
|
||||
UPDATE `requiredfile` SET complete = :complete, bytesRequested = :bytesRequested
|
||||
WHERE rfId = :rfId
|
||||
', [
|
||||
'rfId' => $this->rfId,
|
||||
'bytesRequested' => $this->bytesRequested,
|
||||
'complete' => $this->complete
|
||||
], $options['connection'], $options['useTransaction']);
|
||||
} catch (DeadlockException $deadlockException) {
|
||||
$this->getLog()->error('Failed to update bytes requested on ' . $this->rfId . ' due to deadlock');
|
||||
}
|
||||
}
|
||||
}
|
||||
206
lib/Entity/Resolution.php
Normal file
206
lib/Entity/Resolution.php
Normal file
@@ -0,0 +1,206 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* Class Resolution
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Resolution implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of this Resolution")
|
||||
* @var int
|
||||
*/
|
||||
public $resolutionId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The resolution name")
|
||||
* @var string
|
||||
*/
|
||||
public $resolution;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The display width of the resolution")
|
||||
* @var double
|
||||
*/
|
||||
public $width;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The display height of the resolution")
|
||||
* @var double
|
||||
*/
|
||||
public $height;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The designer width of the resolution")
|
||||
* @var double
|
||||
*/
|
||||
public $designerWidth;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The designer height of the resolution")
|
||||
* @var double
|
||||
*/
|
||||
public $designerHeight;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The layout schema version")
|
||||
* @var int
|
||||
*/
|
||||
public $version = 2;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether this resolution is enabled or not")
|
||||
* @var int
|
||||
*/
|
||||
public $enabled = 1;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The userId who owns this Resolution")
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->resolutionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
// No owner
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!v::stringType()->notEmpty()->validate($this->resolution)) {
|
||||
throw new InvalidArgumentException(__('Please provide a name'), 'name');
|
||||
}
|
||||
|
||||
if (!v::intType()->notEmpty()->min(1)->validate($this->width)) {
|
||||
throw new InvalidArgumentException(__('Please provide a width'), 'width');
|
||||
}
|
||||
|
||||
if (!v::intType()->notEmpty()->min(1)->validate($this->height)) {
|
||||
throw new InvalidArgumentException(__('Please provide a height'), 'height');
|
||||
}
|
||||
|
||||
// Set the designer width and height
|
||||
$factor = min (800 / $this->width, 800 / $this->height);
|
||||
|
||||
$this->designerWidth = round($this->width * $factor);
|
||||
$this->designerHeight = round($this->height * $factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
* @param bool|true $validate
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($validate = true)
|
||||
{
|
||||
if ($validate)
|
||||
$this->validate();
|
||||
|
||||
if ($this->resolutionId == null || $this->resolutionId == 0)
|
||||
$this->add();
|
||||
else
|
||||
$this->edit();
|
||||
|
||||
$this->getLog()->audit('Resolution', $this->resolutionId, 'Saving', $this->getChangedProperties());
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM resolution WHERE resolutionID = :resolutionId', ['resolutionId' => $this->resolutionId]);
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->resolutionId = $this->getStore()->insert('
|
||||
INSERT INTO `resolution` (resolution, width, height, intended_width, intended_height, version, enabled, `userId`)
|
||||
VALUES (:resolution, :width, :height, :intended_width, :intended_height, :version, :enabled, :userId)
|
||||
', [
|
||||
'resolution' => $this->resolution,
|
||||
'width' => $this->designerWidth,
|
||||
'height' => $this->designerHeight,
|
||||
'intended_width' => $this->width,
|
||||
'intended_height' => $this->height,
|
||||
'version' => $this->version,
|
||||
'enabled' => $this->enabled,
|
||||
'userId' => $this->userId
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE resolution SET resolution = :resolution,
|
||||
width = :width,
|
||||
height = :height,
|
||||
intended_width = :intended_width,
|
||||
intended_height = :intended_height,
|
||||
enabled = :enabled
|
||||
WHERE resolutionID = :resolutionId
|
||||
', [
|
||||
'resolutionId' => $this->resolutionId,
|
||||
'resolution' => $this->resolution,
|
||||
'width' => $this->designerWidth,
|
||||
'height' => $this->designerHeight,
|
||||
'intended_width' => $this->width,
|
||||
'intended_height' => $this->height,
|
||||
'enabled' => $this->enabled
|
||||
]);
|
||||
}
|
||||
}
|
||||
275
lib/Entity/SavedReport.php
Normal file
275
lib/Entity/SavedReport.php
Normal file
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
use Xibo\Factory\MediaFactory;
|
||||
use Xibo\Factory\SavedReportFactory;
|
||||
use Xibo\Service\ConfigServiceInterface;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class SavedReport
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class SavedReport implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Saved report ID")
|
||||
* @var int
|
||||
*/
|
||||
public $savedReportId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Saved report name As")
|
||||
* @var string
|
||||
*/
|
||||
public $saveAs;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Report schedule Id of the saved report")
|
||||
* @var int
|
||||
*/
|
||||
public $reportScheduleId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Report schedule name of the saved report")
|
||||
* @var string
|
||||
*/
|
||||
public $reportScheduleName;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Report name")
|
||||
* @var string
|
||||
*/
|
||||
public $reportName;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Saved report generated on")
|
||||
* @var string
|
||||
*/
|
||||
public $generatedOn;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The username of the User that owns this saved report")
|
||||
* @var string
|
||||
*/
|
||||
public $owner;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The ID of the User that owns this saved report")
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Original name of the saved report media file")
|
||||
* @var string
|
||||
*/
|
||||
public $originalFileName;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Stored As")
|
||||
* @var string
|
||||
*/
|
||||
public $storedAs;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Schema Version")
|
||||
* @var int
|
||||
*/
|
||||
public $schemaVersion = 2;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Saved Report file name")
|
||||
* @var string
|
||||
*/
|
||||
public $fileName;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Saved Report file size in bytes")
|
||||
* @var int
|
||||
*/
|
||||
public $size;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A MD5 checksum of the stored Saved Report file")
|
||||
* @var string
|
||||
*/
|
||||
public $md5;
|
||||
|
||||
/**
|
||||
* @var ConfigServiceInterface
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var MediaFactory
|
||||
*/
|
||||
private $mediaFactory;
|
||||
|
||||
/**
|
||||
* @var SavedReportFactory
|
||||
*/
|
||||
private $savedReportFactory;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param ConfigServiceInterface $config
|
||||
* @param MediaFactory $mediaFactory
|
||||
* @param SavedReportFactory $savedReportFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $config, $mediaFactory, $savedReportFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
|
||||
$this->config = $config;
|
||||
$this->mediaFactory = $mediaFactory;
|
||||
$this->savedReportFactory = $savedReportFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add
|
||||
*/
|
||||
private function add()
|
||||
{
|
||||
$this->savedReportId = $this->getStore()->insert('
|
||||
INSERT INTO `saved_report` (`saveAs`, `reportScheduleId`, `generatedOn`, `userId`, `schemaVersion`, `fileName`, `size`, `md5`)
|
||||
VALUES (:saveAs, :reportScheduleId, :generatedOn, :userId, :schemaVersion, :fileName, :size, :md5)
|
||||
', [
|
||||
'saveAs' => $this->saveAs,
|
||||
'reportScheduleId' => $this->reportScheduleId,
|
||||
'generatedOn' => $this->generatedOn,
|
||||
'userId' => $this->userId,
|
||||
'schemaVersion' => $this->schemaVersion,
|
||||
'fileName' => $this->fileName,
|
||||
'size' => $this->size,
|
||||
'md5' => $this->md5
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
*/
|
||||
private function edit()
|
||||
{
|
||||
$sql = '
|
||||
UPDATE `saved_report`
|
||||
SET `saveAs` = :saveAs,
|
||||
`reportScheduleId` = :reportScheduleId,
|
||||
`generatedOn` = :generatedOn,
|
||||
`userId` = :userId,
|
||||
`schemaVersion` = :schemaVersion
|
||||
WHERE savedReportId = :savedReportId
|
||||
';
|
||||
|
||||
$params = [
|
||||
'saveAs' => $this->saveAs,
|
||||
'reportScheduleId' => $this->reportScheduleId,
|
||||
'generatedOn' => $this->generatedOn,
|
||||
'userId' => $this->userId,
|
||||
'schemaVersion' => $this->schemaVersion,
|
||||
'savedReportId' => $this->savedReportId,
|
||||
];
|
||||
|
||||
$this->getStore()->update($sql, $params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->load();
|
||||
|
||||
$this->getLog()->debug('Delete saved report: '.$this->saveAs.'. Generated on: '.$this->generatedOn);
|
||||
$this->getStore()->update('DELETE FROM `saved_report` WHERE `savedReportId` = :savedReportId', [
|
||||
'savedReportId' => $this->savedReportId
|
||||
]);
|
||||
|
||||
// Update last saved report in report schedule
|
||||
$this->getLog()->debug('Update last saved report in report schedule');
|
||||
$this->getStore()->update('
|
||||
UPDATE `reportschedule` SET lastSavedReportId = ( SELECT IFNULL(MAX(`savedReportId`), 0) FROM `saved_report` WHERE `reportScheduleId`= :reportScheduleId)
|
||||
WHERE `reportScheduleId` = :reportScheduleId',
|
||||
[
|
||||
'reportScheduleId' => $this->reportScheduleId
|
||||
]);
|
||||
|
||||
// Library location
|
||||
$libraryLocation = $this->config->getSetting('LIBRARY_LOCATION');
|
||||
|
||||
// delete file
|
||||
if (file_exists($libraryLocation . 'savedreport/'. $this->fileName)) {
|
||||
unlink($libraryLocation . 'savedreport/'. $this->fileName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
if ($this->loaded || $this->savedReportId == null)
|
||||
return;
|
||||
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->savedReportId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Owner Id
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if ($this->savedReportId == null || $this->savedReportId == 0)
|
||||
$this->add();
|
||||
else
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
2190
lib/Entity/Schedule.php
Normal file
2190
lib/Entity/Schedule.php
Normal file
File diff suppressed because it is too large
Load Diff
144
lib/Entity/ScheduleCriteria.php
Normal file
144
lib/Entity/ScheduleCriteria.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Schedule Criteria entity
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class ScheduleCriteria implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public int $id;
|
||||
public int $eventId;
|
||||
public string $type;
|
||||
public string $metric;
|
||||
public string $condition;
|
||||
public string $value;
|
||||
|
||||
public function __construct(
|
||||
StorageServiceInterface $store,
|
||||
LogServiceInterface $logService,
|
||||
EventDispatcherInterface $dispatcher
|
||||
) {
|
||||
$this->setCommonDependencies($store, $logService, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic checks to make sure we have all the fields, etc that we need/
|
||||
* @return void
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function validate(): void
|
||||
{
|
||||
if (empty($this->eventId)) {
|
||||
throw new InvalidArgumentException(__('Criteria must be attached to an event'), 'eventId');
|
||||
}
|
||||
|
||||
if (empty($this->metric)) {
|
||||
throw new InvalidArgumentException(__('Please select a metric'), 'metric');
|
||||
}
|
||||
|
||||
if (!in_array($this->condition, ['set', 'lt', 'lte', 'eq', 'neq', 'gt', 'gte', 'contains', 'ncontains'])) {
|
||||
throw new InvalidArgumentException(__('Please enter a valid condition'), 'condition');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NotFoundException|InvalidArgumentException
|
||||
*/
|
||||
public function save(array $options = []): ScheduleCriteria
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true,
|
||||
'audit' => true,
|
||||
], $options);
|
||||
|
||||
// Validate?
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if (empty($this->id)) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
|
||||
if ($options['audit']) {
|
||||
$this->audit($this->id, 'Saved schedule criteria to event', null, true);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this criteria
|
||||
* @return void
|
||||
*/
|
||||
public function delete(): void
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `schedule_criteria` WHERE `id` = :id', ['id' => $this->id]);
|
||||
}
|
||||
|
||||
private function add(): void
|
||||
{
|
||||
$this->id = $this->getStore()->insert('
|
||||
INSERT INTO `schedule_criteria` (`eventId`, `type`, `metric`, `condition`, `value`)
|
||||
VALUES (:eventId, :type, :metric, :condition, :value)
|
||||
', [
|
||||
'eventId' => $this->eventId,
|
||||
'type' => $this->type,
|
||||
'metric' => $this->metric,
|
||||
'condition' => $this->condition,
|
||||
'value' => $this->value,
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit(): void
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `schedule_criteria` SET
|
||||
`eventId` = :eventId,
|
||||
`type` = :type,
|
||||
`metric` = :metric,
|
||||
`condition` = :condition,
|
||||
`value` = :value
|
||||
WHERE `id` = :id
|
||||
', [
|
||||
'eventId' => $this->eventId,
|
||||
'type' => $this->type,
|
||||
'metric' => $this->metric,
|
||||
'condition' => $this->condition,
|
||||
'value' => $this->value,
|
||||
'id' => $this->id,
|
||||
]);
|
||||
}
|
||||
}
|
||||
38
lib/Entity/ScheduleEvent.php
Normal file
38
lib/Entity/ScheduleEvent.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/*
|
||||
* Spring Signage Ltd - http://www.springsignage.com
|
||||
* Copyright (C) 2016 Spring Signage Ltd
|
||||
* (ScheduleEvent.php)
|
||||
*/
|
||||
|
||||
|
||||
namespace Xibo\Entity;
|
||||
|
||||
/**
|
||||
* Class ScheduleEvent
|
||||
* @package Xibo\Entity
|
||||
*/
|
||||
class ScheduleEvent
|
||||
{
|
||||
public $fromDt;
|
||||
public $toDt;
|
||||
|
||||
/**
|
||||
* ScheduleEvent constructor.
|
||||
* @param $fromDt
|
||||
* @param $toDt
|
||||
*/
|
||||
public function __construct($fromDt, $toDt)
|
||||
{
|
||||
$this->fromDt = $fromDt;
|
||||
$this->toDt = $toDt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->fromDt . $this->toDt;
|
||||
}
|
||||
}
|
||||
91
lib/Entity/ScheduleExclusion.php
Normal file
91
lib/Entity/ScheduleExclusion.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Class ScheduleExclusion
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class ScheduleExclusion implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Excluded Schedule ID")
|
||||
* @var int
|
||||
*/
|
||||
public $scheduleExclusionId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The eventId that this Excluded Schedule applies to")
|
||||
* @var int
|
||||
*/
|
||||
public $eventId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="A Unix timestamp representing the from date of an excluded recurring event in CMS time."
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $fromDt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="A Unix timestamp representing the to date of an excluded recurring event in CMS time."
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $toDt;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->getStore()->insert('INSERT INTO `scheduleexclusions` (`eventId`, `fromDt`, `toDt`) VALUES (:eventId, :fromDt, :toDt)', [
|
||||
'eventId' => $this->eventId,
|
||||
'fromDt' => $this->fromDt,
|
||||
'toDt' => $this->toDt,
|
||||
]);
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `scheduleexclusions` WHERE `scheduleExclusionId` = :scheduleExclusionId', [
|
||||
'scheduleExclusionId' => $this->scheduleExclusionId
|
||||
]);
|
||||
}
|
||||
}
|
||||
237
lib/Entity/ScheduleReminder.php
Normal file
237
lib/Entity/ScheduleReminder.php
Normal file
@@ -0,0 +1,237 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
|
||||
use Xibo\Factory\ScheduleReminderFactory;
|
||||
use Xibo\Service\ConfigServiceInterface;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class ScheduleReminder
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class ScheduleReminder implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public static $TYPE_MINUTE = 1;
|
||||
public static $TYPE_HOUR = 2;
|
||||
public static $TYPE_DAY = 3;
|
||||
public static $TYPE_WEEK = 4;
|
||||
public static $TYPE_MONTH = 5;
|
||||
|
||||
public static $OPTION_BEFORE_START = 1;
|
||||
public static $OPTION_AFTER_START = 2;
|
||||
public static $OPTION_BEFORE_END = 3;
|
||||
public static $OPTION_AFTER_END = 4;
|
||||
|
||||
public static $MINUTE = 60;
|
||||
public static $HOUR = 3600;
|
||||
public static $DAY = 86400;
|
||||
public static $WEEK = 604800;
|
||||
public static $MONTH = 30 * 86400;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Schedule Reminder ID")
|
||||
* @var int
|
||||
*/
|
||||
public $scheduleReminderId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The event ID of the schedule reminder")
|
||||
* @var int
|
||||
*/
|
||||
public $eventId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="An integer number to define minutes, hours etc.")
|
||||
* @var int
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The type of the reminder (i.e. Minute, Hour, Day, Week, Month)")
|
||||
* @var int
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The options regarding sending a reminder for an event. (i.e., Before start, After start, Before end, After end)")
|
||||
* @var int
|
||||
*/
|
||||
public $option;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Email flag for schedule reminder")
|
||||
* @var int
|
||||
*/
|
||||
public $isEmail;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A date that indicates the reminder date")
|
||||
* @var int
|
||||
*/
|
||||
public $reminderDt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Last reminder date a reminder was sent")
|
||||
* @var int
|
||||
*/
|
||||
public $lastReminderDt = 0;
|
||||
|
||||
/**
|
||||
* @var ConfigServiceInterface
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var ScheduleReminderFactory
|
||||
*/
|
||||
private $scheduleReminderFactory;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param ConfigServiceInterface $config
|
||||
* @param ScheduleReminderFactory $scheduleReminderFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $config, $scheduleReminderFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
|
||||
$this->config = $config;
|
||||
$this->scheduleReminderFactory = $scheduleReminderFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add
|
||||
*/
|
||||
private function add()
|
||||
{
|
||||
$this->scheduleReminderId = $this->getStore()->insert('
|
||||
INSERT INTO `schedulereminder` (`eventId`, `value`, `type`, `option`, `reminderDt`, `isEmail`, `lastReminderDt`)
|
||||
VALUES (:eventId, :value, :type, :option, :reminderDt, :isEmail, :lastReminderDt)
|
||||
', [
|
||||
'eventId' => $this->eventId,
|
||||
'value' => $this->value,
|
||||
'type' => $this->type,
|
||||
'option' => $this->option,
|
||||
'reminderDt' => $this->reminderDt,
|
||||
'isEmail' => $this->isEmail,
|
||||
'lastReminderDt' => $this->lastReminderDt,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
*/
|
||||
private function edit()
|
||||
{
|
||||
$sql = '
|
||||
UPDATE `schedulereminder`
|
||||
SET `eventId` = :eventId,
|
||||
`type` = :type,
|
||||
`value` = :value,
|
||||
`option` = :option,
|
||||
`reminderDt` = :reminderDt,
|
||||
`isEmail` = :isEmail,
|
||||
`lastReminderDt` = :lastReminderDt
|
||||
WHERE scheduleReminderId = :scheduleReminderId
|
||||
';
|
||||
|
||||
$params = [
|
||||
'eventId' => $this->eventId,
|
||||
'type' => $this->type,
|
||||
'value' => $this->value,
|
||||
'option' => $this->option,
|
||||
'reminderDt' => $this->reminderDt,
|
||||
'isEmail' => $this->isEmail,
|
||||
'lastReminderDt' => $this->lastReminderDt,
|
||||
'scheduleReminderId' => $this->scheduleReminderId,
|
||||
];
|
||||
|
||||
$this->getStore()->update($sql, $params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->load();
|
||||
|
||||
$this->getLog()->debug('Delete schedule reminder: '.$this->scheduleReminderId);
|
||||
$this->getStore()->update('DELETE FROM `schedulereminder` WHERE `scheduleReminderId` = :scheduleReminderId', [
|
||||
'scheduleReminderId' => $this->scheduleReminderId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
if ($this->loaded || $this->scheduleReminderId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->scheduleReminderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Reminder Date
|
||||
* @return int
|
||||
*/
|
||||
public function getReminderDt()
|
||||
{
|
||||
return $this->reminderDt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if ($this->scheduleReminderId == null || $this->scheduleReminderId == 0) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
}
|
||||
71
lib/Entity/SearchResult.php
Normal file
71
lib/Entity/SearchResult.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Xibo\Connector\ProviderDetails;
|
||||
|
||||
/**
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class SearchResult implements \JsonSerializable
|
||||
{
|
||||
public $title;
|
||||
public $description;
|
||||
public $thumbnail;
|
||||
public $source;
|
||||
public $type;
|
||||
public $id;
|
||||
public $download;
|
||||
public $fileSize;
|
||||
public $width;
|
||||
public $height;
|
||||
public $orientation;
|
||||
public $duration;
|
||||
public $videoThumbnailUrl;
|
||||
public $tags = [];
|
||||
public $isFeatured = 0;
|
||||
|
||||
/** @var ProviderDetails */
|
||||
public $provider;
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'source' => $this->source,
|
||||
'type' => $this->type,
|
||||
'title' => $this->title,
|
||||
'description' => $this->description,
|
||||
'thumbnail' => $this->thumbnail,
|
||||
'duration' => $this->duration,
|
||||
'download' => $this->download,
|
||||
'provider' => $this->provider,
|
||||
'width' => $this->width,
|
||||
'height' => $this->height,
|
||||
'orientation' => $this->orientation,
|
||||
'fileSize' => $this->fileSize,
|
||||
'videoThumbnailUrl' => $this->videoThumbnailUrl,
|
||||
'tags' => $this->tags,
|
||||
'isFeatured' => $this->isFeatured
|
||||
];
|
||||
}
|
||||
}
|
||||
37
lib/Entity/SearchResults.php
Normal file
37
lib/Entity/SearchResults.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?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\Entity;
|
||||
|
||||
/**
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class SearchResults implements \JsonSerializable
|
||||
{
|
||||
public $data = [];
|
||||
|
||||
public function jsonSerialize(): array
|
||||
{
|
||||
return [
|
||||
'data' => $this->data
|
||||
];
|
||||
}
|
||||
}
|
||||
71
lib/Entity/Session.php
Normal file
71
lib/Entity/Session.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class Session
|
||||
* @package Xibo\Entity
|
||||
*/
|
||||
class Session implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public $sessionId;
|
||||
public $userId;
|
||||
public $userName;
|
||||
public $isExpired;
|
||||
public $lastAccessed;
|
||||
public $remoteAddress;
|
||||
public $userAgent;
|
||||
public $expiresAt;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int the userId
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int the owner UserId (always 1)
|
||||
*/
|
||||
public function getOwnerId(): int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
21
lib/Entity/Setting.php
Normal file
21
lib/Entity/Setting.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
/*
|
||||
* Spring Signage Ltd - http://www.springsignage.com
|
||||
* Copyright (C) 2015 Spring Signage Ltd
|
||||
* (Setting.php)
|
||||
*/
|
||||
|
||||
|
||||
namespace Xibo\Entity;
|
||||
|
||||
|
||||
/**
|
||||
* Class Setting
|
||||
* @package Xibo\Entity
|
||||
*/
|
||||
class Setting
|
||||
{
|
||||
use EntityTrait;
|
||||
public $setting;
|
||||
public $value;
|
||||
}
|
||||
454
lib/Entity/SyncGroup.php
Executable file
454
lib/Entity/SyncGroup.php
Executable file
@@ -0,0 +1,454 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Factory\DisplayFactory;
|
||||
use Xibo\Factory\PermissionFactory;
|
||||
use Xibo\Factory\ScheduleFactory;
|
||||
use Xibo\Factory\SyncGroupFactory;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
use Xibo\Support\Sanitizer\SanitizerInterface;
|
||||
|
||||
/**
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class SyncGroup implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
/**
|
||||
* @SWG\Property(description="The ID of this Entity")
|
||||
* @var int
|
||||
*/
|
||||
public $syncGroupId;
|
||||
/**
|
||||
* @SWG\Property(description="The name of this Entity")
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
/**
|
||||
* @SWG\Property(description="The datetime this entity was created")
|
||||
* @var string
|
||||
*/
|
||||
public $createdDt;
|
||||
/**
|
||||
* @SWG\Property(description="The datetime this entity was last modified")
|
||||
* @var ?string
|
||||
*/
|
||||
public $modifiedDt;
|
||||
/**
|
||||
* @SWG\Property(description="The ID of the user that last modified this sync group")
|
||||
* @var int
|
||||
*/
|
||||
public $modifiedBy;
|
||||
/**
|
||||
* @SWG\Property(description="The name of the user that last modified this sync group")
|
||||
* @var string
|
||||
*/
|
||||
public $modifiedByName;
|
||||
/**
|
||||
* @SWG\Property(description="The ID of the owner of this sync group")
|
||||
* @var int
|
||||
*/
|
||||
public $ownerId;
|
||||
/**
|
||||
* @SWG\Property(description="The name of the owner of this sync group")
|
||||
* @var string
|
||||
*/
|
||||
public $owner;
|
||||
/**
|
||||
* @SWG\Property(description="The publisher port number")
|
||||
* @var int
|
||||
*/
|
||||
public $syncPublisherPort = 9590;
|
||||
/**
|
||||
* @SWG\Property(description="The delay (in ms) when displaying the changes in content")
|
||||
* @var int
|
||||
*/
|
||||
public $syncSwitchDelay = 750;
|
||||
/**
|
||||
* @SWG\Property(description="The delay (in ms) before unpausing the video on start.")
|
||||
* @var int
|
||||
*/
|
||||
public $syncVideoPauseDelay = 100;
|
||||
/**
|
||||
* @SWG\Property(description="The ID of the lead Display for this sync group")
|
||||
* @var int
|
||||
*/
|
||||
public $leadDisplayId;
|
||||
/**
|
||||
* @SWG\Property(description="The name of the lead Display for this sync group")
|
||||
* @var string
|
||||
*/
|
||||
public $leadDisplay;
|
||||
/**
|
||||
* @SWG\Property(description="The id of the Folder this Sync Group belongs to")
|
||||
* @var int
|
||||
*/
|
||||
public $folderId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The id of the Folder responsible for providing permissions for this Sync Group")
|
||||
* @var int
|
||||
*/
|
||||
public $permissionsFolderId;
|
||||
|
||||
|
||||
private SyncGroupFactory $syncGroupFactory;
|
||||
private DisplayFactory $displayFactory;
|
||||
private PermissionFactory $permissionFactory;
|
||||
private $permissions = [];
|
||||
private ScheduleFactory $scheduleFactory;
|
||||
|
||||
/**
|
||||
* @param $store
|
||||
* @param $log
|
||||
* @param $dispatcher
|
||||
* @param SyncGroupFactory $syncGroupFactory
|
||||
* @param DisplayFactory $displayFactory
|
||||
*/
|
||||
public function __construct(
|
||||
$store,
|
||||
$log,
|
||||
$dispatcher,
|
||||
SyncGroupFactory $syncGroupFactory,
|
||||
DisplayFactory $displayFactory,
|
||||
PermissionFactory $permissionFactory,
|
||||
ScheduleFactory $scheduleFactory
|
||||
) {
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
$this->setPermissionsClass('Xibo\Entity\SyncGroup');
|
||||
$this->syncGroupFactory = $syncGroupFactory;
|
||||
$this->displayFactory = $displayFactory;
|
||||
$this->permissionFactory = $permissionFactory;
|
||||
$this->scheduleFactory = $scheduleFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Display[]
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function getSyncGroupMembers(): array
|
||||
{
|
||||
return $this->displayFactory->getBySyncGroupId($this->syncGroupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMembersForForm(): array
|
||||
{
|
||||
return $this->getStore()->select('SELECT `display`.displayId, `display`.display, `display`.syncGroupId, `syncgroup`.leadDisplayId, `displaygroup`.displayGroupId
|
||||
FROM `display`
|
||||
INNER JOIN `syncgroup` ON `syncgroup`.syncGroupId = `display`.syncGroupId
|
||||
INNER JOIN `lkdisplaydg` ON lkdisplaydg.displayid = display.displayId
|
||||
INNER JOIN `displaygroup` ON displaygroup.displaygroupid = lkdisplaydg.displaygroupid AND `displaygroup`.isDisplaySpecific = 1
|
||||
WHERE `display`.syncGroupId = :syncGroupId
|
||||
ORDER BY IF(`syncgroup`.leadDisplayId = `display`.displayId, 0, 1), displayId', [
|
||||
'syncGroupId' => $this->syncGroupId
|
||||
]);
|
||||
}
|
||||
|
||||
public function getGroupMembersForEditForm($eventId): array
|
||||
{
|
||||
return $this->getStore()->select('SELECT `display`.displayId, `display`.display, `display`.syncGroupId, `syncgroup`.leadDisplayId, `schedule_sync`.layoutId, `displaygroup`.displayGroupId
|
||||
FROM `display`
|
||||
INNER JOIN `syncgroup` ON `syncgroup`.syncGroupId = `display`.syncGroupId
|
||||
INNER JOIN `schedule_sync` ON `schedule_sync`.displayId = `display`.displayId
|
||||
INNER JOIN `lkdisplaydg` ON lkdisplaydg.displayid = display.displayId
|
||||
INNER JOIN `displaygroup` ON displaygroup.displaygroupid = lkdisplaydg.displaygroupid AND `displaygroup`.isDisplaySpecific = 1
|
||||
WHERE `display`.syncGroupId = :syncGroupId AND `schedule_sync`.eventId = :eventId
|
||||
ORDER BY IF(`syncgroup`.leadDisplayId = `display`.displayId, 0, 1), displayId', [
|
||||
'syncGroupId' => $this->syncGroupId,
|
||||
'eventId' => $eventId
|
||||
]);
|
||||
}
|
||||
|
||||
public function getLayoutIdForDisplay(int $eventId, int $displayId)
|
||||
{
|
||||
$layout = $this->getStore()->select('SELECT `schedule_sync`.layoutId
|
||||
FROM `display`
|
||||
INNER JOIN `schedule_sync` ON `schedule_sync`.displayId = `display`.displayId
|
||||
WHERE `display`.syncGroupId = :syncGroupId AND `schedule_sync`.eventId = :eventId AND `schedule_sync`.displayId = :displayId', [
|
||||
'eventId' => $eventId,
|
||||
'displayId' => $displayId,
|
||||
'syncGroupId' => $this->syncGroupId
|
||||
]);
|
||||
|
||||
if (count($layout) <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $layout[0]['layoutId'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId(): int
|
||||
{
|
||||
return $this->syncGroupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getPermissionFolderId(): int
|
||||
{
|
||||
return $this->permissionsFolderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId(): int
|
||||
{
|
||||
return $this->ownerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the owner of this group
|
||||
* @param $userId
|
||||
*/
|
||||
public function setOwner($userId): void
|
||||
{
|
||||
$this->ownerId = $userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the contents for this display group
|
||||
* @param array $options
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function load($options = [])
|
||||
{
|
||||
$options = array_merge([], $options);
|
||||
|
||||
if ($this->loaded || $this->syncGroupId == null || $this->syncGroupId == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->permissions = $this->permissionFactory->getByObjectId(get_class($this), $this->syncGroupId);
|
||||
|
||||
// We are loaded
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $options
|
||||
* @return void
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($options = []): void
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true,
|
||||
], $options);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if (!isset($this->syncGroupId)) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function validate(): void
|
||||
{
|
||||
if (!v::stringType()->notEmpty()->validate($this->name)) {
|
||||
throw new InvalidArgumentException(__('Name cannot be empty'), 'name');
|
||||
}
|
||||
|
||||
if ($this->syncPublisherPort <= 0 || $this->syncPublisherPort === null) {
|
||||
throw new InvalidArgumentException(__('Sync Publisher Port cannot be empty'), 'syncPublisherPort');
|
||||
}
|
||||
|
||||
if (!isset($this->leadDisplayId) && isset($this->syncGroupId)) {
|
||||
throw new InvalidArgumentException(__('Please select lead Display for this sync group'), 'leadDisplayId');
|
||||
}
|
||||
|
||||
if ($this->syncSwitchDelay < 0) {
|
||||
throw new InvalidArgumentException(__('Switch Delay value cannot be negative'), 'syncSwitchDelay');
|
||||
}
|
||||
|
||||
if ($this->syncVideoPauseDelay < 0) {
|
||||
throw new InvalidArgumentException(__('Video Pause Delay value cannot be negative'), 'syncVideoPauseDelay');
|
||||
}
|
||||
}
|
||||
|
||||
public function validateForSchedule(SanitizerInterface $sanitizer)
|
||||
{
|
||||
foreach ($this->getSyncGroupMembers() as $display) {
|
||||
if (empty($sanitizer->getInt('layoutId_' . $display->displayId))) {
|
||||
$this->getLog()->error('Sync Event : Missing Layout for DisplayID ' . $display->displayId);
|
||||
throw new InvalidArgumentException(
|
||||
__('Please make sure to select a Layout for all Displays in this Sync Group.')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function add(): void
|
||||
{
|
||||
$time = Carbon::now()->format(DateFormatHelper::getSystemFormat());
|
||||
|
||||
$this->syncGroupId = $this->getStore()->insert('
|
||||
INSERT INTO syncgroup (`name`, `createdDt`, `modifiedDt`, `ownerId`, `modifiedBy`, `syncPublisherPort`, `syncSwitchDelay`, `syncVideoPauseDelay`, `folderId`, `permissionsFolderId`)
|
||||
VALUES (:name, :createdDt, :modifiedDt, :ownerId, :modifiedBy, :syncPublisherPort, :syncSwitchDelay, :syncVideoPauseDelay, :folderId, :permissionsFolderId)
|
||||
', [
|
||||
'name' => $this->name,
|
||||
'createdDt' => $time,
|
||||
'modifiedDt' => null,
|
||||
'modifiedBy' => $this->modifiedBy,
|
||||
'ownerId' => $this->ownerId,
|
||||
'syncPublisherPort' => $this->syncPublisherPort,
|
||||
'syncSwitchDelay' => $this->syncSwitchDelay,
|
||||
'syncVideoPauseDelay' => $this->syncVideoPauseDelay,
|
||||
'folderId' => $this->folderId,
|
||||
'permissionsFolderId' => $this->permissionsFolderId
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit(): void
|
||||
{
|
||||
$this->getLog()->debug(sprintf('Updating Sync Group. %s, %d', $this->name, $this->syncGroupId));
|
||||
$time = Carbon::now()->format(DateFormatHelper::getSystemFormat());
|
||||
|
||||
$this->getStore()->update('
|
||||
UPDATE syncgroup
|
||||
SET `name` = :name,
|
||||
`modifiedDt` = :modifiedDt,
|
||||
`ownerId` = :ownerId,
|
||||
`modifiedBy` = :modifiedBy,
|
||||
`syncPublisherPort` = :syncPublisherPort,
|
||||
`syncSwitchDelay` = :syncSwitchDelay,
|
||||
`syncVideoPauseDelay` = :syncVideoPauseDelay,
|
||||
`leadDisplayId` = :leadDisplayId,
|
||||
`folderId` = :folderId,
|
||||
`permissionsFolderId` = :permissionsFolderId
|
||||
WHERE syncGroupId = :syncGroupId
|
||||
', [
|
||||
'name' => $this->name,
|
||||
'modifiedDt' => $time,
|
||||
'ownerId' => $this->ownerId,
|
||||
'modifiedBy' => $this->modifiedBy,
|
||||
'syncPublisherPort' => $this->syncPublisherPort,
|
||||
'syncSwitchDelay' => $this->syncSwitchDelay,
|
||||
'syncVideoPauseDelay' => $this->syncVideoPauseDelay,
|
||||
'leadDisplayId' => $this->leadDisplayId == 0 ? null : $this->leadDisplayId,
|
||||
'folderId' => $this->folderId,
|
||||
'permissionsFolderId' => $this->permissionsFolderId,
|
||||
'syncGroupId' => $this->syncGroupId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function delete(): void
|
||||
{
|
||||
// unlink Displays from this syncGroup
|
||||
foreach ($this->getSyncGroupMembers() as $display) {
|
||||
$this->getStore()->update('UPDATE `display` SET `display`.syncGroupId = NULL WHERE `display`.displayId = :displayId', [
|
||||
'displayId' => $display->displayId
|
||||
]);
|
||||
}
|
||||
|
||||
// go through events using this syncGroupId and remove them
|
||||
// this will also remove links in schedule_sync table
|
||||
foreach ($this->scheduleFactory->getBySyncGroupId($this->syncGroupId) as $event) {
|
||||
$event->delete();
|
||||
}
|
||||
|
||||
$this->getStore()->update('DELETE FROM `syncgroup` WHERE `syncgroup`.syncGroupId = :syncGroupId', [
|
||||
'syncGroupId' => $this->syncGroupId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $displayIds
|
||||
* @return void
|
||||
* @throws InvalidArgumentException
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function setMembers(array $displayIds): void
|
||||
{
|
||||
foreach ($displayIds as $displayId) {
|
||||
$display = $this->displayFactory->getById($displayId);
|
||||
|
||||
if (empty($display->syncGroupId)) {
|
||||
$this->getStore()->update('UPDATE `display` SET `display`.syncGroupId = :syncGroupId WHERE `display`.displayId = :displayId', [
|
||||
'syncGroupId' => $this->syncGroupId,
|
||||
'displayId' => $display->displayId
|
||||
]);
|
||||
|
||||
$display->notify();
|
||||
} else if (!empty($display->syncGroupId) && $display->syncGroupId !== $this->syncGroupId) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
__('Display %s already belongs to a different sync group ID %d'),
|
||||
$display->display,
|
||||
$display->syncGroupId
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $displayIds
|
||||
* @return void
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function unSetMembers(array $displayIds): void
|
||||
{
|
||||
foreach ($displayIds as $displayId) {
|
||||
$display = $this->displayFactory->getById($displayId);
|
||||
|
||||
if ($display->syncGroupId === $this->syncGroupId) {
|
||||
$this->getStore()->update('UPDATE `display` SET `display`.syncGroupId = NULL WHERE `display`.displayId = :displayId', [
|
||||
'displayId' => $display->displayId
|
||||
]);
|
||||
|
||||
$this->getStore()->update(' DELETE FROM `schedule_sync` WHERE `schedule_sync`.displayId = :displayId
|
||||
AND `schedule_sync`.eventId IN (SELECT eventId FROM schedule WHERE schedule.syncGroupId = :syncGroupId)', [
|
||||
'displayId' => $display->displayId,
|
||||
'syncGroupId' => $this->syncGroupId
|
||||
]);
|
||||
}
|
||||
|
||||
$display->notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
236
lib/Entity/Tag.php
Normal file
236
lib/Entity/Tag.php
Normal file
@@ -0,0 +1,236 @@
|
||||
<?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\Entity;
|
||||
use Xibo\Factory\TagFactory;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\DuplicateEntityException;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
|
||||
|
||||
/**
|
||||
* Class Tag
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Tag implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Tag ID")
|
||||
* @var int
|
||||
*/
|
||||
public $tagId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Tag Name")
|
||||
* @var string
|
||||
*/
|
||||
public $tag;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag, whether the tag is a system tag")
|
||||
* @var int
|
||||
*/
|
||||
public $isSystem = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag, whether the tag requires additional values")
|
||||
* @var int
|
||||
*/
|
||||
public $isRequired = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="An array of options assigned to this Tag")
|
||||
* @var ?string
|
||||
*/
|
||||
public $options;
|
||||
|
||||
/** @var TagFactory */
|
||||
private $tagFactory;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param TagFactory $tagFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $tagFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
|
||||
$this->tagFactory = $tagFactory;
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->tagId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
* @throws DuplicateEntityException
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
// Name Validation
|
||||
if (strlen($this->tag) > 50 || strlen($this->tag) < 1) {
|
||||
throw new InvalidArgumentException(__("Tag must be between 1 and 50 characters"), 'tag');
|
||||
}
|
||||
|
||||
// Check for duplicates
|
||||
$duplicates = $this->tagFactory->query(null, [
|
||||
'tagExact' => $this->tag,
|
||||
'notTagId' => $this->tagId,
|
||||
'disableUserCheck' => 1
|
||||
]);
|
||||
|
||||
if (count($duplicates) > 0) {
|
||||
throw new DuplicateEntityException(sprintf(__("You already own a Tag called '%s'. Please choose another name."), $this->tag));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
* @param array $options
|
||||
* @throws DuplicateEntityException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
// Default options
|
||||
$options = array_merge([
|
||||
'validate' => true
|
||||
], $options);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
// If the tag doesn't exist already - save it
|
||||
if ($this->tagId == null || $this->tagId == 0) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->update();
|
||||
}
|
||||
|
||||
$this->getLog()->debug('Saving Tag: %s, %d', $this->tag, $this->tagId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a tag
|
||||
* @throws \PDOException
|
||||
*/
|
||||
private function add()
|
||||
{
|
||||
$this->tagId = $this->getStore()->insert('INSERT INTO `tag` (tag, isRequired, options) VALUES (:tag, :isRequired, :options) ON DUPLICATE KEY UPDATE tag = tag', [
|
||||
'tag' => $this->tag,
|
||||
'isRequired' => $this->isRequired,
|
||||
'options' => ($this->options == null) ? null : $this->options
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a Tag
|
||||
* @throws \PDOException
|
||||
*/
|
||||
private function update()
|
||||
{
|
||||
$this->getStore()->update('UPDATE `tag` SET tag = :tag, isRequired = :isRequired, options = :options WHERE tagId = :tagId', [
|
||||
'tagId' => $this->tagId,
|
||||
'tag' => $this->tag,
|
||||
'isRequired' => $this->isRequired,
|
||||
'options' => ($this->options == null) ? null : $this->options
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete Tag
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
// Delete the Tag record
|
||||
$this->getStore()->update('DELETE FROM `tag` WHERE tagId = :tagId', ['tagId' => $this->tagId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this tag a system tag?
|
||||
* @return bool
|
||||
* @throws \Xibo\Support\Exception\NotFoundException
|
||||
*/
|
||||
public function isSystemTag()
|
||||
{
|
||||
$tag = $this->tagFactory->getById($this->tagId);
|
||||
|
||||
return $tag->isSystem === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes Tag value from lktagtables
|
||||
*
|
||||
* @param array $values An Array of values that should be removed from assignment
|
||||
*/
|
||||
public function updateTagValues($values)
|
||||
{
|
||||
$this->getLog()->debug('Tag options were changed, the following values need to be removed ' . json_encode($values));
|
||||
|
||||
foreach ($values as $value) {
|
||||
$this->getLog()->debug('removing following value from lktag tables ' . $value);
|
||||
|
||||
$this->getStore()->update('UPDATE `lktagcampaign` SET `value` = null WHERE tagId = :tagId AND value = :value',
|
||||
[
|
||||
'value' => $value,
|
||||
'tagId' => $this->tagId
|
||||
]);
|
||||
|
||||
$this->getStore()->update('UPDATE `lktagdisplaygroup` SET `value` = null WHERE tagId = :tagId AND value = :value',
|
||||
[
|
||||
'value' => $value,
|
||||
'tagId' => $this->tagId
|
||||
]);
|
||||
|
||||
$this->getStore()->update('UPDATE `lktaglayout` SET `value` = null WHERE tagId = :tagId AND value = :value',
|
||||
[
|
||||
'value' => $value,
|
||||
'tagId' => $this->tagId
|
||||
]);
|
||||
|
||||
$this->getStore()->update('UPDATE `lktagmedia` SET `value` = null WHERE tagId = :tagId AND value = :value',
|
||||
[
|
||||
'value' => $value,
|
||||
'tagId' => $this->tagId
|
||||
]);
|
||||
|
||||
$this->getStore()->update('UPDATE `lktagplaylist` SET `value` = null WHERE tagId = :tagId AND value = :value',
|
||||
[
|
||||
'value' => $value,
|
||||
'tagId' => $this->tagId
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
92
lib/Entity/TagLink.php
Normal file
92
lib/Entity/TagLink.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class TagLink implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
/**
|
||||
* @SWG\Property(description="The Tag")
|
||||
* @var string
|
||||
*/
|
||||
public $tag;
|
||||
/**
|
||||
* @SWG\Property(description="The Tag ID")
|
||||
* @var int
|
||||
*/
|
||||
public $tagId;
|
||||
/**
|
||||
* @SWG\Property(description="The Tag Value")
|
||||
* @var string
|
||||
*/
|
||||
public $value = null;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
public function validateOptions(Tag $tag)
|
||||
{
|
||||
if ($tag->options) {
|
||||
if (!is_array($tag->options)) {
|
||||
$tag->options = json_decode($tag->options);
|
||||
}
|
||||
|
||||
if (!empty($this->value) && !in_array($this->value, $tag->options)) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
__('Provided tag value %s, not found in tag %s options, please select the correct value'),
|
||||
$this->value,
|
||||
$this->tag
|
||||
),
|
||||
'tagValue'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->value) && $tag->isRequired === 1) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
__('Selected Tag %s requires a value, please enter the Tag in %s|Value format or provide Tag value in the dedicated field.'),
|
||||
$this->tag,
|
||||
$this->tag
|
||||
),
|
||||
'tagValue'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
180
lib/Entity/TagLinkTrait.php
Normal file
180
lib/Entity/TagLinkTrait.php
Normal file
@@ -0,0 +1,180 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class TagLinkTrait
|
||||
* @package Xibo\Entity
|
||||
*/
|
||||
trait TagLinkTrait
|
||||
{
|
||||
/**
|
||||
* Does the Entity have the provided tag?
|
||||
* @param $searchTag
|
||||
* @return bool
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function hasTag($searchTag)
|
||||
{
|
||||
foreach ($this->tags as $tag) {
|
||||
if ($tag->tag == $searchTag) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign Tag
|
||||
* @param TagLink $tag
|
||||
* @return $this
|
||||
*/
|
||||
public function assignTag(TagLink $tag)
|
||||
{
|
||||
$this->linkTags[] = $tag;
|
||||
$this->tags[] = $tag;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unassign tag
|
||||
* @param TagLink $tag
|
||||
* @return $this
|
||||
*/
|
||||
public function unassignTag(TagLink $tag)
|
||||
{
|
||||
$this->unlinkTags[] = $tag;
|
||||
|
||||
foreach ($this->tags as $key => $currentTag) {
|
||||
if ($currentTag->tagId === $tag->tagId) {
|
||||
array_splice($this->tags, $key, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Link
|
||||
*/
|
||||
public function linkTagToEntity($table, $column, $entityId, $tagId, $value)
|
||||
{
|
||||
$this->getLog()->debug(sprintf('Linking %s %d, to tagId %d', $column, $entityId, $tagId));
|
||||
|
||||
$this->getStore()->update('INSERT INTO `' . $table .'` (`tagId`, `'.$column.'`, `value`) VALUES (:tagId, :entityId, :value) ON DUPLICATE KEY UPDATE '.$column.' = :entityId, `value` = :value', [
|
||||
'tagId' => $tagId,
|
||||
'entityId' => $entityId,
|
||||
'value' => $value
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink
|
||||
*/
|
||||
public function unlinkTagFromEntity($table, $column, $entityId, $tagId)
|
||||
{
|
||||
$this->getLog()->debug(sprintf('Unlinking %s %d, from tagId %d', $column, $entityId, $tagId));
|
||||
|
||||
$this->getStore()->update('DELETE FROM `'.$table.'` WHERE tagId = :tagId AND `'.$column.'` = :entityId', [
|
||||
'tagId' => $tagId,
|
||||
'entityId' => $entityId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink all Tags from Entity
|
||||
*/
|
||||
public function unlinkAllTagsFromEntity($table, $column, $entityId)
|
||||
{
|
||||
$this->getLog()->debug(sprintf('Unlinking all Tags from %s %d', $column, $entityId));
|
||||
|
||||
$this->getStore()->update('DELETE FROM `'.$table.'` WHERE `'.$column.'` = :entityId', [
|
||||
'entityId' => $entityId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param TagLink[] $tags
|
||||
*/
|
||||
public function updateTagLinks($tags = [])
|
||||
{
|
||||
if ($this->tags != $tags) {
|
||||
$this->unlinkTags = array_udiff($this->tags, $tags, function ($a, $b) {
|
||||
/* @var TagLink $a */
|
||||
/* @var TagLink $b */
|
||||
return $a->tagId - $b->tagId;
|
||||
});
|
||||
|
||||
$this->getLog()->debug(sprintf('Tags to be removed: %s', json_encode($this->unlinkTags)));
|
||||
|
||||
// see what we need to add
|
||||
$this->linkTags = array_udiff($tags, $this->tags, function ($a, $b) {
|
||||
/* @var TagLink $a */
|
||||
/* @var TagLink $b */
|
||||
if ($a->value !== $b->value && $a->tagId === $b->tagId) {
|
||||
return -1;
|
||||
} else {
|
||||
return $a->tagId - $b->tagId;
|
||||
}
|
||||
});
|
||||
|
||||
// Replace the arrays
|
||||
$this->tags = $tags;
|
||||
|
||||
$this->getLog()->debug(sprintf('Tags to be added: %s', json_encode($this->linkTags)));
|
||||
|
||||
$this->getLog()->debug(sprintf('Tags remaining: %s', json_encode($this->tags)));
|
||||
} else {
|
||||
$this->getLog()->debug('Tags were not changed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert TagLink array into a string for use on forms.
|
||||
* @return string
|
||||
*/
|
||||
public function getTagString()
|
||||
{
|
||||
$tagsString = '';
|
||||
|
||||
if (empty($this->tags)) {
|
||||
return $tagsString;
|
||||
}
|
||||
|
||||
$i = 1;
|
||||
foreach ($this->tags as $tagLink) {
|
||||
/** @var TagLink $tagLink */
|
||||
if ($i > 1) {
|
||||
$tagsString .= ',';
|
||||
}
|
||||
$tagsString .= $tagLink->tag . (($tagLink->value) ? '|' . $tagLink->value : '');
|
||||
$i++;
|
||||
}
|
||||
|
||||
return $tagsString;
|
||||
}
|
||||
}
|
||||
309
lib/Entity/Task.php
Normal file
309
lib/Entity/Task.php
Normal file
@@ -0,0 +1,309 @@
|
||||
<?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\Entity;
|
||||
use Carbon\Carbon;
|
||||
use Cron\CronExpression;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class Task
|
||||
* @package Xibo\XTR
|
||||
*/
|
||||
class Task implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public static $STATUS_RUNNING = 1;
|
||||
public static $STATUS_IDLE = 2;
|
||||
public static $STATUS_ERROR = 3;
|
||||
public static $STATUS_SUCCESS = 4;
|
||||
public static $STATUS_TIMEOUT = 5;
|
||||
|
||||
public $taskId;
|
||||
public $name;
|
||||
public $configFile;
|
||||
public $class;
|
||||
public $status;
|
||||
public $pid = 0;
|
||||
public $options = [];
|
||||
public $schedule;
|
||||
public $lastRunDt = 0;
|
||||
public $lastRunStartDt;
|
||||
public $lastRunMessage;
|
||||
public $lastRunStatus;
|
||||
public $lastRunDuration = 0;
|
||||
public $lastRunExitCode = 0;
|
||||
public $isActive;
|
||||
public $runNow;
|
||||
|
||||
/**
|
||||
* Command constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \DateTime|string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function nextRunDate(): \DateTime|string
|
||||
{
|
||||
try {
|
||||
try {
|
||||
$cron = new CronExpression($this->schedule);
|
||||
} catch (\Exception $e) {
|
||||
// Try and take the first X characters instead.
|
||||
try {
|
||||
$cron = new CronExpression(substr($this->schedule, 0, strlen($this->schedule) - 2));
|
||||
} catch (\Exception) {
|
||||
$this->getLog()->error('nextRunDate: cannot fix CRON syntax error ' . $this->taskId);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->lastRunDt == 0) {
|
||||
return (new \DateTime())->format('U');
|
||||
}
|
||||
|
||||
return $cron->getNextRunDate(\DateTime::createFromFormat('U', $this->lastRunDt))->format('U');
|
||||
} catch (\Exception) {
|
||||
$this->getLog()->error('Invalid CRON expression for TaskId ' . $this->taskId);
|
||||
|
||||
$this->status = self::$STATUS_ERROR;
|
||||
return (new \DateTime())->add(new \DateInterval('P1Y'))->format('U');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set class and options
|
||||
* @throws NotFoundException
|
||||
*/
|
||||
public function setClassAndOptions()
|
||||
{
|
||||
if ($this->configFile == null)
|
||||
throw new NotFoundException(__('No config file recorded for task. Please recreate.'));
|
||||
|
||||
// Get the class and default set of options from the config file.
|
||||
if (!file_exists(PROJECT_ROOT . $this->configFile))
|
||||
throw new NotFoundException(__('Config file not found for Task'));
|
||||
|
||||
$config = json_decode(file_get_contents(PROJECT_ROOT . $this->configFile), true);
|
||||
$this->class = $config['class'];
|
||||
$this->options = array_merge($config['options'], $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
private function validate(): void
|
||||
{
|
||||
// Test the CRON expression
|
||||
if (empty($this->schedule)) {
|
||||
throw new InvalidArgumentException(__('Please enter a CRON expression in the Schedule'), 'schedule');
|
||||
}
|
||||
|
||||
try {
|
||||
$cron = new CronExpression($this->schedule);
|
||||
$cron->getNextRunDate();
|
||||
} catch (\Exception $e) {
|
||||
$this->getLog()->info('run: CRON syntax error for taskId ' . $this->taskId
|
||||
. ', e: ' . $e->getMessage());
|
||||
|
||||
try {
|
||||
$trimmed = substr($this->schedule, 0, strlen($this->schedule) - 2);
|
||||
$cron = new CronExpression($trimmed);
|
||||
$cron->getNextRunDate();
|
||||
} catch (\Exception) {
|
||||
throw new InvalidArgumentException(__('Invalid CRON expression in the Schedule'), 'schedule');
|
||||
}
|
||||
|
||||
// Swap to the trimmed (and correct) schedule
|
||||
$this->schedule = $trimmed;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save(array $options = []): void
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true,
|
||||
], $options);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($this->taskId == null) {
|
||||
$this->add();
|
||||
} else {
|
||||
// If we've transitioned from active to inactive, then reset the task status
|
||||
if ($this->getOriginalValue('isActive') != $this->isActive) {
|
||||
$this->status = Task::$STATUS_IDLE;
|
||||
}
|
||||
|
||||
$this->edit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `task` WHERE `taskId` = :taskId', ['taskId' => $this->taskId]);
|
||||
}
|
||||
|
||||
private function add()
|
||||
{
|
||||
$this->taskId = $this->getStore()->insert('
|
||||
INSERT INTO `task` (`name`, `status`, `configFile`, `class`, `pid`, `options`, `schedule`,
|
||||
`lastRunDt`, `lastRunMessage`, `lastRunStatus`, `lastRunDuration`, `lastRunExitCode`,
|
||||
`isActive`, `runNow`) VALUES
|
||||
(:name, :status, :configFile, :class, :pid, :options, :schedule,
|
||||
:lastRunDt, :lastRunMessage, :lastRunStatus, :lastRunDuration, :lastRunExitCode,
|
||||
:isActive, :runNow)
|
||||
', [
|
||||
'name' => $this->name,
|
||||
'status' => $this->status,
|
||||
'pid' => $this->pid,
|
||||
'configFile' => $this->configFile,
|
||||
'class' => $this->class,
|
||||
'options' => json_encode($this->options),
|
||||
'schedule' => $this->schedule,
|
||||
'lastRunDt' => $this->lastRunDt,
|
||||
'lastRunMessage' => $this->lastRunMessage,
|
||||
'lastRunStatus' => $this->lastRunStatus,
|
||||
'lastRunDuration' => $this->lastRunDuration,
|
||||
'lastRunExitCode' => $this->lastRunExitCode,
|
||||
'isActive' => $this->isActive,
|
||||
'runNow' => $this->runNow
|
||||
]);
|
||||
}
|
||||
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `task` SET
|
||||
`name` = :name,
|
||||
`status` = :status,
|
||||
`pid` = :pid,
|
||||
`configFile` = :configFile,
|
||||
`class` = :class,
|
||||
`options` = :options,
|
||||
`schedule` = :schedule,
|
||||
`lastRunDt` = :lastRunDt,
|
||||
`lastRunMessage` = :lastRunMessage,
|
||||
`lastRunStatus` = :lastRunStatus,
|
||||
`lastRunDuration` = :lastRunDuration,
|
||||
`lastRunExitCode` = :lastRunExitCode,
|
||||
`isActive` = :isActive,
|
||||
`runNow` = :runNow
|
||||
WHERE `taskId` = :taskId
|
||||
', [
|
||||
'taskId' => $this->taskId,
|
||||
'name' => $this->name,
|
||||
'status' => $this->status,
|
||||
'pid' => $this->pid,
|
||||
'configFile' => $this->configFile,
|
||||
'class' => $this->class,
|
||||
'options' => json_encode($this->options),
|
||||
'schedule' => $this->schedule,
|
||||
'lastRunDt' => $this->lastRunDt,
|
||||
'lastRunMessage' => $this->lastRunMessage,
|
||||
'lastRunStatus' => $this->lastRunStatus,
|
||||
'lastRunDuration' => $this->lastRunDuration,
|
||||
'lastRunExitCode' => $this->lastRunExitCode,
|
||||
'isActive' => $this->isActive,
|
||||
'runNow' => $this->runNow
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this task to be started, updating the DB as necessary
|
||||
* @return $this
|
||||
*/
|
||||
public function setStarted(): Task
|
||||
{
|
||||
// Set to running
|
||||
$this->status = \Xibo\Entity\Task::$STATUS_RUNNING;
|
||||
$this->lastRunStartDt = Carbon::now()->format('U');
|
||||
$this->pid = getmypid();
|
||||
|
||||
$this->store->update('
|
||||
UPDATE `task` SET `status` = :status, lastRunStartDt = :lastRunStartDt, pid = :pid
|
||||
WHERE taskId = :taskId
|
||||
', [
|
||||
'taskId' => $this->taskId,
|
||||
'status' => $this->status,
|
||||
'lastRunStartDt' => $this->lastRunStartDt,
|
||||
'pid' => $this->pid,
|
||||
], 'xtr', true, false);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this task to be finished, updating only the fields we might have changed
|
||||
* @return $this
|
||||
*/
|
||||
public function setFinished(): Task
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `task` SET
|
||||
`status` = :status,
|
||||
`pid` = :pid,
|
||||
`lastRunDt` = :lastRunDt,
|
||||
`lastRunMessage` = :lastRunMessage,
|
||||
`lastRunStatus` = :lastRunStatus,
|
||||
`lastRunDuration` = :lastRunDuration,
|
||||
`lastRunExitCode` = :lastRunExitCode,
|
||||
`runNow` = :runNow
|
||||
WHERE `taskId` = :taskId
|
||||
', [
|
||||
'taskId' => $this->taskId,
|
||||
'status' => $this->status,
|
||||
'pid' => $this->pid,
|
||||
'lastRunDt' => $this->lastRunDt,
|
||||
'lastRunMessage' => $this->lastRunMessage,
|
||||
'lastRunStatus' => $this->lastRunStatus,
|
||||
'lastRunDuration' => $this->lastRunDuration,
|
||||
'lastRunExitCode' => $this->lastRunExitCode,
|
||||
'runNow' => $this->runNow
|
||||
], 'xtr', true, false);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
120
lib/Entity/Transition.php
Normal file
120
lib/Entity/Transition.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
|
||||
|
||||
/**
|
||||
* Class Transition
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class Transition
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The transition ID")
|
||||
* @var int
|
||||
*/
|
||||
public $transitionId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The transition name")
|
||||
* @var string
|
||||
*/
|
||||
public $transition;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Code for transition")
|
||||
* @var string
|
||||
*/
|
||||
public $code;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag indicating whether this is a directional transition")
|
||||
* @var int
|
||||
*/
|
||||
public $hasDirection;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag indicating whether this transition has a duration option")
|
||||
* @var int
|
||||
*/
|
||||
public $hasDuration;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag indicating whether this transition should be available for IN assignments")
|
||||
* @var int
|
||||
*/
|
||||
public $availableAsIn;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag indicating whether this transition should be available for OUT assignments")
|
||||
* @var int
|
||||
*/
|
||||
public $availableAsOut;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->transitionId;
|
||||
}
|
||||
|
||||
public function getOwnerId()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
if ($this->transitionId == null || $this->transitionId == 0) {
|
||||
throw new InvalidArgumentException();
|
||||
}
|
||||
|
||||
$this->getStore()->update('
|
||||
UPDATE `transition` SET AvailableAsIn = :availableAsIn, AvailableAsOut = :availableAsOut WHERE transitionID = :transitionId
|
||||
', [
|
||||
'availableAsIn' => $this->availableAsIn,
|
||||
'availableAsOut' => $this->availableAsOut,
|
||||
'transitionId' => $this->transitionId
|
||||
]);
|
||||
}
|
||||
}
|
||||
1577
lib/Entity/User.php
Normal file
1577
lib/Entity/User.php
Normal file
File diff suppressed because it is too large
Load Diff
547
lib/Entity/UserGroup.php
Normal file
547
lib/Entity/UserGroup.php
Normal file
@@ -0,0 +1,547 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
|
||||
use Respect\Validation\Validator as v;
|
||||
use Xibo\Factory\UserFactory;
|
||||
use Xibo\Factory\UserGroupFactory;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
use Xibo\Support\Exception\DuplicateEntityException;
|
||||
use Xibo\Support\Exception\InvalidArgumentException;
|
||||
use Xibo\Support\Exception\NotFoundException;
|
||||
|
||||
/**
|
||||
* Class UserGroup
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class UserGroup
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Group ID")
|
||||
* @var int
|
||||
*/
|
||||
public $groupId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The group name")
|
||||
* @var string
|
||||
*/
|
||||
public $group;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating whether this is a user specific group or not")
|
||||
* @var int
|
||||
*/
|
||||
public $isUserSpecific = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="A flag indicating the special everyone group")
|
||||
* @var int
|
||||
*/
|
||||
public $isEveryone = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Description of this User Group")
|
||||
* @var string
|
||||
*/
|
||||
public $description;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="This users library quota in bytes. 0 = unlimited")
|
||||
* @var int
|
||||
*/
|
||||
public $libraryQuota;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Does this Group receive system notifications.")
|
||||
* @var int
|
||||
*/
|
||||
public $isSystemNotification = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Does this Group receive display notifications.")
|
||||
* @var int
|
||||
*/
|
||||
public $isDisplayNotification = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Does this Group receive DataSet notifications.")
|
||||
* @var int
|
||||
*/
|
||||
public $isDataSetNotification = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Does this Group receive Layout notifications.")
|
||||
* @var int
|
||||
*/
|
||||
public $isLayoutNotification = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Does this Group receive Library notifications.")
|
||||
* @var int
|
||||
*/
|
||||
public $isLibraryNotification = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Does this Group receive Report notifications.")
|
||||
* @var int
|
||||
*/
|
||||
public $isReportNotification = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Does this Group receive Schedule notifications.")
|
||||
* @var int
|
||||
*/
|
||||
public $isScheduleNotification = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Does this Group receive Custom notifications.")
|
||||
* @var int
|
||||
*/
|
||||
public $isCustomNotification = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Is this Group shown in the list of choices when onboarding a new user")
|
||||
* @var int
|
||||
*/
|
||||
public $isShownForAddUser = 0;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Default Home page for new users")
|
||||
* @var string
|
||||
*/
|
||||
public $defaultHomepageId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Features this User Group has direct access to", @SWG\Items(type="string"))
|
||||
* @var array
|
||||
*/
|
||||
public $features = [];
|
||||
|
||||
// Users
|
||||
private $users = [];
|
||||
|
||||
/**
|
||||
* @var UserGroupFactory
|
||||
*/
|
||||
private $userGroupFactory;
|
||||
|
||||
/**
|
||||
* @var UserFactory
|
||||
*/
|
||||
private $userFactory;
|
||||
|
||||
private $assignedUserIds = [];
|
||||
private $unassignedUserIds = [];
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
* @param UserGroupFactory $userGroupFactory
|
||||
* @param UserFactory $userFactory
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher, $userGroupFactory, $userFactory)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
|
||||
$this->userGroupFactory = $userGroupFactory;
|
||||
$this->userFactory = $userFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function __clone()
|
||||
{
|
||||
// Clear the groupId
|
||||
$this->groupId = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('ID = %d, Group = %s, IsUserSpecific = %d', $this->groupId, $this->group, $this->isUserSpecific);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique hash for this User Group
|
||||
*/
|
||||
private function hash()
|
||||
{
|
||||
return md5(json_encode($this));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->groupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getOwnerId()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Owner of this Group
|
||||
* @param User $user
|
||||
*/
|
||||
public function setOwner($user)
|
||||
{
|
||||
$this->load();
|
||||
|
||||
$this->isUserSpecific = 1;
|
||||
$this->isEveryone = 0;
|
||||
$this->assignUser($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign User
|
||||
* @param User $user
|
||||
*/
|
||||
public function assignUser($user)
|
||||
{
|
||||
$this->load();
|
||||
|
||||
if (!in_array($user, $this->users)) {
|
||||
$this->users[] = $user;
|
||||
$this->assignedUserIds[] = $user->userId;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unassign User
|
||||
* @param User $user
|
||||
*/
|
||||
public function unassignUser($user)
|
||||
{
|
||||
$this->load();
|
||||
$this->unassignedUserIds[] = $user->userId;
|
||||
$this->users = array_udiff($this->users, [$user], function ($a, $b) {
|
||||
/**
|
||||
* @var User $a
|
||||
* @var User $b
|
||||
*/
|
||||
return $a->getId() - $b->getId();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
if (!v::stringType()->length(1, 50)->validate($this->group)) {
|
||||
throw new InvalidArgumentException(__('User Group Name cannot be empty.') . $this, 'name');
|
||||
}
|
||||
|
||||
if ($this->libraryQuota !== null && !v::intType()->validate($this->libraryQuota)) {
|
||||
throw new InvalidArgumentException(__('Library Quota must be a whole number.'), 'libraryQuota');
|
||||
}
|
||||
|
||||
try {
|
||||
$group = $this->userGroupFactory->getByName($this->group, $this->isUserSpecific);
|
||||
|
||||
if ($this->groupId == null || $this->groupId != $group->groupId) {
|
||||
throw new DuplicateEntityException(
|
||||
__('There is already a group with this name. Please choose another.')
|
||||
);
|
||||
}
|
||||
} catch (NotFoundException $e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load this User Group
|
||||
* @param array $options
|
||||
*/
|
||||
public function load($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'loadUsers' => true
|
||||
], $options);
|
||||
|
||||
if ($this->loaded || $this->groupId == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($options['loadUsers']) {
|
||||
// Load all assigned users
|
||||
$this->users = $this->userFactory->getByGroupId($this->groupId);
|
||||
}
|
||||
|
||||
// Set the hash
|
||||
$this->hash = $this->hash();
|
||||
$this->loaded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the group
|
||||
* @param array $options
|
||||
* @throws DuplicateEntityException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function save($options = [])
|
||||
{
|
||||
$options = array_merge([
|
||||
'validate' => true,
|
||||
'linkUsers' => true
|
||||
], $options);
|
||||
|
||||
if ($options['validate']) {
|
||||
$this->validate();
|
||||
}
|
||||
|
||||
if ($this->groupId == null || $this->groupId == 0) {
|
||||
$this->add();
|
||||
$this->audit($this->groupId, 'User Group added', ['group' => $this->group]);
|
||||
} else if ($this->hash() != $this->hash) {
|
||||
$this->edit();
|
||||
$this->audit($this->groupId, 'User Group edited');
|
||||
}
|
||||
|
||||
if ($options['linkUsers']) {
|
||||
$this->linkUsers();
|
||||
$this->unlinkUsers();
|
||||
|
||||
if (count($this->assignedUserIds) > 0) {
|
||||
$this->audit($this->groupId, 'Users assigned', ['userIds' => implode(',', $this->assignedUserIds)]);
|
||||
}
|
||||
|
||||
if (count($this->unassignedUserIds) > 0) {
|
||||
$this->audit($this->groupId, 'Users unassigned', ['userIds' => implode(',', $this->unassignedUserIds)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save features
|
||||
* @return $this
|
||||
*/
|
||||
public function saveFeatures()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `group` SET features = :features WHERE groupId = :groupId
|
||||
', [
|
||||
'groupId' => $this->groupId,
|
||||
'features' => json_encode($this->features)
|
||||
]);
|
||||
|
||||
$this->audit($this->groupId, 'User Group feature access modified', [
|
||||
'features' => json_encode($this->features)
|
||||
]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this Group
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
// We must ensure everything is loaded before we delete
|
||||
if ($this->hash == null) {
|
||||
$this->load();
|
||||
}
|
||||
|
||||
// Unlink users
|
||||
$this->removeAssignments();
|
||||
|
||||
$this->getStore()->update('DELETE FROM `permission` WHERE groupId = :groupId', ['groupId' => $this->groupId]);
|
||||
$this->getStore()->update('DELETE FROM `group` WHERE groupId = :groupId', ['groupId' => $this->groupId]);
|
||||
|
||||
$this->audit($this->groupId, 'User group deleted.', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all assignments
|
||||
*/
|
||||
private function removeAssignments()
|
||||
{
|
||||
// Delete Notifications
|
||||
// NB: notifications aren't modelled as child objects because there could be many thousands of notifications on each
|
||||
// usergroup. We consider the notification to be the parent here and it manages the assignments.
|
||||
// This does mean that we might end up with an empty notification (not assigned to anything)
|
||||
$this->getStore()->update('DELETE FROM `lknotificationuser` WHERE `userId` IN (SELECT `userId` FROM `lkusergroup` WHERE `groupId` = :groupId) ', ['groupId' => $this->groupId]);
|
||||
$this->getStore()->update('DELETE FROM `lknotificationgroup` WHERE `groupId` = :groupId', ['groupId' => $this->groupId]);
|
||||
|
||||
// Remove user assignments
|
||||
$this->users = [];
|
||||
$this->unlinkUsers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add
|
||||
*/
|
||||
private function add()
|
||||
{
|
||||
$this->groupId = $this->getStore()->insert('
|
||||
INSERT INTO `group` (
|
||||
`group`,
|
||||
`IsUserSpecific`,
|
||||
`description`,
|
||||
`libraryQuota`,
|
||||
`isSystemNotification`,
|
||||
`isDisplayNotification`,
|
||||
`isDataSetNotification`,
|
||||
`isLayoutNotification`,
|
||||
`isLibraryNotification`,
|
||||
`isReportNotification`,
|
||||
`isScheduleNotification`,
|
||||
`isCustomNotification`,
|
||||
`isShownForAddUser`,
|
||||
`defaultHomepageId`
|
||||
)
|
||||
VALUES (
|
||||
:group,
|
||||
:isUserSpecific,
|
||||
:description,
|
||||
:libraryQuota,
|
||||
:isSystemNotification,
|
||||
:isDisplayNotification,
|
||||
:isDataSetNotification,
|
||||
:isLayoutNotification,
|
||||
:isLibraryNotification,
|
||||
:isReportNotification,
|
||||
:isScheduleNotification,
|
||||
:isCustomNotification,
|
||||
:isShownForAddUser,
|
||||
:defaultHomepageId
|
||||
)
|
||||
', [
|
||||
'group' => $this->group,
|
||||
'isUserSpecific' => $this->isUserSpecific,
|
||||
'description' => $this->description,
|
||||
'libraryQuota' => $this->libraryQuota,
|
||||
'isSystemNotification' => $this->isSystemNotification,
|
||||
'isDisplayNotification' => $this->isDisplayNotification,
|
||||
'isDataSetNotification' => $this->isDataSetNotification,
|
||||
'isLayoutNotification' => $this->isLayoutNotification,
|
||||
'isLibraryNotification' => $this->isLibraryNotification,
|
||||
'isReportNotification' => $this->isReportNotification,
|
||||
'isScheduleNotification' => $this->isScheduleNotification,
|
||||
'isCustomNotification' => $this->isCustomNotification,
|
||||
'isShownForAddUser' => $this->isShownForAddUser,
|
||||
'defaultHomepageId' => $this->defaultHomepageId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
*/
|
||||
private function edit()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `group` SET
|
||||
`group` = :group,
|
||||
`description` = :description,
|
||||
libraryQuota = :libraryQuota,
|
||||
`isSystemNotification` = :isSystemNotification,
|
||||
`isDisplayNotification` = :isDisplayNotification,
|
||||
`isDataSetNotification` = :isDataSetNotification,
|
||||
`isLayoutNotification` = :isLayoutNotification,
|
||||
`isLibraryNotification` = :isLibraryNotification,
|
||||
`isReportNotification` = :isReportNotification,
|
||||
`isScheduleNotification` = :isScheduleNotification,
|
||||
`isCustomNotification` = :isCustomNotification,
|
||||
`isShownForAddUser` = :isShownForAddUser,
|
||||
`defaultHomepageId` = :defaultHomepageId
|
||||
WHERE groupId = :groupId
|
||||
', [
|
||||
'groupId' => $this->groupId,
|
||||
'group' => $this->group,
|
||||
'description' => $this->description,
|
||||
'libraryQuota' => $this->libraryQuota,
|
||||
'isSystemNotification' => $this->isSystemNotification,
|
||||
'isDisplayNotification' => $this->isDisplayNotification,
|
||||
'isDataSetNotification' => $this->isDataSetNotification,
|
||||
'isLayoutNotification' => $this->isLayoutNotification,
|
||||
'isLibraryNotification' => $this->isLibraryNotification,
|
||||
'isReportNotification' => $this->isReportNotification,
|
||||
'isScheduleNotification' => $this->isScheduleNotification,
|
||||
'isCustomNotification' => $this->isCustomNotification,
|
||||
'isShownForAddUser' => $this->isShownForAddUser,
|
||||
'defaultHomepageId' => $this->defaultHomepageId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link Users
|
||||
*/
|
||||
private function linkUsers()
|
||||
{
|
||||
$insert = $this->getStore()->getConnection()->prepare(
|
||||
'INSERT INTO `lkusergroup` (groupId, userId)
|
||||
VALUES (:groupId, :userId) ON DUPLICATE KEY UPDATE groupId = groupId'
|
||||
);
|
||||
|
||||
foreach ($this->users as $user) {
|
||||
/* @var User $user */
|
||||
$this->getLog()->debug('Linking %s to %s', $user->userName, $this->group);
|
||||
|
||||
$insert->execute([
|
||||
'groupId' => $this->groupId,
|
||||
'userId' => $user->userId
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink Users
|
||||
*/
|
||||
private function unlinkUsers()
|
||||
{
|
||||
$params = ['groupId' => $this->groupId];
|
||||
|
||||
$sql = 'DELETE FROM `lkusergroup` WHERE groupId = :groupId AND userId NOT IN (0';
|
||||
|
||||
$i = 0;
|
||||
foreach ($this->users as $user) {
|
||||
/* @var User $user */
|
||||
$i++;
|
||||
$sql .= ',:userId' . $i;
|
||||
$params['userId' . $i] = $user->userId;
|
||||
}
|
||||
|
||||
$sql .= ')';
|
||||
|
||||
$this->getStore()->update($sql, $params);
|
||||
}
|
||||
}
|
||||
222
lib/Entity/UserNotification.php
Normal file
222
lib/Entity/UserNotification.php
Normal file
@@ -0,0 +1,222 @@
|
||||
<?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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class UserGroupNotification
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class UserNotification implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="The User Id"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="The Notification Id"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $notificationId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Release Date expressed as Unix Timestamp"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $releaseDt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Read Date expressed as Unix Timestamp"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $readDt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Email Date expressed as Unix Timestamp"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $emailDt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="A flag indicating whether to show as read or not"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $read;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="The subject"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $subject;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="The body"
|
||||
* )
|
||||
* @var string
|
||||
*/
|
||||
public $body;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Should the notification interrupt the CMS UI on navigate/login"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $isInterrupt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* description="Flag for system notification"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public $isSystem;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $email;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $filename;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $originalFileName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $nonusers;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* Command constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Read
|
||||
* @param int $readDt
|
||||
*/
|
||||
public function setRead($readDt)
|
||||
{
|
||||
$this->read = 1;
|
||||
|
||||
if ($this->readDt == 0) {
|
||||
$this->readDt = $readDt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set unread
|
||||
*/
|
||||
public function setUnread()
|
||||
{
|
||||
$this->read = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Emailed
|
||||
* @param int $emailDt
|
||||
*/
|
||||
public function setEmailed($emailDt)
|
||||
{
|
||||
if ($this->emailDt == 0) {
|
||||
$this->emailDt = $emailDt;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `lknotificationuser`
|
||||
SET `read` = :read,
|
||||
`readDt` = :readDt,
|
||||
`emailDt` = :emailDt
|
||||
WHERE notificationId = :notificationId
|
||||
AND userId = :userId
|
||||
', [
|
||||
'read' => $this->read,
|
||||
'readDt' => $this->readDt,
|
||||
'emailDt' => $this->emailDt,
|
||||
'notificationId' => $this->notificationId,
|
||||
'userId' => $this->userId
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTypeForGroup(): string
|
||||
{
|
||||
return match ($this->type) {
|
||||
'dataset' => 'isDataSetNotification',
|
||||
'display' => 'isDisplayNotification',
|
||||
'layout' => 'isLayoutNotification',
|
||||
'library' => 'isLibraryNotification',
|
||||
'report' => 'isReportNotification',
|
||||
'schedule' => 'isScheduleNotification',
|
||||
default => 'isCustomNotification',
|
||||
};
|
||||
}
|
||||
}
|
||||
99
lib/Entity/UserOption.php
Normal file
99
lib/Entity/UserOption.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (C) 2023 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class UserOption
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class UserOption implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The userId that this Option applies to")
|
||||
* @var int
|
||||
*/
|
||||
public $userId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The option name")
|
||||
* @var string
|
||||
*/
|
||||
public $option;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The option value")
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
$this->excludeProperty('userId');
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->userId . '-' . $this->option . '-' . md5($this->value);
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
// when the option is not in the database and default is on, switching option value to off
|
||||
// would not insert the record, hence the additional condition here xibosignage/xibo#2975
|
||||
if ($this->hasPropertyChanged('value')
|
||||
|| ($this->getOriginalValue('value') === null && $this->value !== null)
|
||||
) {
|
||||
$this->getStore()->insert('INSERT INTO `useroption` (`userId`, `option`, `value`) VALUES (:userId, :option, :value) ON DUPLICATE KEY UPDATE `value` = :value2', [
|
||||
'userId' => $this->userId,
|
||||
'option' => $this->option,
|
||||
'value' => $this->value,
|
||||
'value2' => $this->value,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `useroption` WHERE `userId` = :userId AND `option` = :option', [
|
||||
'userId' => $this->userId,
|
||||
'option' => $this->option
|
||||
]);
|
||||
}
|
||||
}
|
||||
60
lib/Entity/UserType.php
Normal file
60
lib/Entity/UserType.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class UserType
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
*/
|
||||
class UserType
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
public $userTypeId;
|
||||
public $userType;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->userTypeId;
|
||||
}
|
||||
|
||||
public function getOwnerId()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
1353
lib/Entity/Widget.php
Normal file
1353
lib/Entity/Widget.php
Normal file
File diff suppressed because it is too large
Load Diff
115
lib/Entity/WidgetAudio.php
Normal file
115
lib/Entity/WidgetAudio.php
Normal file
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class WidgetAudio
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class WidgetAudio implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Widget Id")
|
||||
* @var int
|
||||
*/
|
||||
public $widgetId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Media Id")
|
||||
* @var int
|
||||
*/
|
||||
public $mediaId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The percentage volume")
|
||||
* @var int
|
||||
*/
|
||||
public $volume;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="Flag indicating whether to loop")
|
||||
* @var int
|
||||
*/
|
||||
public $loop;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Id
|
||||
* @return int
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->mediaId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save this widget audio
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$sql = '
|
||||
INSERT INTO `lkwidgetaudio` (widgetId, mediaId, `volume`, `loop`)
|
||||
VALUES (:widgetId, :mediaId, :volume, :loop)
|
||||
ON DUPLICATE KEY UPDATE volume = :volume, `loop` = :loop
|
||||
';
|
||||
|
||||
$this->getStore()->insert($sql, array(
|
||||
'widgetId' => $this->widgetId,
|
||||
'mediaId' => $this->mediaId,
|
||||
'volume' => $this->volume,
|
||||
'loop' => $this->loop
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this widget audio
|
||||
*/
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update('
|
||||
DELETE FROM `lkwidgetaudio`
|
||||
WHERE widgetId = :widgetId AND mediaId = :mediaId
|
||||
', [
|
||||
'widgetId' => $this->widgetId,
|
||||
'mediaId' => $this->mediaId
|
||||
]);
|
||||
}
|
||||
}
|
||||
169
lib/Entity/WidgetData.php
Normal file
169
lib/Entity/WidgetData.php
Normal file
@@ -0,0 +1,169 @@
|
||||
<?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\Entity;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Xibo\Helper\DateFormatHelper;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
/**
|
||||
* Class representing widget data
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class WidgetData implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* property="id",
|
||||
* description="The ID"
|
||||
* )
|
||||
* @var int|null
|
||||
*/
|
||||
public ?int $id = null;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* property="widgetId",
|
||||
* description="The Widget ID"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public int $widgetId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* property="data",
|
||||
* description="Array of data properties depending on the widget data type this data is for",
|
||||
* @SWG\Items(type="string")
|
||||
* )
|
||||
* @var array
|
||||
*/
|
||||
public array $data;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* property="displayOrder",
|
||||
* description="The Display Order"
|
||||
* )
|
||||
* @var int
|
||||
*/
|
||||
public int $displayOrder;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* property="createdDt",
|
||||
* description="The datetime this entity was created"
|
||||
* )
|
||||
* @var ?string
|
||||
*/
|
||||
public ?string $createdDt;
|
||||
|
||||
/**
|
||||
* @SWG\Property(
|
||||
* property="modifiedDt",
|
||||
* description="The datetime this entity was last modified"
|
||||
* )
|
||||
* @var ?string
|
||||
*/
|
||||
public ?string $modifiedDt;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct(
|
||||
StorageServiceInterface $store,
|
||||
LogServiceInterface $log,
|
||||
EventDispatcherInterface $dispatcher
|
||||
) {
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save this widget data
|
||||
* @return $this
|
||||
*/
|
||||
public function save(): WidgetData
|
||||
{
|
||||
if ($this->id === null) {
|
||||
$this->add();
|
||||
} else {
|
||||
$this->modifiedDt = Carbon::now()->format(DateFormatHelper::getSystemFormat());
|
||||
$this->edit();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete this widget data
|
||||
* @return void
|
||||
*/
|
||||
public function delete(): void
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `widgetdata` WHERE `id` = :id', ['id' => $this->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add and capture the ID
|
||||
* @return void
|
||||
*/
|
||||
private function add(): void
|
||||
{
|
||||
$this->id = $this->getStore()->insert('
|
||||
INSERT INTO `widgetdata` (`widgetId`, `data`, `displayOrder`, `createdDt`, `modifiedDt`)
|
||||
VALUES (:widgetId, :data, :displayOrder, :createdDt, :modifiedDt)
|
||||
', [
|
||||
'widgetId' => $this->widgetId,
|
||||
'data' => $this->data == null ? null : json_encode($this->data),
|
||||
'displayOrder' => $this->displayOrder,
|
||||
'createdDt' => Carbon::now()->format(DateFormatHelper::getSystemFormat()),
|
||||
'modifiedDt' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit
|
||||
* @return void
|
||||
*/
|
||||
private function edit(): void
|
||||
{
|
||||
$this->getStore()->update('
|
||||
UPDATE `widgetdata` SET
|
||||
`data` = :data,
|
||||
`displayOrder` = :displayOrder,
|
||||
`modifiedDt` = :modifiedDt
|
||||
WHERE `id` = :id
|
||||
', [
|
||||
'id' => $this->id,
|
||||
'data' => $this->data == null ? null : json_encode($this->data),
|
||||
'displayOrder' => $this->displayOrder,
|
||||
'modifiedDt' => $this->modifiedDt,
|
||||
]);
|
||||
}
|
||||
}
|
||||
111
lib/Entity/WidgetOption.php
Normal file
111
lib/Entity/WidgetOption.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/*
|
||||
* Copyright (c) 2022 Xibo Signage Ltd
|
||||
*
|
||||
* Xibo - Digital Signage - http://www.xibo.org.uk
|
||||
*
|
||||
* 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\Entity;
|
||||
use Xibo\Service\LogServiceInterface;
|
||||
use Xibo\Storage\StorageServiceInterface;
|
||||
|
||||
|
||||
/**
|
||||
* Class WidgetOption
|
||||
* @package Xibo\Entity
|
||||
*
|
||||
* @SWG\Definition()
|
||||
*/
|
||||
class WidgetOption implements \JsonSerializable
|
||||
{
|
||||
use EntityTrait;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The Widget ID that this Option belongs to")
|
||||
* @var int
|
||||
*/
|
||||
public $widgetId;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The option type, either attrib or raw")
|
||||
* @var string
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The option name")
|
||||
* @var string
|
||||
*/
|
||||
public $option;
|
||||
|
||||
/**
|
||||
* @SWG\Property(description="The option value")
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Entity constructor.
|
||||
* @param StorageServiceInterface $store
|
||||
* @param LogServiceInterface $log
|
||||
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
|
||||
*/
|
||||
public function __construct($store, $log, $dispatcher)
|
||||
{
|
||||
$this->setCommonDependencies($store, $log, $dispatcher);
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->widgetId = null;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
if ($this->type == 'cdata') {
|
||||
return sprintf('%s WidgetOption %s', $this->type, $this->option);
|
||||
}
|
||||
else {
|
||||
return sprintf('%s WidgetOption %s with value %s', $this->type, $this->option, $this->value);
|
||||
}
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->getLog()->debug('Saving ' . $this);
|
||||
|
||||
$this->getStore()->insert('
|
||||
INSERT INTO `widgetoption` (`widgetId`, `type`, `option`, `value`)
|
||||
VALUES (:widgetId, :type, :option, :value) ON DUPLICATE KEY UPDATE `value` = :value2
|
||||
', array(
|
||||
'widgetId' => $this->widgetId,
|
||||
'type' => $this->type,
|
||||
'option' => $this->option,
|
||||
'value' => $this->value,
|
||||
'value2' => $this->value
|
||||
));
|
||||
}
|
||||
|
||||
public function delete()
|
||||
{
|
||||
$this->getStore()->update('DELETE FROM `widgetoption` WHERE `widgetId` = :widgetId AND `option` = :option', array(
|
||||
'widgetId' => $this->widgetId, 'option' => $this->option)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user