vendor/ezsystems/ezpublish-kernel/eZ/Publish/Core/MVC/Symfony/View/Builder/ContentViewBuilder.php line 262

Open in your IDE?
  1. <?php
  2. /**
  3.  * @copyright Copyright (C) eZ Systems AS. All rights reserved.
  4.  * @license For full copyright and license information view LICENSE file distributed with this source code.
  5.  */
  6. namespace eZ\Publish\Core\MVC\Symfony\View\Builder;
  7. use eZ\Publish\API\Repository\Exceptions\NotFoundException;
  8. use eZ\Publish\API\Repository\Repository;
  9. use eZ\Publish\API\Repository\Values\Content\Content;
  10. use eZ\Publish\API\Repository\Values\Content\Location;
  11. use eZ\Publish\API\Repository\Values\Content\VersionInfo;
  12. use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException;
  13. use eZ\Publish\Core\Base\Exceptions\UnauthorizedException;
  14. use eZ\Publish\Core\Helper\ContentInfoLocationLoader;
  15. use eZ\Publish\Core\MVC\Exception\HiddenLocationException;
  16. use eZ\Publish\Core\MVC\Symfony\Controller\Content\PreviewController;
  17. use eZ\Publish\Core\MVC\Symfony\View\Configurator;
  18. use eZ\Publish\Core\MVC\Symfony\View\ContentView;
  19. use eZ\Publish\Core\MVC\Symfony\View\EmbedView;
  20. use eZ\Publish\Core\MVC\Symfony\View\ParametersInjector;
  21. use Symfony\Component\HttpFoundation\RequestStack;
  22. use Symfony\Component\HttpKernel\Controller\ControllerReference;
  23. /**
  24.  * Builds ContentView objects.
  25.  */
  26. class ContentViewBuilder implements ViewBuilder
  27. {
  28.     /** @var \eZ\Publish\API\Repository\Repository */
  29.     private $repository;
  30.     /** @var \eZ\Publish\API\Repository\PermissionResolver */
  31.     private $permissionResolver;
  32.     /** @var \eZ\Publish\Core\MVC\Symfony\View\Configurator */
  33.     private $viewConfigurator;
  34.     /** @var \eZ\Publish\Core\MVC\Symfony\View\ParametersInjector */
  35.     private $viewParametersInjector;
  36.     /** @var \Symfony\Component\HttpFoundation\RequestStack */
  37.     private $requestStack;
  38.     /**
  39.      * Default templates, indexed per viewType (full, line, ...).
  40.      *
  41.      * @var array
  42.      */
  43.     private $defaultTemplates;
  44.     /** @var \eZ\Publish\Core\Helper\ContentInfoLocationLoader */
  45.     private $locationLoader;
  46.     public function __construct(
  47.         Repository $repository,
  48.         Configurator $viewConfigurator,
  49.         ParametersInjector $viewParametersInjector,
  50.         RequestStack $requestStack,
  51.         ContentInfoLocationLoader $locationLoader null
  52.     ) {
  53.         $this->repository $repository;
  54.         $this->viewConfigurator $viewConfigurator;
  55.         $this->viewParametersInjector $viewParametersInjector;
  56.         $this->locationLoader $locationLoader;
  57.         $this->permissionResolver $this->repository->getPermissionResolver();
  58.         $this->requestStack $requestStack;
  59.     }
  60.     public function matches($argument)
  61.     {
  62.         return strpos($argument'ez_content:') !== false;
  63.     }
  64.     /**
  65.      * @param array $parameters
  66.      *
  67.      * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView|\eZ\Publish\Core\MVC\Symfony\View\View
  68.      *         If both contentId and locationId parameters are missing
  69.      *
  70.      * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException
  71.      *         If both contentId and locationId parameters are missing
  72.      * @throws \eZ\Publish\Core\Base\Exceptions\UnauthorizedException
  73.      */
  74.     public function buildView(array $parameters)
  75.     {
  76.         $view = new ContentView(null, [], $parameters['viewType']);
  77.         $view->setIsEmbed($this->isEmbed($parameters));
  78.         if ($view->isEmbed() && $parameters['viewType'] === null) {
  79.             $view->setViewType(EmbedView::DEFAULT_VIEW_TYPE);
  80.         }
  81.         if (isset($parameters['location']) && $parameters['location'] instanceof Location) {
  82.             $location $parameters['location'];
  83.         } elseif (isset($parameters['locationId'])) {
  84.             $location $this->loadLocation($parameters['locationId']);
  85.         } else {
  86.             $location null;
  87.         }
  88.         if (isset($parameters['content'])) {
  89.             $content $parameters['content'];
  90.         } elseif ($location instanceof Location) {
  91.             // if we already have location load content true it so we avoid dual loading in case user does that in view
  92.             $content $location->getContent();
  93.             if (!$this->canRead($content$location$view->isEmbed())) {
  94.                 $missingPermission 'read' . ($view->isEmbed() ? '|view_embed' '');
  95.                 throw new UnauthorizedException(
  96.                     'content',
  97.                     $missingPermission,
  98.                     [
  99.                         'contentId' => $content->id,
  100.                         'locationId' => $location->id,
  101.                     ]
  102.                 );
  103.             }
  104.         } else {
  105.             if (isset($parameters['contentId'])) {
  106.                 $contentId $parameters['contentId'];
  107.             } elseif (isset($location)) {
  108.                 $contentId $location->contentId;
  109.             } else {
  110.                 throw new InvalidArgumentException('Content''No content could be loaded from parameters');
  111.             }
  112.             $content $view->isEmbed() ? $this->loadEmbeddedContent($contentId$location) : $this->loadContent($contentId);
  113.         }
  114.         $view->setContent($content);
  115.         if (isset($location)) {
  116.             if ($location->contentId !== $content->id) {
  117.                 throw new InvalidArgumentException('Location''Provided location does not belong to selected content');
  118.             }
  119.             if (isset($parameters['contentId']) && $location->contentId !== (int)$parameters['contentId']) {
  120.                 throw new InvalidArgumentException(
  121.                     'Location',
  122.                     'Provided location does not belong to selected content as requested via contentId parameter'
  123.                 );
  124.             }
  125.         } elseif (isset($this->locationLoader)) {
  126.             try {
  127.                 $location $this->locationLoader->loadLocation($content->contentInfo);
  128.             } catch (NotFoundException $e) {
  129.                 // nothing else to do
  130.             }
  131.         }
  132.         if (isset($location)) {
  133.             $view->setLocation($location);
  134.         }
  135.         $this->viewParametersInjector->injectViewParameters($view$parameters);
  136.         $this->viewConfigurator->configure($view);
  137.         // deprecated controller actions are replaced with their new equivalent, viewAction and embedAction
  138.         if (!$view->getControllerReference() instanceof ControllerReference) {
  139.             if (\in_array($parameters['_controller'], ['ez_content:viewLocation''ez_content:viewContent'])) {
  140.                 $view->setControllerReference(new ControllerReference('ez_content:viewAction'));
  141.             } elseif (\in_array($parameters['_controller'], ['ez_content:embedLocation''ez_content:embedContent'])) {
  142.                 $view->setControllerReference(new ControllerReference('ez_content:embedAction'));
  143.             }
  144.         }
  145.         return $view;
  146.     }
  147.     /**
  148.      * Loads Content with id $contentId.
  149.      *
  150.      * @param mixed $contentId
  151.      *
  152.      * @return \eZ\Publish\API\Repository\Values\Content\Content
  153.      *
  154.      * @throws \eZ\Publish\Core\Base\Exceptions\UnauthorizedException
  155.      */
  156.     private function loadContent($contentId)
  157.     {
  158.         return $this->repository->getContentService()->loadContent($contentId);
  159.     }
  160.     /**
  161.      * Loads the embedded content with id $contentId.
  162.      * Will load the content with sudo(), and check if the user can view_embed this content, for the given location
  163.      * if provided.
  164.      *
  165.      * @param mixed $contentId
  166.      * @param \eZ\Publish\API\Repository\Values\Content\Location $location
  167.      *
  168.      * @return \eZ\Publish\API\Repository\Values\Content\Content
  169.      *
  170.      * @throws \eZ\Publish\Core\Base\Exceptions\UnauthorizedException
  171.      */
  172.     private function loadEmbeddedContent($contentIdLocation $location null)
  173.     {
  174.         $content $this->repository->sudo(
  175.             function (Repository $repository) use ($contentId) {
  176.                 return $repository->getContentService()->loadContent($contentId);
  177.             }
  178.         );
  179.         if (!$this->canRead($content$location)) {
  180.             throw new UnauthorizedException(
  181.                 'content''read|view_embed',
  182.                 ['contentId' => $contentId'locationId' => $location !== null $location->id 'n/a']
  183.             );
  184.         }
  185.         // Check that Content is published, since sudo allows loading unpublished content.
  186.         if (
  187.             $content->getVersionInfo()->status !== VersionInfo::STATUS_PUBLISHED
  188.             && !$this->permissionResolver->canUser('content''versionread'$content)
  189.         ) {
  190.             throw new UnauthorizedException('content''versionread', ['contentId' => $contentId]);
  191.         }
  192.         return $content;
  193.     }
  194.     /**
  195.      * Loads a visible Location.
  196.      *
  197.      * @param $locationId
  198.      *
  199.      * @return \eZ\Publish\API\Repository\Values\Content\Location
  200.      */
  201.     private function loadLocation($locationId)
  202.     {
  203.         $location $this->repository->sudo(
  204.             function (Repository $repository) use ($locationId) {
  205.                 return $repository->getLocationService()->loadLocation($locationId);
  206.             }
  207.         );
  208.         $request $this->requestStack->getCurrentRequest();
  209.         if (!$request || !$request->attributes->get(PreviewController::PREVIEW_PARAMETER_NAMEfalse)) {
  210.             if ($location->invisible || $location->hidden) {
  211.                 throw new HiddenLocationException($location'Location cannot be displayed as it is flagged as invisible.');
  212.             }
  213.         }
  214.         return $location;
  215.     }
  216.     /**
  217.      * Checks if a user can read a content, or view it as an embed.
  218.      *
  219.      * @param \eZ\Publish\API\Repository\Values\Content\Content $content
  220.      * @param \eZ\Publish\API\Repository\Values\Content\Location $location
  221.      * @param bool $isEmbed
  222.      *
  223.      * @return bool
  224.      */
  225.     private function canRead(Content $contentLocation $location nullbool $isEmbed true): bool
  226.     {
  227.         $targets = isset($location) ? [$location] : [];
  228.         return
  229.             $this->permissionResolver->canUser('content''read'$content->contentInfo$targets) ||
  230.             ($isEmbed && $this->permissionResolver->canUser('content''view_embed'$content->contentInfo$targets));
  231.     }
  232.     /**
  233.      * Checks if the view is an embed one.
  234.      * Uses either the controller action (embedAction), or the viewType (embed/embed-inline).
  235.      *
  236.      * @param array $parameters The ViewBuilder parameters array.
  237.      *
  238.      * @return bool
  239.      */
  240.     private function isEmbed($parameters)
  241.     {
  242.         if ($parameters['_controller'] === 'ez_content:embedAction') {
  243.             return true;
  244.         }
  245.         if (\in_array($parameters['viewType'], ['embed''embed-inline'])) {
  246.             return true;
  247.         }
  248.         return false;
  249.     }
  250. }