Files
Cloud-CMS/lib/Entity/DisplayGroup.php

1116 lines
34 KiB
PHP
Raw Permalink Normal View History

2025-12-02 10:32:59 -05:00
<?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\Factory\DisplayFactory;
use Xibo\Factory\DisplayGroupFactory;
use Xibo\Factory\PermissionFactory;
use Xibo\Helper\DateFormatHelper;
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 DisplayGroup
* @package Xibo\Entity
*
* @SWG\Definition()
*/
class DisplayGroup implements \JsonSerializable
{
use EntityTrait;
use TagLinkTrait;
/**
* @SWG\Property(
* description="The displayGroup Id"
* )
* @var int
*/
public $displayGroupId;
/**
* @SWG\Property(
* description="The displayGroup Name"
* )
* @var string
*/
public $displayGroup;
/**
* @SWG\Property(
* description="The displayGroup Description"
* )
* @var string
*/
public $description;
/**
* @SWG\Property(
* description="A flag indicating whether this displayGroup is a single display displayGroup",
* )
* @var int
*/
public $isDisplaySpecific = 0;
/**
* @SWG\Property(
* description="A flag indicating whether this displayGroup is dynamic",
* )
* @var int
*/
public $isDynamic = 0;
/**
* @SWG\Property(
* description="Criteria for this dynamic group. A comma separated set of regular expressions to apply",
* )
* @var string
*/
public $dynamicCriteria;
/**
* @SWG\Property(description="Which logical operator should be used when filtering by multiple dynamic criteria? OR|AND")
* @var string
*/
public $dynamicCriteriaLogicalOperator;
/**
* @SWG\Property(
* description="Criteria for this dynamic group. A comma separated set of tags to apply",
* )
* @var string
*/
public $dynamicCriteriaTags;
/**
* @SWG\Property(description="Flag indicating whether to filter by exact Tag match")
* @var int
*/
public $dynamicCriteriaExactTags;
/**
* @SWG\Property(description="Which logical operator should be used when filtering by multiple Tags? OR|AND")
* @var string
*/
public $dynamicCriteriaTagsLogicalOperator;
/**
* @SWG\Property(
* description="The UserId who owns this display group",
* )
* @var int
*/
public $userId = 0;
/**
* @SWG\Property(description="Tags associated with this Display Group, array of TagLink objects")
* @var TagLink[]
*/
public $tags = [];
/**
* @SWG\Property(description="The display bandwidth limit")
* @var int
*/
public $bandwidthLimit;
/**
* @SWG\Property(description="A comma separated list of groups/users with permissions to this DisplayGroup")
* @var string
*/
public $groupsWithPermissions;
/**
* @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 Folder this Display Group belongs to")
* @var int
*/
public $folderId;
/**
* @SWG\Property(description="The id of the Folder responsible for providing permissions for this Display Group")
* @var int
*/
public $permissionsFolderId;
/**
* @SWG\Property(description="Optional Reference 1")
* @var string
*/
public $ref1;
/**
* @SWG\Property(description="Optional Reference 2")
* @var string
*/
public $ref2;
/**
* @SWG\Property(description="Optional Reference 3")
* @var string
*/
public $ref3;
/**
* @SWG\Property(description="Optional Reference 4")
* @var string
*/
public $ref4;
/**
* @SWG\Property(description="Optional Reference 5")
* @var string
*/
public $ref5;
// Child Items the Display Group is linked to
public $displays = [];
public $media = [];
public $layouts = [];
public $events = [];
private $displayGroups = [];
private $permissions = [];
/** @var TagLink[] */
private $unlinkTags = [];
/** @var TagLink[] */
private $linkTags = [];
private $jsonInclude = ['displayGroupId', 'displayGroup'];
// Track original assignments
private $originalDisplayGroups = [];
/**
* Is notify required during save?
* @var bool
*/
private $notifyRequired = false;
/**
* Is collect required?
* @var bool
*/
private $collectRequired = true;
/**
* @var bool Are we allowed to notify?
*/
private $allowNotify = true;
/**
* @var DisplayFactory
*/
private $displayFactory;
/**
* @var DisplayGroupFactory
*/
private $displayGroupFactory;
/**
* @var PermissionFactory
*/
private $permissionFactory;
/**
* Entity constructor.
* @param StorageServiceInterface $store
* @param LogServiceInterface $log
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
* @param DisplayGroupFactory $displayGroupFactory
* @param PermissionFactory $permissionFactory
*/
public function __construct($store, $log, $dispatcher, $displayGroupFactory, $permissionFactory)
{
$this->setCommonDependencies($store, $log, $dispatcher);
$this->displayGroupFactory = $displayGroupFactory;
$this->permissionFactory = $permissionFactory;
}
public function setDisplayFactory(DisplayFactory $displayFactory)
{
$this->displayFactory = $displayFactory;
}
public function __clone()
{
$this->displayGroupId = null;
$this->originalDisplayGroups = [];
$this->loaded = false;
if ($this->isDynamic) {
$this->clearDisplays()->clearDisplayGroups();
}
}
/**
* @return int
*/
public function getId()
{
return $this->displayGroupId;
}
public function getPermissionFolderId()
{
return $this->permissionsFolderId;
}
/**
* @return int
*/
public function getOwnerId()
{
return $this->userId;
}
/**
* Set the owner of this group
* @param $userId
*/
public function setOwner($userId)
{
$this->userId = $userId;
}
/**
* @return bool
*/
public function canChangeOwner()
{
return $this->isDisplaySpecific == 0;
}
/**
* Set Collection Required
* If true will send a player action to collect immediately
* @param bool|true $collectRequired
*/
public function setCollectRequired($collectRequired = true)
{
$this->collectRequired = $collectRequired;
}
/**
* Set the Owner of this Group
* @param Display $display
* @throws NotFoundException
*/
public function setDisplaySpecificDisplay($display)
{
$this->load();
$this->isDisplaySpecific = 1;
$this->assignDisplay($display);
}
public function clearDisplays(): DisplayGroup
{
$this->displays = [];
return $this;
}
public function clearDisplayGroups(): DisplayGroup
{
$this->displayGroups = [];
return $this;
}
public function clearTags(): DisplayGroup
{
$this->tags = [];
return $this;
}
public function clearLayouts(): DisplayGroup
{
$this->layouts = [];
return $this;
}
public function clearMedia(): DisplayGroup
{
$this->media = [];
return $this;
}
/**
* Set the Media Status to Incomplete
* @param int[] $displayIds
*/
public function notify($displayIds = [])
{
if ($this->allowNotify) {
$notify = $this->displayFactory->getDisplayNotifyService();
if ($this->collectRequired)
$notify->collectNow();
if (count($displayIds) > 0) {
foreach ($displayIds as $displayId) {
$notify->notifyByDisplayId($displayId);
}
} else {
$notify->notifyByDisplayGroupId($this->displayGroupId);
}
}
}
/**
* Assign Display
* @param Display $display
* @throws NotFoundException
*/
public function assignDisplay($display)
{
$found = false;
foreach ($this->displays as $existingDisplay) {
if ($existingDisplay->getId() === $display->getId()) {
$found = true;
break;
}
}
if (!$found)
$this->displays[] = $display;
}
/**
* Unassign Display
* @param Display $display
* @throws NotFoundException
*/
public function unassignDisplay($display)
{
// Changes made?
$countBefore = count($this->displays);
$this->displays = array_udiff($this->displays, [$display], function($a, $b) {
/**
* @var Display $a
* @var Display $b
*/
return $a->getId() - $b->getId();
});
// Notify if necessary
if ($countBefore !== count($this->displays))
$this->notifyRequired = true;
}
/**
* Assign DisplayGroup
* @param DisplayGroup $displayGroup
* @throws NotFoundException
*/
public function assignDisplayGroup($displayGroup)
{
if (!in_array($displayGroup, $this->displayGroups))
$this->displayGroups[] = $displayGroup;
}
/**
* Unassign DisplayGroup
* @param DisplayGroup $displayGroup
* @throws NotFoundException
*/
public function unassignDisplayGroup($displayGroup)
{
// Changes made?
$countBefore = count($this->displayGroups);
$this->displayGroups = array_udiff($this->displayGroups, [$displayGroup], function($a, $b) {
/**
* @var DisplayGroup $a
* @var DisplayGroup $b
*/
return $a->getId() - $b->getId();
});
// Notify if necessary
if ($countBefore !== count($this->displayGroups))
$this->notifyRequired = true;
}
/**
* Assign Media
* @param Media $media
* @throws NotFoundException
*/
public function assignMedia($media)
{
if (!in_array($media, $this->media)) {
$this->media[] = $media;
// We should notify
$this->notifyRequired = true;
}
}
/**
* Unassign Media
* @param Media $media
* @throws NotFoundException
*/
public function unassignMedia($media)
{
// Changes made?
$countBefore = count($this->media);
$this->media = array_udiff($this->media, [$media], function($a, $b) {
/**
* @var Media $a
* @var Media $b
*/
return $a->getId() - $b->getId();
});
// Notify if necessary
if ($countBefore !== count($this->media))
$this->notifyRequired = true;
}
/**
* Assign Layout
* @param Layout $layout
* @throws NotFoundException
*/
public function assignLayout($layout)
{
if (!in_array($layout, $this->layouts)) {
$this->layouts[] = $layout;
// We should notify
$this->notifyRequired = true;
}
}
/**
* Unassign Layout
* @param Layout $layout
* @throws NotFoundException
*/
public function unassignLayout($layout)
{
// Changes made?
$countBefore = count($this->layouts);
$this->layouts = array_udiff($this->layouts, [$layout], function($a, $b) {
/**
* @var Layout $a
* @var Layout $b
*/
return $a->getId() - $b->getId();
});
// Notify if necessary
if ($countBefore !== count($this->layouts))
$this->notifyRequired = true;
}
/**
* Load the contents for this display group
* @param array $options
* @throws NotFoundException
*/
public function load($options = [])
{
$options = array_merge([
'loadTags' => true
], $options);
if ($this->loaded || $this->displayGroupId == null || $this->displayGroupId == 0) {
return;
}
$this->permissions = $this->permissionFactory->getByObjectId(get_class($this), $this->displayGroupId);
$this->displayGroups = $this->displayGroupFactory->getByParentId($this->displayGroupId);
// Set the originals
$this->originalDisplayGroups = $this->displayGroups;
// We are loaded
$this->loaded = true;
}
/**
* Validate this display
* @throws DuplicateEntityException
* @throws InvalidArgumentException
*/
public function validate()
{
if (!v::stringType()->notEmpty()->validate($this->displayGroup)) {
throw new InvalidArgumentException(__('Please enter a display group name'), 'displayGroup');
}
if (!empty($this->description) && !v::stringType()->length(null, 254)->validate($this->description)) {
throw new InvalidArgumentException(__('Description can not be longer than 254 characters'), 'description');
}
if ($this->isDisplaySpecific == 0) {
// Check the name
$result = $this->getStore()->select('SELECT DisplayGroup FROM displaygroup WHERE DisplayGroup = :displayGroup AND IsDisplaySpecific = 0 AND displayGroupId <> :displayGroupId', [
'displayGroup' => $this->displayGroup,
'displayGroupId' => (($this->displayGroupId == null) ? 0 : $this->displayGroupId)
]);
if (count($result) > 0) {
throw new DuplicateEntityException(sprintf(__('You already own a display group called "%s". Please choose another name.'), $this->displayGroup));
}
// If we are dynamic, then make sure we have some criteria
if ($this->isDynamic == 1 && ($this->dynamicCriteria == '' && $this->dynamicCriteriaTags == '')) {
throw new InvalidArgumentException(__('Dynamic Display Groups must have at least one Criteria specified.'), 'dynamicCriteria');
}
}
}
/**
* Save
* @param array $options
* @throws GeneralException
*/
public function save($options = [])
{
$options = array_merge([
'validate' => true,
'saveGroup' => true,
'manageLinks' => true,
'manageDisplayLinks' => true,
'manageDynamicDisplayLinks' => true,
'allowNotify' => true,
'saveTags' => true,
'setModifiedDt' => true,
], $options);
// Should we allow notification or not?
$this->allowNotify = $options['allowNotify'];
if ($options['validate']) {
$this->validate();
}
if ($this->displayGroupId == null || $this->displayGroupId == 0) {
$this->add();
$this->loaded = true;
} else if ($options['saveGroup']) {
$this->edit($options);
}
if ($options['saveTags']) {
// Remove unwanted ones
if (is_array($this->unlinkTags)) {
foreach ($this->unlinkTags as $tag) {
$this->unlinkTagFromEntity('lktagdisplaygroup', 'displayGroupId', $this->displayGroupId, $tag->tagId);
}
}
// Save the tags
if (is_array($this->linkTags)) {
foreach ($this->linkTags as $tag) {
$this->linkTagToEntity('lktagdisplaygroup', 'displayGroupId', $this->displayGroupId, $tag->tagId, $tag->value);
}
}
}
if ($this->loaded) {
$this->getLog()->debug('Manage links');
if ($options['manageLinks']) {
// Handle any changes in the media linked
$this->linkMedia();
$this->unlinkMedia();
// Handle any changes in the layouts linked
$this->linkLayouts();
$this->unlinkLayouts();
}
if ($options['manageDisplayLinks']) {
// Handle any changes in the displays linked
$this->manageDisplayLinks($options['manageDynamicDisplayLinks']);
// Handle any group links
$this->manageDisplayGroupLinks();
}
} else if ($this->isDynamic == 1 && $options['manageDynamicDisplayLinks']) {
$this->manageDisplayLinks();
}
// Set media incomplete if necessary
if ($this->notifyRequired) {
$this->notify();
}
}
/**
* Delete
* @throws NotFoundException
* @throws GeneralException
*/
public function delete()
{
// Load everything for the delete
$this->load();
// Delete things this group can own
foreach ($this->permissions as $permission) {
/* @var Permission $permission */
$permission->delete();
}
foreach ($this->events as $event) {
/* @var Schedule $event */
$event->unassignDisplayGroup($this);
$event->save([
'audit' => false,
'validate' => false,
'deleteOrphaned' => true,
'notify' => false
]);
}
$this->unlinkAllTagsFromEntity('lktagdisplaygroup', 'displayGroupId', $this->displayGroupId);
// Delete assignments
$this->removeAssignments();
// delete link to ad campaign.
$this->getStore()->update('DELETE FROM `lkcampaigndisplaygroup` WHERE displayGroupId = :displayGroupId', [
'displayGroupId' => $this->displayGroupId
]);
// Delete the Group itself
$this->getStore()->update('DELETE FROM `displaygroup` WHERE DisplayGroupID = :displayGroupId', ['displayGroupId' => $this->displayGroupId]);
}
/**
* Remove any assignments
*/
public function removeAssignments()
{
$this->displays = [];
$this->displayGroups = [];
$this->layouts = [];
$this->media = [];
$this->unlinkDisplays();
$this->unlinkAllDisplayGroups();
$this->unlinkLayouts();
$this->unlinkMedia();
// Delete Notifications
// NB: notifications aren't modelled as child objects because there could be many thousands of notifications on each
// displaygroup. 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 `lknotificationdg` WHERE `displayGroupId` = :displayGroupId', ['displayGroupId' => $this->displayGroupId]);
}
private function add()
{
$time = Carbon::now()->format(DateFormatHelper::getSystemFormat());
$this->displayGroupId = $this->getStore()->insert('
INSERT INTO displaygroup (DisplayGroup, IsDisplaySpecific, Description, `isDynamic`, `dynamicCriteria`, `dynamicCriteriaLogicalOperator`, `dynamicCriteriaTags`, `dynamicCriteriaExactTags`, `dynamicCriteriaTagsLogicalOperator`, `userId`, `createdDt`, `modifiedDt`, `folderId`, `permissionsFolderId`, `ref1`, `ref2`, `ref3`, `ref4`, `ref5`)
VALUES (:displayGroup, :isDisplaySpecific, :description, :isDynamic, :dynamicCriteria, :dynamicCriteriaLogicalOperator, :dynamicCriteriaTags, :dynamicCriteriaExactTags, :dynamicCriteriaTagsLogicalOperator, :userId, :createdDt, :modifiedDt, :folderId, :permissionsFolderId, :ref1, :ref2, :ref3, :ref4, :ref5)
', [
'displayGroup' => $this->displayGroup,
'isDisplaySpecific' => $this->isDisplaySpecific,
'description' => $this->description,
'isDynamic' => $this->isDynamic,
'dynamicCriteria' => $this->dynamicCriteria,
'dynamicCriteriaLogicalOperator' => $this->dynamicCriteriaLogicalOperator ?? 'OR',
'dynamicCriteriaTags' => $this->dynamicCriteriaTags,
'dynamicCriteriaExactTags' => $this->dynamicCriteriaExactTags ?? 0,
'dynamicCriteriaTagsLogicalOperator' => $this->dynamicCriteriaTagsLogicalOperator ?? 'OR',
'userId' => $this->userId,
'createdDt' => $time,
'modifiedDt' => $time,
'folderId' => ($this->folderId === null) ? 1 : $this->folderId,
'permissionsFolderId' => ($this->permissionsFolderId == null) ? 1 : $this-> permissionsFolderId,
'ref1' => $this->ref1,
'ref2' => $this->ref2,
'ref3' => $this->ref3,
'ref4' => $this->ref4,
'ref5' => $this->ref5
]);
// Insert my self link
$this->getStore()->insert('INSERT INTO `lkdgdg` (`parentId`, `childId`, `depth`) VALUES (:parentId, :childId, 0)', [
'parentId' => $this->displayGroupId,
'childId' => $this->displayGroupId
]);
}
private function edit($options = [])
{
$this->getLog()->debug(sprintf('Updating Display Group. %s, %d', $this->displayGroup, $this->displayGroupId));
$this->getStore()->update('
UPDATE displaygroup
SET DisplayGroup = :displayGroup,
Description = :description,
`isDynamic` = :isDynamic,
`dynamicCriteria` = :dynamicCriteria,
`dynamicCriteriaLogicalOperator` = :dynamicCriteriaLogicalOperator,
`dynamicCriteriaTags` = :dynamicCriteriaTags,
`dynamicCriteriaExactTags` = :dynamicCriteriaExactTags,
`dynamicCriteriaTagsLogicalOperator` = :dynamicCriteriaTagsLogicalOperator,
`bandwidthLimit` = :bandwidthLimit,
`userId` = :userId,
`modifiedDt` = :modifiedDt,
`folderId` = :folderId,
`permissionsFolderId` = :permissionsFolderId,
`ref1` = :ref1,
`ref2` = :ref2,
`ref3` = :ref3,
`ref4` = :ref4,
`ref5` = :ref5
WHERE DisplayGroupID = :displayGroupId
', [
'displayGroup' => $this->displayGroup,
'description' => $this->description,
'displayGroupId' => $this->displayGroupId,
'isDynamic' => $this->isDynamic,
'dynamicCriteria' => $this->dynamicCriteria,
'dynamicCriteriaLogicalOperator' => $this->dynamicCriteriaLogicalOperator ?? 'OR',
'dynamicCriteriaTags' => $this->dynamicCriteriaTags,
'dynamicCriteriaExactTags' => $this->dynamicCriteriaExactTags ?? 0,
'dynamicCriteriaTagsLogicalOperator' => $this->dynamicCriteriaTagsLogicalOperator ?? 'OR',
'bandwidthLimit' => $this->bandwidthLimit,
'userId' => $this->userId,
'modifiedDt' => $options['setModifiedDt']
? Carbon::now()->format(DateFormatHelper::getSystemFormat())
: $this->modifiedDt,
'folderId' => $this->folderId,
'permissionsFolderId' => $this->permissionsFolderId,
'ref1' => $this->ref1,
'ref2' => $this->ref2,
'ref3' => $this->ref3,
'ref4' => $this->ref4,
'ref5' => $this->ref5
]);
}
/**
* Manage the links to this display, dynamic or otherwise
* @var bool $manageDynamic
* @throws NotFoundException
*/
private function manageDisplayLinks($manageDynamic = true)
{
$this->getLog()->debug('Manage display links. Manage Dynamic = ' . $manageDynamic . ', Dynamic = ' . $this->isDynamic);
$difference = [];
if ($this->isDynamic == 1 && $manageDynamic) {
$this->getLog()->info('Managing Display Links for Dynamic Display Group ' . $this->displayGroup);
$originalDisplays = ($this->loaded) ? $this->displays : $this->displayFactory->getByDisplayGroupId($this->displayGroupId);
// Update the linked displays based on the filter criteria
// these displays must be permission checked based on the owner of the group NOT the logged in user
$this->displays = $this->displayFactory->query(null, [
'display' => $this->dynamicCriteria,
'logicalOperatorName' => $this->dynamicCriteriaLogicalOperator,
'tags' => $this->dynamicCriteriaTags,
'exactTags' => $this->dynamicCriteriaExactTags,
'logicalOperator' => $this->dynamicCriteriaTagsLogicalOperator,
'userCheckUserId' => $this->getOwnerId(),
'useRegexForName' => true
]);
$this->getLog()->debug(sprintf('There are %d original displays and %d displays that match the filter criteria now.', count($originalDisplays), count($this->displays)));
// Map our arrays to simple displayId lists
$displayIds = array_map(function ($element) { return $element->displayId; }, $this->displays);
$originalDisplayIds = array_map(function ($element) { return $element->displayId; }, $originalDisplays);
$difference = array_merge(array_diff($displayIds, $originalDisplayIds), array_diff($originalDisplayIds, $displayIds));
// This is a dynamic display group
// only manage the links that have changed
if (count($difference) > 0) {
$this->getLog()->debug(count($difference) . ' changes in dynamic Displays, will notify individually');
$this->notifyRequired = true;
} else {
$this->getLog()->debug('No changes in dynamic Displays, wont notify');
$this->notifyRequired = false;
}
}
// Manage the links we've made either way
// Link
$this->linkDisplays();
// Check if we should notify
if ($this->notifyRequired) {
// We must notify before we unlink
$this->notify($difference);
}
// Unlink
// we never unlink from a display specific display group, unless we're deleting which does not call
// manage display links.
if ($this->isDisplaySpecific == 0) {
$this->unlinkDisplays();
}
// Don't do it again
$this->notifyRequired = false;
}
/**
* Manage display group links
* @throws InvalidArgumentException
*/
private function manageDisplayGroupLinks()
{
$this->linkDisplayGroups();
$this->unlinkDisplayGroups();
// Check for circular references
// this is a lazy last minute check as we can't really tell if there is a circular reference unless
// we've inserted the records already.
if ($this->getStore()->exists('SELECT depth FROM `lkdgdg` WHERE parentId = :parentId AND childId = parentId AND depth > 0', ['parentId' => $this->displayGroupId]))
throw new InvalidArgumentException(__('This assignment creates a circular reference'));
}
private function linkDisplays()
{
foreach ($this->displays as $display) {
/* @var Display $display */
$this->getStore()->update('INSERT INTO lkdisplaydg (DisplayGroupID, DisplayID) VALUES (:displayGroupId, :displayId) ON DUPLICATE KEY UPDATE DisplayID = DisplayID', [
'displayGroupId' => $this->displayGroupId,
'displayId' => $display->displayId
]);
}
}
private function unlinkDisplays()
{
// Unlink any displays that are NOT in the collection
$params = ['displayGroupId' => $this->displayGroupId];
$sql = 'DELETE FROM lkdisplaydg WHERE DisplayGroupID = :displayGroupId AND DisplayID NOT IN (0';
$i = 0;
foreach ($this->displays as $display) {
/* @var Display $display */
$i++;
$sql .= ',:displayId' . $i;
$params['displayId' . $i] = $display->displayId;
}
$sql .= ')';
$this->getStore()->update($sql, $params);
}
/**
* Links the display groups that have been added to the OM
* adding them to the closure table `lkdgdg`
*/
private function linkDisplayGroups()
{
$links = array_udiff($this->displayGroups, $this->originalDisplayGroups, function($a, $b) {
/**
* @var DisplayGroup $a
* @var DisplayGroup $b
*/
return $a->getId() - $b->getId();
});
$this->getLog()->debug('Linking %d display groups to Display Group %s', count($links), $this->displayGroup);
foreach ($links as $displayGroup) {
/* @var DisplayGroup $displayGroup */
$this->getStore()->insert('
INSERT INTO lkdgdg (parentId, childId, depth)
SELECT p.parentId, c.childId, p.depth + c.depth + 1
FROM lkdgdg p, lkdgdg c
WHERE p.childId = :parentId AND c.parentId = :childId
', [
'parentId' => $this->displayGroupId,
'childId' => $displayGroup->displayGroupId
]);
}
}
/**
* Unlinks the display groups that have been removed from the OM
* removing them from the closure table `lkdgdg`
*/
private function unlinkDisplayGroups()
{
$links = array_udiff($this->originalDisplayGroups, $this->displayGroups, function($a, $b) {
/**
* @var DisplayGroup $a
* @var DisplayGroup $b
*/
return $a->getId() - $b->getId();
});
$this->getLog()->debug('Unlinking ' . count($links) . ' display groups to Display Group ' . $this->displayGroup);
foreach ($links as $displayGroup) {
/* @var DisplayGroup $displayGroup */
// Only ever delete 1 because if there are more than 1, we can assume that it is linked at that level from
// somewhere else
// https://github.com/xibosignage/xibo/issues/1417
$linksToDelete = $this->getStore()->select('
SELECT DISTINCT link.parentId, link.childId, link.depth
FROM `lkdgdg` p
INNER JOIN `lkdgdg` link
ON p.parentId = link.parentId
INNER JOIN `lkdgdg` c
ON c.childId = link.childId
WHERE p.childId = :parentId
AND c.parentId = :childId
', [
'parentId' => $this->displayGroupId,
'childId' => $displayGroup->displayGroupId
]);
foreach ($linksToDelete as $linkToDelete) {
$this->getStore()->update('
DELETE FROM `lkdgdg`
WHERE parentId = :parentId
AND childId = :childId
AND depth = :depth
LIMIT 1
', [
'parentId' => $linkToDelete['parentId'],
'childId' => $linkToDelete['childId'],
'depth' => $linkToDelete['depth']
]);
}
}
}
/**
* Unlinks all display groups
* usually in preparation for a delete
*/
private function unlinkAllDisplayGroups()
{
$this->getStore()->update('
DELETE link
FROM `lkdgdg` p, `lkdgdg` link, `lkdgdg` c, `lkdgdg` to_delete
WHERE p.parentId = link.parentId AND c.childId = link.childId
AND p.childId = to_delete.parentId AND c.parentId = to_delete.childId
AND (to_delete.parentId = :parentId OR to_delete.childId = :childId)
AND to_delete.depth < 2
', [
'parentId' => $this->displayGroupId,
'childId' => $this->displayGroupId
]);
}
private function linkMedia()
{
foreach ($this->media as $media) {
/* @var Media $media */
$this->getStore()->update('INSERT INTO `lkmediadisplaygroup` (mediaid, displaygroupid) VALUES (:mediaId, :displayGroupId) ON DUPLICATE KEY UPDATE mediaid = mediaid', [
'displayGroupId' => $this->displayGroupId,
'mediaId' => $media->mediaId
]);
}
}
private function unlinkMedia()
{
// Unlink any media that is NOT in the collection
$params = ['displayGroupId' => $this->displayGroupId];
$sql = 'DELETE FROM `lkmediadisplaygroup` WHERE DisplayGroupID = :displayGroupId AND mediaId NOT IN (0';
$i = 0;
foreach ($this->media as $media) {
/* @var Media $media */
$i++;
$sql .= ',:mediaId' . $i;
$params['mediaId' . $i] = $media->mediaId;
}
$sql .= ')';
$this->getStore()->update($sql, $params);
}
private function linkLayouts()
{
foreach ($this->layouts as $layout) {
/* @var Layout $media */
$this->getStore()->update('INSERT INTO `lklayoutdisplaygroup` (layoutid, displaygroupid) VALUES (:layoutId, :displayGroupId) ON DUPLICATE KEY UPDATE layoutid = layoutid', [
'displayGroupId' => $this->displayGroupId,
'layoutId' => $layout->layoutId
]);
}
}
private function unlinkLayouts()
{
// Unlink any layout that is NOT in the collection
$params = ['displayGroupId' => $this->displayGroupId];
$sql = 'DELETE FROM `lklayoutdisplaygroup` WHERE DisplayGroupID = :displayGroupId AND layoutId NOT IN (0';
$i = 0;
foreach ($this->layouts as $layout) {
/* @var Layout $layout */
$i++;
$sql .= ',:layoutId' . $i;
$params['layoutId' . $i] = $layout->layoutId;
}
$sql .= ')';
$this->getStore()->update($sql, $params);
}
}