Files
Cloud-CMS/lib/Factory/MediaFactory.php

1015 lines
35 KiB
PHP
Raw Permalink Normal View History

2025-12-02 10:32:59 -05:00
<?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\Factory;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Pool;
use Psr\Http\Message\ResponseInterface;
use Xibo\Entity\Media;
use Xibo\Entity\User;
use Xibo\Helper\ByteFormatter;
use Xibo\Helper\Environment;
use Xibo\Service\ConfigServiceInterface;
use Xibo\Support\Exception\NotFoundException;
/**
* Class MediaFactory
* @package Xibo\Factory
*/
class MediaFactory extends BaseFactory
{
use TagTrait;
/** @var Media[] */
private $remoteDownloadQueue = [];
/** @var Media[] */
private $remoteDownloadNotRequiredQueue = [];
/**
* @var ConfigServiceInterface
*/
private $config;
/**
* @var PermissionFactory
*/
private $permissionFactory;
/**
* @var PlaylistFactory
*/
private $playlistFactory;
/**
* Construct a factory
* @param User $user
* @param UserFactory $userFactory
* @param ConfigServiceInterface $config
* @param PermissionFactory $permissionFactory
* @param PlaylistFactory $playlistFactory
*/
public function __construct($user, $userFactory, $config, $permissionFactory, $playlistFactory)
{
$this->setAclDependencies($user, $userFactory);
$this->config = $config;
$this->permissionFactory = $permissionFactory;
$this->playlistFactory = $playlistFactory;
}
/**
* Create Empty
* @return Media
*/
public function createEmpty()
{
return new Media(
$this->getStore(),
$this->getLog(),
$this->getDispatcher(),
$this->config,
$this,
$this->permissionFactory
);
}
/**
* Create New Media
* @param string $name
* @param string $fileName
* @param string $type
* @param int $ownerId
* @param int $duration
* @return Media
*/
public function create($name, $fileName, $type, $ownerId, $duration = 0)
{
$media = $this->createEmpty();
$media->name = $name;
$media->fileName = $fileName;
$media->mediaType = $type;
$media->ownerId = $ownerId;
$media->duration = $duration;
return $media;
}
/**
* Create Module File
* @param $name
* @param string|null $file
* @return Media
* @throws \Xibo\Support\Exception\NotFoundException
*/
public function createModuleFile($name, ?string $file = ''): Media
{
if ($file == '') {
$file = $name;
$name = basename($file);
}
try {
$media = $this->getByNameAndType($name, 'module');
// Reassert the new file (which we might want to download)
$media->fileName = $file;
$media->storedAs = $name;
} catch (NotFoundException $e) {
$media = $this->createEmpty();
$media->name = $name;
$media->fileName = $file;
$media->mediaType = 'module';
$media->expires = 0;
$media->storedAs = $name;
$media->ownerId = $this->getUserFactory()->getSystemUser()->getOwnerId();
$media->moduleSystemFile = 0;
}
return $media;
}
/**
* Queue remote file download
* @param $name
* @param $uri
* @param $expiry
* @param array $requestOptions
* @return Media
* @throws \Xibo\Support\Exception\ConfigurationException
* @throws \Xibo\Support\Exception\DuplicateEntityException
* @throws \Xibo\Support\Exception\GeneralException
* @throws \Xibo\Support\Exception\InvalidArgumentException
*/
public function queueDownload($name, $uri, $expiry, $requestOptions = [])
{
// Determine the save name
if (!isset($requestOptions['fileType'])) {
$media = $this->createModuleFile($name, $uri);
$media->isRemote = true;
} else {
$media = $this->createEmpty();
$media->name = $name;
$media->fileName = $uri;
$media->ownerId = $this->getUserFactory()->getUser()->userId;
$media->mediaType = $requestOptions['fileType'];
$media->duration = $requestOptions['duration'];
$media->moduleSystemFile = 0;
$media->isRemote = true;
$media->setUnmatchedProperty('urlDownload', true);
$media->setUnmatchedProperty('extension', $requestOptions['extension'] ?? null);
$media->enableStat = $requestOptions['enableStat'];
$media->folderId = $requestOptions['folderId'];
$media->permissionsFolderId = $requestOptions['permissionsFolderId'];
$media->apiRef = $requestOptions['apiRef'] ?? null;
}
$this->getLog()->debug('Queue download of: ' . $uri . ', current mediaId for this download is '
. $media->mediaId . '.');
// We update the desired expiry here - isSavedRequired is tested against the original value
$media->expires = $expiry;
// Save the file, but do not download yet.
$media->saveAsync(['requestOptions' => $requestOptions]);
// Add to our collection of queued downloads
// but only if it's not already in the queue (we might have tried to queue it multiple times in
// the same request)
if ($media->isSaveRequired) {
$this->getLog()->debug('We are required to download as this file is either expired or not existing');
$queueItem = true;
if ($media->getId() != null) {
// Existing media, check to see if we're already queued
foreach ($this->remoteDownloadQueue as $queue) {
// If we find this item already, don't queue
if ($queue->getId() === $media->getId()) {
$queueItem = false;
break;
}
}
}
if ($queueItem) {
$this->remoteDownloadQueue[] = $media;
}
} else {
// Queue in the not required download queue
$this->getLog()->debug('Download not required as this file exists and is up to date. Expires = ' . $media->getOriginalValue('expires'));
$queueItem = true;
if ($media->getId() != null) {
// Existing media, check to see if we're already queued
foreach ($this->remoteDownloadNotRequiredQueue as $queue) {
// If we find this item already, don't queue
if ($queue->getId() === $media->getId()) {
$queueItem = false;
break;
}
}
}
if ($queueItem) {
$this->remoteDownloadNotRequiredQueue[] = $media;
}
}
// Return the media item
return $media;
}
/**
* Process the queue of downloads
* @param null|callable $success success callable
* @param null|callable $failure failure callable
* @param null|callable $rejected rejected callable
*/
public function processDownloads($success = null, $failure = null, $rejected = null)
{
if (count($this->remoteDownloadQueue) > 0) {
$this->getLog()->debug('Processing Queue of ' . count($this->remoteDownloadQueue) . ' downloads.');
// Create a generator and Pool
$log = $this->getLog();
$queue = $this->remoteDownloadQueue;
$client = new Client($this->config->getGuzzleProxy(['timeout' => 0]));
$downloads = function () use ($client, $queue) {
foreach ($queue as $media) {
$url = $media->downloadUrl();
$sink = $media->downloadSink();
$requestOptions = array_merge($media->downloadRequestOptions(), [
'sink' => $sink,
'on_headers' => function (ResponseInterface $response) {
$this->getLog()->debug('processDownloads: on_headers status code = '
. $response->getStatusCode());
if ($response->getStatusCode() < 299) {
$this->getLog()->debug('processDownloads: successful, headers = '
. var_export($response->getHeaders(), true));
// Get the content length
$contentLength = $response->getHeaderLine('Content-Length');
if (empty($contentLength)
|| intval($contentLength) > ByteFormatter::toBytes(Environment::getMaxUploadSize())
) {
throw new \Exception(__('File too large'));
}
}
}
]);
yield function () use ($client, $url, $requestOptions) {
return $client->getAsync($url, $requestOptions);
};
}
};
$pool = new Pool($client, $downloads(), [
'concurrency' => 5,
'fulfilled' => function ($response, $index) use ($log, $queue, $success, $failure) {
$item = $queue[$index];
// File is downloaded, call save to move it appropriately
try {
$item->saveFile();
// If a success callback has been provided, call it
if ($success !== null && is_callable($success)) {
$success($item);
}
} catch (\Exception $e) {
$this->getLog()->error('processDownloads: Unable to save mediaId '
. $item->mediaId . '. ' . $e->getMessage());
// Remove it
$item->delete(['rollback' => true]);
// If a failure callback has been provided, call it
if ($failure !== null && is_callable($failure)) {
$failure($item);
}
}
},
'rejected' => function ($reason, $index) use ($log, $queue, $rejected) {
/* @var RequestException $reason */
$log->error(
sprintf(
'Rejected Request %d to %s because %s',
$index,
$reason->getRequest()->getUri(),
$reason->getMessage()
)
);
// We should remove the media record.
$queue[$index]->delete(['rollback' => true]);
// If a rejected callback has been provided, call it
if ($rejected !== null && is_callable($rejected)) {
// Do we have a wrapped exception?
$reasonMessage = $reason->getPrevious() !== null
? $reason->getPrevious()->getMessage()
: $reason->getMessage();
call_user_func($rejected, $reasonMessage);
}
}
]);
$promise = $pool->promise();
$promise->wait();
}
// Handle the downloads that did not require downloading
if (count($this->remoteDownloadNotRequiredQueue) > 0) {
$this->getLog()->debug('Processing Queue of ' . count($this->remoteDownloadNotRequiredQueue) . ' items which do not need downloading.');
foreach ($this->remoteDownloadNotRequiredQueue as $item) {
// If a success callback has been provided, call it
if ($success !== null && is_callable($success)) {
$success($item);
}
}
}
// Clear the queue for next time.
$this->remoteDownloadQueue = [];
$this->remoteDownloadNotRequiredQueue = [];
}
/**
* Get by Media Id
* @param int $mediaId
* @return Media
* @throws NotFoundException
*/
public function getById($mediaId, bool $isDisableUserCheck = true)
{
$media = $this->query(null, [
'disableUserCheck' => $isDisableUserCheck ? 1 : 0,
'mediaId' => $mediaId,
'allModules' => 1,
]);
if (count($media) <= 0) {
throw new NotFoundException(__('Cannot find media'));
}
return $media[0];
}
/**
* Get by Parent Media Id
* @param int $mediaId
* @return Media
* @throws NotFoundException
*/
public function getParentById($mediaId)
{
$media = $this->query(null, array('disableUserCheck' => 1, 'parentMediaId' => $mediaId, 'allModules' => 1));
if (count($media) <= 0) {
throw new NotFoundException(__('Cannot find media'));
}
return $media[0];
}
/**
* Get by Media Name
* @param string $name
* @return Media
* @throws NotFoundException
*/
public function getByName($name)
{
$media = $this->query(null, array('disableUserCheck' => 1, 'nameExact' => $name, 'allModules' => 1));
if (count($media) <= 0)
throw new NotFoundException(__('Cannot find media'));
return $media[0];
}
/**
* Get by Media Name
* @param string $name
* @param string $type
* @return Media
* @throws NotFoundException
*/
public function getByNameAndType($name, $type)
{
$media = $this->query(null, array('disableUserCheck' => 1, 'nameExact' => $name, 'type' => $type, 'allModules' => 1));
if (count($media) <= 0)
throw new NotFoundException(__('Cannot find media'));
return $media[0];
}
/**
* Get by Owner Id
* @param int $ownerId
* @return Media[]
* @throws NotFoundException
*/
public function getByOwnerId($ownerId, $allModules = 0)
{
return $this->query(null, ['disableUserCheck' => 1, 'ownerId' => $ownerId, 'isEdited' => 1, 'allModules' => $allModules]);
}
/**
* Get by Type
* @param string $type
* @return Media[]
* @throws NotFoundException
*/
public function getByMediaType($type)
{
return $this->query(null, array('disableUserCheck' => 1, 'type' => $type, 'allModules' => 1));
}
/**
* Get by Display Group Id
* @param int $displayGroupId
* @return Media[]
* @throws NotFoundException
*/
public function getByDisplayGroupId($displayGroupId)
{
if ($displayGroupId == null) {
return [];
}
return $this->query(null, array('disableUserCheck' => 1, 'displayGroupId' => $displayGroupId));
}
/**
* Get Media by LayoutId
* @param int $layoutId
* @param int $edited
* @param int $excludeDynamicPlaylistMedia
* @return Media[]
* @throws NotFoundException
*/
public function getByLayoutId($layoutId, $edited = -1, $excludeDynamicPlaylistMedia = 0)
{
return $this->query(null, ['disableUserCheck' => 1, 'layoutId' => $layoutId, 'isEdited' => $edited, 'excludeDynamicPlaylistMedia' => $excludeDynamicPlaylistMedia]);
}
/**
* Get Media by campaignId
* @param int $campaignId
* @return Media[]
* @throws NotFoundException
*/
public function getByCampaignId($campaignId)
{
return $this->query(null, ['disableUserCheck' => 1, 'campaignId' => $campaignId]);
}
public function getForMenuBoards()
{
return $this->query(null, ['onlyMenuBoardAllowed' => 1]);
}
/**
* @param int $folderId
* @return Media[]
* @throws NotFoundException
*/
public function getByFolderId(int $folderId)
{
return $this->query(null, ['disableUserCheck' => 1, 'folderId' => $folderId]);
}
/**
* @param null $sortOrder
* @param array $filterBy
* @return Media[]
* @throws NotFoundException
*/
public function query($sortOrder = null, $filterBy = [])
{
$sanitizedFilter = $this->getSanitizer($filterBy);
if ($sortOrder === null) {
$sortOrder = ['name'];
}
$newSortOrder = [];
foreach ($sortOrder as $sort) {
if ($sort == '`revised`') {
$newSortOrder[] = '`parentId`';
continue;
}
if ($sort == '`revised` DESC') {
$newSortOrder[] = '`parentId` DESC';
continue;
}
$newSortOrder[] = $sort;
}
$sortOrder = $newSortOrder;
$entries = [];
$params = [];
$select = '
SELECT `media`.mediaId,
`media`.name,
`media`.type AS mediaType,
`media`.duration,
`media`.userId AS ownerId,
`media`.fileSize,
`media`.storedAs,
`media`.valid,
`media`.moduleSystemFile,
`media`.expires,
`media`.md5,
`media`.retired,
`media`.isEdited,
IFNULL(parentmedia.mediaId, 0) AS parentId,
`media`.released,
`media`.apiRef,
`media`.createdDt,
`media`.modifiedDt,
`media`.enableStat,
`media`.folderId,
`media`.permissionsFolderId,
`media`.orientation,
`media`.width,
`media`.height,
`user`.UserName AS owner,
';
$select .= ' (SELECT GROUP_CONCAT(DISTINCT `group`.group)
FROM `permission`
INNER JOIN `permissionentity`
ON `permissionentity`.entityId = permission.entityId
INNER JOIN `group`
ON `group`.groupId = `permission`.groupId
WHERE entity = :entity
AND objectId = media.mediaId
AND view = 1
) AS groupsWithPermissions, ';
$params['entity'] = 'Xibo\\Entity\\Media';
$select .= ' media.originalFileName AS fileName ';
$body = ' FROM media ';
$body .= ' LEFT OUTER JOIN media parentmedia ';
$body .= ' ON media.editedMediaId = parentmedia.mediaId ';
// Media might be linked to the system user (userId 0)
$body .= ' LEFT OUTER JOIN `user` ON `user`.userId = `media`.userId ';
if ($sanitizedFilter->getInt('displayGroupId') !== null) {
$body .= '
INNER JOIN `lkmediadisplaygroup`
ON lkmediadisplaygroup.mediaid = media.mediaid
AND lkmediadisplaygroup.displayGroupId = :displayGroupId
';
$params['displayGroupId'] = $sanitizedFilter->getInt('displayGroupId');
}
$body .= ' WHERE 1 = 1 ';
if ($sanitizedFilter->getInt('allModules') == 0) {
$body .= ' AND media.type <> \'module\' ';
}
if ($sanitizedFilter->getInt('assignable', ['default'=> -1]) == 1) {
$body .= '
AND media.type <> \'genericfile\'
AND media.type <> \'playersoftware\'
AND media.type <> \'savedreport\'
AND media.type <> \'font\'
';
}
if ($sanitizedFilter->getInt('assignable', ['default'=> -1]) == 0) {
$body .= '
AND (media.type = \'genericfile\'
OR media.type = \'playersoftware\'
OR media.type = \'savedreport\'
OR media.type = \'font\')
';
}
// Unused only?
if ($sanitizedFilter->getInt('unusedOnly') === 1) {
$body .= '
AND `media`.`mediaId` NOT IN (SELECT `mediaId` FROM `display_media`)
AND `media`.`mediaId` NOT IN (SELECT `mediaId` FROM `lkwidgetmedia`)
AND `media`.`mediaId` NOT IN (SELECT `mediaId` FROM `lkmediadisplaygroup`)
AND `media`.`mediaId` NOT IN (SELECT `mediaId` FROM `menu_category` WHERE `mediaId` IS NOT NULL)
AND `media`.`mediaId` NOT IN (SELECT `mediaId` FROM `menu_product` WHERE `mediaId` IS NOT NULL)
AND `media`.`mediaId` NOT IN (
SELECT `backgroundImageId` FROM `layout` WHERE `backgroundImageId` IS NOT NULL
)
AND `media`.`type` <> \'module\'
AND `media`.`type` <> \'font\'
AND `media`.`type` <> \'playersoftware\'
AND `media`.`type` <> \'savedreport\'
';
// DataSets with library images
$dataSetSql = '
SELECT dataset.dataSetId, datasetcolumn.heading
FROM dataset
INNER JOIN datasetcolumn
ON datasetcolumn.DataSetID = dataset.DataSetID
WHERE DataTypeID = 5 AND `datasetcolumn`.dataSetColumnTypeId <> 2;
';
$dataSets = $this->getStore()->select($dataSetSql, []);
if (count($dataSets) > 0) {
$body .= ' AND media.mediaID NOT IN (';
$first = true;
foreach ($dataSets as $dataSet) {
$sanitizedDataSet = $this->getSanitizer($dataSet);
if (!$first) {
$body .= ' UNION ALL ';
}
$first = false;
$dataSetId = $sanitizedDataSet->getInt('dataSetId');
$heading = $sanitizedDataSet->getString('heading');
$body .= ' SELECT `' . $heading . '` AS mediaId FROM `dataset_' . $dataSetId . '`';
}
$body .= ') ';
}
}
// Unlinked only?
if ($sanitizedFilter->getInt('unlinkedOnly') === 1) {
$body .= '
AND `media`.`mediaId` NOT IN (SELECT `mediaId` FROM `display_media`)
';
}
if ($sanitizedFilter->getString('name') != null) {
$terms = explode(',', $sanitizedFilter->getString('name'));
$logicalOperator = $sanitizedFilter->getString('logicalOperatorName', ['default' => 'OR']);
$this->nameFilter(
'media',
'name',
$terms,
$body,
$params,
($sanitizedFilter->getCheckbox('useRegexForName') == 1),
$logicalOperator
);
}
if ($sanitizedFilter->getString('nameExact') != '') {
$body .= ' AND media.name = :exactName ';
$params['exactName'] = $sanitizedFilter->getString('nameExact');
}
if ($sanitizedFilter->getInt('mediaId', ['default'=> -1]) != -1) {
$body .= ' AND media.mediaId = :mediaId ';
$params['mediaId'] = $sanitizedFilter->getInt('mediaId');
} else if ($sanitizedFilter->getInt('parentMediaId') !== null) {
$body .= ' AND media.editedMediaId = :mediaId ';
$params['mediaId'] = $sanitizedFilter->getInt('parentMediaId');
} else if ($sanitizedFilter->getInt('isEdited', ['default' => -1]) != -1) {
$body .= ' AND media.isEdited <> -1 ';
} else {
$body .= ' AND media.isEdited = 0 ';
}
if ($sanitizedFilter->getString('type') != '') {
$body .= 'AND media.type = :type ';
$params['type'] = $sanitizedFilter->getString('type');
}
if (!empty($sanitizedFilter->getArray('types'))) {
$body .= 'AND (';
foreach ($sanitizedFilter->getArray('types') as $key => $type) {
$body .= 'media.type = :types' . $key . ' ';
if ($key !== array_key_last($sanitizedFilter->getArray('types'))) {
$body .= ' OR ';
}
$params['types' . $key] = $type;
}
$body .= ') ';
}
if ($sanitizedFilter->getInt('imageProcessing') !== null) {
$body .= 'AND ( media.type = \'image\' OR (media.type = \'module\' AND media.moduleSystemFile = 0) ) ';
}
if ($sanitizedFilter->getString('storedAs') != '') {
$body .= 'AND media.storedAs = :storedAs ';
$params['storedAs'] = $sanitizedFilter->getString('storedAs');
}
if ($sanitizedFilter->getInt('ownerId') !== null) {
$body .= ' AND media.userid = :ownerId ';
$params['ownerId'] = $sanitizedFilter->getInt('ownerId');
}
// User Group filter
if ($sanitizedFilter->getInt('ownerUserGroupId', ['default'=> 0]) != 0) {
$body .= ' AND media.userid IN (SELECT DISTINCT userId FROM `lkusergroup` WHERE groupId = :ownerUserGroupId) ';
$params['ownerUserGroupId'] = $sanitizedFilter->getInt('ownerUserGroupId');
}
if ($sanitizedFilter->getInt('released') !== null) {
$body .= ' AND media.released = :released ';
$params['released'] = $sanitizedFilter->getInt('released');
}
if ($sanitizedFilter->getCheckbox('unreleasedOnly') === 1) {
$body .= ' AND media.released <> 1 ';
}
if ($sanitizedFilter->getInt('retired', ['default'=> -1]) == 1)
$body .= ' AND media.retired = 1 ';
if ($sanitizedFilter->getInt('retired', ['default'=> -1]) == 0)
$body .= ' AND media.retired = 0 ';
// Expired files?
if ($sanitizedFilter->getInt('expires') != 0) {
$body .= '
AND media.expires < :expires
AND IFNULL(media.expires, 0) <> 0
AND ( media.mediaId NOT IN (SELECT mediaId FROM `lkwidgetmedia`) OR media.type <> \'module\')
';
$params['expires'] = $sanitizedFilter->getInt('expires');
}
if ($sanitizedFilter->getInt('layoutId') !== null) {
// handles the closure table link with sub-playlists
$body .= '
AND media.mediaId IN (
SELECT `lkwidgetmedia`.mediaId
FROM region
INNER JOIN playlist
ON playlist.regionId = region.regionId
INNER JOIN lkplaylistplaylist
ON lkplaylistplaylist.parentId = playlist.playlistId
INNER JOIN widget
ON widget.playlistId = lkplaylistplaylist.childId
INNER JOIN lkwidgetmedia
ON widget.widgetId = lkwidgetmedia.widgetId
WHERE region.layoutId = :layoutId ';
// include Media only for non-dynamic Playlists #2392
if ($sanitizedFilter->getInt('excludeDynamicPlaylistMedia') === 1) {
$body .= ' AND lkplaylistplaylist.childId IN (
SELECT playlistId
FROM playlist
WHERE playlist.playlistId = lkplaylistplaylist.childId
AND playlist.isDynamic = 0
) ';
}
if ($sanitizedFilter->getInt('widgetId') !== null) {
$body .= ' AND `widget`.widgetId = :widgetId ';
$params['widgetId'] = $sanitizedFilter->getInt('widgetId');
}
if ($sanitizedFilter->getInt('includeLayoutBackgroundImage') === 1) {
$body .= ' UNION ALL
SELECT `layout`.backgroundImageId AS mediaId
FROM `layout`
WHERE `layout`.layoutId = :layoutId
';
}
$body .= ' )
AND media.type <> \'module\'
';
$params['layoutId'] = $sanitizedFilter->getInt('layoutId');
}
if ($sanitizedFilter->getInt('campaignId') !== null) {
$body .= '
AND media.mediaId IN (
SELECT `lkwidgetmedia`.mediaId
FROM region
INNER JOIN playlist
ON playlist.regionId = region.regionId
INNER JOIN lkplaylistplaylist
ON lkplaylistplaylist.parentId = playlist.playlistId
INNER JOIN widget
ON widget.playlistId = lkplaylistplaylist.childId
INNER JOIN lkwidgetmedia
ON widget.widgetId = lkwidgetmedia.widgetId
INNER JOIN `lkcampaignlayout` lkcl
ON lkcl.layoutid = region.layoutid
AND lkcl.CampaignID = :campaignId)';
$params['campaignId'] = $sanitizedFilter->getInt('campaignId');
}
// Tags
if ($sanitizedFilter->getString('tags') != '') {
$tagFilter = $sanitizedFilter->getString('tags');
if (trim($tagFilter) === '--no-tag') {
$body .= ' AND `media`.mediaId NOT IN (
SELECT `lktagmedia`.mediaId
FROM tag
INNER JOIN `lktagmedia`
ON `lktagmedia`.tagId = tag.tagId
)
';
} else {
$operator = $sanitizedFilter->getCheckbox('exactTags') == 1 ? '=' : 'LIKE';
$logicalOperator = $sanitizedFilter->getString('logicalOperator', ['default' => 'OR']);
$allTags = explode(',', $tagFilter);
$notTags = [];
$tags = [];
foreach ($allTags as $tag) {
if (str_starts_with($tag, '-')) {
$notTags[] = ltrim(($tag), '-');
} else {
$tags[] = $tag;
}
}
if (!empty($notTags)) {
$body .= ' AND `media`.mediaId NOT IN (
SELECT `lktagmedia`.mediaId
FROM tag
INNER JOIN `lktagmedia`
ON `lktagmedia`.tagId = tag.tagId
';
$this->tagFilter(
$notTags,
'lktagmedia',
'lkTagMediaId',
'mediaId',
$logicalOperator,
$operator,
true,
$body,
$params
);
}
if (!empty($tags)) {
$body .= ' AND `media`.mediaId IN (
SELECT `lktagmedia`.mediaId
FROM tag
INNER JOIN `lktagmedia`
ON `lktagmedia`.tagId = tag.tagId
';
$this->tagFilter(
$tags,
'lktagmedia',
'lkTagMediaId',
'mediaId',
$logicalOperator,
$operator,
false,
$body,
$params
);
}
}
}
// File size
if ($sanitizedFilter->getString('fileSize') != null) {
$fileSize = $this->parseComparisonOperator($sanitizedFilter->getString('fileSize'));
$body .= ' AND `media`.fileSize ' . $fileSize['operator'] . ' :fileSize ';
$params['fileSize'] = $fileSize['variable'];
}
// Duration
if ($sanitizedFilter->getInt('duration') != null) {
$duration = $this->parseComparisonOperator($sanitizedFilter->getInt('duration'));
$body .= ' AND `media`.duration ' . $duration['operator'] . ' :duration ';
$params['duration'] = $duration['variable'];
}
if ($sanitizedFilter->getInt('folderId') !== null) {
$body .= ' AND media.folderId = :folderId ';
$params['folderId'] = $sanitizedFilter->getInt('folderId');
}
if ($sanitizedFilter->getInt('onlyMenuBoardAllowed') !== null) {
$body .= ' AND ( media.type = \'image\' OR media.type = \'video\' ) ';
}
if ($sanitizedFilter->getString('orientation') !== null) {
$body .= ' AND media.orientation = :orientation ';
$params['orientation'] = $sanitizedFilter->getString('orientation');
}
if ($sanitizedFilter->getInt('requiresMetaUpdate') === 1) {
$body .= ' AND (`media`.orientation IS NULL OR IFNULL(`media`.width, 0) = 0)';
}
// View Permissions
$this->viewPermissionSql(
'Xibo\Entity\Media',
$body,
$params,
'`media`.mediaId',
'`media`.userId',
$filterBy,
'`media`.permissionsFolderId'
);
// Sorting?
$order = '';
if (is_array($sortOrder)) {
$order .= ' ORDER BY ' . implode(',', $sortOrder);
}
$limit = '';
// Paging
if ($sanitizedFilter->hasParam('start') && $sanitizedFilter->hasParam('length')) {
$limit = ' LIMIT ' . $sanitizedFilter->getInt('start', ['default' => 0])
. ', ' . $sanitizedFilter->getInt('length', ['default'=> 10]);
}
$sql = $select . $body . $order . $limit;
$mediaIds = [];
foreach ($this->getStore()->select($sql, $params) as $row) {
$media = $this->createEmpty()->hydrate($row, [
'intProperties' => [
'duration',
'size',
'released',
'moduleSystemFile',
'isEdited',
'expires',
'valid',
'width',
'height'
]
]);
$mediaIds[] = $media->mediaId;
$media->excludeProperty('layoutBackgroundImages');
$media->excludeProperty('widgets');
$media->excludeProperty('displayGroups');
$entries[] = $media;
}
// decorate with TagLinks
if (count($entries) > 0) {
$this->decorateWithTagLinks('lktagmedia', 'mediaId', $mediaIds, $entries);
}
// Paging
if ($limit != '' && count($entries) > 0) {
unset($params['entity']);
$results = $this->getStore()->select('SELECT COUNT(*) AS total ' . $body, $params);
$this->_countLast = intval($results[0]['total']);
}
return $entries;
}
}