芝麻web文件管理V1.00
编辑当前文件:/home/mgatv524/public_html/avenida/views/Controller.zip
PK lqYZ̛M M Layout.phpnu [ . */ namespace Xibo\Controller; use Parsedown; use Xibo\Entity\Permission; use Xibo\Entity\Playlist; use Xibo\Entity\Region; use Xibo\Entity\Session; use Xibo\Entity\Widget; use Xibo\Exception\AccessDeniedException; use Xibo\Exception\InvalidArgumentException; use Xibo\Exception\NotFoundException; use Xibo\Exception\XiboException; use Xibo\Factory\CampaignFactory; use Xibo\Factory\DataSetFactory; use Xibo\Factory\DisplayGroupFactory; use Xibo\Factory\LayoutFactory; use Xibo\Factory\MediaFactory; use Xibo\Factory\ModuleFactory; use Xibo\Factory\PermissionFactory; use Xibo\Factory\ResolutionFactory; use Xibo\Factory\TagFactory; use Xibo\Factory\UserFactory; use Xibo\Factory\UserGroupFactory; use Xibo\Helper\LayoutUploadHandler; use Xibo\Service\ConfigServiceInterface; use Xibo\Service\DateServiceInterface; use Xibo\Service\LogServiceInterface; use Xibo\Service\SanitizerServiceInterface; use Xibo\Widget\ModuleWidget; /** * Class Layout * @package Xibo\Controller * */ class Layout extends Base { /** * @var Session */ private $session; /** * @var UserFactory */ private $userFactory; /** * @var ResolutionFactory */ private $resolutionFactory; /** * @var LayoutFactory */ private $layoutFactory; /** * @var ModuleFactory */ private $moduleFactory; /** * @var PermissionFactory */ private $permissionFactory; /** * @var UserGroupFactory */ private $userGroupFactory; /** * @var TagFactory */ private $tagFactory; /** * @var MediaFactory */ private $mediaFactory; /** @var DataSetFactory */ private $dataSetFactory; /** @var CampaignFactory */ private $campaignFactory; /** @var DisplayGroupFactory */ private $displayGroupFactory; /** * Set common dependencies. * @param LogServiceInterface $log * @param SanitizerServiceInterface $sanitizerService * @param \Xibo\Helper\ApplicationState $state * @param \Xibo\Entity\User $user * @param \Xibo\Service\HelpServiceInterface $help * @param DateServiceInterface $date * @param ConfigServiceInterface $config * @param Session $session * @param UserFactory $userFactory * @param ResolutionFactory $resolutionFactory * @param LayoutFactory $layoutFactory * @param ModuleFactory $moduleFactory * @param PermissionFactory $permissionFactory * @param UserGroupFactory $userGroupFactory * @param TagFactory $tagFactory * @param MediaFactory $mediaFactory * @param DataSetFactory $dataSetFactory * @param CampaignFactory $campaignFactory */ public function __construct($log, $sanitizerService, $state, $user, $help, $date, $config, $session, $userFactory, $resolutionFactory, $layoutFactory, $moduleFactory, $permissionFactory, $userGroupFactory, $tagFactory, $mediaFactory, $dataSetFactory, $campaignFactory, $displayGroupFactory) { $this->setCommonDependencies($log, $sanitizerService, $state, $user, $help, $date, $config); $this->session = $session; $this->userFactory = $userFactory; $this->resolutionFactory = $resolutionFactory; $this->layoutFactory = $layoutFactory; $this->moduleFactory = $moduleFactory; $this->permissionFactory = $permissionFactory; $this->userGroupFactory = $userGroupFactory; $this->tagFactory = $tagFactory; $this->mediaFactory = $mediaFactory; $this->dataSetFactory = $dataSetFactory; $this->campaignFactory = $campaignFactory; $this->displayGroupFactory = $displayGroupFactory; } /** * @return LayoutFactory */ public function getLayoutFactory() { return $this->layoutFactory; } /** * @return DataSetFactory */ public function getDataSetFactory() { return $this->dataSetFactory; } /** * Displays the Layout Page */ function displayPage() { // Call to render the template $this->getState()->template = 'layout-page'; $this->getState()->setData([ 'users' => $this->userFactory->query(), 'groups' => $this->userGroupFactory->query(), 'displayGroups' => $this->displayGroupFactory->query(null, ['isDisplaySpecific' => -1]) ]); } /** * Display the Layout Designer * @param int $layoutId */ public function displayDesigner($layoutId) { $layout = $this->layoutFactory->loadById($layoutId); if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(); // Get the parent layout if it's editable if ($layout->isEditable()) { // Get the Layout using the Draft ID $layout = $this->layoutFactory->getByParentId($layoutId); } // Work out our resolution if ($layout->schemaVersion < 2) $resolution = $this->resolutionFactory->getByDesignerDimensions($layout->width, $layout->height); else $resolution = $this->resolutionFactory->getByDimensions($layout->width, $layout->height); $moduleFactory = $this->moduleFactory; $isTemplate = $layout->hasTag('template'); // Set up any JavaScript translations $data = [ 'layout' => $layout, 'resolution' => $resolution, 'isTemplate' => $isTemplate, 'zoom' => $this->getSanitizer()->getDouble('zoom', $this->getUser()->getOptionValue('defaultDesignerZoom', 1)), 'users' => $this->userFactory->query(), 'modules' => array_map(function($element) use ($moduleFactory) { $module = $moduleFactory->createForInstall($element->class); $module->setModule($element); return $module; }, $moduleFactory->getAssignableModules()) ]; // Call the render the template $this->getState()->template = 'layout-designer-page'; $this->getState()->setData($data); } /** * Add a Layout * @SWG\Post( * path="/layout", * operationId="layoutAdd", * tags={"layout"}, * summary="Add a Layout", * description="Add a new Layout to the CMS", * @SWG\Parameter( * name="name", * in="formData", * description="The layout name", * type="string", * required=true * ), * @SWG\Parameter( * name="description", * in="formData", * description="The layout description", * type="string", * required=false * ), * @SWG\Parameter( * name="layoutId", * in="formData", * description="If the Layout should be created with a Template, provide the ID, otherwise don't provide", * type="integer", * required=false * ), * @SWG\Parameter( * name="resolutionId", * in="formData", * description="If a Template is not provided, provide the resolutionId for this Layout.", * type="integer", * required=false * ), * @SWG\Parameter( * name="returnDraft", * in="formData", * description="Should we return the Draft Layout or the Published Layout on Success?", * type="boolean", * required=false * ), * @SWG\Response( * response=201, * description="successful operation", * @SWG\Schema(ref="#/definitions/Layout"), * @SWG\Header( * header="Location", * description="Location of the new record", * type="string" * ) * ) * ) * * @throws XiboException */ function add() { $name = $this->getSanitizer()->getString('name'); $description = $this->getSanitizer()->getString('description'); $templateId = $this->getSanitizer()->getInt('layoutId'); $resolutionId = $this->getSanitizer()->getInt('resolutionId'); $enableStat = $this->getSanitizer()->getCheckbox('enableStat'); $autoApplyTransitions = $this->getSanitizer()->getCheckbox('autoApplyTransitions'); $template = null; if ($templateId != 0) { // Load the template $template = $this->layoutFactory->loadById($templateId); $template->load(); // Empty all of the ID's $layout = clone $template; // Overwrite our new properties $layout->layout = $name; $layout->description = $description; // Create some tags (overwriting the old ones) $layout->tags = $this->tagFactory->tagsFromString($this->getSanitizer()->getString('tags')); // Set the owner $layout->setOwner($this->getUser()->userId); // Ensure we have Playlists for each region foreach ($layout->regions as $region) { // Set the ownership of this region to the user creating from template $region->setOwner($this->getUser()->userId, true); } } else { $layout = $this->layoutFactory->createFromResolution($resolutionId, $this->getUser()->userId, $name, $description, $this->getSanitizer()->getString('tags')); } // Set layout enableStat flag $layout->enableStat = $enableStat; // Set auto apply transitions flag $layout->autoApplyTransitions = $autoApplyTransitions; // Save $layout->save(); // Permissions foreach ($this->permissionFactory->createForNewEntity($this->getUser(), 'Xibo\\Entity\\Campaign', $layout->getId(), $this->getConfig()->getSetting('LAYOUT_DEFAULT'), $this->userGroupFactory) as $permission) { /* @var Permission $permission */ $permission->save(); } foreach ($layout->regions as $region) { /* @var Region $region */ if ($templateId != null && $template !== null) { // Match our original region id to the id in the parent layout $original = $template->getRegion($region->getOriginalValue('regionId')); // Make sure Playlist closure table from the published one are copied over $original->getPlaylist()->cloneClosureTable($region->getPlaylist()->playlistId); } foreach ($this->permissionFactory->createForNewEntity($this->getUser(), get_class($region), $region->getId(), $this->getConfig()->getSetting('LAYOUT_DEFAULT'), $this->userGroupFactory) as $permission) { /* @var Permission $permission */ $permission->save(); } $playlist = $region->getPlaylist(); foreach ($this->permissionFactory->createForNewEntity($this->getUser(), get_class($playlist), $playlist->getId(), $this->getConfig()->getSetting('LAYOUT_DEFAULT'), $this->userGroupFactory) as $permission) { /* @var Permission $permission */ $permission->save(); } foreach ($playlist->widgets as $widget) { /* @var Widget $widget */ foreach ($this->permissionFactory->createForNewEntity($this->getUser(), get_class($widget), $widget->getId(), $this->getConfig()->getSetting('LAYOUT_DEFAULT'), $this->userGroupFactory) as $permission) { /* @var Permission $permission */ $permission->save(); } } } $this->getLog()->debug('Layout Added'); // automatically checkout the new layout for edit $this->checkout($layout->layoutId); if ($this->getSanitizer()->getCheckbox('returnDraft')) { // This is a workaround really - the call to checkout above ought to be separated into a public/private // method, with the private method returning the draft layout // is it stands the checkout method will have already set the draft layout id to the state data property // we just need to set the message. $this->getState()->hydrate([ 'httpStatus' => 201, 'message' => sprintf(__('Added %s'), $layout->layout), ]); } else { // Make sure we adjust the published status // again, this is a workaround because checkout doesn't return a Layout object $layout->publishedStatus = __('Draft'); $layout->publishedStatusId = 2; // Return $this->getState()->hydrate([ 'httpStatus' => 201, 'message' => sprintf(__('Added %s'), $layout->layout), 'id' => $layout->layoutId, 'data' => $layout ]); } } /** * Edit Layout * @param int $layoutId * * @SWG\Put( * path="/layout/{layoutId}", * operationId="layoutEdit", * summary="Edit Layout", * description="Edit a Layout", * tags={"layout"}, * @SWG\Parameter( * name="layoutId", * type="integer", * in="path", * required=true * ), * @SWG\Parameter( * name="name", * in="formData", * description="The Layout Name", * type="string", * required=true * ), * @SWG\Parameter( * name="description", * in="formData", * description="The Layout Description", * type="string", * required=false * ), * @SWG\Parameter( * name="tags", * in="formData", * description="A comma separated list of Tags", * type="string", * required=false * ), * @SWG\Parameter( * name="retired", * in="formData", * description="A flag indicating whether this Layout is retired.", * type="integer", * required=false * ), * @SWG\Parameter( * name="enableStat", * in="formData", * description="Flag indicating whether the Layout stat is enabled", * type="integer", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema(ref="#/definitions/Layout") * ) * ) * * @throws InvalidArgumentException * @throws NotFoundException * @throws XiboException */ function edit($layoutId) { $layout = $this->layoutFactory->getById($layoutId); $isTemplate = false; // check if we're dealing with the template $currentTags = explode(',', $layout->tags); foreach ($currentTags as $tag) { if ($tag === 'template') { $isTemplate = true; } } // Make sure we have permission if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(); // Make sure we're not a draft if ($layout->isChild()) { throw new InvalidArgumentException(__('Cannot edit Layout properties on a Draft'), 'layoutId'); } $layout->layout = $this->getSanitizer()->getString('name'); $layout->description = $this->getSanitizer()->getString('description'); $layout->replaceTags($this->tagFactory->tagsFromString($this->getSanitizer()->getString('tags'))); $layout->retired = $this->getSanitizer()->getCheckbox('retired'); $layout->enableStat = $this->getSanitizer()->getCheckbox('enableStat'); $tags = $this->getSanitizer()->getString('tags'); $tagsArray = explode(',', $tags); if (!$isTemplate) { foreach ($tagsArray as $tag) { if ($tag === 'template') { throw new InvalidArgumentException('Cannot assign a Template tag to a Layout, to create a template use the Save Template button instead.', 'tags'); } } } // Save $layout->save([ 'saveLayout' => true, 'saveRegions' => false, 'saveTags' => true, 'setBuildRequired' => false, 'notify' => false ]); // Return $this->getState()->hydrate([ 'message' => sprintf(__('Edited %s'), $layout->layout), 'id' => $layout->layoutId, 'data' => $layout ]); } /** * Edit Layout Background * @param int $layoutId * * @SWG\Put( * path="/layout/background/{layoutId}", * operationId="layoutEditBackground", * summary="Edit Layout Background", * description="Edit a Layout Background", * tags={"layout"}, * @SWG\Parameter( * name="layoutId", * type="integer", * in="path", * required=true * ), * @SWG\Parameter( * name="backgroundColor", * in="formData", * description="A HEX color to use as the background color of this Layout.", * type="string", * required=true * ), * @SWG\Parameter( * name="backgroundImageId", * in="formData", * description="A media ID to use as the background image for this Layout.", * type="integer", * required=false * ), * @SWG\Parameter( * name="backgroundzIndex", * in="formData", * description="The Layer Number to use for the background.", * type="integer", * required=true * ), * @SWG\Parameter( * name="resolutionId", * in="formData", * description="The Resolution ID to use on this Layout.", * type="integer", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema(ref="#/definitions/Layout") * ) * ) * * @throws XiboException */ function editBackground($layoutId) { $layout = $this->layoutFactory->getById($layoutId); // Make sure we have permission if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(); // Check that this Layout is a Draft if (!$layout->isChild()) throw new InvalidArgumentException(__('This Layout is not a Draft, please checkout.'), 'layoutId'); $layout->backgroundColor = $this->getSanitizer()->getString('backgroundColor'); $layout->backgroundImageId = $this->getSanitizer()->getInt('backgroundImageId'); $layout->backgroundzIndex = $this->getSanitizer()->getInt('backgroundzIndex'); $layout->autoApplyTransitions = $this->getSanitizer()->getCheckbox('autoApplyTransitions'); // Resolution $saveRegions = false; $resolution = $this->resolutionFactory->getById($this->getSanitizer()->getInt('resolutionId')); if ($layout->width != $resolution->width || $layout->height != $resolution->height) { $saveRegions = true; $layout->width = $resolution->width; $layout->height = $resolution->height; } // Save $layout->save([ 'saveLayout' => true, 'saveRegions' => $saveRegions, 'saveTags' => true, 'setBuildRequired' => true, 'notify' => false ]); // Return $this->getState()->hydrate([ 'message' => sprintf(__('Edited %s'), $layout->layout), 'id' => $layout->layoutId, 'data' => $layout ]); } /** * Delete Layout Form * @param int $layoutId */ function deleteForm($layoutId) { $layout = $this->layoutFactory->getById($layoutId); if (!$this->getUser()->checkDeleteable($layout)) throw new AccessDeniedException(__('You do not have permissions to delete this layout')); $data = [ 'layout' => $layout, 'help' => [ 'delete' => $this->getHelp()->link('Layout', 'Delete') ] ]; $this->getState()->template = 'layout-form-delete'; $this->getState()->setData($data); } /** * Retire Layout Form * @param int $layoutId */ public function retireForm($layoutId) { $layout = $this->layoutFactory->getById($layoutId); // Make sure we have permission if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(__('You do not have permissions to edit this layout')); $data = [ 'layout' => $layout, 'help' => [ 'delete' => $this->getHelp()->link('Layout', 'Retire') ] ]; $this->getState()->template = 'layout-form-retire'; $this->getState()->setData($data); } /** * Deletes a layout * @param int $layoutId * * @SWG\Delete( * path="/layout/{layoutId}", * operationId="layoutDelete", * tags={"layout"}, * summary="Delete Layout", * description="Delete a Layout", * @SWG\Parameter( * name="layoutId", * in="path", * description="The Layout ID to Delete", * type="integer", * required=true * ), * @SWG\Response( * response=204, * description="successful operation" * ) * ) * * @throws XiboException */ function delete($layoutId) { $layout = $this->layoutFactory->loadById($layoutId); if (!$this->getUser()->checkDeleteable($layout)) throw new AccessDeniedException(__('You do not have permissions to delete this layout')); // Make sure we're not a draft if ($layout->isChild()) throw new InvalidArgumentException(__('Cannot delete Layout from its Draft, delete the parent'), 'layoutId'); $layout->delete(); // Return $this->getState()->hydrate([ 'httpStatus' => 204, 'message' => sprintf(__('Deleted %s'), $layout->layout) ]); } /** * Retires a layout * @param int $layoutId * * @SWG\Put( * path="/layout/retire/{layoutId}", * operationId="layoutRetire", * tags={"layout"}, * summary="Retire Layout", * description="Retire a Layout so that it isn't available to Schedule. Existing Layouts will still be played", * @SWG\Parameter( * name="layoutId", * in="path", * description="The Layout ID", * type="integer", * required=true * ), * @SWG\Response( * response=204, * description="successful operation" * ) * ) * * @throws XiboException */ function retire($layoutId) { $layout = $this->layoutFactory->getById($layoutId); if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(__('You do not have permissions to edit this layout')); // Make sure we're not a draft if ($layout->isChild()) throw new InvalidArgumentException(__('Cannot modify Layout from its Draft'), 'layoutId'); // Make sure we aren't the global default if ($layout->layoutId == $this->getConfig()->getSetting('DEFAULT_LAYOUT')) { throw new InvalidArgumentException(__('This Layout is used as the global default and cannot be retired'), 'layoutId'); } $layout->retired = 1; $layout->save([ 'saveLayout' => true, 'saveRegions' => false, 'saveTags' => false, 'setBuildRequired' => false ]); // Return $this->getState()->hydrate([ 'httpStatus' => 204, 'message' => sprintf(__('Retired %s'), $layout->layout) ]); } /** * Unretire Layout Form * @param int $layoutId * @throws XiboException */ public function unretireForm($layoutId) { $layout = $this->layoutFactory->getById($layoutId); // Make sure we have permission if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(__('You do not have permissions to edit this layout')); $data = [ 'layout' => $layout, 'help' => $this->getHelp()->link('Layout', 'Retire') ]; $this->getState()->template = 'layout-form-unretire'; $this->getState()->setData($data); } /** * Unretires a layout * @param int $layoutId * * @SWG\Put( * path="/layout/unretire/{layoutId}", * operationId="layoutUnretire", * tags={"layout"}, * summary="Unretire Layout", * description="Retire a Layout so that it isn't available to Schedule. Existing Layouts will still be played", * @SWG\Parameter( * name="layoutId", * in="path", * description="The Layout ID", * type="integer", * required=true * ), * @SWG\Response( * response=204, * description="successful operation" * ) * ) * * @throws XiboException */ function unretire($layoutId) { $layout = $this->layoutFactory->getById($layoutId); if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(__('You do not have permissions to edit this layout')); // Make sure we're not a draft if ($layout->isChild()) throw new InvalidArgumentException(__('Cannot modify Layout from its Draft'), 'layoutId'); $layout->retired = 0; $layout->save([ 'saveLayout' => true, 'saveRegions' => false, 'saveTags' => false, 'setBuildRequired' => false, 'notify' => false ]); // Return $this->getState()->hydrate([ 'httpStatus' => 204, 'message' => sprintf(__('Unretired %s'), $layout->layout) ]); } /** * Set Enable Stats Collection of a layout * @param int $layoutId * * @SWG\Put( * path="/layout/setenablestat/{layoutId}", * operationId="layoutSetEnableStat", * tags={"layout"}, * summary="Enable Stats Collection", * description="Set Enable Stats Collection? to use for the collection of Proof of Play statistics for a Layout.", * @SWG\Parameter( * name="layoutId", * in="path", * description="The Layout ID", * type="integer", * required=true * ), * @SWG\Parameter( * name="enableStat", * in="formData", * description="Flag indicating whether the Layout stat is enabled", * type="integer", * required=true * ), * @SWG\Response( * response=204, * description="successful operation" * ) * ) * * @throws XiboException */ function setEnableStat($layoutId) { $layout = $this->layoutFactory->getById($layoutId); if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(__('You do not have permissions to edit this layout')); // Make sure we're not a draft if ($layout->isChild()) throw new InvalidArgumentException(__('Cannot modify Layout from its Draft'), 'layoutId'); $enableStat = $this->getSanitizer()->getCheckbox('enableStat'); $layout->enableStat = $enableStat; $layout->save(['saveTags' => false]); // Return $this->getState()->hydrate([ 'httpStatus' => 204, 'message' => sprintf(__('For Layout %s Enable Stats Collection is set to %s'), $layout->layout, ($layout->enableStat == 1) ? __('On') : __('Off')) ]); } /** * Set Enable Stat Form * @param int $layoutId * @throws XiboException */ public function setEnableStatForm($layoutId) { $layout = $this->layoutFactory->getById($layoutId); // Make sure we have permission if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(__('You do not have permissions to edit this layout')); $data = [ 'layout' => $layout, 'help' => $this->getHelp()->link('Layout', 'EnableStat') ]; $this->getState()->template = 'layout-form-setenablestat'; $this->getState()->setData($data); } /** * Shows the Layout Grid * * @SWG\Get( * path="/layout", * operationId="layoutSearch", * tags={"layout"}, * summary="Search Layouts", * description="Search for Layouts viewable by this user", * @SWG\Parameter( * name="layoutId", * in="query", * description="Filter by Layout Id", * type="integer", * required=false * ), * @SWG\Parameter( * name="parentId", * in="query", * description="Filter by parent Id", * type="integer", * required=false * ), * @SWG\Parameter( * name="showDrafts", * in="query", * description="Flag indicating whether to show drafts", * type="integer", * required=false * ), * @SWG\Parameter( * name="layout", * in="query", * description="Filter by partial Layout name", * type="string", * required=false * ), * @SWG\Parameter( * name="userId", * in="query", * description="Filter by user Id", * type="integer", * required=false * ), * @SWG\Parameter( * name="retired", * in="query", * description="Filter by retired flag", * type="integer", * required=false * ), * @SWG\Parameter( * name="tags", * in="query", * description="Filter by Tags", * type="string", * required=false * ), * @SWG\Parameter( * name="exactTags", * in="query", * description="A flag indicating whether to treat the tags filter as an exact match", * type="integer", * required=false * ), * @SWG\Parameter( * name="ownerUserGroupId", * in="query", * description="Filter by users in this UserGroupId", * type="integer", * required=false * ), * @SWG\Parameter( * name="publishedStatusId", * in="query", * description="Filter by published status id, 1 - Published, 2 - Draft", * type="integer", * required=false * ), * @SWG\Parameter( * name="embed", * in="query", * description="Embed related data such as regions, playlists, widgets, tags, campaigns, permissions", * type="string", * required=false * ), * @SWG\Parameter( * name="campaignId", * in="query", * description="Get all Layouts for a given campaignId", * type="integer", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema( * type="array", * @SWG\Items(ref="#/definitions/Layout") * ) * ) * ) * * @throws NotFoundException * @throws XiboException */ function grid() { $this->getState()->template = 'grid'; // Should we parse the description into markdown $showDescriptionId = $this->getSanitizer()->getInt('showDescriptionId'); // We might need to embed some extra content into the response if the "Show Description" // is set to media listing if ($showDescriptionId === 3) { $embed = ['regions', 'playlists', 'widgets']; } else { // Embed? $embed = ($this->getSanitizer()->getString('embed') != null) ? explode(',', $this->getSanitizer()->getString('embed')) : []; } // Get all layouts $layouts = $this->layoutFactory->query($this->gridRenderSort(), $this->gridRenderFilter([ 'layout' => $this->getSanitizer()->getString('layout'), 'useRegexForName' => $this->getSanitizer()->getCheckbox('useRegexForName'), 'userId' => $this->getSanitizer()->getInt('userId'), 'retired' => $this->getSanitizer()->getInt('retired'), 'tags' => $this->getSanitizer()->getString('tags'), 'exactTags' => $this->getSanitizer()->getCheckbox('exactTags'), 'filterLayoutStatusId' => $this->getSanitizer()->getInt('layoutStatusId'), 'layoutId' => $this->getSanitizer()->getInt('layoutId'), 'parentId' => $this->getSanitizer()->getInt('parentId'), 'showDrafts' => $this->getSanitizer()->getInt('showDrafts'), 'ownerUserGroupId' => $this->getSanitizer()->getInt('ownerUserGroupId'), 'mediaLike' => $this->getSanitizer()->getString('mediaLike'), 'publishedStatusId' => $this->getSanitizer()->getInt('publishedStatusId'), 'activeDisplayGroupId' => $this->getSanitizer()->getInt('activeDisplayGroupId'), 'campaignId' => $this->getSanitizer()->getInt('campaignId'), ])); foreach ($layouts as $layout) { /* @var \Xibo\Entity\Layout $layout */ if (in_array('regions', $embed)) { $layout->load([ 'loadPlaylists' => in_array('playlists', $embed), 'loadCampaigns' => in_array('campaigns', $embed), 'loadPermissions' => in_array('permissions', $embed), 'loadTags' => in_array('tags', $embed), 'loadWidgets' => in_array('widgets', $embed) ]); } // Populate the status message $layout->getStatusMessage(); // Annotate each Widget with its validity, tags and permissions if (in_array('widget_validity', $embed) || in_array('tags', $embed) || in_array('permissions', $embed)) { foreach ($layout->getWidgets() as $widget) { /* @var Widget $widget */ $module = $this->moduleFactory->createWithWidget($widget); $widget->name = $module->getName(); // Augment with tags $widget->tags = $module->getMediaTags(); // Add widget module type name $widget->moduleName = $module->getModuleName(); // apply default transitions to a dynamic parameters on widget object. if ($layout->autoApplyTransitions == 1) { $widgetTransIn = $widget->getOptionValue('transIn', $this->getConfig()->getSetting('DEFAULT_TRANSITION_IN')); $widgetTransOut = $widget->getOptionValue('transOut', $this->getConfig()->getSetting('DEFAULT_TRANSITION_OUT')); $widgetTransInDuration = $widget->getOptionValue('transInDuration', $this->getConfig()->getSetting('DEFAULT_TRANSITION_DURATION')); $widgetTransOutDuration = $widget->getOptionValue('transOutDuration', $this->getConfig()->getSetting('DEFAULT_TRANSITION_DURATION')); } else { $widgetTransIn = $widget->getOptionValue('transIn', null); $widgetTransOut = $widget->getOptionValue('transOut', null); $widgetTransInDuration = $widget->getOptionValue('transInDuration', null); $widgetTransOutDuration = $widget->getOptionValue('transOutDuration', null); } $widget->transitionIn = $widgetTransIn; $widget->transitionOut = $widgetTransOut; $widget->transitionDurationIn = $widgetTransInDuration; $widget->transitionDurationOut = $widgetTransOutDuration; if (in_array('permissions', $embed)) { // Augment with editable flag $widget->isEditable = $this->getUser()->checkEditable($widget); // Augment with deletable flag $widget->isDeletable = $this->getUser()->checkDeleteable($widget); // Augment with permissions flag $widget->isPermissionsModifiable = $this->getUser()->checkPermissionsModifyable($widget); } if (in_array('widget_validity', $embed)) { try { $widget->isValid = (int)$module->isValid(); } catch (XiboException $xiboException) { $widget->isValid = 0; } } } // Augment regions with permissions foreach ($layout->regions as $region) { if (in_array('permissions', $embed)) { // Augment with editable flag $region->isEditable = $this->getUser()->checkEditable($region); // Augment with deletable flag $region->isDeletable = $this->getUser()->checkDeleteable($region); // Augment with permissions flag $region->isPermissionsModifiable = $this->getUser()->checkPermissionsModifyable($region); } } } if ($this->isApi()) continue; $layout->includeProperty('buttons'); //$layout->excludeProperty('regions'); $layout->thumbnail = ''; if ($layout->backgroundImageId != 0) { $download = $this->urlFor('layout.download.background', ['id' => $layout->layoutId]) . '?preview=1' . '&backgroundImageId=' . $layout->backgroundImageId; $layout->thumbnail = '
'; } // Fix up the description $layout->descriptionFormatted = $layout->description; if ($layout->description != '') { if ($showDescriptionId == 1) { // Parse down for description $layout->descriptionFormatted = Parsedown::instance()->text($layout->description); } else if ($showDescriptionId == 2) { $layout->descriptionFormatted = strtok($layout->description, "\n"); } } if ($showDescriptionId === 3) { // Load in the entire object model - creating module objects so that we can get the name of each // widget and its items. foreach ($layout->regions as $region) { foreach ($region->getPlaylist()->widgets as $widget) { /* @var Widget $widget */ $widget->module = $this->moduleFactory->createWithWidget($widget, $region); } } // provide our layout object to a template to render immediately $layout->descriptionFormatted = $this->renderTemplateToString('layout-page-grid-widgetlist', $layout); } switch ($layout->status) { case ModuleWidget::$STATUS_VALID: $layout->statusDescription = __('This Layout is ready to play'); break; case ModuleWidget::$STATUS_PLAYER: $layout->statusDescription = __('There are items on this Layout that can only be assessed by the Display'); break; case 3: $layout->statusDescription = __('This Layout has not been built yet'); break; default: $layout->statusDescription = __('This Layout is invalid and should not be scheduled'); } switch ($layout->enableStat) { case 1: $layout->enableStatDescription = __('This Layout has enable stat collection set to ON'); break; default: $layout->enableStatDescription = __('This Layout has enable stat collection set to OFF'); } // Published status, draft with set publishedDate $layout->publishedStatusFuture = __('Publishing %s'); $layout->publishedStatusFailed = __('Publish failed '); // Check if user has view permissions to the schedule now page - for layout designer to show/hide Schedule Now button $layout->scheduleNowPermission = $this->getUser()->routeViewable('/schedulenow/form/now/:from/:id'); // Add some buttons for this row if ($this->getUser()->checkEditable($layout)) { // Design Button $layout->buttons[] = array( 'id' => 'layout_button_design', 'linkType' => '_self', 'external' => true, 'url' => $this->urlFor('layout.designer', array('id' => $layout->layoutId)), 'text' => __('Design') ); // Should we show a publish/discard button? if ($layout->isEditable()) { $layout->buttons[] = ['divider' => true]; $layout->buttons[] = array( 'id' => 'layout_button_publish', 'url' => $this->urlFor('layout.publish.form', ['id' => $layout->layoutId]), 'text' => __('Publish') ); $layout->buttons[] = array( 'id' => 'layout_button_discard', 'url' => $this->urlFor('layout.discard.form', ['id' => $layout->layoutId]), 'text' => __('Discard') ); $layout->buttons[] = ['divider' => true]; } else { $layout->buttons[] = ['divider' => true]; // Checkout Button $layout->buttons[] = array( 'id' => 'layout_button_checkout', 'url' => $this->urlFor('layout.checkout.form', ['id' => $layout->layoutId]), 'text' => __('Checkout') ); $layout->buttons[] = ['divider' => true]; } } // Preview $layout->buttons[] = array( 'id' => 'layout_button_preview', 'linkType' => '_blank', 'external' => true, 'url' => $this->urlFor('layout.preview', ['id' => $layout->layoutId]), 'text' => __('Preview Layout') ); $layout->buttons[] = ['divider' => true]; // Schedule Now if ($this->getUser()->routeViewable('/schedulenow/form/now/:from/:id') === true) { $layout->buttons[] = array( 'id' => 'layout_button_schedulenow', 'url' => $this->urlFor('schedulenow.now.form', ['id' => $layout->campaignId, 'from' => 'Campaign']), 'text' => __('Schedule Now') ); } // Assign to Campaign if ($this->getUser()->routeViewable('/campaign')) { $layout->buttons[] = array( 'id' => 'layout_button_assignTo_campaign', 'url' => $this->urlFor('layout.assignTo.campaign.form', ['id' => $layout->layoutId]), 'text' => __('Assign to Campaign') ); } $layout->buttons[] = ['divider' => true]; // Only proceed if we have edit permissions if ($this->getUser()->checkEditable($layout)) { // Edit Button $layout->buttons[] = array( 'id' => 'layout_button_edit', 'url' => $this->urlFor('layout.edit.form', ['id' => $layout->layoutId]), 'text' => __('Edit') ); // Copy Button $layout->buttons[] = array( 'id' => 'layout_button_copy', 'url' => $this->urlFor('layout.copy.form', ['id' => $layout->layoutId]), 'text' => __('Copy') ); // Retire Button if ($layout->retired == 0) { $layout->buttons[] = array( 'id' => 'layout_button_retire', 'url' => $this->urlFor('layout.retire.form', ['id' => $layout->layoutId]), 'text' => __('Retire'), 'multi-select' => true, 'dataAttributes' => array( array('name' => 'commit-url', 'value' => $this->urlFor('layout.retire', ['id' => $layout->layoutId])), array('name' => 'commit-method', 'value' => 'put'), array('name' => 'id', 'value' => 'layout_button_retire'), array('name' => 'text', 'value' => __('Retire')), array('name' => 'rowtitle', 'value' => $layout->layout) ) ); } else { $layout->buttons[] = array( 'id' => 'layout_button_unretire', 'url' => $this->urlFor('layout.unretire.form', ['id' => $layout->layoutId]), 'text' => __('Unretire'), ); } // Extra buttons if have delete permissions if ($this->getUser()->checkDeleteable($layout)) { // Delete Button $layout->buttons[] = array( 'id' => 'layout_button_delete', 'url' => $this->urlFor('layout.delete.form', ['id' => $layout->layoutId]), 'text' => __('Delete'), 'multi-select' => true, 'dataAttributes' => array( array('name' => 'commit-url', 'value' => $this->urlFor('layout.delete', ['id' => $layout->layoutId])), array('name' => 'commit-method', 'value' => 'delete'), array('name' => 'id', 'value' => 'layout_button_delete'), array('name' => 'text', 'value' => __('Delete')), array('name' => 'rowtitle', 'value' => $layout->layout) ) ); } // Set Enable Stat $layout->buttons[] = array( 'id' => 'layout_button_setenablestat', 'url' => $this->urlFor('layout.setenablestat.form', ['id' => $layout->layoutId]), 'text' => __('Enable stats collection?'), 'multi-select' => true, 'dataAttributes' => array( array('name' => 'commit-url', 'value' => $this->urlFor('layout.setenablestat', ['id' => $layout->layoutId])), array('name' => 'commit-method', 'value' => 'put'), array('name' => 'id', 'value' => 'layout_button_setenablestat'), array('name' => 'text', 'value' => __('Enable stats collection?')), array('name' => 'rowtitle', 'value' => $layout->layout), ['name' => 'form-callback', 'value' => 'setEnableStatMultiSelectFormOpen'] ) ); $layout->buttons[] = ['divider' => true]; if ($this->getUser()->routeViewable('template') && !$layout->isEditable()) { // Save template button $layout->buttons[] = array( 'id' => 'layout_button_save_template', 'url' => $this->urlFor('template.from.layout.form', ['id' => $layout->layoutId]), 'text' => __('Save Template') ); } // Export Button $layout->buttons[] = array( 'id' => 'layout_button_export', 'url' => $this->urlFor('layout.export.form', ['id' => $layout->layoutId]), 'text' => __('Export') ); // Extra buttons if we have modify permissions if ($this->getUser()->checkPermissionsModifyable($layout)) { // Permissions button $layout->buttons[] = array( 'id' => 'layout_button_permissions', 'url' => $this->urlFor('user.permissions.form', ['entity' => 'Campaign', 'id' => $layout->campaignId]), 'text' => __('Permissions') ); } } } // Store the table rows $this->getState()->recordsTotal = $this->layoutFactory->countLast(); $this->getState()->setData($layouts); } /** * Displays an Add/Edit form */ function addForm() { $this->getState()->template = 'layout-form-add'; $this->getState()->setData([ 'layouts' => $this->layoutFactory->query(['layout'], ['excludeTemplates' => 0, 'tags' => 'template']), 'resolutions' => $this->resolutionFactory->query(['resolution']), 'help' => $this->getHelp()->link('Layout', 'Add') ]); } /** * Edit form * @param int $layoutId * @throws XiboException */ function editForm($layoutId) { // Get the layout $layout = $this->layoutFactory->getById($layoutId); $tags = ''; $arrayOfTags = array_filter(explode(',', $layout->tags)); $arrayOfTagValues = array_filter(explode(',', $layout->tagValues)); for ($i=0; $i
getUser()->checkEditable($layout)) throw new AccessDeniedException(); $this->getState()->template = 'layout-form-edit'; $this->getState()->setData([ 'layout' => $layout, 'tags' => $tags, 'help' => $this->getHelp()->link('Layout', 'Edit') ]); } /** * Edit form * @param int $layoutId * @throws XiboException */ function editBackgroundForm($layoutId) { // Get the layout $layout = $this->layoutFactory->getById($layoutId); // Check Permissions if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(); // Edits always happen on Drafts, get the draft Layout using the Parent Layout ID if ($layout->schemaVersion < 2) { $resolution = $this->resolutionFactory->getByDesignerDimensions($layout->width, $layout->height); } else { $resolution = $this->resolutionFactory->getByDimensions($layout->width, $layout->height); } // If we have a background image, output it $backgroundId = $this->getSanitizer()->getInt('backgroundOverride', $layout->backgroundImageId); $backgrounds = ($backgroundId != null) ? [$this->mediaFactory->getById($backgroundId)] : []; $this->getState()->template = 'layout-form-background'; $this->getState()->setData([ 'layout' => $layout, 'resolution' => $resolution, 'resolutions' => $this->resolutionFactory->query(['resolution'], ['withCurrent' => $resolution->resolutionId]), 'backgroundId' => $backgroundId, 'backgrounds' => $backgrounds, 'help' => $this->getHelp()->link('Layout', 'Edit') ]); } /** * Copy layout form * @param int $layoutId */ public function copyForm($layoutId) { // Get the layout $layout = $this->layoutFactory->getById($layoutId); // Check Permissions if (!$this->getUser()->checkViewable($layout)) throw new AccessDeniedException(); $this->getState()->template = 'layout-form-copy'; $this->getState()->setData([ 'layout' => $layout, 'help' => $this->getHelp()->link('Layout', 'Copy') ]); } /** * Copies a layout * @param int $layoutId * * @SWG\Post( * path="/layout/copy/{layoutId}", * operationId="layoutCopy", * tags={"layout"}, * summary="Copy Layout", * description="Copy a Layout, providing a new name if applicable", * @SWG\Parameter( * name="layoutId", * in="path", * description="The Layout ID to Copy", * type="integer", * required=true * ), * @SWG\Parameter( * name="name", * in="formData", * description="The name for the new Layout", * type="string", * required=true * ), * @SWG\Parameter( * name="description", * in="formData", * description="The Description for the new Layout", * type="string", * required=false * ), * @SWG\Parameter( * name="copyMediaFiles", * in="formData", * description="Flag indicating whether to make new Copies of all Media Files assigned to the Layout being Copied", * type="integer", * required=true * ), * @SWG\Response( * response=201, * description="successful operation", * @SWG\Schema(ref="#/definitions/Layout"), * @SWG\Header( * header="Location", * description="Location of the new record", * type="string" * ) * ) * ) * * @throws XiboException */ public function copy($layoutId) { // Get the layout $layout = $this->layoutFactory->getById($layoutId); // Check Permissions if (!$this->getUser()->checkViewable($layout)) throw new AccessDeniedException(); // Make sure we're not a draft if ($layout->isChild()) throw new InvalidArgumentException('Cannot copy a Draft Layout', 'layoutId'); // Load the layout for Copy $layout->load(['loadTags' => false]); $originalLayout = $layout; // Clone $layout = clone $layout; $tags = ''; $arrayOfTags = array_filter(explode(',', $layout->tags)); $arrayOfTagValues = array_filter(explode(',', $layout->tagValues)); for ($i=0; $i
layout = $this->getSanitizer()->getString('name'); $layout->description = $this->getSanitizer()->getString('description'); $layout->replaceTags($this->tagFactory->tagsFromString($tags)); $layout->setOwner($this->getUser()->userId, true); // Copy the media on the layout and change the assignments. // https://github.com/xibosignage/xibo/issues/1283 if ($this->getSanitizer()->getCheckbox('copyMediaFiles') == 1) { // track which Media Id we already copied $copiedMediaIds = []; foreach ($layout->getWidgets() as $widget) { // Copy the media if ( $widget->type === 'image' || $widget->type === 'video' || $widget->type === 'pdf' || $widget->type === 'powerpoint' || $widget->type === 'audio' ) { $oldMedia = $this->mediaFactory->getById($widget->getPrimaryMediaId()); // check if we already cloned this media, if not, do it and add it the array if (!array_key_exists($oldMedia->mediaId, $copiedMediaIds)) { $media = clone $oldMedia; $media->setOwner($this->getUser()->userId); $media->save(); $copiedMediaIds[$oldMedia->mediaId] = $media->mediaId; } else { // if we already cloned that media, look it up and assign to Widget. $mediaId = $copiedMediaIds[$oldMedia->mediaId]; $media = $this->mediaFactory->getById($mediaId); } $widget->unassignMedia($oldMedia->mediaId); $widget->assignMedia($media->mediaId); // Update the widget option with the new ID $widget->setOptionValue('uri', 'attrib', $media->storedAs); } } // Also handle the background image, if there is one if ($layout->backgroundImageId != 0) { $oldMedia = $this->mediaFactory->getById($layout->backgroundImageId); // check if we already cloned this media, if not, do it and add it the array if (!array_key_exists($oldMedia->mediaId, $copiedMediaIds)) { $media = clone $oldMedia; $media->setOwner($this->getUser()->userId); $media->save(); $copiedMediaIds[$oldMedia->mediaId] = $media->mediaId; } else { // if we already cloned that media, look it up and assign to Layout backgroundImage. $mediaId = $copiedMediaIds[$oldMedia->mediaId]; $media = $this->mediaFactory->getById($mediaId); } $layout->backgroundImageId = $media->mediaId; } } // Save the new layout $layout->save(); // Sub-Playlist foreach ($layout->regions as $region) { // Match our original region id to the id in the parent layout $original = $originalLayout->getRegion($region->getOriginalValue('regionId')); // Make sure Playlist closure table from the published one are copied over $original->getPlaylist()->cloneClosureTable($region->getPlaylist()->playlistId); } // Permissions foreach ($this->permissionFactory->createForNewEntity($this->getUser(), 'Xibo\\Entity\\Campaign', $layout->getId(), $this->getConfig()->getSetting('LAYOUT_DEFAULT'), $this->userGroupFactory) as $permission) { /* @var Permission $permission */ $permission->save(); } foreach ($layout->regions as $region) { /* @var Region $region */ foreach ($this->permissionFactory->createForNewEntity($this->getUser(), get_class($region), $region->getId(), $this->getConfig()->getSetting('LAYOUT_DEFAULT'), $this->userGroupFactory) as $permission) { /* @var Permission $permission */ $permission->save(); } $playlist = $region->getPlaylist(); /* @var Playlist $playlist */ foreach ($this->permissionFactory->createForNewEntity($this->getUser(), get_class($playlist), $playlist->getId(), $this->getConfig()->getSetting('LAYOUT_DEFAULT'), $this->userGroupFactory) as $permission) { /* @var Permission $permission */ $permission->save(); } foreach ($playlist->widgets as $widget) { /* @var Widget $widget */ foreach ($this->permissionFactory->createForNewEntity($this->getUser(), get_class($widget), $widget->getId(), $this->getConfig()->getSetting('LAYOUT_DEFAULT'), $this->userGroupFactory) as $permission) { /* @var Permission $permission */ $permission->save(); } } } // Return $this->getState()->hydrate([ 'httpStatus' => 201, 'message' => sprintf(__('Copied as %s'), $layout->layout), 'id' => $layout->layoutId, 'data' => $layout ]); } /** * @SWG\Post( * path="/layout/{layoutId}/tag", * operationId="layoutTag", * tags={"layout"}, * summary="Tag Layout", * description="Tag a Layout with one or more tags", * @SWG\Parameter( * name="layoutId", * in="path", * description="The Layout Id to Tag", * type="integer", * required=true * ), * @SWG\Parameter( * name="tag", * in="formData", * description="An array of tags", * type="array", * required=true, * @SWG\Items(type="string") * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema(ref="#/definitions/Layout") * ) * ) * * @param $layoutId * @throws XiboException */ public function tag($layoutId) { // Edit permission // Get the layout $layout = $this->layoutFactory->getById($layoutId); // Check Permissions if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(); // Make sure we're not a draft if ($layout->isChild()) throw new InvalidArgumentException('Cannot manage tags on a Draft Layout', 'layoutId'); $tags = $this->getSanitizer()->getStringArray('tag'); if (count($tags) <= 0) throw new \InvalidArgumentException(__('No tags to assign')); foreach ($tags as $tag) { $layout->assignTag($this->tagFactory->tagFromString($tag)); } $layout->save(); // Return $this->getState()->hydrate([ 'message' => sprintf(__('Tagged %s'), $layout->layout), 'id' => $layout->layoutId, 'data' => $layout ]); } /** * @SWG\Post( * path="/layout/{layoutId}/untag", * operationId="layoutUntag", * tags={"layout"}, * summary="Untag Layout", * description="Untag a Layout with one or more tags", * @SWG\Parameter( * name="layoutId", * in="path", * description="The Layout Id to Untag", * type="integer", * required=true * ), * @SWG\Parameter( * name="tag", * in="formData", * description="An array of tags", * type="array", * required=true, * @SWG\Items(type="string") * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema(ref="#/definitions/Layout") * ) * ) * * @param $layoutId * @throws XiboException */ public function untag($layoutId) { // Edit permission // Get the layout $layout = $this->layoutFactory->getById($layoutId); // Check Permissions if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(); // Make sure we're not a draft if ($layout->isChild()) throw new InvalidArgumentException('Cannot manage tags on a Draft Layout', 'layoutId'); $tags = $this->getSanitizer()->getStringArray('tag'); if (count($tags) <= 0) throw new InvalidArgumentException(__('No tags to unassign'), 'tag'); foreach ($tags as $tag) { $layout->unassignTag($this->tagFactory->tagFromString($tag)); } $layout->save(); // Return $this->getState()->hydrate([ 'message' => sprintf(__('Untagged %s'), $layout->layout), 'id' => $layout->layoutId, 'data' => $layout ]); } /** * Layout Status * @param int $layoutId * * @throws \Xibo\Exception\InvalidArgumentException * @throws \Xibo\Exception\NotFoundException * @throws \Xibo\Exception\XiboException * @SWG\Get( * path="/layout/status/{layoutId}", * operationId="layoutStatus", * tags={"layout"}, * summary="Layout Status", * description="Calculate the Layout status and return a Layout", * @SWG\Parameter( * name="layoutId", * in="path", * description="The Layout Id to get the status", * type="integer", * required=true * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema(ref="#/definitions/Layout") * ) * ) */ public function status($layoutId) { // Get the layout $layout = $this->layoutFactory->getById($layoutId); $layout->xlfToDisk(); switch ($layout->status) { case ModuleWidget::$STATUS_VALID: $status = __('This Layout is ready to play'); break; case ModuleWidget::$STATUS_PLAYER: $status = __('There are items on this Layout that can only be assessed by the Display'); break; case 3: $status = __('This Layout has not been built yet'); break; default: $status = __('This Layout is invalid and should not be scheduled'); } // We want a different return depending on whether we are arriving through the API or WEB routes if ($this->isApi()) { $this->getState()->hydrate([ 'httpStatus' => 200, 'message' => $status, 'id' => $layout->status, 'data' => $layout ]); } else { $this->getState()->html = $status; $this->getState()->extra = [ 'status' => $layout->status, 'duration' => $layout->duration, 'statusMessage' => $layout->getStatusMessage() ]; $this->getState()->success = true; $this->session->refreshExpiry = false; } } /** * Export Form * @param $layoutId */ public function exportForm($layoutId) { // Get the layout $layout = $this->layoutFactory->getById($layoutId); // Check Permissions if (!$this->getUser()->checkViewable($layout)) throw new AccessDeniedException(); // Make sure we're not a draft if ($layout->isChild()) throw new InvalidArgumentException('Cannot manage tags on a Draft Layout', 'layoutId'); // Render the form $this->getState()->template = 'layout-form-export'; $this->getState()->setData([ 'layout' => $layout, 'saveAs' => 'export_' . preg_replace('/[^a-z0-9]+/', '-', strtolower($layout->layout)) ]); } /** * @param int $layoutId * @throws XiboException */ public function export($layoutId) { $this->setNoOutput(true); // Get the layout $layout = $this->layoutFactory->getById($layoutId); // Check Permissions if (!$this->getUser()->checkViewable($layout)) throw new AccessDeniedException(); // Make sure we're not a draft if ($layout->isChild()) throw new InvalidArgumentException('Cannot manage tags on a Draft Layout', 'layoutId'); // Save As? $saveAs = $this->getSanitizer()->getString('saveAs'); // Make sure our file name is reasonable if (empty($saveAs)) { $saveAs = 'export_' . preg_replace('/[^a-z0-9]+/', '-', strtolower($layout->layout)); } else { $saveAs = preg_replace('/[^a-z0-9]+/', '-', strtolower($saveAs)); } $fileName = $this->getConfig()->getSetting('LIBRARY_LOCATION') . 'temp/' . $saveAs . '.zip'; $layout->toZip($this->dataSetFactory, $fileName, ['includeData' => ($this->getSanitizer()->getCheckbox('includeData')== 1)]); if (ini_get('zlib.output_compression')) { ini_set('zlib.output_compression', 'Off'); } header('Content-Type: application/octet-stream'); header("Content-Transfer-Encoding: Binary"); header("Content-disposition: attachment; filename=\"" . basename($fileName) . "\""); header('Content-Length: ' . filesize($fileName)); // Send via Apache X-Sendfile header? if ($this->getConfig()->getSetting('SENDFILE_MODE') == 'Apache') { header("X-Sendfile: $fileName"); $this->getApp()->halt(200); } // Send via Nginx X-Accel-Redirect? if ($this->getConfig()->getSetting('SENDFILE_MODE') == 'Nginx') { header("X-Accel-Redirect: /download/temp/" . basename($fileName)); $this->getApp()->halt(200); } // Return the file with PHP // Disable any buffering to prevent OOM errors. while (ob_get_level() > 0) { ob_end_clean(); } readfile($fileName); exit; } /** * TODO: Not sure how to document this. * SWG\Post( * path="/layout/import", * operationId="layoutImport", * tags={"layout"}, * summary="Import Layout", * description="Upload and Import a Layout", * consumes="multipart/form-data", * SWG\Parameter( * name="file", * in="formData", * description="The file", * type="file", * required=true * ), * @SWG\Response( * response=200, * description="successful operation" * ) * ) * * @throws XiboException * @throws \Exception */ public function import() { $this->getLog()->debug('Import Layout'); $libraryFolder = $this->getConfig()->getSetting('LIBRARY_LOCATION'); // Make sure the library exists Library::ensureLibraryExists($this->getConfig()->getSetting('LIBRARY_LOCATION')); // Make sure there is room in the library /** @var Library $libraryController */ $libraryController = $this->getApp()->container->get('\Xibo\Controller\Library')->setApp($this->getApp()); $libraryLimit = $this->getConfig()->getSetting('LIBRARY_SIZE_LIMIT_KB') * 1024; $options = array( 'userId' => $this->getUser()->userId, 'controller' => $this, 'libraryController' => $libraryController, 'upload_dir' => $libraryFolder . 'temp/', 'download_via_php' => true, 'script_url' => $this->urlFor('layout.import'), 'upload_url' => $this->urlFor('layout.import'), 'image_versions' => array(), 'accept_file_types' => '/\.zip$/i', 'libraryLimit' => $libraryLimit, 'libraryQuotaFull' => ($libraryLimit > 0 && $libraryController->libraryUsage() > $libraryLimit) ); $this->setNoOutput(true); // Hand off to the Upload Handler provided by jquery-file-upload new LayoutUploadHandler($options); } /** * Gets a file from the library * @param int $layoutId * @throws NotFoundException * @throws AccessDeniedException */ public function downloadBackground($layoutId) { $this->getLog()->debug('Layout Download background request for layoutId ' . $layoutId); $layout = $this->layoutFactory->getById($layoutId); if (!$this->getUser()->checkViewable($layout)) throw new AccessDeniedException(); if ($layout->backgroundImageId == null) throw new NotFoundException(); // This media may not be viewable, but we won't check it because the user has permission to view the // layout that it is assigned to. $media = $this->mediaFactory->getById($layout->backgroundImageId); // Make a media module $widget = $this->moduleFactory->createWithMedia($media); if ($widget->getModule()->regionSpecific == 1) throw new NotFoundException('Cannot download non-region specific module'); $widget->getResource(0); $this->setNoOutput(true); } /** * Assign to Campaign Form * @param $layoutId */ public function assignToCampaignForm($layoutId) { // Get the layout $layout = $this->layoutFactory->getById($layoutId); // Check Permissions if (!$this->getUser()->checkViewable($layout)) throw new AccessDeniedException(); // Render the form $this->getState()->template = 'layout-form-assign-to-campaign'; $this->getState()->setData([ 'layout' => $layout, 'campaigns' => $this->campaignFactory->query() ]); } /** * Checkout Layout Form * @param int $layoutId * @throws XiboException */ public function checkoutForm($layoutId) { $layout = $this->layoutFactory->getById($layoutId); // Make sure we have permission if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(__('You do not have permissions to edit this layout')); $data = ['layout' => $layout]; $this->getState()->template = 'layout-form-checkout'; $this->getState()->setData($data); } /** * Checkout Layout * * @SWG\Put( * path="/layout/checkout/{layoutId}", * operationId="layoutCheckout", * tags={"layout"}, * summary="Checkout Layout", * description="Checkout a Layout so that it can be edited. The original Layout will still be played", * @SWG\Parameter( * name="layoutId", * in="path", * description="The Layout ID", * type="integer", * required=true * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema(ref="#/definitions/Layout") * ) * ) * * @param int $layoutId * @throws XiboException */ public function checkout($layoutId) { $layout = $this->layoutFactory->getById($layoutId); // Make sure we have permission if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(__('You do not have permissions to edit this layout')); // Can't checkout a Layout which can already be edited if ($layout->isEditable()) throw new InvalidArgumentException(__('Layout is already checked out'), 'statusId'); // Load the Layout $layout->load(); // Make a skeleton copy of the Layout $draft = clone $layout; $draft->parentId = $layout->layoutId; $draft->campaignId = $layout->campaignId; $draft->publishedStatusId = 2; // Draft $draft->publishedStatus = __('Draft'); $draft->autoApplyTransitions = $layout->autoApplyTransitions; // Do not copy any of the tags, these will belong on the parent and are not editable from the draft. $draft->tags = []; // Save without validation or notification. $draft->save([ 'validate' => false, 'notify' => false ]); // Update the original $layout->publishedStatusId = 2; // Draft $layout->save([ 'saveLayout' => true, 'saveRegions' => false, 'saveTags' => false, 'setBuildRequired' => false, 'validate' => false, 'notify' => false ]); // Permissions && Sub-Playlists // Layout level permissions are managed on the Campaign entity, so we do not need to worry about that // Regions/Widgets need to copy down our layout permissions foreach ($draft->regions as $region) { // Match our original region id to the id in the parent layout $original = $layout->getRegion($region->getOriginalValue('regionId')); // Make sure Playlist closure table from the published one are copied over $original->getPlaylist()->cloneClosureTable($region->getPlaylist()->playlistId); // Copy over original permissions foreach ($original->permissions as $permission) { $new = clone $permission; $new->objectId = $region->regionId; $new->save(); } // Playlist foreach ($original->getPlaylist()->permissions as $permission) { $new = clone $permission; $new->objectId = $region->getPlaylist()->playlistId; $new->save(); } // Widgets foreach ($region->getPlaylist()->widgets as $widget) { $originalWidget = $original->getPlaylist()->getWidget($widget->getOriginalValue('widgetId')); // Copy over original permissions foreach ($originalWidget->permissions as $permission) { $new = clone $permission; $new->objectId = $widget->widgetId; $new->save(); } } } // Return $this->getState()->hydrate([ 'httpStatus' => 200, 'message' => sprintf(__('Checked out %s'), $layout->layout), 'id' => $draft->layoutId, 'data' => $draft ]); } /** * Publish Layout Form * @param int $layoutId * @throws XiboException */ public function publishForm($layoutId) { $layout = $this->layoutFactory->getById($layoutId); // Make sure we have permission if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(__('You do not have permissions to edit this layout')); $data = ['layout' => $layout]; $this->getState()->template = 'layout-form-publish'; $this->getState()->setData($data); } /** * Publish Layout * * @SWG\Put( * path="/layout/publish/{layoutId}", * operationId="layoutPublish", * tags={"layout"}, * summary="Publish Layout", * description="Publish a Layout, discarding the original", * @SWG\Parameter( * name="layoutId", * in="path", * description="The Layout ID", * type="integer", * required=true * ), * @SWG\Parameter( * name="publishNow", * in="formData", * description="Flag, indicating whether to publish layout now", * type="integer", * required=false * ), * @SWG\Parameter( * name="publishDate", * in="formData", * description="The date/time at which layout should be published", * type="string", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema(ref="#/definitions/Layout") * ) * ) * * @param $layoutId * @throws XiboException */ public function publish($layoutId) { $layout = $this->layoutFactory->getById($layoutId); $publishDate = $this->getSanitizer()->getDate('publishDate'); $publishNow = $this->getSanitizer()->getCheckbox('publishNow'); // Make sure we have permission if (!$this->getUser()->checkEditable($layout)) { throw new AccessDeniedException(__('You do not have permissions to edit this layout')); } // if we have publish date update it in database if (isset($publishDate) && !$publishNow) { $layout->setPublishedDate($publishDate); } // We want to take the draft layout, and update the campaign links to point to the draft, then remove the // parent. if ($publishNow || (isset($publishDate) && $publishDate->format('U') < $this->getDate()->getLocalDate(null, 'U')) ) { $draft = $this->layoutFactory->getByParentId($layoutId); $draft->publishDraft(); $draft->load(); // We also build the XLF at this point, and if we have a problem we prevent publishing and raise as an // error message $draft->xlfToDisk(['notify' => true, 'exceptionOnError' => true, 'exceptionOnEmptyRegion' => false]); // Return $this->getState()->hydrate([ 'httpStatus' => 200, 'message' => sprintf(__('Published %s'), $draft->layout), 'data' => $draft ]); } else { // Return $this->getState()->hydrate([ 'httpStatus' => 200, 'message' => sprintf(__('Layout will be published on %s'), $publishDate), 'data' => $layout ]); } } /** * Discard Layout Form * @param int $layoutId * @throws XiboException */ public function discardForm($layoutId) { $layout = $this->layoutFactory->getById($layoutId); // Make sure we have permission if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(__('You do not have permissions to edit this layout')); $data = ['layout' => $layout]; $this->getState()->template = 'layout-form-discard'; $this->getState()->setData($data); } /** * Discard Layout * * @SWG\Put( * path="/layout/discard/{layoutId}", * operationId="layoutDiscard", * tags={"layout"}, * summary="Discard Layout", * description="Discard a Layout restoring the original", * @SWG\Parameter( * name="layoutId", * in="path", * description="The Layout ID", * type="integer", * required=true * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema(ref="#/definitions/Layout") * ) * ) * * @param $layoutId * @throws InvalidArgumentException * @throws NotFoundException * @throws XiboException */ public function discard($layoutId) { $layout = $this->layoutFactory->getById($layoutId); // Make sure we have permission if (!$this->getUser()->checkEditable($layout)) throw new AccessDeniedException(__('You do not have permissions to edit this layout')); // Make sure the Layout is checked out to begin with if (!$layout->isEditable()) throw new InvalidArgumentException(__('Layout is not checked out'), 'statusId'); $draft = $this->layoutFactory->getByParentId($layoutId); $draft->discardDraft(); // The parent is no longer a draft $layout->publishedStatusId = 1; // Return $this->getState()->hydrate([ 'httpStatus' => 200, 'message' => sprintf(__('Discarded %s'), $draft->layout), 'data' => $layout ]); } } PK lqY-d Stats.phpnu [ . */ namespace Xibo\Controller; use Xibo\Exception\InvalidArgumentException; use Xibo\Exception\NotFoundException; use Xibo\Factory\DisplayFactory; use Xibo\Factory\DisplayGroupFactory; use Xibo\Factory\LayoutFactory; use Xibo\Factory\MediaFactory; use Xibo\Factory\UserFactory; use Xibo\Factory\UserGroupFactory; use Xibo\Helper\ByteFormatter; use Xibo\Service\ConfigServiceInterface; use Xibo\Service\DateServiceInterface; use Xibo\Service\LogServiceInterface; use Xibo\Service\ReportServiceInterface; use Xibo\Service\SanitizerServiceInterface; use Xibo\Storage\StorageServiceInterface; use Xibo\Storage\TimeSeriesStoreInterface; /** * Class Stats * @package Xibo\Controller */ class Stats extends Base { /** * @var StorageServiceInterface */ private $store; /** * @var TimeSeriesStoreInterface */ private $timeSeriesStore; /** * @var ReportServiceInterface */ private $reportService; /** * @var DisplayFactory */ private $displayFactory; /** * @var DisplayGroupFactory */ private $displayGroupFactory; /** * @var MediaFactory */ private $mediaFactory; /** @var LayoutFactory */ private $layoutFactory; /** @var UserFactory */ private $userFactory; /** @var UserGroupFactory */ private $userGroupFactory; /** * Set common dependencies. * @param LogServiceInterface $log * @param SanitizerServiceInterface $sanitizerService * @param \Xibo\Helper\ApplicationState $state * @param \Xibo\Entity\User $user * @param \Xibo\Service\HelpServiceInterface $help * @param DateServiceInterface $date * @param ConfigServiceInterface $config * @param StorageServiceInterface $store * @param TimeSeriesStoreInterface $timeSeriesStore * @param ReportServiceInterface $reportService * @param DisplayFactory $displayFactory * @param LayoutFactory $layoutFactory * @param MediaFactory $mediaFactory * @param UserFactory $userFactory * @param UserGroupFactory $userGroupFactory * @param DisplayGroupFactory $displayGroupFactory */ public function __construct($log, $sanitizerService, $state, $user, $help, $date, $config, $store, $timeSeriesStore, $reportService, $displayFactory, $layoutFactory, $mediaFactory, $userFactory, $userGroupFactory, $displayGroupFactory) { $this->setCommonDependencies($log, $sanitizerService, $state, $user, $help, $date, $config); $this->store = $store; $this->timeSeriesStore = $timeSeriesStore; $this->reportService = $reportService; $this->displayFactory = $displayFactory; $this->layoutFactory = $layoutFactory; $this->mediaFactory = $mediaFactory; $this->userFactory = $userFactory; $this->userGroupFactory = $userGroupFactory; $this->displayGroupFactory = $displayGroupFactory; } /** * Stats page */ function displayPage() { $data = [ // List of Displays this user has permission for 'defaults' => [ 'fromDate' => $this->getDate()->getLocalDate(time() - (86400 * 35)), 'fromDateOneDay' => $this->getDate()->getLocalDate(time() - 86400), 'toDate' => $this->getDate()->getLocalDate() ] ]; $this->getState()->template = 'statistics-page'; $this->getState()->setData($data); } /** * Stats page */ function displayProofOfPlayPage() { $data = [ // List of Displays this user has permission for 'defaults' => [ 'fromDate' => $this->getDate()->getLocalDate(time() - (86400 * 35)), 'fromDateOneDay' => $this->getDate()->getLocalDate(time() - 86400), 'toDate' => $this->getDate()->getLocalDate(), 'availableReports' => $this->reportService->listReports() ] ]; $this->getState()->template = 'stats-proofofplay-page'; $this->getState()->setData($data); } /** * @SWG\Definition( * definition="StatisticsData", * @SWG\Property( * property="type", * type="string" * ), * @SWG\Property( * property="display", * type="string" * ), * @SWG\Property( * property="displayId", * type="integer" * ), * @SWG\Property( * property="layout", * type="string" * ), * @SWG\Property( * property="layoutId", * type="integer" * ), * @SWG\Property( * property="media", * type="string" * ), * @SWG\Property( * property="mediaId", * type="integer" * ), * @SWG\Property( * property="widgetId", * type="integer" * ), * @SWG\Property( * property="scheduleId", * type="integer" * ), * @SWG\Property( * property="numberPlays", * type="integer" * ), * @SWG\Property( * property="duration", * type="integer" * ), * @SWG\Property( * property="minStart", * description="DEPRECATED - will be removed in v3", * type="string" * ), * @SWG\Property( * property="maxEnd", * description="DEPRECATED - will be removed in v3", * type="string" * ), * @SWG\Property( * property="start", * type="string" * ), * @SWG\Property( * property="end", * type="string" * ), * @SWG\Property( * property="statDate", * type="string" * ), * @SWG\Property( * property="tag", * type="string" * ) * ) * * * Shows the stats grid * * @SWG\Get( * path="/stats", * operationId="statsSearch", * tags={"statistics"}, * @SWG\Parameter( * name="type", * in="query", * description="The type of stat to return. Layout|Media|Widget", * type="string", * required=false * ), * @SWG\Parameter( * name="fromDt", * in="query", * description="The start date for the filter. Default = 24 hours ago", * type="string", * required=false * ), * @SWG\Parameter( * name="toDt", * in="query", * description="The end date for the filter. Default = now.", * type="string", * required=false * ), * @SWG\Parameter( * name="statDate", * in="query", * description="The statDate filter returns records that are greater than or equal a particular date", * type="string", * required=false * ), * @SWG\Parameter( * name="statId", * in="query", * description="The statId filter returns records that are greater than a particular statId", * type="string", * required=false * ), * @SWG\Parameter( * name="displayId", * in="query", * description="An optional display Id to filter", * type="integer", * required=false * ), * @SWG\Parameter( * name="displayIds", * description="An optional array of display Id to filter", * in="query", * required=false, * type="array", * @SWG\Items( * type="integer" * ) * ), * @SWG\Parameter( * name="layoutId", * description="An optional array of layout Id to filter", * in="query", * required=false, * type="array", * @SWG\Items( * type="integer" * ) * ), * @SWG\Parameter( * name="mediaId", * description="An optional array of media Id to filter", * in="query", * required=false, * type="array", * @SWG\Items( * type="integer" * ) * ), * @SWG\Parameter( * name="campaignId", * in="query", * description="An optional Campaign Id to filter", * type="integer", * required=false * ), * @SWG\Parameter( * name="returnDisplayLocalTime", * in="query", * description="true/1/On if the results should be in display local time, otherwise CMS time", * type="boolean", * required=false * ), * @SWG\Parameter( * name="returnDateFormat", * in="query", * description="A PHP formatted date format for how the dates in this call should be returned.", * type="string", * required=false * ), * @SWG\Parameter( * name="embed", * in="query", * description="Should the return embed additional data, options are layoutTags,displayTags and mediaTags", * type="string", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema( * type="array", * @SWG\Items( * ref="#/definitions/StatisticsData" * ) * ) * ) * ) * @throws \Xibo\Exception\InvalidArgumentException */ public function grid() { // This endpoint is only ever used by API $fromDt = $this->getSanitizer()->getDate('fromDt'); $toDt = $this->getSanitizer()->getDate('toDt'); $type = strtolower($this->getSanitizer()->getString('type')); $displayId = $this->getSanitizer()->getInt('displayId'); $displays = $this->getSanitizer()->getIntArray('displayIds'); $layoutIds = $this->getSanitizer()->getIntArray('layoutId'); $mediaIds = $this->getSanitizer()->getIntArray('mediaId'); $statDate = $this->getSanitizer()->getDate('statDate'); $statId = $this->getSanitizer()->getString('statId'); $campaignId = $this->getSanitizer()->getInt('campaignId'); $eventTag = $this->getSanitizer()->getString('eventTag'); $returnDisplayLocalTime = $this->getSanitizer()->getCheckbox('returnDisplayLocalTime'); $returnDateFormat = $this->getSanitizer()->getString('returnDateFormat', 'Y-m-d H:i:s'); $start = $this->getSanitizer()->getInt('start', 0); $length = $this->getSanitizer()->getInt('length', 10); // Embed Tags $embed = explode(',', $this->getSanitizer()->getString('embed', '')); // CMS timezone $defaultTimezone = $this->getConfig()->getSetting('defaultTimezone'); // Merge displayId and displayIds if ($displayId != 0) { $displays = array_unique(array_merge($displays, [$displayId])); } // Do not filter by display if super admin and no display is selected // Super admin will be able to see stat records of deleted display, we will not filter by display later $timeZoneCache = []; $displayIds = $this->authoriseDisplayIds($displays, $timeZoneCache); // Call the time series interface getStats $resultSet = $this->timeSeriesStore->getStats( [ 'fromDt'=> $fromDt, 'toDt'=> $toDt, 'type' => $type, 'displayIds' => $displayIds, 'layoutIds' => $layoutIds, 'mediaIds' => $mediaIds, 'statDate' => $statDate, 'statId' => $statId, 'campaignId' => $campaignId, 'eventTag' => $eventTag, 'displayTags' => in_array('displayTags', $embed), 'layoutTags' => in_array('layoutTags', $embed), 'mediaTags' => in_array('mediaTags', $embed), 'start' => $start, 'length' => $length, ]); // Get results as array $result = $resultSet->getArray(); $rows = []; foreach ($result['statData'] as $row) { $entry = []; // Core details $entry['id'] = $this->getSanitizer()->string($row['id']); $entry['type'] = $this->getSanitizer()->string($row['type']); $entry['displayId'] = $this->getSanitizer()->int(($row['displayId'])); // Get the start/end date $start = $this->getDate()->parse($row['start'], 'U'); $end = $this->getDate()->parse($row['end'], 'U'); if ($returnDisplayLocalTime) { // Convert the dates to the display timezone. if (!array_key_exists($entry['displayId'], $timeZoneCache)) { try { $display = $this->displayFactory->getById($entry['displayId']); $timeZoneCache[$entry['displayId']] = (empty($display->timeZone)) ? $defaultTimezone : $display->timeZone; } catch (NotFoundException $e) { $timeZoneCache[$entry['displayId']] = $defaultTimezone; } } $start = $start->tz($timeZoneCache[$entry['displayId']]); $end = $end->tz($timeZoneCache[$entry['displayId']]); } $widgetId = $this->getSanitizer()->int($row['widgetId']); $widgetName = $this->getSanitizer()->string($row['media']); $widgetName = ($widgetName == '' && $widgetId != 0) ? __('Deleted from Layout') : $widgetName; $displayName = isset($row['display']) ? $this->getSanitizer()->string($row['display']) : ''; $layoutName = isset($row['layout']) ? $this->getSanitizer()->string($row['layout']) : ''; $entry['display'] = ($displayName != '') ? $displayName : __('Not Found'); $entry['layout'] = ($layoutName != '') ? $layoutName : __('Not Found'); $entry['media'] = $widgetName; $entry['numberPlays'] = $this->getSanitizer()->int($row['count']); $entry['duration'] = $this->getSanitizer()->int($row['duration']); $entry['start'] = $start->format($returnDateFormat); $entry['end'] = $end->format($returnDateFormat); $entry['layoutId'] = $this->getSanitizer()->int($row['layoutId']); $entry['widgetId'] = $this->getSanitizer()->int($row['widgetId']); $entry['mediaId'] = $this->getSanitizer()->int($row['mediaId']); $entry['campaignId'] = $this->getSanitizer()->int($row['campaignId']); $entry['scheduleId'] = $this->getSanitizer()->int($row['scheduleId'] ?? 0); $entry['tag'] = $this->getSanitizer()->string($row['tag']); $entry['statDate'] = isset($row['statDate']) ? $this->getDate()->parse($row['statDate'], 'U')->format($returnDateFormat) : ''; $entry['engagements'] = $row['engagements']; // These are duplicated for backwards compatibility // DEPRECATED $entry['minStart'] = $this->getDate()->parse($row['start'], 'U')->format('Y-m-d H:i:s'); $entry['maxEnd'] = $this->getDate()->parse($row['end'], 'U')->format('Y-m-d H:i:s'); // Tags // ---- // Display tags if (in_array('displayTags', $embed)) { $entry['displayTags'] = $row['tagFilter']['dg'] ?? []; } // Layout tags if (in_array('layoutTags', $embed)) { $entry['layoutTags'] = $row['tagFilter']['layout'] ?? []; } // Media tags if (in_array('mediaTags', $embed)) { $entry['mediaTags'] = $row['tagFilter']['media'] ?? []; } $rows[] = $entry; } $this->getState()->template = 'grid'; $this->getState()->recordsTotal = $resultSet->getTotalCount(); $this->getState()->setData($rows); } /** * Bandwidth Data */ public function bandwidthData() { $fromDt = $this->getSanitizer()->getDate('fromDt', $this->getSanitizer()->getDate('bandwidthFromDt')); $toDt = $this->getSanitizer()->getDate('toDt', $this->getSanitizer()->getDate('bandwidthToDt')); // Get an array of display id this user has access to. $displayIds = []; foreach ($this->displayFactory->query() as $display) { $displayIds[] = $display->displayId; } if (count($displayIds) <= 0) throw new InvalidArgumentException(__('No displays with View permissions'), 'displays'); // Get some data for a bandwidth chart $dbh = $this->store->getConnection(); $displayId = $this->getSanitizer()->getInt('displayId'); $params = array( 'month' => $this->getDate()->getLocalDate($fromDt->setDateTime($fromDt->year, $fromDt->month, 1, 0, 0), 'U'), 'month2' => $this->getDate()->getLocalDate($toDt->addMonth(1)->setDateTime($toDt->year, $toDt->month, 1, 0, 0), 'U') ); $SQL = 'SELECT display.display, IFNULL(SUM(Size), 0) AS size '; if ($displayId != 0) $SQL .= ', bandwidthtype.name AS type '; $SQL .= ' FROM `bandwidth` LEFT OUTER JOIN `display` ON display.displayid = bandwidth.displayid AND display.displayId IN (' . implode(',', $displayIds) . ') '; if ($displayId != 0) $SQL .= ' INNER JOIN bandwidthtype ON bandwidthtype.bandwidthtypeid = bandwidth.type '; $SQL .= ' WHERE month > :month AND month < :month2 '; if ($displayId != 0) { $SQL .= ' AND display.displayid = :displayid '; $params['displayid'] = $displayId; } $SQL .= 'GROUP BY display.display '; if ($displayId != 0) $SQL .= ' , bandwidthtype.name '; $SQL .= 'ORDER BY display.display'; $sth = $dbh->prepare($SQL); $sth->execute($params); // Get the results $results = $sth->fetchAll(); $maxSize = 0; foreach ($results as $library) { $maxSize = ($library['size'] > $maxSize) ? $library['size'] : $maxSize; } // Decide what our units are going to be, based on the size $base = floor(log($maxSize) / log(1024)); $labels = []; $data = []; $backgroundColor = []; foreach ($results as $row) { // label depends whether we are filtered by display if ($displayId != 0) { $labels[] = $row['type']; } else { $labels[] = $row['display'] === null ? __('Deleted Displays') : $row['display']; } $backgroundColor[] = ($row['display'] === null) ? 'rgb(255,0,0)' : 'rgb(11, 98, 164)'; $data[] = round((double)$row['size'] / (pow(1024, $base)), 2); } // Set up some suffixes $suffixes = array('bytes', 'k', 'M', 'G', 'T'); $this->getState()->extra = [ 'labels' => $labels, 'data' => $data, 'backgroundColor' => $backgroundColor, 'postUnits' => (isset($suffixes[$base]) ? $suffixes[$base] : '') ]; } /** * Output CSV Form */ public function exportForm() { $this->getState()->template = 'statistics-form-export'; } /** * Total count of stats */ public function getExportStatsCount() { // We are expecting some parameters $fromDt = $this->getSanitizer()->getDate('fromDt'); $toDt = $this->getSanitizer()->getDate('toDt'); $displayId = $this->getSanitizer()->getInt('displayId'); if ($fromDt != null) { $fromDt->startOfDay(); } if ($toDt != null) { $toDt->addDay()->startOfDay(); } // What if the fromdt and todt are exactly the same? // in this case assume an entire day from midnight on the fromdt to midnight on the todt (i.e. add a day to the todt) if ($fromDt != null && $toDt != null && $fromDt == $toDt) { $toDt->addDay(); } // Do not filter by display if super admin and no display is selected // Super admin will be able to see stat records of deleted display, we will not filter by display later $displayIds = []; if (!$this->getUser()->isSuperAdmin()) { // Get an array of display id this user has access to. foreach ($this->displayFactory->query() as $display) { $displayIds[] = $display->displayId; } if (count($displayIds) <= 0) throw new InvalidArgumentException(__('No displays with View permissions'), 'displays'); // Set displayIds as [-1] if the user selected a display for which they don't have permission if ($displayId != 0) { if (!in_array($displayId, $displayIds)) { $displayIds = [-1]; } else { $displayIds = [$displayId]; } } } else { if ($displayId != 0) { $displayIds = [$displayId]; } } // Call the time series interface getStats $resultSet = $this->timeSeriesStore->getExportStatsCount( [ 'fromDt'=> $fromDt, 'toDt'=> $toDt, 'displayIds' => $displayIds ]); $response = [ 'total' => $resultSet ]; $this->getState()->template = 'statistics-form-export'; $this->getState()->recordsTotal = $resultSet; $this->getState()->setData($response); } /** * Outputs a CSV of stats */ public function export() { // We are expecting some parameters $fromDt = $this->getSanitizer()->getDate('fromDt'); $toDt = $this->getSanitizer()->getDate('toDt'); $displayId = $this->getSanitizer()->getInt('displayId'); // Do not filter by display if super admin and no display is selected // Super admin will be able to see stat records of deleted display, we will not filter by display later $displayIds = []; if (!$this->getUser()->isSuperAdmin()) { // Get an array of display id this user has access to. foreach ($this->displayFactory->query() as $display) { $displayIds[] = $display->displayId; } if (count($displayIds) <= 0) throw new InvalidArgumentException(__('No displays with View permissions'), 'displays'); // Set displayIds as [-1] if the user selected a display for which they don't have permission if ($displayId != 0) { if (!in_array($displayId, $displayIds)) { $displayIds = [-1]; } else { $displayIds = [$displayId]; } } } else { if ($displayId != 0) { $displayIds = [$displayId]; } } if ($fromDt == null || $toDt == null) { throw new InvalidArgumentException(__('Both fromDt/toDt should be provided'), 'fromDt/toDt'); } $fromDt->startOfDay(); $toDt->addDay()->startOfDay(); // What if the fromdt and todt are exactly the same? // in this case assume an entire day from midnight on the fromdt to midnight on the todt (i.e. add a day to the todt) if ($fromDt == $toDt) { $toDt->addDay(); } // Get result set $resultSet = $this->timeSeriesStore->getStats([ 'fromDt' => $fromDt, 'toDt' => $toDt, 'displayIds' => $displayIds, ]); $out = fopen('php://output', 'w'); fputcsv($out, ['Stat Date', 'Type', 'FromDT', 'ToDT', 'Layout', 'Display', 'Media', 'Tag', 'Duration', 'Count', 'Engagements']); while ($row = $resultSet->getNextRow()) { $displayName = isset($row['display']) ? $this->getSanitizer()->string($row['display']) : ''; $layoutName = isset($row['layout']) ? $this->getSanitizer()->string($row['layout']) : ''; // Read the columns $type = $this->getSanitizer()->string($row['type']); if ($this->timeSeriesStore->getEngine() == 'mongodb') { $statDate = isset($row['statDate']) ? $this->getDate()->parse($row['statDate']->toDateTime()->format('U'), 'U')->format('Y-m-d H:i:s') : null; $fromDt = $this->getDate()->parse($row['start']->toDateTime()->format('U'), 'U')->format('Y-m-d H:i:s'); $toDt = $this->getDate()->parse($row['end']->toDateTime()->format('U'), 'U')->format('Y-m-d H:i:s'); $engagements = isset($row['engagements']) ? json_encode($row['engagements']) : '[]'; } else { $statDate = isset($row['statDate']) ? $this->getDate()->parse($row['statDate'], 'U')->format('Y-m-d H:i:s') : null; $fromDt = $this->getDate()->parse($row['start'], 'U')->format('Y-m-d H:i:s'); $toDt = $this->getDate()->parse($row['end'], 'U')->format('Y-m-d H:i:s'); $engagements = isset($row['engagements']) ? $row['engagements'] : '[]'; } $layout = ($layoutName != '') ? $layoutName : __('Not Found'); $display = ($displayName != '') ? $displayName : __('Not Found'); $media = isset($row['media']) ? $this->getSanitizer()->string($row['media']) : ''; $tag = isset($row['tag']) ? $this->getSanitizer()->string($row['tag']) : ''; $duration = isset($row['duration']) ? $this->getSanitizer()->string($row['duration']) : ''; $count = isset($row['count']) ? $this->getSanitizer()->string($row['count']) : ''; fputcsv($out, [$statDate, $type, $fromDt, $toDt, $layout, $display, $media, $tag, $duration, $count, $engagements]); } fclose($out); // We want to output a load of stuff to the browser as a text file. $app = $this->getApp(); $app->response()->header('Content-Type', 'text/csv'); $app->response()->header('Content-Disposition', 'attachment; filename="stats.csv"'); $app->response()->header('Content-Transfer-Encoding', 'binary"'); $app->response()->header('Accept-Ranges', 'bytes'); $this->setNoOutput(true); } /** * Stats page */ function displayLibraryPage() { $this->getState()->template = 'stats-library-page'; $data = []; // Set up some suffixes $suffixes = array('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'); // Widget for the library usage pie chart try { if ($this->getUser()->libraryQuota != 0) { $libraryLimit = $this->getUser()->libraryQuota * 1024; } else { $libraryLimit = $this->getConfig()->getSetting('LIBRARY_SIZE_LIMIT_KB') * 1024; } // Library Size in Bytes $params = []; $sql = 'SELECT IFNULL(SUM(FileSize), 0) AS SumSize, type FROM `media` WHERE 1 = 1 '; $this->mediaFactory->viewPermissionSql('Xibo\Entity\Media', $sql, $params, '`media`.mediaId', '`media`.userId'); $sql .= ' GROUP BY type '; $sth = $this->store->getConnection()->prepare($sql); $sth->execute($params); $results = $sth->fetchAll(); // Do we base the units on the maximum size or the library limit $maxSize = 0; if ($libraryLimit > 0) { $maxSize = $libraryLimit; } else { // Find the maximum sized chunk of the items in the library foreach ($results as $library) { $maxSize = ($library['SumSize'] > $maxSize) ? $library['SumSize'] : $maxSize; } } // Decide what our units are going to be, based on the size $base = ($maxSize == 0) ? 0 : floor(log($maxSize) / log(1024)); $libraryUsage = []; $libraryLabels = []; $totalSize = 0; foreach ($results as $library) { $libraryUsage[] = round((double)$library['SumSize'] / (pow(1024, $base)), 2); $libraryLabels[] = ucfirst($library['type']) . ' ' . $suffixes[$base]; $totalSize = $totalSize + $library['SumSize']; } // Do we need to add the library remaining? if ($libraryLimit > 0) { $remaining = round(($libraryLimit - $totalSize) / (pow(1024, $base)), 2); $libraryUsage[] = $remaining; $libraryLabels[] = __('Free') . ' ' . $suffixes[$base]; } // What if we are empty? if (count($results) == 0 && $libraryLimit <= 0) { $libraryUsage[] = 0; $libraryLabels[] = __('Empty'); } $data['libraryLimitSet'] = ($libraryLimit > 0); $data['libraryLimit'] = (round((double)$libraryLimit / (pow(1024, $base)), 2)) . ' ' . $suffixes[$base]; $data['librarySize'] = ByteFormatter::format($totalSize, 1); $data['librarySuffix'] = $suffixes[$base]; $data['libraryWidgetLabels'] = json_encode($libraryLabels); $data['libraryWidgetData'] = json_encode($libraryUsage); } catch (\Exception $exception) { $this->getLog()->error('Error rendering the library stats page widget'); } $data['users'] = $this->userFactory->query(); $data['groups'] = $this->userGroupFactory->query(); $this->getState()->setData($data); } public function libraryUsageGrid() { $params = []; $select = ' SELECT `user`.userId, `user`.userName, IFNULL(SUM(`media`.FileSize), 0) AS bytesUsed, COUNT(`media`.mediaId) AS numFiles '; $body = ' FROM `user` LEFT OUTER JOIN `media` ON `media`.userID = `user`.UserID WHERE 1 = 1 '; // Restrict on the users we have permission to see // Normal users can only see themselves $permissions = ''; if ($this->getUser()->userTypeId == 3) { $permissions .= ' AND user.userId = :currentUserId '; $filterBy['currentUserId'] = $this->getUser()->userId; } // Group admins can only see users from their groups. else if ($this->getUser()->userTypeId == 2) { $permissions .= ' AND user.userId IN ( SELECT `otherUserLinks`.userId FROM `lkusergroup` INNER JOIN `group` ON `group`.groupId = `lkusergroup`.groupId AND `group`.isUserSpecific = 0 INNER JOIN `lkusergroup` `otherUserLinks` ON `otherUserLinks`.groupId = `group`.groupId WHERE `lkusergroup`.userId = :currentUserId ) '; $params['currentUserId'] = $this->getUser()->userId; } // Filter by userId if ($this->getSanitizer()->getInt('userId') !== null) { $body .= ' AND user.userId = :userId '; $params['userId'] = $this->getSanitizer()->getInt('userId'); } // Filter by groupId if ($this->getSanitizer()->getInt('groupId') !== null) { $body .= ' AND user.userId IN (SELECT userId FROM `lkusergroup` WHERE groupId = :groupId) '; $params['groupId'] = $this->getSanitizer()->getInt('groupId'); } $body .= $permissions; $body .= ' GROUP BY `user`.userId, `user`.userName '; // Sorting? $filterBy = $this->gridRenderFilter(); $sortOrder = $this->gridRenderSort(); $order = ''; if (is_array($sortOrder)) $order .= 'ORDER BY ' . implode(',', $sortOrder); $limit = ''; // Paging if ($filterBy !== null && $this->getSanitizer()->getInt('start', $filterBy) !== null && $this->getSanitizer()->getInt('length', $filterBy) !== null) { $limit = ' LIMIT ' . intval($this->getSanitizer()->getInt('start', $filterBy), 0) . ', ' . $this->getSanitizer()->getInt('length', 10, $filterBy); } $sql = $select . $body . $order . $limit; $rows = []; foreach ($this->store->select($sql, $params) as $row) { $entry = []; $entry['userId'] = $this->getSanitizer()->int($row['userId']); $entry['userName'] = $this->getSanitizer()->string($row['userName']); $entry['bytesUsed'] = $this->getSanitizer()->int($row['bytesUsed']); $entry['bytesUsedFormatted'] = ByteFormatter::format($this->getSanitizer()->int($row['bytesUsed']), 2); $entry['numFiles'] = $this->getSanitizer()->int($row['numFiles']); $rows[] = $entry; } // Paging if ($limit != '' && count($rows) > 0) { $results = $this->store->select('SELECT COUNT(*) AS total FROM `user` ' . $permissions, $params); $this->getState()->recordsTotal = intval($results[0]['total']); } $this->getState()->template = 'grid'; $this->getState()->setData($rows); } /** * @throws InvalidArgumentException */ public function timeDisconnectedData() { $fromDt = $this->getSanitizer()->getDate('fromDt', $this->getSanitizer()->getDate('availabilityFromDt')); $toDt = $this->getSanitizer()->getDate('toDt', $this->getSanitizer()->getDate('availabilityToDt')); $displayId = $this->getSanitizer()->getInt('displayId'); $displayGroupId = $this->getSanitizer()->getInt('displayGroupId'); $tags = $this->getSanitizer()->getString('tags'); $onlyLoggedIn = $this->getSanitizer()->getCheckbox('onlyLoggedIn') == 1; $currentDate = $this->getDate()->parse(); // fromDt is always start of selected day $fromDt = $this->getDate()->parse($fromDt)->startOfDay(); $toDt = $this->getDate()->parse($toDt); // If toDt is current date then make it current datetime // Else todat is next day if ($toDt->format('Y-m-d') == $currentDate->format('Y-m-d')) { $toDt = $this->getDate()->parse(); } else { $toDt = $toDt->addDay()->startOfDay(); } // Get an array of display id this user has access to. $displayIds = []; foreach ($this->displayFactory->query() as $display) { $displayIds[] = $display->displayId; } if (count($displayIds) <= 0) throw new InvalidArgumentException(__('No displays with View permissions'), 'displays'); // Get an array of display groups this user has access to $displayGroupIds = []; foreach ($this->displayGroupFactory->query(null, ['isDisplaySpecific' => -1]) as $displayGroup) { $displayGroupIds[] = $displayGroup->displayGroupId; } if (count($displayGroupIds) <= 0) throw new InvalidArgumentException(__('No display groups with View permissions'), 'displayGroup'); $params = array( 'start' => $fromDt->format('U'), 'end' => $toDt->format('U') ); $select = ' SELECT display.display, display.displayId, SUM(LEAST(IFNULL(`end`, :end), :end) - GREATEST(`start`, :start)) AS duration, :end - :start as filter '; if ($tags != '') { $select .= ', (SELECT GROUP_CONCAT(DISTINCT tag) FROM tag INNER JOIN lktagdisplaygroup ON lktagdisplaygroup.tagId = tag.tagId WHERE lktagdisplaygroup.displayGroupId = displaygroup.DisplayGroupID GROUP BY lktagdisplaygroup.displayGroupId) AS tags '; } $body = 'FROM `displayevent` INNER JOIN `display` ON display.displayId = `displayevent`.displayId '; if ($displayGroupId != 0) { $body .= 'INNER JOIN `lkdisplaydg` ON lkdisplaydg.DisplayID = display.displayid '; } if ($tags != '') { $body .= 'INNER JOIN `lkdisplaydg` ON lkdisplaydg.DisplayID = display.displayid INNER JOIN `displaygroup` ON displaygroup.displaygroupId = lkdisplaydg.displaygroupId AND `displaygroup`.isDisplaySpecific = 1 '; } $body .= 'WHERE `start` <= :end AND IFNULL(`end`, :end) >= :start AND :end <= UNIX_TIMESTAMP(NOW()) AND display.displayId IN (' . implode(',', $displayIds) . ') '; if ($displayGroupId != 0) { $body .= ' AND lkdisplaydg.displaygroupid = :displayGroupId '; $params['displayGroupId'] = $displayGroupId; } if ($tags != '') { if (trim($tags) === '--no-tag') { $body .= ' AND `displaygroup`.displaygroupId NOT IN ( SELECT `lktagdisplaygroup`.displaygroupId FROM tag INNER JOIN `lktagdisplaygroup` ON `lktagdisplaygroup`.tagId = tag.tagId ) '; } else { $operator = $this->getSanitizer()->getCheckbox('exactTags') == 1 ? '=' : 'LIKE'; $body .= " AND `displaygroup`.displaygroupId IN ( SELECT `lktagdisplaygroup`.displaygroupId FROM tag INNER JOIN `lktagdisplaygroup` ON `lktagdisplaygroup`.tagId = tag.tagId "; $i = 0; foreach (explode(',', $tags) as $tag) { $i++; if ($i == 1) $body .= ' WHERE `tag` ' . $operator . ' :tags' . $i; else $body .= ' OR `tag` ' . $operator . ' :tags' . $i; if ($operator === '=') $params['tags' . $i] = $tag; else $params['tags' . $i] = '%' . $tag . '%'; } $body .= " ) "; } } if ($displayId != 0) { $body .= ' AND display.displayId = :displayId '; $params['displayId'] = $displayId; } if ($onlyLoggedIn) { $body .= ' AND `display`.loggedIn = 1 '; } $body .= ' GROUP BY display.display '; // Sorting? $filterBy = $this->gridRenderFilter(); $sortOrder = $this->gridRenderSort(); $order = ''; if (is_array($sortOrder)) $order .= 'ORDER BY ' . implode(',', $sortOrder); $limit = ''; // Paging if ($filterBy !== null && $this->getSanitizer()->getInt('start', $filterBy) !== null && $this->getSanitizer()->getInt('length', $filterBy) !== null) { $limit = ' LIMIT ' . intval($this->getSanitizer()->getInt('start', $filterBy), 0) . ', ' . $this->getSanitizer()->getInt('length', 10, $filterBy); } $sql = $select . $body . $order . $limit; $maxDuration = 0; $rows = []; foreach ($this->store->select($sql, $params) as $row) { $maxDuration = $maxDuration + $this->getSanitizer()->double($row['duration']); } if ($maxDuration > 86400) { $postUnits = __('Days'); $divisor = 86400; } else if ($maxDuration > 3600) { $postUnits = __('Hours'); $divisor = 3600; } else { $postUnits = __('Minutes'); $divisor = 60; } foreach ($this->store->select($sql, $params) as $row) { $entry = []; $entry['displayId'] = $this->getSanitizer()->int(($row['displayId'])); $entry['display'] = $this->getSanitizer()->string(($row['display'])); $entry['timeDisconnected'] = round($this->getSanitizer()->double($row['duration']) / $divisor, 2); $entry['timeConnected'] = round($this->getSanitizer()->double($row['filter'] / $divisor) - $entry['timeDisconnected'], 2); $entry['postUnits'] = $postUnits; $rows[] = $entry; } // Paging if ($limit != '' && count($rows) > 0) { $results = $this->store->select($select . $body, $params); $this->getState()->recordsTotal = count($results); } $this->getState()->template = 'grid'; $this->getState()->setData($rows); } /** * @SWG\Definition( * definition="TimeDisconnectedData", * @SWG\Property( * property="display", * type="string" * ), * @SWG\Property( * property="displayId", * type="integer" * ), * @SWG\Property( * property="duration", * type="integer" * ), * @SWG\Property( * property="start", * type="string" * ), * @SWG\Property( * property="end", * type="string" * ), * @SWG\Property( * property="isFinished", * type="boolean" * ) * ) * * @SWG\Get( * path="/stats/timeDisconnected", * operationId="timeDisconnectedSearch", * tags={"statistics"}, * @SWG\Parameter( * name="fromDt", * in="query", * description="The start date for the filter.", * type="string", * required=true * ), * @SWG\Parameter( * name="toDt", * in="query", * description="The end date for the filter.", * type="string", * required=true * ), * @SWG\Parameter( * name="displayId", * in="query", * description="An optional display Id to filter", * type="integer", * required=false * ), * @SWG\Parameter( * name="displayIds", * description="An optional array of display Id to filter", * in="query", * required=false, * type="array", * @SWG\Items( * type="integer" * ) * ), * @SWG\Parameter( * name="returnDisplayLocalTime", * in="query", * description="true/1/On if the results should be in display local time, otherwise CMS time", * type="boolean", * required=false * ), * @SWG\Parameter( * name="returnDateFormat", * in="query", * description="A PHP formatted date format for how the dates in this call should be returned.", * type="string", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema( * type="array", * @SWG\Items( * ref="#/definitions/TimeDisconnectedData" * ) * ) * ) * ) * @throws \Xibo\Exception\InvalidArgumentException */ public function gridTimeDisconnected() { // CMS timezone $defaultTimezone = $this->getConfig()->getSetting('defaultTimezone'); $fromDt = $this->getSanitizer()->getDate('fromDt'); $toDt = $this->getSanitizer()->getDate('toDt'); $displayId = $this->getSanitizer()->getInt('displayId'); $displays = $this->getSanitizer()->getIntArray('displayIds'); $returnDisplayLocalTime = $this->getSanitizer()->getCheckbox('returnDisplayLocalTime'); $returnDateFormat = $this->getSanitizer()->getString('returnDateFormat', 'Y-m-d H:i:s'); // Merge displayId and displayIds if ($displayId != 0) { $displays = array_unique(array_merge($displays, [$displayId])); } $timeZoneCache = []; $displayIds = $this->authoriseDisplayIds($displays, $timeZoneCache); $params = []; $select = ' SELECT displayevent.eventDate, display.displayId, display.display, displayevent.start, displayevent.end '; $body = ' FROM displayevent INNER JOIN display ON displayevent.displayId = display.displayId WHERE 1 = 1 '; if (count($displays) > 0) { $body .= ' AND display.displayId IN (' . implode(',', $displayIds) . ') '; } if ($fromDt != null) { $body .= ' AND displayevent.start >= :start '; $params['start'] = $fromDt->format('U'); } if ($toDt != null) { $body .= ' AND displayevent.end < :end '; $params['end'] = $toDt->format('U'); } // Sorting? $filterBy = $this->gridRenderFilter(); $sortOrder = $this->gridRenderSort(); $order = ''; if (is_array($sortOrder)) $order .= 'ORDER BY ' . implode(',', $sortOrder); $limit = ''; // Paging if ($filterBy !== null && $this->getSanitizer()->getInt('start', $filterBy) !== null && $this->getSanitizer()->getInt('length', $filterBy) !== null) { $limit = ' LIMIT ' . intval($this->getSanitizer()->getInt('start', $filterBy), 0) . ', ' . $this->getSanitizer()->getInt('length', 10, $filterBy); } $sql = $select . $body . $order . $limit; // Run the main query $rows = []; foreach ($this->store->select($sql, $params) as $row) { $entry = []; $entry['displayId'] = $this->getSanitizer()->int($row['displayId']); $entry['display'] = $this->getSanitizer()->string($row['display']); $entry['isFinished'] = $row['end'] !== null; // Get the start/end date $start = $this->getDate()->parse($row['start'], 'U'); $end = $this->getDate()->parse($row['end'], 'U'); if ($returnDisplayLocalTime) { // Convert the dates to the display timezone. if (!array_key_exists($entry['displayId'], $timeZoneCache)) { try { $display = $this->displayFactory->getById($entry['displayId']); $timeZoneCache[$entry['displayId']] = (empty($display->timeZone)) ? $defaultTimezone : $display->timeZone; } catch (NotFoundException $e) { $timeZoneCache[$entry['displayId']] = $defaultTimezone; } } $start = $start->tz($timeZoneCache[$entry['displayId']]); $end = $end->tz($timeZoneCache[$entry['displayId']]); } $entry['start'] = $start->format($returnDateFormat); $entry['end'] = $end->format($returnDateFormat); $entry['duration'] = $end->diffInSeconds($start); $rows[] = $entry; } // Paging if ($limit != '' && count($rows) > 0) { $results = $this->store->select($select . $body, $params); $this->getState()->recordsTotal = count($results); } $this->getState()->template = 'grid'; $this->getState()->setData($rows); } /** * @param $displays * @param $timeZoneCache * @return array|int[] * @throws \Xibo\Exception\InvalidArgumentException */ private function authoriseDisplayIds($displays, &$timeZoneCache) { $displayIds = []; $displaysAccessible = []; if (!$this->getUser()->isSuperAdmin()) { // Get an array of display id this user has access to. foreach ($this->displayFactory->query() as $display) { $displaysAccessible[] = $display->displayId; // Cache the display timezone. $timeZoneCache[$display->displayId] = $display->timeZone; } if (count($displaysAccessible) <= 0) throw new InvalidArgumentException(__('No displays with View permissions'), 'displays'); // Set displayIds as [-1] if the user selected a display for which they don't have permission if (count($displays) <= 0) { $displayIds = $displaysAccessible; } else { foreach ($displays as $key => $id) { if (!in_array($id, $displaysAccessible)) { unset($displays[$key]); } else { $displayIds[] = $id; } } if (count($displays) <= 0 ) { $displayIds = [-1]; } } } else { $displayIds = $displays; } return $displayIds; } } PK lqY$ac c MediaManager.phpnu [ . */ namespace Xibo\Controller; use Xibo\Factory\LayoutFactory; use Xibo\Factory\ModuleFactory; use Xibo\Factory\PlaylistFactory; use Xibo\Factory\RegionFactory; use Xibo\Factory\WidgetFactory; use Xibo\Service\ConfigServiceInterface; use Xibo\Service\DateServiceInterface; use Xibo\Service\LogServiceInterface; use Xibo\Service\SanitizerServiceInterface; /** * Class MediaManager * @package Xibo\Controller */ class MediaManager extends Base { /** @var ModuleFactory */ private $moduleFactory; /** @var LayoutFactory */ private $layoutFactory; /** @var RegionFactory */ private $regionFactory; /** @var PlaylistFactory */ private $playlistFactory; /** @var WidgetFactory */ private $widgetFactory; /** * Set common dependencies. * @param LogServiceInterface $log * @param SanitizerServiceInterface $sanitizerService * @param \Xibo\Helper\ApplicationState $state * @param \Xibo\Entity\User $user * @param \Xibo\Service\HelpServiceInterface $help * @param DateServiceInterface $date * @param ConfigServiceInterface $config * @param ModuleFactory $moduleFactory * @param LayoutFactory $layoutFactory * @param RegionFactory $regionFactory * @param PlaylistFactory $playlistFactory * @param WidgetFactory $widgetFactory */ public function __construct($log, $sanitizerService, $state, $user, $help, $date, $config, $moduleFactory, $layoutFactory, $regionFactory, $playlistFactory, $widgetFactory) { $this->setCommonDependencies($log, $sanitizerService, $state, $user, $help, $date, $config); $this->moduleFactory = $moduleFactory; $this->layoutFactory = $layoutFactory; $this->regionFactory = $regionFactory; $this->playlistFactory = $playlistFactory; $this->widgetFactory = $widgetFactory; } public function displayPage() { $moduleFactory = $this->moduleFactory; $this->getState()->template .= 'media-manager-page'; $this->getState()->setData([ // Users we have permission to see 'modules' => $this->moduleFactory->query(null, ['assignable' => 1, 'enabled' => 1]), 'assignableModules' => array_map(function($element) use ($moduleFactory) { $module = $moduleFactory->createForInstall($element->class); $module->setModule($element); return $module; }, $moduleFactory->getAssignableModules()) ]); } public function grid() { $this->getState()->template = 'grid'; $rows = []; $widgets = $this->widgetFactory->query($this->gridRenderSort(), $this->gridRenderFilter([ 'layout' => $this->getSanitizer()->getString('layout'), 'region' => $this->getSanitizer()->getString('region'), 'media' => $this->getSanitizer()->getString('media'), 'type' => $this->getSanitizer()->getString('type'), 'playlist' => $this->getSanitizer()->getString('playlist'), 'showWidgetsFrom' => $this->getSanitizer()->getInt('showWidgetsFrom') ])); $widgetsCount = $this->widgetFactory->countLast(); foreach ($widgets as $widget) { // Load the widget $widget->load(); // Create a module $module = $this->moduleFactory->createWithWidget($widget); // Get a list of Layouts that this playlist uses $layouts = $this->layoutFactory->query(null, ['playlistId' => $widget->playlistId, 'showDrafts' => 1]); $layoutNames = array_map(function($layout) { return $layout->layout; }, $layouts); // Get a list of Regions that this playlists uses $regions = $this->regionFactory->getByPlaylistId($widget->playlistId); $regionNames = array_map(function($region) { return $region->name; }, $regions); // We are good to go $row = [ 'layout' => implode(',', $layoutNames), 'region' => implode(',', $regionNames), 'playlist' => $widget->playlist, 'widget' => $module->getName(), 'widgetId' => $widget->widgetId, 'type' => $module->getModuleName(), 'displayOrder' => $widget->displayOrder, 'thumbnail' => '', 'thumbnailUrl' => '' ]; $row['buttons'] = []; // Check editable if (!$this->getUser()->checkEditable($widget)) { $rows[] = $row; continue; } // for widgets on Playlist not inside of a region $regionWidth = null; $regionHeight = null; // Get region dimensions foreach ($regions as $region) { $regionWidth = $region->width; $regionHeight = $region->height; } $row['buttons'][] = [ 'id' => 'WidgetEditForm', 'class' => 'WidgetEditForm', 'dataAttributes' => [ ['name' => 'region-width', 'value' => $regionWidth], ['name' => 'region-height', 'value' => $regionHeight] ], 'url' => $this->urlFor('module.widget.edit.form', ['id' => $widget->widgetId]), 'text' => __('Edit') ]; // Thumbnail URL $row['thumbnail'] = ''; $row['thumbnailUrl'] = ''; if ($module->getModule()->regionSpecific == 0) { if ($widget->type == 'image') { $download = $this->urlFor('library.download', ['id' => $widget->getPrimaryMediaId()]) . '?preview=1'; $row['thumbnail'] = '
'; $row['thumbnailUrl'] = $download . '&width=100&height=56&cache=1'; } // Add a replace button directly on the drop down menu $row['buttons'][] = [ 'id' => 'MediaReplaceForm', 'url' => '#', 'text' => __('Replace'), 'dataAttributes' => [ ['name' => 'media-id', 'value' => $widget->getPrimaryMediaId()], ['name' => 'widget-id', 'value' => $widget->widgetId], ['name' => 'valid-extensions', 'value' => implode('|', $this->moduleFactory->getValidExtensions(['type' => $widget->type]))] ], 'class' => 'MediaManagerReplaceButton' ]; } $rows[] = $row; } $this->getState()->recordsTotal = $widgetsCount; $this->getState()->setData($rows); } } PK lqYUv;l ;l UserGroup.phpnu [ . */ namespace Xibo\Controller; use Xibo\Entity\Page; use Xibo\Entity\Permission; use Xibo\Entity\User; use Xibo\Exception\AccessDeniedException; use Xibo\Factory\PageFactory; use Xibo\Factory\PermissionFactory; use Xibo\Factory\UserFactory; use Xibo\Factory\UserGroupFactory; use Xibo\Helper\ByteFormatter; use Xibo\Service\ConfigServiceInterface; use Xibo\Service\DateServiceInterface; use Xibo\Service\LogServiceInterface; use Xibo\Service\SanitizerServiceInterface; /** * Class UserGroup * @package Xibo\Controller */ class UserGroup extends Base { /** * @var UserGroupFactory */ private $userGroupFactory; /** * @var PageFactory */ private $pageFactory; /** * @var PermissionFactory */ private $permissionFactory; /** * @var UserFactory */ private $userFactory; /** * Set common dependencies. * @param LogServiceInterface $log * @param SanitizerServiceInterface $sanitizerService * @param \Xibo\Helper\ApplicationState $state * @param \Xibo\Entity\User $user * @param \Xibo\Service\HelpServiceInterface $help * @param DateServiceInterface $date * @param ConfigServiceInterface $config * @param UserGroupFactory $userGroupFactory * @param PageFactory $pageFactory * @param PermissionFactory $permissionFactory * @param UserFactory $userFactory */ public function __construct($log, $sanitizerService, $state, $user, $help, $date, $config, $userGroupFactory, $pageFactory, $permissionFactory, $userFactory) { $this->setCommonDependencies($log, $sanitizerService, $state, $user, $help, $date, $config); $this->userGroupFactory = $userGroupFactory; $this->pageFactory = $pageFactory; $this->permissionFactory = $permissionFactory; $this->userFactory = $userFactory; } /** * Display page logic */ function displayPage() { $this->getState()->template = 'usergroup-page'; } /** * Group Grid * @SWG\Get( * path="/group", * operationId="userGroupSearch", * tags={"usergroup"}, * summary="UserGroup Search", * description="Search User Groups", * @SWG\Parameter( * name="userGroupId", * in="query", * description="Filter by UserGroup Id", * type="integer", * required=false * ), * @SWG\Parameter( * name="userGroup", * in="query", * description="Filter by UserGroup Name", * type="string", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema( * type="array", * @SWG\Items(ref="#/definitions/UserGroup") * ) * ) * ) */ function grid() { $filterBy = [ 'groupId' => $this->getSanitizer()->getInt('userGroupId'), 'group' => $this->getSanitizer()->getString('userGroup') ]; $groups = $this->userGroupFactory->query($this->gridRenderSort(), $this->gridRenderFilter($filterBy)); foreach ($groups as $group) { /* @var \Xibo\Entity\UserGroup $group */ $group->libraryQuotaFormatted = ByteFormatter::format($group->libraryQuota * 1024); if ($this->isApi()) continue; // we only want to show certain buttons, depending on the user logged in if ($this->isEditable($group)) { // Edit $group->buttons[] = array( 'id' => 'usergroup_button_edit', 'url' => $this->urlFor('group.edit.form', ['id' => $group->groupId]), 'text' => __('Edit') ); if ($this->getUser()->isSuperAdmin()) { // Delete $group->buttons[] = array( 'id' => 'usergroup_button_delete', 'url' => $this->urlFor('group.delete.form', ['id' => $group->groupId]), 'text' => __('Delete') ); $group->buttons[] = ['divider' => true]; // Copy $group->buttons[] = array( 'id' => 'usergroup_button_copy', 'url' => $this->urlFor('group.copy.form', ['id' => $group->groupId]), 'text' => __('Copy') ); $group->buttons[] = ['divider' => true]; } // Members $group->buttons[] = array( 'id' => 'usergroup_button_members', 'url' => $this->urlFor('group.members.form', ['id' => $group->groupId]), 'text' => __('Members') ); if ($this->getUser()->isSuperAdmin()) { // Page Security $group->buttons[] = array( 'id' => 'usergroup_button_page_security', 'url' => $this->urlFor('group.acl.form', ['id' => $group->groupId]), 'text' => __('Page Security') ); } } } $this->getState()->template = 'grid'; $this->getState()->recordsTotal = $this->userGroupFactory->countLast(); $this->getState()->setData($groups); } /** * Form to Add a Group */ function addForm() { $this->getState()->template = 'usergroup-form-add'; $this->getState()->setData([ 'help' => [ 'add' => $this->getHelp()->link('UserGroup', 'Add') ] ]); } /** * Form to Edit a Group * @param int $groupId * @throws \Xibo\Exception\NotFoundException */ function editForm($groupId) { $group = $this->userGroupFactory->getById($groupId); if (!$this->isEditable($group)) throw new AccessDeniedException(); $this->getState()->template = 'usergroup-form-edit'; $this->getState()->setData([ 'group' => $group, 'help' => [ 'add' => $this->getHelp()->link('UserGroup', 'Edit') ] ]); } /** * Shows the Delete Group Form * @param int $groupId * @throws \Xibo\Exception\NotFoundException */ function deleteForm($groupId) { $group = $this->userGroupFactory->getById($groupId); if (!$this->isEditable($group)) throw new AccessDeniedException(); $this->getState()->template = 'usergroup-form-delete'; $this->getState()->setData([ 'group' => $group, 'help' => [ 'delete' => $this->getHelp()->link('UserGroup', 'Delete') ] ]); } /** * Add User Group * @SWG\Post( * path="/group", * operationId="userGroupAdd", * tags={"usergroup"}, * summary="UserGroup Add", * description="Add User Group", * @SWG\Parameter( * name="group", * in="formData", * description="Name of the User Group", * type="string", * required=true * ), * @SWG\Parameter( * name="libraryQuota", * in="formData", * description="The quota that should be applied (KiB). Provide 0 for no quota", * type="string", * required=false * ), * @SWG\Parameter( * name="isSystemNotification", * in="formData", * description="Flag (0, 1), should members of this Group receive system notifications?", * type="integer", * required=false * ), * @SWG\Parameter( * name="isDisplayNotification", * in="formData", * description="Flag (0, 1), should members of this Group receive Display notifications for Displays they have permissions to see", * type="integer", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema( * type="array", * @SWG\Items(ref="#/definitions/UserGroup") * ) * ) * ) */ function add() { // Check permissions if (!$this->getUser()->isSuperAdmin()) throw new AccessDeniedException(); // Build a user entity and save it $group = $this->userGroupFactory->createEmpty(); $group->group = $this->getSanitizer()->getString('group'); $group->libraryQuota = $this->getSanitizer()->getInt('libraryQuota'); if ($this->getUser()->userTypeId == 1) { $group->isSystemNotification = $this->getSanitizer()->getCheckbox('isSystemNotification'); $group->isDisplayNotification = $this->getSanitizer()->getCheckbox('isDisplayNotification'); } // Save $group->save(); // Return $this->getState()->hydrate([ 'message' => sprintf(__('Added %s'), $group->group), 'id' => $group->groupId, 'data' => $group ]); } /** * Edit User Group * @SWG\Put( * path="/group/{userGroupId}", * operationId="userGroupEdit", * tags={"usergroup"}, * summary="UserGroup Edit", * description="Edit User Group", * @SWG\Parameter( * name="userGroupId", * in="path", * description="ID of the User Group", * type="integer", * required=true * ), * @SWG\Parameter( * name="group", * in="formData", * description="Name of the User Group", * type="string", * required=true * ), * @SWG\Parameter( * name="libraryQuota", * in="formData", * description="The quota that should be applied (KiB). Provide 0 for no quota", * type="string", * required=false * ), * @SWG\Parameter( * name="isSystemNotification", * in="formData", * description="Flag (0, 1), should members of this Group receive system notifications?", * type="integer", * required=false * ), * @SWG\Parameter( * name="isDisplayNotification", * in="formData", * description="Flag (0, 1), should members of this Group receive Display notifications for Displays they have permissions to see", * type="integer", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema( * type="array", * @SWG\Items(ref="#/definitions/UserGroup") * ) * ) * ) * @param $groupId * @throws \Xibo\Exception\NotFoundException */ function edit($groupId) { // Check permissions if (!$this->getUser()->isSuperAdmin() && !$this->getUser()->isGroupAdmin()) throw new AccessDeniedException(); $group = $this->userGroupFactory->getById($groupId); if (!$this->isEditable($group)) throw new AccessDeniedException(); $group->load(); $group->group = $this->getSanitizer()->getString('group'); $group->libraryQuota = $this->getSanitizer()->getInt('libraryQuota'); if ($this->getUser()->userTypeId == 1) { $group->isSystemNotification = $this->getSanitizer()->getCheckbox('isSystemNotification'); $group->isDisplayNotification = $this->getSanitizer()->getCheckbox('isDisplayNotification'); } // Save $group->save(); // Return $this->getState()->hydrate([ 'message' => sprintf(__('Edited %s'), $group->group), 'id' => $group->groupId, 'data' => $group ]); } /** * Delete User Group * @param $groupId * @throws \Xibo\Exception\NotFoundException * @SWG\Delete( * path="/group/{userGroupId}", * operationId="userGroupDelete", * tags={"usergroup"}, * summary="Delete User Group", * description="Delete User Group", * @SWG\Parameter( * name="userGroupId", * in="path", * description="The user Group ID to Delete", * type="integer", * required=true * ), * @SWG\Response( * response=204, * description="successful operation" * ) * ) */ function delete($groupId) { // Check permissions if (!$this->getUser()->isSuperAdmin()) throw new AccessDeniedException(); $group = $this->userGroupFactory->getById($groupId); if (!$this->isEditable($group)) throw new AccessDeniedException(); $group->delete(); // Return $this->getState()->hydrate([ 'message' => sprintf(__('Deleted %s'), $group->group), 'id' => $group->groupId ]); } /** * ACL Form for the provided GroupId * @param int $groupId * @throws \Xibo\Exception\NotFoundException */ public function aclForm($groupId) { // Check permissions to this function if (!$this->getUser()->isSuperAdmin()) throw new AccessDeniedException(); // Use the factory to get all the entities $entities = $this->pageFactory->query(); // Load the Group we are working on // Get the object if ($groupId == 0) throw new \InvalidArgumentException(__('ACL form requested without a User Group')); $group = $this->userGroupFactory->getById($groupId); // Get all permissions for this user and this object $permissions = $this->permissionFactory->getByGroupId('Page', $groupId); $checkboxes = array(); foreach ($entities as $entity) { /* @var Page $entity */ // Check to see if this entity is set or not $entityId = $entity->getId(); $viewChecked = 0; foreach ($permissions as $permission) { /* @var Permission $permission */ if ($permission->objectId == $entityId && $permission->view == 1) { $viewChecked = 1; break; } } // Store this checkbox $checkbox = array( 'id' => $entityId, 'name' => $entity->title, 'value_view' => $entityId . '_view', 'value_view_checked' => (($viewChecked == 1) ? 'checked' : '') ); $checkboxes[] = $checkbox; } $data = [ 'title' => sprintf(__('ACL for %s'), $group->group), 'groupId' => $groupId, 'group' => $group->group, 'permissions' => $checkboxes, 'help' => $this->getHelp()->link('User', 'Acl') ]; $this->getState()->template = 'usergroup-form-acl'; $this->getState()->setData($data); } /** * ACL update * @param int $groupId * @throws \Xibo\Exception\NotFoundException */ public function acl($groupId) { // Check permissions to this function if (!$this->getUser()->isSuperAdmin()) throw new AccessDeniedException(); // Load the Group we are working on // Get the object if ($groupId == 0) throw new \InvalidArgumentException(__('ACL form requested without a User Group')); $group = $this->userGroupFactory->getById($groupId); // Use the factory to get all the entities $entities = $this->pageFactory->query(); // Get all permissions for this user and this object $permissions = $this->permissionFactory->getByGroupId('Page', $groupId); $objectIds = $this->getSanitizer()->getParam('objectId', null); if (!is_array($objectIds)) $objectIds = []; $newAcl = array(); array_map(function ($string) use (&$newAcl) { $array = explode('_', $string); return $newAcl[$array[0]][$array[1]] = 1; }, $objectIds); $this->getLog()->debug(var_export($newAcl, true)); foreach ($entities as $page) { /* @var Page $page */ // Check to see if this entity is set or not $objectId = $page->getId(); $permission = null; $view = (array_key_exists($objectId, $newAcl)); // Is the permission currently assigned? foreach ($permissions as $row) { /* @var \Xibo\Entity\Permission $row */ if ($row->objectId == $objectId) { $permission = $row; break; } } if ($permission == null) { if ($view) { // Not currently assigned and needs to be $permission = $this->permissionFactory->create($groupId, get_class($page), $objectId, 1, 0, 0); $permission->save(); } } else { $this->getLog()->debug('Permission Exists for %s, and has been set to %d.', $page->getName(), $view); // Currently assigned if ($view) { $permission->view = 1; $permission->save(); } else { $permission->delete(); } } } // Return $this->getState()->hydrate([ 'message' => sprintf(__('ACL set for %s'), $group->group), 'id' => $group->groupId ]); } /** * Shows the Members of a Group * @param int $groupId * @throws \Xibo\Exception\NotFoundException */ public function membersForm($groupId) { $group = $this->userGroupFactory->getById($groupId); if (!$this->isEditable($group)) throw new AccessDeniedException(); // Users in group $usersAssigned = $this->userFactory->query(null, array('groupIds' => array($groupId))); // Users not in group $allUsers = $this->userFactory->query(); // The available users are all users except users already in assigned users $checkboxes = array(); foreach ($allUsers as $user) { /* @var User $user */ // Check to see if it exists in $usersAssigned $exists = false; foreach ($usersAssigned as $userAssigned) { /* @var User $userAssigned */ if ($userAssigned->userId == $user->userId) { $exists = true; break; } } // Store this checkbox $checkbox = array( 'id' => $user->userId, 'name' => $user->userName, 'value_checked' => (($exists) ? 'checked' : '') ); $checkboxes[] = $checkbox; } $this->getState()->template = 'usergroup-form-members'; $this->getState()->setData([ 'group' => $group, 'checkboxes' => $checkboxes, 'help' => $this->getHelp()->link('UserGroup', 'Members') ]); } /** * Assign User to the User Group * @SWG\Post( * path="/group/members/assign/{userGroupId}", * operationId="userGroupAssign", * tags={"usergroup"}, * summary="Assign User to User Group", * description="Assign User to User Group", * @SWG\Parameter( * name="userGroupId", * in="path", * description="ID of the user group to which assign the user", * type="integer", * required=true * ), * @SWG\Parameter( * name="userId", * in="formData", * description="Array of userIDs to assign", * type="array", * required=true, * @SWG\Items(type="integer") * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema( * type="array", * @SWG\Items(ref="#/definitions/UserGroup") * ) * ) * ) * @param $groupId * @throws \Xibo\Exception\NotFoundException */ public function assignUser($groupId) { $this->getLog()->debug('Assign User for groupId %d', $groupId); $group = $this->userGroupFactory->getById($groupId); $group->load(); if (!$this->isEditable($group)) throw new AccessDeniedException(); $users = $this->getSanitizer()->getIntArray('userId'); foreach ($users as $userId) { $this->getLog()->debug('Assign User %d for groupId %d', $userId, $groupId); $user = $this->userFactory->getById($userId); if (!$this->getUser()->checkViewable($user)) throw new AccessDeniedException(__('Access Denied to User')); $group->assignUser($user); $group->save(['validate' => false]); } // Check to see if unassign has been provided. $users = $this->getSanitizer()->getIntArray('unassignUserId'); foreach ($users as $userId) { $this->getLog()->debug('Unassign User %d for groupId %d', $userId, $groupId); $user = $this->userFactory->getById($userId); if (!$this->getUser()->checkViewable($user)) throw new AccessDeniedException(__('Access Denied to User')); $group->unassignUser($user); $group->save(['validate' => false]); } // Return $this->getState()->hydrate([ 'message' => sprintf(__('Membership set for %s'), $group->group), 'id' => $group->groupId ]); } /** * Unassign User to the User Group * @SWG\Post( * path="/group/members/unassign/{userGroupId}", * operationId="userGroupUnassign", * tags={"usergroup"}, * summary="Unassign User from User Group", * description="Unassign User from User Group", * @SWG\Parameter( * name="userGroupId", * in="path", * description="ID of the user group from which to unassign the user", * type="integer", * required=true * ), * @SWG\Parameter( * name="userId", * in="formData", * description="Array of userIDs to unassign", * type="array", * required=true, * @SWG\Items(type="integer") * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema( * type="array", * @SWG\Items(ref="#/definitions/UserGroup") * ) * ) * ) * @param $groupId * @throws \Xibo\Exception\NotFoundException */ public function unassignUser($groupId) { $group = $this->userGroupFactory->getById($groupId); if (!$this->isEditable($group)) throw new AccessDeniedException(); $users = $this->getSanitizer()->getIntArray('userId'); foreach ($users as $userId) { $group->unassignUser($this->userFactory->getById($userId)); } $group->save(['validate' => false]); // Return $this->getState()->hydrate([ 'message' => sprintf(__('Membership set for %s'), $group->group), 'id' => $group->groupId ]); } /** * Form to Copy Group * @param int $groupId * @throws \Xibo\Exception\NotFoundException */ function copyForm($groupId) { $group = $this->userGroupFactory->getById($groupId); if (!$this->isEditable($group)) throw new AccessDeniedException(); $this->getState()->template = 'usergroup-form-copy'; $this->getState()->setData([ 'group' => $group ]); } /** * @SWG\Post( * path="/group/{userGroupId}/copy", * operationId="userGroupCopy", * tags={"usergroup"}, * summary="Copy User Group", * description="Copy an user group, optionally copying the group members", * @SWG\Parameter( * name="userGroupId", * in="path", * description="The User Group ID to Copy", * type="integer", * required=true * ), * @SWG\Parameter( * name="group", * in="formData", * description="The Group Name", * type="string", * required=true * ), * @SWG\Parameter( * name="copyMembers", * in="formData", * description="Flag indicating whether to copy group members", * type="integer", * required=false * ), * @SWG\Response( * response=201, * description="successful operation", * @SWG\Schema(ref="#/definitions/UserGroup"), * @SWG\Header( * header="Location", * description="Location of the new record", * type="string" * ) * ) * ) * * @param int $userGroupId * @throws \Xibo\Exception\NotFoundException */ public function copy($userGroupId) { $group = $this->userGroupFactory->getById($userGroupId); // Check we have permission to view this group if (!$this->isEditable($group)) throw new AccessDeniedException(); // Clone the group $group->load([ 'loadUsers' => ($this->getSanitizer()->getCheckbox('copyMembers') == 1) ]); $newGroup = clone $group; $newGroup->group = $this->getSanitizer()->getString('group'); $newGroup->save(); // Copy permissions foreach ($this->permissionFactory->getByGroupId('Page', $group->groupId) as $permission) { /* @var Permission $permission */ $permission = clone $permission; $permission->groupId = $newGroup->groupId; $permission->save(); } $this->getState()->hydrate([ 'httpStatus' => 201, 'message' => sprintf(__('Copied %s'), $group->group), 'id' => $newGroup->groupId, 'data' => $newGroup ]); } /** * @param \Xibo\Entity\UserGroup $group * @return bool */ private function isEditable($group) { return $this->getUser()->isSuperAdmin() || ($this->getUser()->isGroupAdmin() && count(array_intersect($this->getUser()->groups, [$group]))); } } PK lqY* * Resolution.phpnu [ . */ namespace Xibo\Controller; use baseDAO; use Kit; use Xibo\Exception\AccessDeniedException; use Xibo\Factory\ResolutionFactory; use Xibo\Helper\Form; use Xibo\Service\ConfigServiceInterface; use Xibo\Service\DateServiceInterface; use Xibo\Service\LogServiceInterface; use Xibo\Service\SanitizerServiceInterface; /** * Class Resolution * @package Xibo\Controller */ class Resolution extends Base { /** * @var ResolutionFactory */ private $resolutionFactory; /** * Set common dependencies. * @param LogServiceInterface $log * @param SanitizerServiceInterface $sanitizerService * @param \Xibo\Helper\ApplicationState $state * @param \Xibo\Entity\User $user * @param \Xibo\Service\HelpServiceInterface $help * @param DateServiceInterface $date * @param ConfigServiceInterface $config * @param ResolutionFactory $resolutionFactory */ public function __construct($log, $sanitizerService, $state, $user, $help, $date, $config, $resolutionFactory) { $this->setCommonDependencies($log, $sanitizerService, $state, $user, $help, $date, $config); $this->resolutionFactory = $resolutionFactory; } /** * Display the Resolution Page */ function displayPage() { $this->getState()->template = 'resolution-page'; } /** * Resolution Grid * * @SWG\Get( * path="/resolution", * operationId="resolutionSearch", * tags={"resolution"}, * summary="Resolution Search", * description="Search Resolutions this user has access to", * @SWG\Parameter( * name="resolutionId", * in="query", * description="Filter by Resolution Id", * type="integer", * required=false * ), * @SWG\Parameter( * name="resolution", * in="query", * description="Filter by Resolution Name", * type="string", * required=false * ), * @SWG\Parameter( * name="enabled", * in="query", * description="Filter by Enabled", * type="integer", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema( * type="array", * @SWG\Items(ref="#/definitions/Resolution") * ) * ) * ) */ function grid() { // Show enabled $filter = [ 'enabled' => $this->getSanitizer()->getInt('enabled', -1), 'resolutionId' => $this->getSanitizer()->getInt('resolutionId'), 'resolution' => $this->getSanitizer()->getString('resolution') ]; $resolutions = $this->resolutionFactory->query($this->gridRenderSort(), $this->gridRenderFilter($filter)); foreach ($resolutions as $resolution) { /* @var \Xibo\Entity\Resolution $resolution */ if ($this->isApi()) break; $resolution->includeProperty('buttons'); if ($this->getUser()->checkEditable($resolution)) { // Edit Button $resolution->buttons[] = array( 'id' => 'resolution_button_edit', 'url' => $this->urlFor('resolution.edit.form', ['id' => $resolution->resolutionId]), 'text' => __('Edit') ); } if ($this->getUser()->checkDeleteable($resolution)) { // Delete Button $resolution->buttons[] = array( 'id' => 'resolution_button_delete', 'url' => $this->urlFor('resolution.delete.form', ['id' => $resolution->resolutionId]), 'text' => __('Delete') ); } } $this->getState()->template = 'grid'; $this->getState()->setData($resolutions); $this->getState()->recordsTotal = $this->resolutionFactory->countLast(); } /** * Resolution Add */ function addForm() { $this->getState()->template = 'resolution-form-add'; $this->getState()->setData([ 'help' => $this->getHelp()->link('Resolution', 'Add') ]); } /** * Resolution Edit Form * @param int $resolutionId */ function editForm($resolutionId) { $resolution = $this->resolutionFactory->getById($resolutionId); if (!$this->getUser()->checkEditable($resolution)) throw new AccessDeniedException(); $this->getState()->template = 'resolution-form-edit'; $this->getState()->setData([ 'resolution' => $resolution, 'help' => $this->getHelp()->link('Resolution', 'Edit') ]); } /** * Resolution Delete Form * @param int $resolutionId */ function deleteForm($resolutionId) { $resolution = $this->resolutionFactory->getById($resolutionId); if (!$this->getUser()->checkEditable($resolution)) throw new AccessDeniedException(); $this->getState()->template = 'resolution-form-delete'; $this->getState()->setData([ 'resolution' => $resolution, 'help' => $this->getHelp()->link('Resolution', 'Delete') ]); } /** * Add Resolution * * @SWG\Post( * path="/resolution", * operationId="resolutionAdd", * tags={"resolution"}, * summary="Add Resolution", * description="Add new Resolution", * @SWG\Parameter( * name="resolution", * in="formData", * description="A name for the Resolution", * type="string", * required=true * ), * @SWG\Parameter( * name="width", * in="formData", * description="The Display Width of the Resolution", * type="integer", * required=true * ), * @SWG\Parameter( * name="height", * in="formData", * description="The Display Height of the Resolution", * type="integer", * required=true * ), * @SWG\Response( * response=201, * description="successful operation", * @SWG\Schema(ref="#/definitions/Resolution"), * @SWG\Header( * header="Location", * description="Location of the new record", * type="string" * ) * ) * ) */ function add() { /* @var \Xibo\Entity\Resolution $resolution */ $resolution = $this->resolutionFactory->create($this->getSanitizer()->getString('resolution'), $this->getSanitizer()->getInt('width'), $this->getSanitizer()->getInt('height')); $resolution->userId = $this->getUser()->userId; $resolution->save(); // Return $this->getState()->hydrate([ 'httpStatus' => 201, 'message' => sprintf(__('Added %s'), $resolution->resolution), 'id' => $resolution->resolutionId, 'data' => $resolution ]); } /** * Edit Resolution * @param int $resolutionId * * @SWG\Put( * path="/resolution/{resolutionId}", * operationId="resolutionEdit", * tags={"resolution"}, * summary="Edit Resolution", * description="Edit new Resolution", * @SWG\Parameter( * name="resolutionId", * in="path", * description="The Resolution ID to Edit", * type="integer", * required=true * ), * @SWG\Parameter( * name="resolution", * in="formData", * description="A name for the Resolution", * type="string", * required=true * ), * @SWG\Parameter( * name="width", * in="formData", * description="The Display Width of the Resolution", * type="integer", * required=true * ), * @SWG\Parameter( * name="height", * in="formData", * description="The Display Height of the Resolution", * type="integer", * required=true * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema(ref="#/definitions/Resolution") * ) * ) */ function edit($resolutionId) { $resolution = $this->resolutionFactory->getById($resolutionId); if (!$this->getUser()->checkEditable($resolution)) throw new AccessDeniedException(); $resolution->resolution = $this->getSanitizer()->getString('resolution'); $resolution->width = $this->getSanitizer()->getInt('width'); $resolution->height = $this->getSanitizer()->getInt('height'); $resolution->enabled = $this->getSanitizer()->getCheckbox('enabled'); $resolution->save(); // Return $this->getState()->hydrate([ 'message' => sprintf(__('Edited %s'), $resolution->resolution), 'id' => $resolution->resolutionId, 'data' => $resolution ]); } /** * Delete Resolution * @param int $resolutionId * * @SWG\Delete( * path="/resolution/{resolutionId}", * operationId="resolutionDelete", * tags={"resolution"}, * summary="Delete Resolution", * description="Delete Resolution", * @SWG\Parameter( * name="resolutionId", * in="path", * description="The Resolution ID to Delete", * type="integer", * required=true * ), * @SWG\Response( * response=204, * description="successful operation" * ) * ) */ function delete($resolutionId) { $resolution = $this->resolutionFactory->getById($resolutionId); if (!$this->getUser()->checkDeleteable($resolution)) throw new AccessDeniedException(); $resolution->delete(); // Return $this->getState()->hydrate([ 'message' => sprintf(__('Deleted %s'), $resolution->resolution), ]); } } PK lqY0LE E DataSetColumn.phpnu [ setCommonDependencies($log, $sanitizerService, $state, $user, $help, $date, $config); $this->dataSetFactory = $dataSetFactory; $this->dataSetColumnFactory = $dataSetColumnFactory; $this->dataSetColumnTypeFactory = $dataSetColumnTypeFactory; $this->dataTypeFactory = $dataTypeFactory; $this->pool = $pool; } /** * Column Page * @param $dataSetId */ public function displayPage($dataSetId) { $dataSet = $this->dataSetFactory->getById($dataSetId); if (!$this->getUser()->checkEditable($dataSet)) throw new AccessDeniedException(); $this->getState()->template = 'dataset-column-page'; $this->getState()->setData([ 'dataSet' => $dataSet ]); } /** * Column Search * @param $dataSetId * * @SWG\Get( * path="/dataset/{dataSetId}/column", * operationId="dataSetColumnSearch", * tags={"dataset"}, * summary="Search Columns", * description="Search Columns for DataSet", * @SWG\Parameter( * name="dataSetId", * in="path", * description="The DataSet ID", * type="integer", * required=true * ), * @SWG\Parameter( * name="dataSetColumnId", * in="query", * description="Filter by DataSet ColumnID", * type="integer", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema( * type="array", * @SWG\Items(ref="#/definitions/DataSetColumn") * ) * ) * ) * @throws \Xibo\Exception\NotFoundException */ public function grid($dataSetId) { $dataSet = $this->dataSetFactory->getById($dataSetId); if (!$this->getUser()->checkEditable($dataSet)) throw new AccessDeniedException(); $dataSetColumns = $this->dataSetColumnFactory->query($this->gridRenderSort(), [ 'dataSetId' => $dataSetId, 'dataSetColumnId' => $this->getSanitizer()->getInt('dataSetColumnId') ]); foreach ($dataSetColumns as $column) { /* @var \Xibo\Entity\DataSetColumn $column */ $column->dataType = __($column->dataType); $column->dataSetColumnType = __($column->dataSetColumnType); if ($this->isApi()) break; $column->includeProperty('buttons'); // Edit $column->buttons[] = array( 'id' => 'dataset_button_edit', 'url' => $this->urlFor('dataSet.column.edit.form', ['id' => $dataSetId, 'colId' => $column->dataSetColumnId]), 'text' => __('Edit') ); if ($this->getUser()->checkDeleteable($dataSet)) { // Delete $column->buttons[] = array( 'id' => 'dataset_button_delete', 'url' => $this->urlFor('dataSet.column.delete.form', ['id' => $dataSetId, 'colId' => $column->dataSetColumnId]), 'text' => __('Delete') ); } } $this->getState()->template = 'grid'; $this->getState()->setData($dataSetColumns); } /** * Add form * @param int $dataSetId */ public function addForm($dataSetId) { $dataSet = $this->dataSetFactory->getById($dataSetId); if (!$this->getUser()->checkEditable($dataSet)) throw new AccessDeniedException(); $this->getState()->template = 'dataset-column-form-add'; $this->getState()->setData([ 'dataSet' => $dataSet, 'dataTypes' => $this->dataTypeFactory->query(), 'dataSetColumnTypes' => $this->dataSetColumnTypeFactory->query(), 'help' => $this->getHelp()->link('DataSet', 'AddColumn') ]); } /** * Add * @param $dataSetId * * @SWG\Post( * path="/dataset/{dataSetId}/column", * operationId="dataSetColumnAdd", * tags={"dataset"}, * summary="Add Column", * description="Add a Column to a DataSet", * @SWG\Parameter( * name="dataSetId", * in="path", * description="The DataSet ID", * type="integer", * required=true * ), * @SWG\Parameter( * name="heading", * in="formData", * description="The heading for the Column", * type="string", * required=true * ), * @SWG\Parameter( * name="listContent", * in="formData", * description="A comma separated list of content for drop downs", * type="string", * required=false * ), * @SWG\Parameter( * name="columnOrder", * in="formData", * description="The display order for this column", * type="integer", * required=true * ), * @SWG\Parameter( * name="dataTypeId", * in="formData", * description="The data type ID for this column", * type="integer", * required=true * ), * @SWG\Parameter( * name="dataSetColumnTypeId", * in="formData", * description="The column type for this column", * type="integer", * required=true * ), * @SWG\Parameter( * name="formula", * in="formData", * description="MySQL SELECT syntax formula for this Column if the column type is formula", * type="string", * required=false * ), * @SWG\Parameter( * name="remoteField", * in="formData", * description="JSON-String to select Data from the Remote DataSet", * type="string", * required=false * ), * @SWG\Parameter( * name="showFilter", * in="formData", * description="Flag indicating whether this column should present a filter on DataEntry", * type="integer", * required=true * ), * @SWG\Parameter( * name="showSort", * in="formData", * description="Flag indicating whether this column should allow sorting on DataEntry", * type="integer", * required=true * ), * @SWG\Response( * response=201, * description="successful operation", * @SWG\Schema(ref="#/definitions/DataSetColumn"), * @SWG\Header( * header="Location", * description="Location of the new record", * type="string" * ) * ) * ) * * @throws XiboException */ public function add($dataSetId) { $dataSet = $this->dataSetFactory->getById($dataSetId); if (!$this->getUser()->checkEditable($dataSet)) throw new AccessDeniedException(); // Create a Column $column = $this->dataSetColumnFactory->createEmpty(); $column->heading = $this->getSanitizer()->getString('heading'); $column->listContent = $this->getSanitizer()->getString('listContent'); $column->columnOrder = $this->getSanitizer()->getInt('columnOrder'); $column->dataTypeId = $this->getSanitizer()->getInt('dataTypeId'); $column->dataSetColumnTypeId = $this->getSanitizer()->getInt('dataSetColumnTypeId'); $column->formula = $this->getSanitizer()->getParam('formula', null); $column->remoteField = $this->getSanitizer()->getParam('remoteField', null); $column->showFilter = $this->getSanitizer()->getCheckbox('showFilter'); $column->showSort = $this->getSanitizer()->getCheckbox('showSort'); if ($column->dataSetColumnTypeId == 3){ $this->pool->deleteItem('/dataset/cache/' . $dataSet->dataSetId); $this->getLog()->debug('New remote column detected, clear cache for remote dataSet ID ' . $dataSet->dataSetId); } // Assign the column to set the column order if necessary $dataSet->assignColumn($column); // Save the column $column->save(); // Notify the change $dataSet->notify(); // Return $this->getState()->hydrate([ 'httpStatus' => 201, 'message' => sprintf(__('Added %s'), $column->heading), 'id' => $column->dataSetColumnId, 'data' => $column ]); } /** * Edit Form * @param $dataSetId * @param $dataSetColumnId */ public function editForm($dataSetId, $dataSetColumnId) { $dataSet = $this->dataSetFactory->getById($dataSetId); if (!$this->getUser()->checkEditable($dataSet)) throw new AccessDeniedException(); $this->getState()->template = 'dataset-column-form-edit'; $this->getState()->setData([ 'dataSet' => $dataSet, 'dataSetColumn' => $this->dataSetColumnFactory->getById($dataSetColumnId), 'dataTypes' => $this->dataTypeFactory->query(), 'dataSetColumnTypes' => $this->dataSetColumnTypeFactory->query(), 'help' => $this->getHelp()->link('DataSet', 'EditColumn') ]); } /** * Edit * @param $dataSetId * @param $dataSetColumnId * * @SWG\Put( * path="/dataset/{dataSetId}/column/{dataSetColumnId}", * operationId="dataSetColumnEdit", * tags={"dataset"}, * summary="Edit Column", * description="Edit a Column to a DataSet", * @SWG\Parameter( * name="dataSetId", * in="path", * description="The DataSet ID", * type="integer", * required=true * ), * @SWG\Parameter( * name="dataSetColumnId", * in="path", * description="The Column ID", * type="integer", * required=true * ), * @SWG\Parameter( * name="heading", * in="formData", * description="The heading for the Column", * type="string", * required=true * ), * @SWG\Parameter( * name="listContent", * in="formData", * description="A comma separated list of content for drop downs", * type="string", * required=false * ), * @SWG\Parameter( * name="columnOrder", * in="formData", * description="The display order for this column", * type="integer", * required=true * ), * @SWG\Parameter( * name="dataTypeId", * in="formData", * description="The data type ID for this column", * type="integer", * required=true * ), * @SWG\Parameter( * name="dataSetColumnTypeId", * in="formData", * description="The column type for this column", * type="integer", * required=true * ), * @SWG\Parameter( * name="formula", * in="formData", * description="MySQL SELECT syntax formula for this Column if the column type is formula", * type="string", * required=false * ), * @SWG\Parameter( * name="remoteField", * in="formData", * description="JSON-String to select Data from the Remote DataSet", * type="string", * required=false * ), * @SWG\Parameter( * name="showFilter", * in="formData", * description="Flag indicating whether this column should present a filter on DataEntry", * type="integer", * required=true * ), * @SWG\Parameter( * name="showSort", * in="formData", * description="Flag indicating whether this column should allow sorting on DataEntry", * type="integer", * required=true * ), * @SWG\Response( * response=201, * description="successful operation", * @SWG\Schema(ref="#/definitions/DataSetColumn"), * @SWG\Header( * header="Location", * description="Location of the new record", * type="string" * ) * ) * ) * * @throws XiboException */ public function edit($dataSetId, $dataSetColumnId) { $dataSet = $this->dataSetFactory->getById($dataSetId); if (!$this->getUser()->checkEditable($dataSet)) throw new AccessDeniedException(); // Column $column = $this->dataSetColumnFactory->getById($dataSetColumnId); $column->heading = $this->getSanitizer()->getString('heading'); $column->listContent = $this->getSanitizer()->getString('listContent'); $column->columnOrder = $this->getSanitizer()->getInt('columnOrder'); $column->dataTypeId = $this->getSanitizer()->getInt('dataTypeId'); $column->dataSetColumnTypeId = $this->getSanitizer()->getInt('dataSetColumnTypeId'); $column->formula = $this->getSanitizer()->getParam('formula', null); $column->remoteField = $this->getSanitizer()->getParam('remoteField', null); $column->showFilter = $this->getSanitizer()->getCheckbox('showFilter'); $column->showSort = $this->getSanitizer()->getCheckbox('showSort'); $column->save(); if ($column->dataSetColumnTypeId == 3 && $column->hasPropertyChanged('remoteField')){ $this->pool->deleteItem('/dataset/cache/' . $dataSet->dataSetId); $this->getLog()->debug('Edited remoteField detected, clear cache for remote dataSet ID ' . $dataSet->dataSetId); } $dataSet->notify(); // Return $this->getState()->hydrate([ 'message' => sprintf(__('Edited %s'), $column->heading), 'id' => $column->dataSetColumnId, 'data' => $column ]); } /** * Delete Form * @param $dataSetId * @param $dataSetColumnId */ public function deleteForm($dataSetId, $dataSetColumnId) { $dataSet = $this->dataSetFactory->getById($dataSetId); if (!$this->getUser()->checkDeleteable($dataSet)) throw new AccessDeniedException(); $this->getState()->template = 'dataset-column-form-delete'; $this->getState()->setData([ 'dataSet' => $dataSet, 'dataSetColumn' => $this->dataSetColumnFactory->getById($dataSetColumnId), 'help' => $this->getHelp()->link('DataSet', 'DeleteColumn') ]); } /** * Delete * @param $dataSetId * @param $dataSetColumnId * * @SWG\Delete( * path="/dataset/{dataSetId}/column/{dataSetColumnId}", * operationId="dataSetColumnDelete", * tags={"dataset"}, * summary="Delete Column", * description="Delete DataSet Column", * @SWG\Parameter( * name="dataSetId", * in="path", * description="The DataSet ID", * type="integer", * required=true * ), * @SWG\Parameter( * name="dataSetColumnId", * in="path", * description="The Column ID", * type="integer", * required=true * ), * @SWG\Response( * response=204, * description="successful operation" * ) * ) */ public function delete($dataSetId, $dataSetColumnId) { $dataSet = $this->dataSetFactory->getById($dataSetId); if (!$this->getUser()->checkDeleteable($dataSet)) throw new AccessDeniedException(); // Get the column $column = $this->dataSetColumnFactory->getById($dataSetColumnId); $column->delete(); // Return $this->getState()->hydrate([ 'httpStatus' => 204, 'message' => sprintf(__('Deleted %s'), $column->heading) ]); } }PK lqYlu<