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

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_deprecation('sulu/sulu''2.2''Instantiating the "ContentTwigExtension" without the "$securityChecker" and "$webspaceManager" parameter is deprecated');
  87.             $requestStack $securityChecker;
  88.             $securityChecker null;
  89.         }
  90.         $this->securityChecker $securityChecker;
  91.         $this->webspaceManager $webspaceManager;
  92.         $this->requestStack $requestStack;
  93.         if (null === $this->requestStack) {
  94.             @trigger_deprecation('sulu/sulu''2.3''Instantiating the "ContentTwigExtension" without the "$requestStack" parameter is deprecated');
  95.         }
  96.     }
  97.     public function getFunctions()
  98.     {
  99.         return [
  100.             new TwigFunction('sulu_content_load', [$this'load']),
  101.             new TwigFunction('sulu_content_load_parent', [$this'loadParent']),
  102.         ];
  103.     }
  104.     public function load($uuid, ?array $properties null)
  105.     {
  106.         if (!$uuid) {
  107.             return;
  108.         }
  109.         $locale $this->requestAnalyzer->getCurrentLocalization()->getLocale();
  110.         try {
  111.             $contentStructure $this->contentMapper->load(
  112.                 $uuid,
  113.                 $this->requestAnalyzer->getWebspace()->getKey(),
  114.                 $locale
  115.             );
  116.         } catch (DocumentNotFoundException $e) {
  117.             $this->logger->error((string) $e);
  118.             return;
  119.         }
  120.         $document $contentStructure->getDocument();
  121.         if ($this->securityChecker && $document instanceof WebspaceBehavior && $document instanceof SecurityBehavior) {
  122.             $targetWebspace $this->webspaceManager->findWebspaceByKey($contentStructure->getWebspaceKey());
  123.             $security $targetWebspace->getSecurity();
  124.             $system $security $security->getSystem() : null;
  125.             if ($targetWebspace->hasWebsiteSecurity()
  126.                 && !$this->securityChecker->hasPermission(
  127.                     new SecurityCondition(
  128.                         PageAdmin::SECURITY_CONTEXT_PREFIX $contentStructure->getWebspaceKey(),
  129.                         $locale,
  130.                         SecurityBehavior::class,
  131.                         $uuid,
  132.                         $system
  133.                     ),
  134.                     PermissionTypes::VIEW
  135.                 )
  136.             ) {
  137.                 return null;
  138.             }
  139.         }
  140.         if (null === $properties) {
  141.             @trigger_deprecation('sulu/sulu''2.3''Calling the "sulu_content_load" function without a properties parameter is deprecated and has a negative impact on performance.');
  142.             return $this->resolveStructure($contentStructure);
  143.         }
  144.         return $this->resolveProperties($contentStructure$properties);
  145.     }
  146.     public function loadParent($uuid, ?array $properties null)
  147.     {
  148.         $session $this->sessionManager->getSession();
  149.         $contentsNode $this->sessionManager->getContentNode($this->requestAnalyzer->getWebspace()->getKey());
  150.         $node $session->getNodeByIdentifier($uuid);
  151.         if ($node->getDepth() <= $contentsNode->getDepth()) {
  152.             throw new ParentNotFoundException($uuid);
  153.         }
  154.         return $this->load($node->getParent()->getIdentifier(), $properties);
  155.     }
  156.     private function resolveStructure(
  157.         StructureInterface $structure,
  158.         bool $loadExcerpt true,
  159.         ?array $includedProperties null
  160.     ) {
  161.         if (null === $this->requestStack) {
  162.             return $this->structureResolver->resolve($structure$loadExcerpt$includedProperties);
  163.         }
  164.         $currentRequest $this->requestStack->getCurrentRequest();
  165.         // This sets query parameters, request parameters and files to an empty array
  166.         $subRequest $currentRequest->duplicate([], [], nullnull, []);
  167.         $this->requestStack->push($subRequest);
  168.         try {
  169.             return $this->structureResolver->resolve($structure$loadExcerpt$includedProperties);
  170.         } finally {
  171.             $this->requestStack->pop();
  172.         }
  173.     }
  174.     private function resolveProperties(StructureInterface $contentStructure, array $properties): array
  175.     {
  176.         $contentProperties = [];
  177.         $extensionProperties = [];
  178.         foreach ($properties as $targetProperty => $sourceProperty) {
  179.             if (!\is_string($targetProperty)) {
  180.                 $targetProperty $sourceProperty;
  181.             }
  182.             if (!\strpos($sourceProperty'.')) {
  183.                 $contentProperties[$targetProperty] = $sourceProperty;
  184.             } else {
  185.                 $extensionProperties[$targetProperty] = $sourceProperty;
  186.             }
  187.         }
  188.         $resolvedStructure $this->resolveStructure(
  189.             $contentStructure,
  190.             !empty($extensionProperties),
  191.             \array_values($contentProperties)
  192.         );
  193.         foreach ($contentProperties as $targetProperty => $sourceProperty) {
  194.             if (isset($resolvedStructure['content'][$sourceProperty]) && $sourceProperty !== $targetProperty) {
  195.                 $resolvedStructure['content'][$targetProperty] = $resolvedStructure['content'][$sourceProperty];
  196.                 $resolvedStructure['view'][$targetProperty] = $resolvedStructure['view'][$sourceProperty] ?? [];
  197.                 unset($resolvedStructure['content'][$sourceProperty]);
  198.                 unset($resolvedStructure['view'][$sourceProperty]);
  199.             }
  200.         }
  201.         foreach ($extensionProperties as $targetProperty => $sourceProperty) {
  202.             [$extensionName$propertyName] = \explode('.'$sourceProperty);
  203.             $propertyValue $resolvedStructure['extension'][$extensionName][$propertyName];
  204.             $resolvedStructure['content'][$targetProperty] = $propertyValue;
  205.             $resolvedStructure['view'][$targetProperty] = [];
  206.         }
  207.         unset($resolvedStructure['extension']);
  208.         return $resolvedStructure;
  209.     }
  210. }