vendor/sulu/sulu/src/Sulu/Component/Content/Mapper/ContentMapper.php line 1005

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\Component\Content\Mapper;
  11. use Doctrine\Common\Cache\ArrayCache;
  12. use Doctrine\Common\Cache\Cache;
  13. use Jackalope\Query\Row;
  14. use PHPCR\NodeInterface;
  15. use PHPCR\Query\QueryInterface;
  16. use PHPCR\Query\QueryResultInterface;
  17. use Sulu\Bundle\DocumentManagerBundle\Bridge\DocumentInspector;
  18. use Sulu\Bundle\DocumentManagerBundle\Bridge\PropertyEncoder;
  19. use Sulu\Bundle\PageBundle\Admin\PageAdmin;
  20. use Sulu\Bundle\PageBundle\Document\BasePageDocument;
  21. use Sulu\Bundle\PageBundle\Document\HomeDocument;
  22. use Sulu\Bundle\SnippetBundle\Document\SnippetDocument;
  23. use Sulu\Component\Content\BreadcrumbItem;
  24. use Sulu\Component\Content\Compat\Property as LegacyProperty;
  25. use Sulu\Component\Content\Compat\Structure as LegacyStructure;
  26. use Sulu\Component\Content\Compat\StructureInterface;
  27. use Sulu\Component\Content\Compat\StructureManagerInterface;
  28. use Sulu\Component\Content\ContentTypeManager;
  29. use Sulu\Component\Content\ContentTypeManagerInterface;
  30. use Sulu\Component\Content\Document\Behavior\ExtensionBehavior;
  31. use Sulu\Component\Content\Document\Behavior\LocalizedAuthorBehavior;
  32. use Sulu\Component\Content\Document\Behavior\OrderBehavior;
  33. use Sulu\Component\Content\Document\Behavior\RedirectTypeBehavior;
  34. use Sulu\Component\Content\Document\Behavior\ResourceSegmentBehavior;
  35. use Sulu\Component\Content\Document\Behavior\SecurityBehavior;
  36. use Sulu\Component\Content\Document\Behavior\ShadowLocaleBehavior;
  37. use Sulu\Component\Content\Document\Behavior\StructureBehavior;
  38. use Sulu\Component\Content\Document\Behavior\WorkflowStageBehavior;
  39. use Sulu\Component\Content\Document\LocalizationState;
  40. use Sulu\Component\Content\Document\RedirectType;
  41. use Sulu\Component\Content\Document\Subscriber\WorkflowStageSubscriber;
  42. use Sulu\Component\Content\Document\WorkflowStage;
  43. use Sulu\Component\Content\Exception\InvalidOrderPositionException;
  44. use Sulu\Component\Content\Exception\TranslatedNodeNotFoundException;
  45. use Sulu\Component\Content\Extension\ExtensionInterface;
  46. use Sulu\Component\Content\Extension\ExtensionManagerInterface;
  47. use Sulu\Component\Content\Mapper\Event\ContentNodeEvent;
  48. use Sulu\Component\Content\Metadata\Factory\Exception\StructureTypeNotFoundException;
  49. use Sulu\Component\Content\Types\ResourceLocator;
  50. use Sulu\Component\Content\Types\ResourceLocator\Strategy\ResourceLocatorStrategyPoolInterface;
  51. use Sulu\Component\DocumentManager\Behavior\Mapping\ParentBehavior;
  52. use Sulu\Component\DocumentManager\Document\UnknownDocument;
  53. use Sulu\Component\DocumentManager\DocumentAccessor;
  54. use Sulu\Component\DocumentManager\DocumentManagerInterface;
  55. use Sulu\Component\DocumentManager\Exception\DocumentNotFoundException;
  56. use Sulu\Component\DocumentManager\NamespaceRegistry;
  57. use Sulu\Component\PHPCR\SessionManager\SessionManagerInterface;
  58. use Sulu\Component\Security\Authorization\AccessControl\AccessControlManagerInterface;
  59. use Sulu\Component\Webspace\Manager\WebspaceManagerInterface;
  60. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  61. use Symfony\Component\Form\FormFactoryInterface;
  62. use Symfony\Component\Security\Core\Security;
  63. /**
  64.  * Maps content nodes to phpcr nodes with content types and provides utility function to handle content nodes.
  65.  *
  66.  * @deprecated since 1.0-? use the DocumentManager instead
  67.  */
  68. class ContentMapper implements ContentMapperInterface
  69. {
  70.     /**
  71.      * @var ContentTypeManager
  72.      */
  73.     private $contentTypeManager;
  74.     /**
  75.      * @var StructureManagerInterface
  76.      */
  77.     private $structureManager;
  78.     /**
  79.      * @var ExtensionManagerInterface
  80.      */
  81.     private $extensionManager;
  82.     /**
  83.      * @var SessionManagerInterface
  84.      */
  85.     private $sessionManager;
  86.     /**
  87.      * @var EventDispatcherInterface
  88.      */
  89.     private $eventDispatcher;
  90.     /**
  91.      * @var WebspaceManagerInterface
  92.      */
  93.     private $webspaceManager;
  94.     /**
  95.      * @var Cache
  96.      */
  97.     private $extensionDataCache;
  98.     /**
  99.      * @var ResourceLocatorStrategyPoolInterface
  100.      */
  101.     private $resourceLocatorStrategyPool;
  102.     /**
  103.      * @var DocumentManagerInterface
  104.      */
  105.     private $documentManager;
  106.     /**
  107.      * @var FormFactoryInterface
  108.      */
  109.     private $formFactory;
  110.     /**
  111.      * @var DocumentInspector
  112.      */
  113.     private $inspector;
  114.     /**
  115.      * @var PropertyEncoder
  116.      */
  117.     private $encoder;
  118.     /**
  119.      * @var NamespaceRegistry
  120.      */
  121.     private $namespaceRegistry;
  122.     /**
  123.      * @var AccessControlManagerInterface
  124.      */
  125.     private $accessControlManager;
  126.     /**
  127.      * @var array
  128.      */
  129.     private $permissions;
  130.     /**
  131.      * @var ?Security
  132.      */
  133.     private $security;
  134.     public function __construct(
  135.         DocumentManagerInterface $documentManager,
  136.         WebspaceManagerInterface $webspaceManager,
  137.         FormFactoryInterface $formFactory,
  138.         DocumentInspector $inspector,
  139.         PropertyEncoder $encoder,
  140.         StructureManagerInterface $structureManager,
  141.         ExtensionManagerInterface $extensionManager,
  142.         ContentTypeManagerInterface $contentTypeManager,
  143.         SessionManagerInterface $sessionManager,
  144.         EventDispatcherInterface $eventDispatcher,
  145.         ResourceLocatorStrategyPoolInterface $resourceLocatorStrategyPool,
  146.         NamespaceRegistry $namespaceRegistry,
  147.         AccessControlManagerInterface $accessControlManager,
  148.         $permissions,
  149.         Security $security null
  150.     ) {
  151.         $this->contentTypeManager $contentTypeManager;
  152.         $this->structureManager $structureManager;
  153.         $this->extensionManager $extensionManager;
  154.         $this->sessionManager $sessionManager;
  155.         $this->webspaceManager $webspaceManager;
  156.         $this->documentManager $documentManager;
  157.         $this->formFactory $formFactory;
  158.         $this->inspector $inspector;
  159.         $this->encoder $encoder;
  160.         $this->namespaceRegistry $namespaceRegistry;
  161.         $this->resourceLocatorStrategyPool $resourceLocatorStrategyPool;
  162.         $this->accessControlManager $accessControlManager;
  163.         $this->permissions $permissions;
  164.         $this->security $security;
  165.         // deprecated
  166.         $this->eventDispatcher $eventDispatcher;
  167.     }
  168.     /**
  169.      * TODO: Refactor this .. this should be handled in a listener or in the form, or something
  170.      * {@inheritdoc}
  171.      */
  172.     public function saveExtension(
  173.         $uuid,
  174.         $data,
  175.         $extensionName,
  176.         $webspaceKey,
  177.         $locale,
  178.         $userId
  179.     ) {
  180.         $document $this->loadDocument(
  181.             $uuid,
  182.             $locale,
  183.             [
  184.                 'exclude_ghost' => true,
  185.             ]
  186.         );
  187.         if (null === $document) {
  188.             throw new TranslatedNodeNotFoundException($uuid$locale);
  189.         }
  190.         if (!$document instanceof ExtensionBehavior) {
  191.             throw new \RuntimeException(
  192.                 \sprintf(
  193.                     'Document of class "%s" must implement the ExtensionableBehavior if it is to be extended',
  194.                     \get_class($document)
  195.                 )
  196.             );
  197.         }
  198.         // save data of extensions
  199.         $extension $this->extensionManager->getExtension($document->getStructureType(), $extensionName);
  200.         $node $this->inspector->getNode($document);
  201.         $extension->save($node$data$webspaceKey$locale);
  202.         $extensionData $extension->load($node$webspaceKey$locale);
  203.         $document->setExtension($extension->getName(), $extensionData);
  204.         $this->documentManager->flush();
  205.         $structure $this->documentToStructure($document);
  206.         $event = new ContentNodeEvent($node$structure);
  207.         $this->eventDispatcher->dispatch($eventContentEvents::NODE_POST_SAVE);
  208.         return $structure;
  209.     }
  210.     public function loadByParent(
  211.         $uuid,
  212.         $webspaceKey,
  213.         $languageCode,
  214.         $depth 1,
  215.         $flat true,
  216.         $ignoreExceptions false,
  217.         $excludeGhosts false
  218.     ) {
  219.         $parent null;
  220.         $options = ['load_ghost_content' => true];
  221.         if ($uuid) {
  222.             $parent $this->documentManager->find($uuid$languageCode$options);
  223.         }
  224.         if (null === $parent) {
  225.             $parent $this->getContentDocument($webspaceKey$languageCode$options);
  226.         }
  227.         $fetchDepth = -1;
  228.         if (false === $flat) {
  229.             $fetchDepth $depth;
  230.         }
  231.         $children $this->inspector->getChildren($parent$options);
  232.         $children $this->documentsToStructureCollection(
  233.             $children->toArray(),
  234.             [
  235.                 'exclude_ghost' => $excludeGhosts,
  236.             ]
  237.         );
  238.         if ($flat) {
  239.             foreach ($children as $child) {
  240.                 if (null === $depth || $depth 1) {
  241.                     $childChildren $this->loadByParent(
  242.                         $child->getUuid(),
  243.                         $webspaceKey,
  244.                         $languageCode,
  245.                         $depth 1,
  246.                         $flat,
  247.                         $ignoreExceptions,
  248.                         $excludeGhosts
  249.                     );
  250.                     $children \array_merge($children$childChildren);
  251.                 }
  252.             }
  253.         }
  254.         return $children;
  255.     }
  256.     public function load($uuid$webspaceKey$locale$loadGhostContent false)
  257.     {
  258.         $document $this->documentManager->find(
  259.             $uuid,
  260.             $locale,
  261.             [
  262.                 'load_ghost_content' => $loadGhostContent,
  263.             ]
  264.         );
  265.         if ($document instanceof UnknownDocument) {
  266.             throw new DocumentNotFoundException();
  267.         }
  268.         return $this->documentToStructure($document);
  269.     }
  270.     public function loadStartPage($webspaceKey$locale)
  271.     {
  272.         $startPage $this->getContentDocument($webspaceKey$locale);
  273.         $startPage->setWorkflowStage(WorkflowStage::PUBLISHED);
  274.         $startPage->setNavigationContexts([]);
  275.         return $this->documentToStructure($startPage);
  276.     }
  277.     public function loadBySql2($sql2$locale$webspaceKey$limit null)
  278.     {
  279.         $query $this->documentManager->createQuery($sql2$locale);
  280.         if (null !== $limit) {
  281.             $query->setMaxResults($limit);
  282.         }
  283.         $documents $query->execute();
  284.         return $this->documentsToStructureCollection($documentsnull);
  285.     }
  286.     public function loadByQuery(
  287.         QueryInterface $query,
  288.         $locale,
  289.         $webspaceKey null,
  290.         $excludeGhost true,
  291.         $loadGhostContent false
  292.     ) {
  293.         $options = [
  294.             'exclude_ghost' => $excludeGhost,
  295.             'load_ghost_content' => $loadGhostContent,
  296.         ];
  297.         $documents $this->documentManager->createQuery($query$locale$options)->execute();
  298.         return $this->documentsToStructureCollection($documents$options);
  299.     }
  300.     public function loadNodeAndAncestors(
  301.         $uuid,
  302.         $locale,
  303.         $webspaceKey null,
  304.         $excludeGhost true,
  305.         $excludeShadow true
  306.     ) {
  307.         $document $this->loadDocument(
  308.             $uuid,
  309.             $locale,
  310.             $options = [
  311.                 'load_ghost_content' => true,
  312.                 'exclude_ghost' => $excludeGhost,
  313.                 'exclude_shadow' => $excludeShadow,
  314.             ],
  315.             false
  316.         );
  317.         if (null === $document) {
  318.             return [];
  319.         }
  320.         $documents = [];
  321.         if (!$this->optionsShouldExcludeDocument($document$options)) {
  322.             $documents[] = $document;
  323.         }
  324.         if ($document instanceof HomeDocument) {
  325.             return $this->documentsToStructureCollection($documents$options);
  326.         }
  327.         while ($document) {
  328.             $parentDocument $this->inspector->getParent($document);
  329.             $documents[] = $parentDocument;
  330.             if ($parentDocument instanceof HomeDocument) {
  331.                 return $this->documentsToStructureCollection($documents$options);
  332.             }
  333.             $document $parentDocument;
  334.         }
  335.         throw new \RuntimeException(
  336.             \sprintf(
  337.                 'Did not traverse an instance of HomeDocument when searching for desendants of document "%s"',
  338.                 $uuid
  339.             )
  340.         );
  341.     }
  342.     /**
  343.      * Load/hydrate a shalow structure with the given node.
  344.      * Shallow structures do not have content properties / extensions
  345.      * hydrated.
  346.      *
  347.      * @param string $localization
  348.      * @param string $webspaceKey
  349.      *
  350.      * @return StructureInterface
  351.      */
  352.     public function loadShallowStructureByNode(NodeInterface $contentNode$localization$webspaceKey)
  353.     {
  354.         $document $this->documentManager->find($contentNode->getPath(), $localization);
  355.         return $this->documentToStructure($document);
  356.     }
  357.     public function loadByNode(
  358.         NodeInterface $node,
  359.         $locale,
  360.         $webspaceKey null,
  361.         $excludeGhost true,
  362.         $loadGhostContent false,
  363.         $excludeShadow true
  364.     ) {
  365.         $document $this->loadDocument(
  366.             $node->getIdentifier(),
  367.             $locale,
  368.             [
  369.                 'load_ghost_content' => $loadGhostContent,
  370.                 'exclude_ghost' => $excludeGhost,
  371.                 'exclude_shadow' => $excludeShadow,
  372.             ]
  373.         );
  374.         return $this->documentToStructure($document);
  375.     }
  376.     public function loadBreadcrumb($uuid$locale$webspaceKey)
  377.     {
  378.         $document $this->documentManager->find($uuid$locale);
  379.         $documents = [];
  380.         $contentDocument $this->getContentDocument($webspaceKey$locale);
  381.         $contentDepth $this->inspector->getDepth($contentDocument);
  382.         $document $this->inspector->getParent($document);
  383.         $documentDepth $this->inspector->getDepth($document);
  384.         while ($document instanceof StructureBehavior && $documentDepth >= $contentDepth) {
  385.             $documents[] = $document;
  386.             $document $this->inspector->getParent($document);
  387.             $documentDepth $this->inspector->getDepth($document);
  388.         }
  389.         $items = [];
  390.         foreach ($documents as $document) {
  391.             $items[] = new BreadcrumbItem(
  392.                 $this->inspector->getDepth($document) - $contentDepth,
  393.                 $this->inspector->getUuid($document),
  394.                 $document->getTitle()
  395.             );
  396.         }
  397.         $items \array_reverse($items);
  398.         return $items;
  399.     }
  400.     public function delete($uuid$webspaceKey/*, bool $forceRemoveChildren = false*/)
  401.     {
  402.         $forceRemoveChildren \func_num_args() >= ? (bool) \func_get_arg(2) : false;
  403.         $document $this->documentManager->find($uuid);
  404.         $this->documentManager->remove($document, [
  405.             'force_remove_children' => $forceRemoveChildren,
  406.         ]);
  407.         $this->documentManager->flush();
  408.     }
  409.     public function copyLanguage(
  410.         $uuid,
  411.         $userId,
  412.         $webspaceKey,
  413.         $srcLocale,
  414.         $destLocales,
  415.         $structureType LegacyStructure::TYPE_PAGE
  416.     ) {
  417.         @\trigger_error(
  418.             'The ContentMapperInterface::copyLanguage method is deprecated and will be removed in the future.'
  419.             ' Use DocumentManagerInterface::copyLocale instead.',
  420.             \E_USER_DEPRECATED
  421.         );
  422.         if (!\is_array($destLocales)) {
  423.             $destLocales = [$destLocales];
  424.         }
  425.         $document $this->documentManager->find($uuid$srcLocale);
  426.         $resourceLocatorStrategy null;
  427.         if ($document instanceof ResourceSegmentBehavior) {
  428.             $resourceLocatorStrategy $this->resourceLocatorStrategyPool->getStrategyByWebspaceKey($webspaceKey);
  429.         }
  430.         $parentUuid null;
  431.         if ($document instanceof ParentBehavior) {
  432.             $parentDocument $this->inspector->getParent($document);
  433.             $parentUuid $this->inspector->getUuid($parentDocument);
  434.         }
  435.         foreach ($destLocales as $destLocale) {
  436.             $destDocument $this->documentManager->find(
  437.                 $uuid,
  438.                 $destLocale
  439.             );
  440.             $destDocument->setLocale($destLocale);
  441.             $destDocument->setTitle($document->getTitle());
  442.             $destDocument->setStructureType($document->getStructureType());
  443.             $destDocument->getStructure()->bind($document->getStructure()->toArray());
  444.             if ($document instanceof WorkflowStageBehavior) {
  445.                 $documentAccessor = new DocumentAccessor($destDocument);
  446.                 $documentAccessor->set(WorkflowStageSubscriber::PUBLISHED_FIELDnull);
  447.             }
  448.             if ($document instanceof ExtensionBehavior) {
  449.                 $destDocument->setExtensionsData($document->getExtensionsData());
  450.             }
  451.             // TODO: This can be removed if RoutingAuto replaces the ResourceLocator code.
  452.             if ($destDocument instanceof ResourceSegmentBehavior) {
  453.                 $resourceLocator $resourceLocatorStrategy->generate(
  454.                     $destDocument->getTitle(),
  455.                     $parentUuid,
  456.                     $webspaceKey,
  457.                     $destLocale
  458.                 );
  459.                 $destDocument->setResourceSegment($resourceLocator);
  460.             }
  461.             $this->documentManager->persist($destDocument$destLocale, ['omit_modified_domain_event' => true]);
  462.         }
  463.         $this->documentManager->flush();
  464.         return $this->documentToStructure($document);
  465.     }
  466.     public function orderBefore($uuid$beforeUuid$userId$webspaceKey$locale)
  467.     {
  468.         $document $this->documentManager->find($uuid$locale);
  469.         $this->documentManager->reorder($document$beforeUuid);
  470.         $this->documentManager->persist(
  471.             $document,
  472.             $locale,
  473.             [
  474.                 'user' => $userId,
  475.             ]
  476.         );
  477.         return $this->documentToStructure($document);
  478.     }
  479.     /**
  480.      * TODO: Move this logic to the DocumentManager
  481.      * {@inheritdoc}
  482.      */
  483.     public function orderAt($uuid$position$userId$webspaceKey$locale)
  484.     {
  485.         $document $this->documentManager->find($uuid$locale);
  486.         $parentDocument $this->inspector->getParent($document);
  487.         $siblingDocuments $this->inspector->getChildren($parentDocument);
  488.         $siblings \array_values($siblingDocuments->toArray()); // get indexed array
  489.         $countSiblings \count($siblings);
  490.         $currentPosition \array_search($document$siblings) + 1;
  491.         if ($countSiblings $position || $position <= 0) {
  492.             throw new InvalidOrderPositionException(
  493.                 \sprintf(
  494.                     'Cannot order node "%s" at out-of-range position "%s", must be >= 0 && < %d"',
  495.                     $this->inspector->getPath($document),
  496.                     $position,
  497.                     $countSiblings
  498.                 )
  499.             );
  500.         }
  501.         if ($position === $countSiblings) {
  502.             // move to the end
  503.             $this->documentManager->reorder($documentnull);
  504.         } else {
  505.             if ($currentPosition $position) {
  506.                 $targetSibling $siblings[$position];
  507.                 $this->documentManager->reorder($document$targetSibling->getUuid());
  508.             } elseif ($currentPosition $position) {
  509.                 $targetSibling $siblings[$position 1];
  510.                 $this->documentManager->reorder($document$targetSibling->getUuid());
  511.             }
  512.         }
  513.         $this->documentManager->flush();
  514.         return $this->documentToStructure($document);
  515.     }
  516.     /**
  517.      * Return the resource locator content type.
  518.      *
  519.      * @return ResourceLocator
  520.      */
  521.     public function getResourceLocator()
  522.     {
  523.         return $this->contentTypeManager->get('resource_locator');
  524.     }
  525.     /**
  526.      * Return the content document (aka the home page).
  527.      *
  528.      * @param string $webspaceKey
  529.      *
  530.      * @return BasePageDocument|SnippetDocument
  531.      */
  532.     private function getContentDocument($webspaceKey$locale, array $options = [])
  533.     {
  534.         return $this->documentManager->find(
  535.             $this->sessionManager->getContentPath($webspaceKey),
  536.             $locale,
  537.             $options
  538.         );
  539.     }
  540.     /**
  541.      * Return the node in the content repository which contains all of the routes.
  542.      *
  543.      * @param string $webspaceKey
  544.      * @param string $locale
  545.      * @param string $segment
  546.      *
  547.      * @return NodeInterface
  548.      */
  549.     protected function getRootRouteNode($webspaceKey$locale$segment)
  550.     {
  551.         return $this->documentManager->find(
  552.             $this->sessionManager->getRoutePath($webspaceKey$locale$segment)
  553.         );
  554.     }
  555.     public function convertQueryResultToArray(
  556.         QueryResultInterface $queryResult,
  557.         $webspaceKey,
  558.         $locales,
  559.         $fields,
  560.         $maxDepth,
  561.         $onlyPublished true,
  562.         $permission null
  563.     ) {
  564.         $rootDepth \substr_count($this->sessionManager->getContentPath($webspaceKey), '/');
  565.         $result = [];
  566.         foreach ($locales as $locale) {
  567.             foreach ($queryResult->getRows() as $row) {
  568.                 $pageDepth \substr_count($row->getPath('page'), '/') - $rootDepth;
  569.                 if (null === $maxDepth || $maxDepth || ($maxDepth && $pageDepth <= $maxDepth)) {
  570.                     $item $this->rowToArray($row$locale$webspaceKey$fields$onlyPublished$permission);
  571.                     if (false === $item || \in_array($item$result)) {
  572.                         continue;
  573.                     }
  574.                     $result[] = $item;
  575.                 }
  576.             }
  577.         }
  578.         return $result;
  579.     }
  580.     /**
  581.      * converts a query row to an array.
  582.      */
  583.     private function rowToArray(
  584.         Row $row,
  585.         $locale,
  586.         $webspaceKey,
  587.         $fields,
  588.         $onlyPublished true,
  589.         $permission null
  590.     ) {
  591.         // reset cache
  592.         $this->initializeExtensionCache();
  593.         $templateName $this->encoder->localizedSystemName('template'$locale);
  594.         // check and determine shadow-nodes
  595.         $node $row->getNode('page');
  596.         try {
  597.             $document $this->documentManager->find($node->getIdentifier(), $locale);
  598.         } catch (StructureTypeNotFoundException $e) {
  599.             return false;
  600.         }
  601.         $originalDocument $document;
  602.         if (!$node->hasProperty($templateName) && !$node->hasProperty('template')) {
  603.             return false;
  604.         }
  605.         if ($document instanceof SecurityBehavior
  606.             && $this->security
  607.             && $permission
  608.         ) {
  609.             $permissionKey \array_search($permission$this->permissions);
  610.             $documentWebspaceKey $document->getWebspaceName();
  611.             $documentWebspace $this->webspaceManager->findWebspaceByKey($documentWebspaceKey);
  612.             if ($documentWebspace && $documentWebspace->hasWebsiteSecurity()) {
  613.                 $documentWebspaceSecurity $documentWebspace->getSecurity();
  614.                 $permissions $this->accessControlManager->getUserPermissionByArray(
  615.                     $document->getLocale(),
  616.                     PageAdmin::SECURITY_CONTEXT_PREFIX $documentWebspaceKey,
  617.                     $document->getPermissions(),
  618.                     $this->security->getUser(),
  619.                     $documentWebspaceSecurity->getSystem()
  620.                 );
  621.                 if (isset($permissions[$permissionKey]) && !$permissions[$permissionKey]) {
  622.                     return false;
  623.                 }
  624.             }
  625.         }
  626.         $redirectType null;
  627.         $url null;
  628.         if ($document instanceof RedirectTypeBehavior) {
  629.             $redirectType $document->getRedirectType();
  630.             if (RedirectType::INTERNAL === $redirectType) {
  631.                 $target $document->getRedirectTarget();
  632.                 if (!$target instanceof ResourceSegmentBehavior) {
  633.                     return false;
  634.                 }
  635.                 $url $target->getResourceSegment();
  636.                 $document $target;
  637.                 $node $this->inspector->getNode($document);
  638.             } elseif (RedirectType::EXTERNAL === $redirectType) {
  639.                 $url $document->getRedirectExternal();
  640.             }
  641.         }
  642.         $originLocale $locale;
  643.         if ($document instanceof ShadowLocaleBehavior) {
  644.             $locale $document->isShadowLocaleEnabled() ? $document->getShadowLocale() : $originLocale;
  645.         }
  646.         $nodeState null;
  647.         if ($document instanceof WorkflowStageBehavior) {
  648.             $nodeState $document->getWorkflowStage();
  649.         }
  650.         // if page is not piblished ignore it
  651.         if ($onlyPublished && WorkflowStage::PUBLISHED !== $nodeState) {
  652.             return false;
  653.         }
  654.         if ($document instanceof ResourceSegmentBehavior && !isset($url)) {
  655.             $url $document->getResourceSegment();
  656.         }
  657.         // generate field data
  658.         $fieldsData $this->getFieldsData(
  659.             $row,
  660.             $node,
  661.             $document,
  662.             $fields[$originLocale],
  663.             $document->getStructureType(),
  664.             $webspaceKey,
  665.             $locale
  666.         );
  667.         $structureType $document->getStructureType();
  668.         $shortPath $this->inspector->getContentPath($originalDocument);
  669.         $documentData = [
  670.             'id' => $originalDocument->getUuid(),
  671.             'uuid' => $originalDocument->getUuid(),
  672.             'nodeType' => $redirectType,
  673.             'path' => $shortPath,
  674.             'changed' => $document->getChanged(),
  675.             'changer' => $document->getChanger(),
  676.             'created' => $document->getCreated(),
  677.             'publishedState' => WorkflowStage::PUBLISHED === $nodeState,
  678.             'published' => $document->getPublished(),
  679.             'creator' => $document->getCreator(),
  680.             'title' => $originalDocument->getTitle(),
  681.             'locale' => $locale,
  682.             'webspaceKey' => $this->inspector->getWebspace($document),
  683.             'template' => $structureType,
  684.             'parent' => $this->inspector->getParent($document)->getUuid(),
  685.         ];
  686.         if (isset($url)) {
  687.             $documentData['url'] = $url;
  688.             $documentData['urls'] = $this->inspector->getLocalizedUrlsForPage($document);
  689.         }
  690.         if ($document instanceof LocalizedAuthorBehavior) {
  691.             $documentData['author'] = $document->getAuthor();
  692.             $documentData['authored'] = $document->getAuthored();
  693.         }
  694.         if ($document instanceof OrderBehavior) {
  695.             $documentData['order'] = $document->getSuluOrder();
  696.         }
  697.         return \array_merge($documentData$fieldsData);
  698.     }
  699.     /**
  700.      * Return extracted data (configured by fields array) from node.
  701.      */
  702.     private function getFieldsData(
  703.         Row $row,
  704.         NodeInterface $node,
  705.         $document,
  706.         $fields,
  707.         $templateKey,
  708.         $webspaceKey,
  709.         $locale
  710.     ) {
  711.         $fieldsData = [];
  712.         foreach ($fields as $field) {
  713.             // determine target for data in result array
  714.             if (isset($field['target'])) {
  715.                 if (!isset($fieldsData[$field['target']])) {
  716.                     $fieldsData[$field['target']] = [];
  717.                 }
  718.                 $target = &$fieldsData[$field['target']];
  719.             } else {
  720.                 $target = &$fieldsData;
  721.             }
  722.             // create target
  723.             if (!isset($target[$field['name']])) {
  724.                 $target[$field['name']] = '';
  725.             }
  726.             if (null !== ($data $this->getFieldData(
  727.                 $field,
  728.                 $row,
  729.                 $node,
  730.                 $document,
  731.                 $templateKey,
  732.                 $webspaceKey,
  733.                 $locale
  734.             ))
  735.             ) {
  736.                 $target[$field['name']] = $data;
  737.             }
  738.         }
  739.         return $fieldsData;
  740.     }
  741.     /**
  742.      * Return data for one field.
  743.      */
  744.     private function getFieldData($fieldRow $rowNodeInterface $node$document$templateKey$webspaceKey$locale)
  745.     {
  746.         if (isset($field['column'])) {
  747.             // normal data from node property
  748.             return $row->getValue($field['column']);
  749.         } elseif (isset($field['extension'])) {
  750.             // data from extension
  751.             return $this->getExtensionData(
  752.                 $node,
  753.                 $field['extension'],
  754.                 $field['property'],
  755.                 $webspaceKey,
  756.                 $locale
  757.             );
  758.         } elseif (isset($field['property'])
  759.             && (!isset($field['templateKey']) || $field['templateKey'] === $templateKey)
  760.         ) {
  761.             // not extension data but property of node
  762.             return $this->getPropertyData($document$field['property']);
  763.         }
  764.         return;
  765.     }
  766.     /**
  767.      * Returns data for property.
  768.      */
  769.     private function getPropertyData($documentLegacyProperty $property)
  770.     {
  771.         return $document->getStructure()->getContentViewProperty($property->getName())->getValue();
  772.     }
  773.     /**
  774.      * Returns data for extension and property name.
  775.      */
  776.     private function getExtensionData(
  777.         NodeInterface $node,
  778.         ExtensionInterface $extension,
  779.         $propertyName,
  780.         $webspaceKey,
  781.         $locale
  782.     ) {
  783.         // extension data: load ones
  784.         if (!$this->extensionDataCache->contains($extension->getName())) {
  785.             $this->extensionDataCache->save(
  786.                 $extension->getName(),
  787.                 $this->loadExtensionData(
  788.                     $node,
  789.                     $extension,
  790.                     $webspaceKey,
  791.                     $locale
  792.                 )
  793.             );
  794.         }
  795.         // get extension data from cache
  796.         $data $this->extensionDataCache->fetch($extension->getName());
  797.         return isset($data[$propertyName]) ? $data[$propertyName] : null;
  798.     }
  799.     /**
  800.      * load data from extension.
  801.      */
  802.     private function loadExtensionData(NodeInterface $nodeExtensionInterface $extension$webspaceKey$locale)
  803.     {
  804.         $extension->setLanguageCode($locale$this->namespaceRegistry->getPrefix('extension_localized'), '');
  805.         $data $extension->load(
  806.             $node,
  807.             $webspaceKey,
  808.             $locale
  809.         );
  810.         return $extension->getContentData($data);
  811.     }
  812.     private function loadDocument($pathOrUuid$locale$options$shouldExclude true)
  813.     {
  814.         $document $this->documentManager->find(
  815.             $pathOrUuid,
  816.             $locale,
  817.             [
  818.                 'load_ghost_content' => isset($options['load_ghost_content']) ? $options['load_ghost_content'] : true,
  819.             ]
  820.         );
  821.         if ($shouldExclude && $this->optionsShouldExcludeDocument($document$options)) {
  822.             return;
  823.         }
  824.         return $document;
  825.     }
  826.     private function optionsShouldExcludeDocument($document, array $options null)
  827.     {
  828.         if (null === $options) {
  829.             return false;
  830.         }
  831.         $options \array_merge(
  832.             [
  833.                 'exclude_ghost' => true,
  834.                 'exclude_shadow' => true,
  835.             ],
  836.             $options
  837.         );
  838.         $state $this->inspector->getLocalizationState($document);
  839.         if ($options['exclude_ghost'] && LocalizationState::GHOST == $state) {
  840.             return true;
  841.         }
  842.         if ($options['exclude_ghost'] && $options['exclude_shadow'] && LocalizationState::SHADOW == $state) {
  843.             return true;
  844.         }
  845.         return false;
  846.     }
  847.     /**
  848.      * Initializes cache of extension data.
  849.      */
  850.     private function initializeExtensionCache()
  851.     {
  852.         $this->extensionDataCache = new ArrayCache();
  853.     }
  854.     /**
  855.      * Return a structure bridge corresponding to the given document.
  856.      *
  857.      * @param StructureBehavior $document
  858.      *
  859.      * @return StructureInterface
  860.      */
  861.     private function documentToStructure($document)
  862.     {
  863.         if (null === $document) {
  864.             return;
  865.         }
  866.         $structure $this->inspector->getStructureMetadata($document);
  867.         $documentAlias $this->inspector->getMetadata($document)->getAlias();
  868.         $structureBridge $this->structureManager->wrapStructure($documentAlias$structure);
  869.         $structureBridge->setDocument($document);
  870.         return $structureBridge;
  871.     }
  872.     /**
  873.      * Return a collection of structures for the given documents, optionally filtering according
  874.      * to the given options (as defined in optionsShouldExcludeDocument).
  875.      *
  876.      * @param object[] $documents
  877.      * @param array|null $filterOptions
  878.      */
  879.     private function documentsToStructureCollection($documents$filterOptions null)
  880.     {
  881.         $collection = [];
  882.         foreach ($documents as $document) {
  883.             if (!$document instanceof StructureBehavior) {
  884.                 continue;
  885.             }
  886.             if ($this->optionsShouldExcludeDocument($document$filterOptions)) {
  887.                 continue;
  888.             }
  889.             $collection[] = $this->documentToStructure($document);
  890.         }
  891.         return $collection;
  892.     }
  893. }