. */ namespace Xibo\Controller; use Slim\Http\Response as Response; use Slim\Http\ServerRequest as Request; use Xibo\Factory\ActionFactory; use Xibo\Factory\LayoutFactory; use Xibo\Factory\ModuleFactory; use Xibo\Factory\RegionFactory; use Xibo\Factory\WidgetFactory; use Xibo\Support\Exception\GeneralException; use Xibo\Support\Exception\InvalidArgumentException; use Xibo\Support\Exception\NotFoundException; /** * Class Action * @package Xibo\Controller */ class Action extends Base { /** * @var ActionFactory */ private $actionFactory; /** @var LayoutFactory */ private $layoutFactory; /** @var RegionFactory */ private $regionFactory; /** @var WidgetFactory */ private $widgetFactory; /** @var ModuleFactory */ private $moduleFactory; /** * Set common dependencies. * @param ActionFactory $actionFactory * @param LayoutFactory $layoutFactory * @param RegionFactory $regionFactory * @param WidgetFactory $widgetFactory * @param ModuleFactory $moduleFactory */ public function __construct($actionFactory, $layoutFactory, $regionFactory, $widgetFactory, $moduleFactory) { $this->actionFactory = $actionFactory; $this->layoutFactory = $layoutFactory; $this->regionFactory = $regionFactory; $this->widgetFactory = $widgetFactory; $this->moduleFactory = $moduleFactory; } /** * Returns a Grid of Actions * * @SWG\Get( * path="/action", * operationId="actionSearch", * tags={"action"}, * summary="Search Actions", * description="Search all Actions this user has access to", * @SWG\Parameter( * name="actionId", * in="query", * description="Filter by Action Id", * type="integer", * required=false * ), * @SWG\Parameter( * name="ownerId", * in="query", * description="Filter by Owner Id", * type="integer", * required=false * ), * @SWG\Parameter( * name="triggerType", * in="query", * description="Filter by Action trigger type", * type="string", * required=false * ), * @SWG\Parameter( * name="triggerCode", * in="query", * description="Filter by Action trigger code", * type="string", * required=false * ), * @SWG\Parameter( * name="actionType", * in="query", * description="Filter by Action type", * type="string", * required=false * ), * @SWG\Parameter( * name="source", * in="query", * description="Filter by Action source", * type="string", * required=false * ), * @SWG\Parameter( * name="sourceId", * in="query", * description="Filter by Action source Id", * type="integer", * required=false * ), * @SWG\Parameter( * name="target", * in="query", * description="Filter by Action target", * type="string", * required=false * ), * @SWG\Parameter( * name="targetId", * in="query", * description="Filter by Action target Id", * type="integer", * required=false * ), * @SWG\Parameter( * name="layoutId", * in="query", * description="Return all actions pertaining to a particular Layout", * type="integer", * required=false * ), * @SWG\Parameter( * name="sourceOrTargetId", * in="query", * description="Return all actions related to a source or target with the provided ID", * type="integer", * required=false * ), * @SWG\Response( * response=200, * description="successful operation", * @SWG\Schema( * type="array", * @SWG\Items(ref="#/definitions/Action") * ) * ) * ) * @param Request $request * @param Response $response * @return \Psr\Http\Message\ResponseInterface|Response * @throws GeneralException */ public function grid(Request $request, Response $response) : Response { $parsedParams = $this->getSanitizer($request->getQueryParams()); $filter = [ 'actionId' => $parsedParams->getInt('actionId'), 'ownerId' => $parsedParams->getInt('ownerId'), 'triggerType' => $parsedParams->getString('triggerType'), 'triggerCode' => $parsedParams->getString('triggerCode'), 'actionType' => $parsedParams->getString('actionType'), 'source' => $parsedParams->getString('source'), 'sourceId' => $parsedParams->getInt('sourceId'), 'target' => $parsedParams->getString('target'), 'targetId' => $parsedParams->getInt('targetId'), 'widgetId' => $parsedParams->getInt('widgetId'), 'layoutCode' => $parsedParams->getString('layoutCode'), 'layoutId' => $parsedParams->getInt('layoutId'), 'sourceOrTargetId' => $parsedParams->getInt('sourceOrTargetId'), ]; $actions = $this->actionFactory->query( $this->gridRenderSort($parsedParams), $this->gridRenderFilter($filter, $parsedParams) ); foreach ($actions as $action) { $action->setUnmatchedProperty('widgetName', null); $action->setUnmatchedProperty('regionName', null); if ($action->actionType === 'navWidget' && $action->widgetId != null) { try { $widget = $this->widgetFactory->loadByWidgetId($action->widgetId); $module = $this->moduleFactory->getByType($widget->type); // dynamic field to display in the grid instead of widgetId $action->setUnmatchedProperty('widgetName', $widget->getOptionValue('name', $module->name)); } catch (NotFoundException $e) { // Widget not found, leave widgetName as null } } if ($action->target === 'region' && $action->targetId != null) { try { $region = $this->regionFactory->getById($action->targetId); // dynamic field to display in the grid instead of regionId $action->setUnmatchedProperty('regionName', $region->name); } catch (NotFoundException $e) { // Region not found, leave regionName as null } } } $this->getState()->template = 'grid'; $this->getState()->recordsTotal = $this->actionFactory->countLast(); $this->getState()->setData($actions); return $this->render($request, $response); } /** * Add a new Action * * @SWG\Post( * path="/action", * operationId="actionAdd", * tags={"action"}, * summary="Add Action", * description="Add a new Action", * @SWG\Parameter( * name="layoutId", * in="formData", * description="LayoutId associted with this Action", * type="integer", * required=true * ), * @SWG\Parameter( * name="actionType", * in="formData", * description="Action type, next, previous, navLayout, navWidget", * type="string", * required=true * ), * @SWG\Parameter( * name="target", * in="formData", * description="Target for this action, screen or region", * type="string", * required=true * ), * @SWG\Parameter( * name="targetId", * in="formData", * description="The id of the target for this action - regionId if the target is set to region", * type="string", * required=false * ), * @SWG\Parameter( * name="source", * in="formData", * description="Source for this action layout, region or widget", * type="string", * required=false * ), * @SWG\Parameter( * name="sourceId", * in="formData", * description="The id of the source object, layoutId, regionId or widgetId", * type="integer", * required=false * ), * @SWG\Parameter( * name="triggerType", * in="formData", * description="Action trigger type, touch or webhook", * type="string", * required=false * ), * @SWG\Parameter( * name="triggerCode", * in="formData", * description="Action trigger code", * type="string", * required=false * ), * @SWG\Parameter( * name="widgetId", * in="formData", * description="For navWidget actionType, the WidgetId to navigate to", * type="integer", * required=false * ), * @SWG\Parameter( * name="layoutCode", * in="formData", * description="For navLayout, the Layout Code identifier to navigate to", * type="string", * required=false * ), * @SWG\Response( * response=201, * description="successful operation", * @SWG\Schema(ref="#/definitions/Action"), * @SWG\Header( * header="Location", * description="Location of the new record", * type="string" * ) * ) * ) * @param Request $request * @param Response $response * @return \Psr\Http\Message\ResponseInterface|Response * @throws GeneralException */ public function add(Request $request, Response $response) : Response { $sanitizedParams = $this->getSanitizer($request->getParams()); $triggerType = $sanitizedParams->getString('triggerType'); $triggerCode = $sanitizedParams->getString('triggerCode', ['defaultOnEmptyString' => true]); $actionType = $sanitizedParams->getString('actionType'); $target = $sanitizedParams->getString('target'); $targetId = $sanitizedParams->getInt('targetId'); $widgetId = $sanitizedParams->getInt('widgetId'); $layoutCode = $sanitizedParams->getString('layoutCode'); $layoutId = $sanitizedParams->getInt('layoutId'); $source = $sanitizedParams->getString('source'); $sourceId = $sanitizedParams->getInt('sourceId'); if ($layoutId === null) { throw new InvalidArgumentException(__('Please provide LayoutId'), 'layoutId'); } $layout = $this->layoutFactory->getById($layoutId); // Make sure the Layout is checked out to begin with if (!$layout->isEditable()) { throw new InvalidArgumentException(__('Layout is not checked out'), 'statusId'); } // restrict to one touch Action per source if ( (!empty($source) && $sourceId !== null && !empty($triggerType)) && $this->actionFactory->checkIfActionExist($source, $sourceId, $triggerType) ) { throw new InvalidArgumentException(__('Action with specified Trigger Type already exists'), 'triggerType'); } $action = $this->actionFactory->create( $triggerType, $triggerCode, $actionType, $source, $sourceId, $target, $targetId, $widgetId, $layoutCode, $layoutId ); $action->save(['notifyLayout' => true]); // Return $this->getState()->hydrate([ 'message' => __('Added Action'), 'httpStatus' => 201, 'id' => $action->actionId, 'data' => $action, ]); return $this->render($request, $response); } /** * Edit Action * * @SWG\PUT( * path="/action/{actionId}", * operationId="actionAdd", * tags={"action"}, * summary="Add Action", * description="Add a new Action", * @SWG\Parameter( * name="actionId", * in="path", * description="Action ID to edit", * type="integer", * required=true * ), * @SWG\Parameter( * name="layoutId", * in="formData", * description="LayoutId associted with this Action", * type="integer", * required=true * ), * @SWG\Parameter( * name="actionType", * in="formData", * description="Action type, next, previous, navLayout, navWidget", * type="string", * required=true * ), * @SWG\Parameter( * name="target", * in="formData", * description="Target for this action, screen or region", * type="string", * required=true * ), * @SWG\Parameter( * name="targetId", * in="formData", * description="The id of the target for this action - regionId if the target is set to region", * type="string", * required=false * ), * @SWG\Parameter( * name="source", * in="formData", * description="Source for this action layout, region or widget", * type="string", * required=true * ), * @SWG\Parameter( * name="sourceId", * in="formData", * description="The id of the source object, layoutId, regionId or widgetId", * type="integer", * required=true * ), * @SWG\Parameter( * name="triggerType", * in="formData", * description="Action trigger type, touch or webhook", * type="string", * required=true * ), * @SWG\Parameter( * name="triggerCode", * in="formData", * description="Action trigger code", * type="string", * required=false * ), * @SWG\Parameter( * name="widgetId", * in="formData", * description="For navWidget actionType, the WidgetId to navigate to", * type="integer", * required=false * ), * @SWG\Parameter( * name="layoutCode", * in="formData", * description="For navLayout, the Layout Code identifier to navigate to", * type="string", * required=false * ), * @SWG\Response( * response=201, * description="successful operation", * @SWG\Schema(ref="#/definitions/Action"), * @SWG\Header( * header="Location", * description="Location of the new record", * type="string" * ) * ) * ) * * @param Request $request * @param Response $response * @param int $id * @return Response * @throws GeneralException */ public function edit(Request $request, Response $response, int $id) : Response { $action = $this->actionFactory->getById($id); $sanitizedParams = $this->getSanitizer($request->getParams()); $layout = $this->layoutFactory->getById($action->layoutId); // Make sure the Layout is checked out to begin with if (!$layout->isEditable()) { throw new InvalidArgumentException(__('Layout is not checked out'), 'statusId'); } $action->source = $sanitizedParams->getString('source'); $action->sourceId = $sanitizedParams->getInt('sourceId'); $action->triggerType = $sanitizedParams->getString('triggerType'); $action->triggerCode = $sanitizedParams->getString('triggerCode', ['defaultOnEmptyString' => true]); $action->actionType = $sanitizedParams->getString('actionType'); $action->target = $sanitizedParams->getString('target'); $action->targetId = $sanitizedParams->getInt('targetId'); $action->widgetId = $sanitizedParams->getInt('widgetId'); $action->layoutCode = $sanitizedParams->getString('layoutCode'); $action->validate(); // restrict to one touch Action per source if ($this->actionFactory->checkIfActionExist($action->source, $action->sourceId, $action->triggerType, $action->actionId)) { throw new InvalidArgumentException(__('Action with specified Trigger Type already exists'), 'triggerType'); } $action->save(['notifyLayout' => true, 'validate' => false]); // Return $this->getState()->hydrate([ 'message' => __('Edited Action'), 'id' => $action->actionId, 'data' => $action ]); return $this->render($request, $response); } /** * Delete Action * @param Request $request * @param Response $response * @param int $id * @return \Psr\Http\Message\ResponseInterface|Response * @throws GeneralException * * @SWG\Delete( * path="/action/{actionId}", * operationId="actionDelete", * tags={"action"}, * summary="Delete Action", * description="Delete an existing Action", * @SWG\Parameter( * name="actionId", * in="path", * description="The Action ID to Delete", * type="integer", * required=true * ), * @SWG\Response( * response=204, * description="successful operation" * ) * ) */ public function delete(Request $request, Response $response, int $id) : Response { $action = $this->actionFactory->getById($id); $layout = $this->layoutFactory->getById($action->layoutId); // Make sure the Layout is checked out to begin with if (!$layout->isEditable()) { throw new InvalidArgumentException(__('Layout is not checked out'), 'statusId'); } $action->notifyLayout($layout->layoutId); $action->delete(); // Return $this->getState()->hydrate([ 'httpStatus' => 204, 'message' => sprintf(__('Deleted Action')) ]); return $this->render($request, $response); } /** * @param string $source * @param int $sourceId * @return \Xibo\Entity\Layout|\Xibo\Entity\Region|\Xibo\Entity\Widget * @throws InvalidArgumentException * @throws NotFoundException */ public function checkIfSourceExists(string $source, int $sourceId) { if (strtolower($source) === 'layout') { $object = $this->layoutFactory->getById($sourceId); } elseif (strtolower($source) === 'region') { $object = $this->regionFactory->getById($sourceId); } elseif (strtolower($source) === 'widget') { $object = $this->widgetFactory->getById($sourceId); } else { throw new InvalidArgumentException(__('Provided source is invalid. ') , 'source'); } return $object; } }