vendor/sulu/sulu/src/Sulu/Bundle/WebsiteBundle/Twig/Content/ContentTwigExtension.php line 212

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Sulu.
  4.  *
  5.  * (c) Sulu GmbH
  6.  *
  7.  * This source file is subject to the MIT license that is bundled
  8.  * with this source code in the file LICENSE.
  9.  */
  10. namespace Sulu\Bundle\WebsiteBundle\Twig\Content;
  11. use Psr\Log\LoggerInterface;
  12. use Psr\Log\NullLogger;
  13. use Sulu\Bundle\PageBundle\Admin\PageAdmin;
  14. use Sulu\Bundle\WebsiteBundle\Resolver\StructureResolverInterface;
  15. use Sulu\Bundle\WebsiteBundle\Twig\Exception\ParentNotFoundException;
  16. use Sulu\Component\Content\Compat\StructureInterface;
  17. use Sulu\Component\Content\Document\Behavior\SecurityBehavior;
  18. use Sulu\Component\Content\Document\Behavior\WebspaceBehavior;
  19. use Sulu\Component\Content\Mapper\ContentMapperInterface;
  20. use Sulu\Component\DocumentManager\Exception\DocumentNotFoundException;
  21. use Sulu\Component\PHPCR\SessionManager\SessionManagerInterface;
  22. use Sulu\Component\Security\Authorization\PermissionTypes;
  23. use Sulu\Component\Security\Authorization\SecurityCheckerInterface;
  24. use Sulu\Component\Security\Authorization\SecurityCondition;
  25. use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface;
  26. use Sulu\Component\Webspace\Manager\WebspaceManagerInterface;
  27. use Symfony\Component\HttpFoundation\RequestStack;
  28. use Twig\Extension\AbstractExtension;
  29. use Twig\TwigFunction;
  30. /**
  31.  * Provides Interface to load content.
  32.  */
  33. class ContentTwigExtension extends AbstractExtension implements ContentTwigExtensionInterface
  34. {
  35.     /**
  36.      * @var ContentMapperInterface
  37.      */
  38.     private $contentMapper;
  39.     /**
  40.      * @var StructureResolverInterface
  41.      */
  42.     private $structureResolver;
  43.     /**
  44.      * @var RequestAnalyzerInterface
  45.      */
  46.     private $requestAnalyzer;
  47.     /**
  48.      * @var SessionManagerInterface
  49.      */
  50.     private $sessionManager;
  51.     /**
  52.      * @var LoggerInterface
  53.      */
  54.     private $logger;
  55.     /**
  56.      * @var SecurityCheckerInterface|null
  57.      */
  58.     private $securityChecker;
  59.     /**
  60.      * @var WebspaceManagerInterface|null
  61.      */
  62.     private $webspaceManager;
  63.     /**
  64.      * @var RequestStack|null
  65.      */
  66.     private $requestStack;
  67.     /**
  68.      * Constructor.
  69.      */
  70.     public function __construct(
  71.         ContentMapperInterface $contentMapper,
  72.         StructureResolverInterface $structureResolver,
  73.         SessionManagerInterface $sessionManager,
  74.         RequestAnalyzerInterface $requestAnalyzer,
  75.         LoggerInterface $logger null,
  76.         $securityChecker null,
  77.         WebspaceManagerInterface $webspaceManager null,
  78.         RequestStack $requestStack null
  79.     ) {
  80.         $this->contentMapper $contentMapper;
  81.         $this->structureResolver $structureResolver;
  82.         $this->sessionManager $sessionManager;
  83.         $this->requestAnalyzer $requestAnalyzer;
  84.         $this->logger $logger ?: new NullLogger();
  85.         if ($securityChecker instanceof RequestStack) {
  86.             @\trigger_error(
  87.                 'Instantiating the "ContentTwigExtension" without the "$securityChecker" and "$webspaceManager" parameter is deprecated',
  88.                 \E_USER_DEPRECATED
  89.             );
  90.             $requestStack $securityChecker;
  91.             $securityChecker null;
  92.         }
  93.         $this->securityChecker $securityChecker;
  94.         $this->webspaceManager $webspaceManager;
  95.         $this->requestStack $requestStack;
  96.         if (null === $this->requestStack) {
  97.             @\trigger_error(
  98.                 'Instantiating the "ContentTwigExtension" without the "$requestStack" parameter is deprecated',
  99.                 \E_USER_DEPRECATED
  100.             );
  101.         }
  102.     }
  103.     public function getFunctions()
  104.     {
  105.         return [
  106.             new TwigFunction('sulu_content_load', [$this'load']),
  107.             new TwigFunction('sulu_content_load_parent', [$this'loadParent']),
  108.         ];
  109.     }
  110.     public function load($uuid, array $properties null)
  111.     {
  112.         if (!$uuid) {
  113.             return;
  114.         }
  115.         $locale $this->requestAnalyzer->getCurrentLocalization()->getLocale();
  116.         try {
  117.             $contentStructure $this->contentMapper->load(
  118.                 $uuid,
  119.                 $this->requestAnalyzer->getWebspace()->getKey(),
  120.                 $locale
  121.             );
  122.         } catch (DocumentNotFoundException $e) {
  123.             $this->logger->error((string) $e);
  124.             return;
  125.         }
  126.         $document $contentStructure->getDocument();
  127.         if ($this->securityChecker && $document instanceof WebspaceBehavior && $document instanceof SecurityBehavior) {
  128.             $targetWebspace $this->webspaceManager->findWebspaceByKey($contentStructure->getWebspaceKey());
  129.             $security $targetWebspace->getSecurity();
  130.             $system $security $security->getSystem() : null;
  131.             if ($targetWebspace->hasWebsiteSecurity()
  132.                 && !$this->securityChecker->hasPermission(
  133.                     new SecurityCondition(
  134.                         PageAdmin::SECURITY_CONTEXT_PREFIX $contentStructure->getWebspaceKey(),
  135.                         $locale,
  136.                         SecurityBehavior::class,
  137.                         $uuid,
  138.                         $system
  139.                     ),
  140.                     PermissionTypes::VIEW
  141.                 )
  142.             ) {
  143.                 return null;
  144.             }
  145.         }
  146.         if (null === $properties) {
  147.             @\trigger_error(
  148.                 'Calling the "sulu_content_load" function without a properties parameter is deprecated and has a negative impact on performance.',
  149.                 \E_USER_DEPRECATED
  150.             );
  151.             return $this->resolveStructure($contentStructure);
  152.         }
  153.         return $this->resolveProperties($contentStructure$properties);
  154.     }
  155.     public function loadParent($uuid, array $properties null)
  156.     {
  157.         $session $this->sessionManager->getSession();
  158.         $contentsNode $this->sessionManager->getContentNode($this->requestAnalyzer->getWebspace()->getKey());
  159.         $node $session->getNodeByIdentifier($uuid);
  160.         if ($node->getDepth() <= $contentsNode->getDepth()) {
  161.             throw new ParentNotFoundException($uuid);
  162.         }
  163.         return $this->load($node->getParent()->getIdentifier(), $properties);
  164.     }
  165.     private function resolveStructure(
  166.         StructureInterface $structure,
  167.         bool $loadExcerpt true,
  168.         array $includedProperties null
  169.     ) {
  170.         if (null === $this->requestStack) {
  171.             return $this->structureResolver->resolve($structure$loadExcerpt$includedProperties);
  172.         }
  173.         $currentRequest $this->requestStack->getCurrentRequest();
  174.         // This sets query parameters, request parameters and files to an empty array
  175.         $subRequest $currentRequest->duplicate([], [], nullnull, []);
  176.         $this->requestStack->push($subRequest);
  177.         try {
  178.             return $this->structureResolver->resolve($structure$loadExcerpt$includedProperties);
  179.         } finally {
  180.             $this->requestStack->pop();
  181.         }
  182.     }
  183.     private function resolveProperties(StructureInterface $contentStructure, array $properties): array
  184.     {
  185.         $contentProperties = [];
  186.         $extensionProperties = [];
  187.         foreach ($properties as $targetProperty => $sourceProperty) {
  188.             if (!\is_string($targetProperty)) {
  189.                 $targetProperty $sourceProperty;
  190.             }
  191.             if (!\strpos($sourceProperty'.')) {
  192.                 $contentProperties[$targetProperty] = $sourceProperty;
  193.             } else {
  194.                 $extensionProperties[$targetProperty] = $sourceProperty;
  195.             }
  196.         }
  197.         $resolvedStructure $this->resolveStructure(
  198.             $contentStructure,
  199.             !empty($extensionProperties),
  200.             \array_values($contentProperties)
  201.         );
  202.         foreach ($contentProperties as $targetProperty => $sourceProperty) {
  203.             if (isset($resolvedStructure['content'][$sourceProperty]) && $sourceProperty !== $targetProperty) {
  204.                 $resolvedStructure['content'][$targetProperty] = $resolvedStructure['content'][$sourceProperty];
  205.                 $resolvedStructure['view'][$targetProperty] = $resolvedStructure['view'][$sourceProperty] ?? [];
  206.                 unset($resolvedStructure['content'][$sourceProperty]);
  207.                 unset($resolvedStructure['view'][$sourceProperty]);
  208.             }
  209.         }
  210.         foreach ($extensionProperties as $targetProperty => $sourceProperty) {
  211.             [$extensionName$propertyName] = \explode('.'$sourceProperty);
  212.             $propertyValue $resolvedStructure['extension'][$extensionName][$propertyName];
  213.             $resolvedStructure['content'][$targetProperty] = $propertyValue;
  214.             $resolvedStructure['view'][$targetProperty] = [];
  215.         }
  216.         unset($resolvedStructure['extension']);
  217.         return $resolvedStructure;
  218.     }
  219. }