vendor/sulu/sulu/src/Sulu/Component/Content/Mapper/ContentMapper.php line 309
<?php/** This file is part of Sulu.** (c) Sulu GmbH** This source file is subject to the MIT license that is bundled* with this source code in the file LICENSE.*/namespace Sulu\Component\Content\Mapper;use Doctrine\Common\Cache\ArrayCache;use Doctrine\Common\Cache\Cache;use Jackalope\Query\Row;use PHPCR\NodeInterface;use PHPCR\Query\QueryInterface;use PHPCR\Query\QueryResultInterface;use Sulu\Bundle\DocumentManagerBundle\Bridge\DocumentInspector;use Sulu\Bundle\DocumentManagerBundle\Bridge\PropertyEncoder;use Sulu\Bundle\PageBundle\Admin\PageAdmin;use Sulu\Bundle\PageBundle\Document\BasePageDocument;use Sulu\Bundle\PageBundle\Document\HomeDocument;use Sulu\Bundle\SnippetBundle\Document\SnippetDocument;use Sulu\Component\Content\BreadcrumbItem;use Sulu\Component\Content\Compat\Property as LegacyProperty;use Sulu\Component\Content\Compat\Structure as LegacyStructure;use Sulu\Component\Content\Compat\StructureInterface;use Sulu\Component\Content\Compat\StructureManagerInterface;use Sulu\Component\Content\ContentTypeManager;use Sulu\Component\Content\ContentTypeManagerInterface;use Sulu\Component\Content\Document\Behavior\ExtensionBehavior;use Sulu\Component\Content\Document\Behavior\LocalizedAuthorBehavior;use Sulu\Component\Content\Document\Behavior\OrderBehavior;use Sulu\Component\Content\Document\Behavior\RedirectTypeBehavior;use Sulu\Component\Content\Document\Behavior\ResourceSegmentBehavior;use Sulu\Component\Content\Document\Behavior\SecurityBehavior;use Sulu\Component\Content\Document\Behavior\ShadowLocaleBehavior;use Sulu\Component\Content\Document\Behavior\StructureBehavior;use Sulu\Component\Content\Document\Behavior\WorkflowStageBehavior;use Sulu\Component\Content\Document\LocalizationState;use Sulu\Component\Content\Document\RedirectType;use Sulu\Component\Content\Document\Subscriber\WorkflowStageSubscriber;use Sulu\Component\Content\Document\WorkflowStage;use Sulu\Component\Content\Exception\InvalidOrderPositionException;use Sulu\Component\Content\Exception\TranslatedNodeNotFoundException;use Sulu\Component\Content\Extension\ExtensionInterface;use Sulu\Component\Content\Extension\ExtensionManagerInterface;use Sulu\Component\Content\Mapper\Event\ContentNodeEvent;use Sulu\Component\Content\Metadata\Factory\Exception\StructureTypeNotFoundException;use Sulu\Component\Content\Types\ResourceLocator;use Sulu\Component\Content\Types\ResourceLocator\Strategy\ResourceLocatorStrategyPoolInterface;use Sulu\Component\DocumentManager\Behavior\Mapping\ParentBehavior;use Sulu\Component\DocumentManager\Document\UnknownDocument;use Sulu\Component\DocumentManager\DocumentAccessor;use Sulu\Component\DocumentManager\DocumentManagerInterface;use Sulu\Component\DocumentManager\Exception\DocumentNotFoundException;use Sulu\Component\DocumentManager\NamespaceRegistry;use Sulu\Component\PHPCR\SessionManager\SessionManagerInterface;use Sulu\Component\Security\Authorization\AccessControl\AccessControlManagerInterface;use Sulu\Component\Webspace\Manager\WebspaceManagerInterface;use Symfony\Component\EventDispatcher\EventDispatcherInterface;use Symfony\Component\Form\FormFactoryInterface;use Symfony\Component\Security\Core\Security;/*** Maps content nodes to phpcr nodes with content types and provides utility function to handle content nodes.** @deprecated since 1.0-? use the DocumentManager instead*/class ContentMapper implements ContentMapperInterface{/*** @var ContentTypeManager*/private $contentTypeManager;/*** @var StructureManagerInterface*/private $structureManager;/*** @var ExtensionManagerInterface*/private $extensionManager;/*** @var SessionManagerInterface*/private $sessionManager;/*** @var EventDispatcherInterface*/private $eventDispatcher;/*** @var WebspaceManagerInterface*/private $webspaceManager;/*** @var Cache*/private $extensionDataCache;/*** @var ResourceLocatorStrategyPoolInterface*/private $resourceLocatorStrategyPool;/*** @var DocumentManagerInterface*/private $documentManager;/*** @var FormFactoryInterface*/private $formFactory;/*** @var DocumentInspector*/private $inspector;/*** @var PropertyEncoder*/private $encoder;/*** @var NamespaceRegistry*/private $namespaceRegistry;/*** @var AccessControlManagerInterface*/private $accessControlManager;/*** @var array*/private $permissions;/*** @var ?Security*/private $security;public function __construct(DocumentManagerInterface $documentManager,WebspaceManagerInterface $webspaceManager,FormFactoryInterface $formFactory,DocumentInspector $inspector,PropertyEncoder $encoder,StructureManagerInterface $structureManager,ExtensionManagerInterface $extensionManager,ContentTypeManagerInterface $contentTypeManager,SessionManagerInterface $sessionManager,EventDispatcherInterface $eventDispatcher,ResourceLocatorStrategyPoolInterface $resourceLocatorStrategyPool,NamespaceRegistry $namespaceRegistry,AccessControlManagerInterface $accessControlManager,$permissions,Security $security = null) {$this->contentTypeManager = $contentTypeManager;$this->structureManager = $structureManager;$this->extensionManager = $extensionManager;$this->sessionManager = $sessionManager;$this->webspaceManager = $webspaceManager;$this->documentManager = $documentManager;$this->formFactory = $formFactory;$this->inspector = $inspector;$this->encoder = $encoder;$this->namespaceRegistry = $namespaceRegistry;$this->resourceLocatorStrategyPool = $resourceLocatorStrategyPool;$this->accessControlManager = $accessControlManager;$this->permissions = $permissions;$this->security = $security;// deprecated$this->eventDispatcher = $eventDispatcher;}/*** TODO: Refactor this .. this should be handled in a listener or in the form, or something* {@inheritdoc}*/public function saveExtension($uuid,$data,$extensionName,$webspaceKey,$locale,$userId) {$document = $this->loadDocument($uuid,$locale,['exclude_ghost' => true,]);if (null === $document) {throw new TranslatedNodeNotFoundException($uuid, $locale);}if (!$document instanceof ExtensionBehavior) {throw new \RuntimeException(\sprintf('Document of class "%s" must implement the ExtensionableBehavior if it is to be extended',\get_class($document)));}// save data of extensions$extension = $this->extensionManager->getExtension($document->getStructureType(), $extensionName);$node = $this->inspector->getNode($document);$extension->save($node, $data, $webspaceKey, $locale);$extensionData = $extension->load($node, $webspaceKey, $locale);$document->setExtension($extension->getName(), $extensionData);$this->documentManager->flush();$structure = $this->documentToStructure($document);$event = new ContentNodeEvent($node, $structure);$this->eventDispatcher->dispatch($event, ContentEvents::NODE_POST_SAVE);return $structure;}public function loadByParent($uuid,$webspaceKey,$languageCode,$depth = 1,$flat = true,$ignoreExceptions = false,$excludeGhosts = false) {$parent = null;$options = ['load_ghost_content' => true];if ($uuid) {$parent = $this->documentManager->find($uuid, $languageCode, $options);}if (null === $parent) {$parent = $this->getContentDocument($webspaceKey, $languageCode, $options);}$fetchDepth = -1;if (false === $flat) {$fetchDepth = $depth;}$children = $this->inspector->getChildren($parent, $options);$children = $this->documentsToStructureCollection($children->toArray(),['exclude_ghost' => $excludeGhosts,]);if ($flat) {foreach ($children as $child) {if (null === $depth || $depth > 1) {$childChildren = $this->loadByParent($child->getUuid(),$webspaceKey,$languageCode,$depth - 1,$flat,$ignoreExceptions,$excludeGhosts);$children = \array_merge($children, $childChildren);}}}return $children;}public function load($uuid, $webspaceKey, $locale, $loadGhostContent = false){$document = $this->documentManager->find($uuid,$locale,['load_ghost_content' => $loadGhostContent,]);if ($document instanceof UnknownDocument) {throw new DocumentNotFoundException();}return $this->documentToStructure($document);}public function loadStartPage($webspaceKey, $locale){$startPage = $this->getContentDocument($webspaceKey, $locale);$startPage->setWorkflowStage(WorkflowStage::PUBLISHED);$startPage->setNavigationContexts([]);return $this->documentToStructure($startPage);}public function loadBySql2($sql2, $locale, $webspaceKey, $limit = null){$query = $this->documentManager->createQuery($sql2, $locale);if (null !== $limit) {$query->setMaxResults($limit);}$documents = $query->execute();return $this->documentsToStructureCollection($documents, null);}public function loadByQuery(QueryInterface $query,$locale,$webspaceKey = null,$excludeGhost = true,$loadGhostContent = false) {$options = ['exclude_ghost' => $excludeGhost,'load_ghost_content' => $loadGhostContent,];$documents = $this->documentManager->createQuery($query, $locale, $options)->execute();return $this->documentsToStructureCollection($documents, $options);}public function loadNodeAndAncestors($uuid,$locale,$webspaceKey = null,$excludeGhost = true,$excludeShadow = true) {$document = $this->loadDocument($uuid,$locale,$options = ['load_ghost_content' => true,'exclude_ghost' => $excludeGhost,'exclude_shadow' => $excludeShadow,],false);if (null === $document) {return [];}$documents = [];if (!$this->optionsShouldExcludeDocument($document, $options)) {$documents[] = $document;}if ($document instanceof HomeDocument) {return $this->documentsToStructureCollection($documents, $options);}while ($document) {$parentDocument = $this->inspector->getParent($document);$documents[] = $parentDocument;if ($parentDocument instanceof HomeDocument) {return $this->documentsToStructureCollection($documents, $options);}$document = $parentDocument;}throw new \RuntimeException(\sprintf('Did not traverse an instance of HomeDocument when searching for desendants of document "%s"',$uuid));}/*** Load/hydrate a shalow structure with the given node.* Shallow structures do not have content properties / extensions* hydrated.** @param string $localization* @param string $webspaceKey** @return StructureInterface*/public function loadShallowStructureByNode(NodeInterface $contentNode, $localization, $webspaceKey){$document = $this->documentManager->find($contentNode->getPath(), $localization);return $this->documentToStructure($document);}public function loadByNode(NodeInterface $node,$locale,$webspaceKey = null,$excludeGhost = true,$loadGhostContent = false,$excludeShadow = true) {$document = $this->loadDocument($node->getIdentifier(),$locale,['load_ghost_content' => $loadGhostContent,'exclude_ghost' => $excludeGhost,'exclude_shadow' => $excludeShadow,]);return $this->documentToStructure($document);}public function loadBreadcrumb($uuid, $locale, $webspaceKey){$document = $this->documentManager->find($uuid, $locale);$documents = [];$contentDocument = $this->getContentDocument($webspaceKey, $locale);$contentDepth = $this->inspector->getDepth($contentDocument);$document = $this->inspector->getParent($document);$documentDepth = $this->inspector->getDepth($document);while ($document instanceof StructureBehavior && $documentDepth >= $contentDepth) {$documents[] = $document;$document = $this->inspector->getParent($document);$documentDepth = $this->inspector->getDepth($document);}$items = [];foreach ($documents as $document) {$items[] = new BreadcrumbItem($this->inspector->getDepth($document) - $contentDepth,$this->inspector->getUuid($document),$document->getTitle());}$items = \array_reverse($items);return $items;}public function delete($uuid, $webspaceKey/*, bool $forceRemoveChildren = false*/){$forceRemoveChildren = \func_num_args() >= 3 ? (bool) \func_get_arg(2) : false;$document = $this->documentManager->find($uuid);$this->documentManager->remove($document, ['force_remove_children' => $forceRemoveChildren,]);$this->documentManager->flush();}public function copyLanguage($uuid,$userId,$webspaceKey,$srcLocale,$destLocales,$structureType = LegacyStructure::TYPE_PAGE) {@\trigger_error('The ContentMapperInterface::copyLanguage method is deprecated and will be removed in the future.'. ' Use DocumentManagerInterface::copyLocale instead.',\E_USER_DEPRECATED);if (!\is_array($destLocales)) {$destLocales = [$destLocales];}$document = $this->documentManager->find($uuid, $srcLocale);$resourceLocatorStrategy = null;if ($document instanceof ResourceSegmentBehavior) {$resourceLocatorStrategy = $this->resourceLocatorStrategyPool->getStrategyByWebspaceKey($webspaceKey);}$parentUuid = null;if ($document instanceof ParentBehavior) {$parentDocument = $this->inspector->getParent($document);$parentUuid = $this->inspector->getUuid($parentDocument);}foreach ($destLocales as $destLocale) {$destDocument = $this->documentManager->find($uuid,$destLocale);$destDocument->setLocale($destLocale);$destDocument->setTitle($document->getTitle());$destDocument->setStructureType($document->getStructureType());$destDocument->getStructure()->bind($document->getStructure()->toArray());if ($document instanceof WorkflowStageBehavior) {$documentAccessor = new DocumentAccessor($destDocument);$documentAccessor->set(WorkflowStageSubscriber::PUBLISHED_FIELD, null);}if ($document instanceof ExtensionBehavior) {$destDocument->setExtensionsData($document->getExtensionsData());}// TODO: This can be removed if RoutingAuto replaces the ResourceLocator code.if ($destDocument instanceof ResourceSegmentBehavior) {$resourceLocator = $resourceLocatorStrategy->generate($destDocument->getTitle(),$parentUuid,$webspaceKey,$destLocale);$destDocument->setResourceSegment($resourceLocator);}$this->documentManager->persist($destDocument, $destLocale, ['omit_modified_domain_event' => true]);}$this->documentManager->flush();return $this->documentToStructure($document);}public function orderBefore($uuid, $beforeUuid, $userId, $webspaceKey, $locale){$document = $this->documentManager->find($uuid, $locale);$this->documentManager->reorder($document, $beforeUuid);$this->documentManager->persist($document,$locale,['user' => $userId,]);return $this->documentToStructure($document);}/*** TODO: Move this logic to the DocumentManager* {@inheritdoc}*/public function orderAt($uuid, $position, $userId, $webspaceKey, $locale){$document = $this->documentManager->find($uuid, $locale);$parentDocument = $this->inspector->getParent($document);$siblingDocuments = $this->inspector->getChildren($parentDocument);$siblings = \array_values($siblingDocuments->toArray()); // get indexed array$countSiblings = \count($siblings);$currentPosition = \array_search($document, $siblings) + 1;if ($countSiblings < $position || $position <= 0) {throw new InvalidOrderPositionException(\sprintf('Cannot order node "%s" at out-of-range position "%s", must be >= 0 && < %d"',$this->inspector->getPath($document),$position,$countSiblings));}if ($position === $countSiblings) {// move to the end$this->documentManager->reorder($document, null);} else {if ($currentPosition < $position) {$targetSibling = $siblings[$position];$this->documentManager->reorder($document, $targetSibling->getUuid());} elseif ($currentPosition > $position) {$targetSibling = $siblings[$position - 1];$this->documentManager->reorder($document, $targetSibling->getUuid());}}$this->documentManager->flush();return $this->documentToStructure($document);}/*** Return the resource locator content type.** @return ResourceLocator*/public function getResourceLocator(){return $this->contentTypeManager->get('resource_locator');}/*** Return the content document (aka the home page).** @param string $webspaceKey** @return BasePageDocument|SnippetDocument*/private function getContentDocument($webspaceKey, $locale, array $options = []){return $this->documentManager->find($this->sessionManager->getContentPath($webspaceKey),$locale,$options);}/*** Return the node in the content repository which contains all of the routes.** @param string $webspaceKey* @param string $locale* @param string $segment** @return NodeInterface*/protected function getRootRouteNode($webspaceKey, $locale, $segment){return $this->documentManager->find($this->sessionManager->getRoutePath($webspaceKey, $locale, $segment));}public function convertQueryResultToArray(QueryResultInterface $queryResult,$webspaceKey,$locales,$fields,$maxDepth,$onlyPublished = true,$permission = null) {$rootDepth = \substr_count($this->sessionManager->getContentPath($webspaceKey), '/');$result = [];foreach ($locales as $locale) {foreach ($queryResult->getRows() as $row) {$pageDepth = \substr_count($row->getPath('page'), '/') - $rootDepth;if (null === $maxDepth || $maxDepth < 0 || ($maxDepth > 0 && $pageDepth <= $maxDepth)) {$item = $this->rowToArray($row, $locale, $webspaceKey, $fields, $onlyPublished, $permission);if (false === $item || \in_array($item, $result)) {continue;}$result[] = $item;}}}return $result;}/*** converts a query row to an array.*/private function rowToArray(Row $row,$locale,$webspaceKey,$fields,$onlyPublished = true,$permission = null) {// reset cache$this->initializeExtensionCache();$templateName = $this->encoder->localizedSystemName('template', $locale);// check and determine shadow-nodes$node = $row->getNode('page');try {$document = $this->documentManager->find($node->getIdentifier(), $locale);} catch (StructureTypeNotFoundException $e) {return false;}$originalDocument = $document;if (!$node->hasProperty($templateName) && !$node->hasProperty('template')) {return false;}if ($document instanceof SecurityBehavior&& $this->security&& $permission) {$permissionKey = \array_search($permission, $this->permissions);$documentWebspaceKey = $document->getWebspaceName();$documentWebspace = $this->webspaceManager->findWebspaceByKey($documentWebspaceKey);if ($documentWebspace && $documentWebspace->hasWebsiteSecurity()) {$documentWebspaceSecurity = $documentWebspace->getSecurity();$permissions = $this->accessControlManager->getUserPermissionByArray($document->getLocale(),PageAdmin::SECURITY_CONTEXT_PREFIX . $documentWebspaceKey,$document->getPermissions(),$this->security->getUser(),$documentWebspaceSecurity->getSystem());if (isset($permissions[$permissionKey]) && !$permissions[$permissionKey]) {return false;}}}$redirectType = null;$url = null;if ($document instanceof RedirectTypeBehavior) {$redirectType = $document->getRedirectType();if (RedirectType::INTERNAL === $redirectType) {$target = $document->getRedirectTarget();if (!$target instanceof ResourceSegmentBehavior) {return false;}$url = $target->getResourceSegment();$document = $target;$node = $this->inspector->getNode($document);} elseif (RedirectType::EXTERNAL === $redirectType) {$url = $document->getRedirectExternal();}}$originLocale = $locale;if ($document instanceof ShadowLocaleBehavior) {$locale = $document->isShadowLocaleEnabled() ? $document->getShadowLocale() : $originLocale;}$nodeState = null;if ($document instanceof WorkflowStageBehavior) {$nodeState = $document->getWorkflowStage();}// if page is not piblished ignore itif ($onlyPublished && WorkflowStage::PUBLISHED !== $nodeState) {return false;}if ($document instanceof ResourceSegmentBehavior && !isset($url)) {$url = $document->getResourceSegment();}// generate field data$fieldsData = $this->getFieldsData($row,$node,$document,$fields[$originLocale],$document->getStructureType(),$webspaceKey,$locale);$structureType = $document->getStructureType();$shortPath = $this->inspector->getContentPath($originalDocument);$documentData = ['id' => $originalDocument->getUuid(),'uuid' => $originalDocument->getUuid(),'nodeType' => $redirectType,'path' => $shortPath,'changed' => $document->getChanged(),'changer' => $document->getChanger(),'created' => $document->getCreated(),'publishedState' => WorkflowStage::PUBLISHED === $nodeState,'published' => $document->getPublished(),'creator' => $document->getCreator(),'title' => $originalDocument->getTitle(),'locale' => $locale,'webspaceKey' => $this->inspector->getWebspace($document),'template' => $structureType,'parent' => $this->inspector->getParent($document)->getUuid(),];if (isset($url)) {$documentData['url'] = $url;$documentData['urls'] = $this->inspector->getLocalizedUrlsForPage($document);}if ($document instanceof LocalizedAuthorBehavior) {$documentData['author'] = $document->getAuthor();$documentData['authored'] = $document->getAuthored();}if ($document instanceof OrderBehavior) {$documentData['order'] = $document->getSuluOrder();}return \array_merge($documentData, $fieldsData);}/*** Return extracted data (configured by fields array) from node.*/private function getFieldsData(Row $row,NodeInterface $node,$document,$fields,$templateKey,$webspaceKey,$locale) {$fieldsData = [];foreach ($fields as $field) {// determine target for data in result arrayif (isset($field['target'])) {if (!isset($fieldsData[$field['target']])) {$fieldsData[$field['target']] = [];}$target = &$fieldsData[$field['target']];} else {$target = &$fieldsData;}// create targetif (!isset($target[$field['name']])) {$target[$field['name']] = '';}if (null !== ($data = $this->getFieldData($field,$row,$node,$document,$templateKey,$webspaceKey,$locale))) {$target[$field['name']] = $data;}}return $fieldsData;}/*** Return data for one field.*/private function getFieldData($field, Row $row, NodeInterface $node, $document, $templateKey, $webspaceKey, $locale){if (isset($field['column'])) {// normal data from node propertyreturn $row->getValue($field['column']);} elseif (isset($field['extension'])) {// data from extensionreturn $this->getExtensionData($node,$field['extension'],$field['property'],$webspaceKey,$locale);} elseif (isset($field['property'])&& (!isset($field['templateKey']) || $field['templateKey'] === $templateKey)) {// not extension data but property of nodereturn $this->getPropertyData($document, $field['property']);}return;}/*** Returns data for property.*/private function getPropertyData($document, LegacyProperty $property){return $document->getStructure()->getContentViewProperty($property->getName())->getValue();}/*** Returns data for extension and property name.*/private function getExtensionData(NodeInterface $node,ExtensionInterface $extension,$propertyName,$webspaceKey,$locale) {// extension data: load onesif (!$this->extensionDataCache->contains($extension->getName())) {$this->extensionDataCache->save($extension->getName(),$this->loadExtensionData($node,$extension,$webspaceKey,$locale));}// get extension data from cache$data = $this->extensionDataCache->fetch($extension->getName());return isset($data[$propertyName]) ? $data[$propertyName] : null;}/*** load data from extension.*/private function loadExtensionData(NodeInterface $node, ExtensionInterface $extension, $webspaceKey, $locale){$extension->setLanguageCode($locale, $this->namespaceRegistry->getPrefix('extension_localized'), '');$data = $extension->load($node,$webspaceKey,$locale);return $extension->getContentData($data);}private function loadDocument($pathOrUuid, $locale, $options, $shouldExclude = true){$document = $this->documentManager->find($pathOrUuid,$locale,['load_ghost_content' => isset($options['load_ghost_content']) ? $options['load_ghost_content'] : true,]);if ($shouldExclude && $this->optionsShouldExcludeDocument($document, $options)) {return;}return $document;}private function optionsShouldExcludeDocument($document, array $options = null){if (null === $options) {return false;}$options = \array_merge(['exclude_ghost' => true,'exclude_shadow' => true,],$options);$state = $this->inspector->getLocalizationState($document);if ($options['exclude_ghost'] && LocalizationState::GHOST == $state) {return true;}if ($options['exclude_ghost'] && $options['exclude_shadow'] && LocalizationState::SHADOW == $state) {return true;}return false;}/*** Initializes cache of extension data.*/private function initializeExtensionCache(){$this->extensionDataCache = new ArrayCache();}/*** Return a structure bridge corresponding to the given document.** @param StructureBehavior $document** @return StructureInterface*/private function documentToStructure($document){if (null === $document) {return;}$structure = $this->inspector->getStructureMetadata($document);$documentAlias = $this->inspector->getMetadata($document)->getAlias();$structureBridge = $this->structureManager->wrapStructure($documentAlias, $structure);$structureBridge->setDocument($document);return $structureBridge;}/*** Return a collection of structures for the given documents, optionally filtering according* to the given options (as defined in optionsShouldExcludeDocument).** @param object[] $documents* @param array|null $filterOptions*/private function documentsToStructureCollection($documents, $filterOptions = null){$collection = [];foreach ($documents as $document) {if (!$document instanceof StructureBehavior) {continue;}if ($this->optionsShouldExcludeDocument($document, $filterOptions)) {continue;}$collection[] = $this->documentToStructure($document);}return $collection;}}