vendor/sulu/sulu/src/Sulu/Bundle/DocumentManagerBundle/Bridge/DocumentInspector.php line 99

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\DocumentManagerBundle\Bridge;
  11. use Sulu\Bundle\PageBundle\Document\BasePageDocument;
  12. use Sulu\Component\Content\Document\Behavior\ShadowLocaleBehavior;
  13. use Sulu\Component\Content\Document\Behavior\StructureBehavior;
  14. use Sulu\Component\Content\Document\LocalizationState;
  15. use Sulu\Component\Content\Document\Subscriber\ShadowLocaleSubscriber;
  16. use Sulu\Component\Content\Document\Subscriber\WorkflowStageSubscriber;
  17. use Sulu\Component\Content\Document\WorkflowStage;
  18. use Sulu\Component\Content\Metadata\Factory\Exception\StructureTypeNotFoundException;
  19. use Sulu\Component\Content\Metadata\Factory\StructureMetadataFactoryInterface;
  20. use Sulu\Component\Content\Metadata\StructureMetadata;
  21. use Sulu\Component\DocumentManager\Behavior\Mapping\LocaleBehavior;
  22. use Sulu\Component\DocumentManager\Behavior\Mapping\PathBehavior;
  23. use Sulu\Component\DocumentManager\DocumentInspector as BaseDocumentInspector;
  24. use Sulu\Component\DocumentManager\DocumentRegistry;
  25. use Sulu\Component\DocumentManager\Metadata;
  26. use Sulu\Component\DocumentManager\MetadataFactoryInterface;
  27. use Sulu\Component\DocumentManager\NamespaceRegistry;
  28. use Sulu\Component\DocumentManager\PathSegmentRegistry;
  29. use Sulu\Component\DocumentManager\ProxyFactory;
  30. use Sulu\Component\Webspace\Manager\WebspaceManagerInterface;
  31. use Sulu\Component\Webspace\Webspace;
  32. /**
  33. * This class infers information about documents, for example
  34. * the documents locale, webspace, path, etc.
  35. *
  36. * TODO: Add feature to the document manager to map inspectors for each document class
  37. */
  38. class DocumentInspector extends BaseDocumentInspector
  39. {
  40. public function __construct(
  41. DocumentRegistry $documentRegistry,
  42. PathSegmentRegistry $pathSegmentRegistry,
  43. private NamespaceRegistry $namespaceRegistry,
  44. ProxyFactory $proxyFactory,
  45. private MetadataFactoryInterface $metadataFactory,
  46. private StructureMetadataFactoryInterface $structureFactory,
  47. private PropertyEncoder $encoder,
  48. private WebspaceManagerInterface $webspaceManager
  49. ) {
  50. parent::__construct($documentRegistry, $pathSegmentRegistry, $proxyFactory);
  51. }
  52. /**
  53. * Return the webspace name for the given document.
  54. *
  55. * @param object $document
  56. *
  57. * @return string
  58. */
  59. public function getWebspace($document)
  60. {
  61. return $this->extractWebspaceFromPath($this->getPath($document));
  62. }
  63. /**
  64. * Return the path of the document in relation to the content root.
  65. *
  66. * TODO: We need a better solution for retrieving webspace paths (the existing
  67. * "session manager" is not a good solution).
  68. *
  69. * @return string
  70. */
  71. public function getContentPath(PathBehavior $document)
  72. {
  73. $path = $this->getPath($document);
  74. $webspaceKey = $this->getWebspace($document);
  75. return \str_replace(
  76. \sprintf(
  77. '/%s/%s/%s',
  78. $this->pathSegmentRegistry->getPathSegment('base'),
  79. $webspaceKey,
  80. $this->pathSegmentRegistry->getPathSegment('content')
  81. ),
  82. '',
  83. $path
  84. );
  85. }
  86. /**
  87. * Return the structure for the given StructureBehavior implementing document.
  88. *
  89. * @return StructureMetadata|null
  90. */
  91. public function getStructureMetadata(StructureBehavior $document)
  92. {
  93. try {
  94. return $this->structureFactory->getStructureMetadata(
  95. $this->getMetadata($document)->getAlias(),
  96. $document->getStructureType()
  97. );
  98. } catch (StructureTypeNotFoundException $exception) {
  99. return;
  100. }
  101. }
  102. /**
  103. * Return the (DocumentManager) Metadata for the given document.
  104. *
  105. * @param object $document
  106. *
  107. * @return Metadata
  108. */
  109. public function getMetadata($document)
  110. {
  111. return $this->metadataFactory->getMetadataForClass(\get_class($document));
  112. }
  113. /**
  114. * Return the localization state of the node.
  115. *
  116. * @param object $document
  117. *
  118. * @return string
  119. */
  120. public function getLocalizationState($document)
  121. {
  122. if ($document instanceof ShadowLocaleBehavior) {
  123. if (true === $document->isShadowLocaleEnabled()) {
  124. return LocalizationState::SHADOW;
  125. }
  126. }
  127. $originalLocale = $document->getOriginalLocale();
  128. $currentLocale = $document->getLocale();
  129. if ($originalLocale === $currentLocale) {
  130. return LocalizationState::LOCALIZED;
  131. }
  132. return LocalizationState::GHOST;
  133. }
  134. /**
  135. * Return the locale for the given document or null
  136. * if the document is not managed.
  137. *
  138. * @return string|null
  139. */
  140. public function getLocale($document)
  141. {
  142. if ($document instanceof LocaleBehavior) {
  143. return $document->getLocale();
  144. }
  145. if ($this->documentRegistry->hasDocument($document)) {
  146. return $this->documentRegistry->getLocaleForDocument($document);
  147. }
  148. return null;
  149. }
  150. /**
  151. * Return the original (requested) locale for this document before
  152. * any fallback logic was applied to it.
  153. *
  154. * @param object $document
  155. *
  156. * @return string
  157. */
  158. public function getOriginalLocale($document)
  159. {
  160. return $this->documentRegistry->getOriginalLocaleForDocument($document);
  161. }
  162. /**
  163. * Return the concrete localizations for the given document.
  164. *
  165. * @param object $document
  166. *
  167. * @return array<string>
  168. */
  169. public function getLocales($document)
  170. {
  171. $locales = [];
  172. $node = $this->getNode($document);
  173. $prefix = $this->namespaceRegistry->getPrefix('system_localized');
  174. foreach ($node->getProperties() as $property) {
  175. \preg_match(
  176. \sprintf('/^%s:([a-zA-Z_]*?)-.*/', $prefix),
  177. $property->getName(),
  178. $matches
  179. );
  180. if ($matches) {
  181. $locales[$matches[1]] = $matches[1];
  182. }
  183. }
  184. return \array_values(\array_unique($locales));
  185. }
  186. /**
  187. * Return locales which are not shadows.
  188. *
  189. * @param ShadowLocaleBehavior $document
  190. *
  191. * @return array<string>
  192. */
  193. public function getConcreteLocales($document)
  194. {
  195. $locales = $this->getLocales($document);
  196. if ($document instanceof ShadowLocaleBehavior) {
  197. $locales = \array_diff($locales, \array_keys($this->getShadowLocales($document)));
  198. }
  199. return \array_values($locales);
  200. }
  201. /**
  202. * Return the enabled shadow locales for the given document.
  203. *
  204. * @return array
  205. */
  206. public function getShadowLocales(ShadowLocaleBehavior $document)
  207. {
  208. $shadowLocales = [];
  209. $locales = $this->getLocales($document);
  210. $node = $this->getNode($document);
  211. foreach ($locales as $locale) {
  212. $shadowEnabledName = $this->encoder->localizedSystemName(
  213. ShadowLocaleSubscriber::SHADOW_ENABLED_FIELD,
  214. $locale
  215. );
  216. $shadowLocaleName = $this->encoder->localizedSystemName(
  217. ShadowLocaleSubscriber::SHADOW_LOCALE_FIELD,
  218. $locale
  219. );
  220. if ($node->getPropertyValueWithDefault($shadowEnabledName, false)) {
  221. $shadowLocales[$locale] = $node->getPropertyValue($shadowLocaleName);
  222. }
  223. }
  224. return $shadowLocales;
  225. }
  226. /**
  227. * Return the published locales for the given document.
  228. *
  229. * @return array
  230. */
  231. public function getPublishedLocales(ShadowLocaleBehavior $document)
  232. {
  233. $node = $this->getNode($document);
  234. $locales = $this->getLocales($document);
  235. $publishedLocales = [];
  236. foreach ($locales as $locale) {
  237. $publishedPropertyName = $this->encoder->localizedSystemName(
  238. WorkflowStageSubscriber::PUBLISHED_FIELD,
  239. $locale
  240. );
  241. if ($node->getPropertyValueWithDefault($publishedPropertyName, false)) {
  242. $publishedLocales[] = $locale;
  243. }
  244. }
  245. return $publishedLocales;
  246. }
  247. /**
  248. * Returns urls for given page for all locales in webspace.
  249. *
  250. * TODO: Implement a router service instead of this.
  251. *
  252. * @return array
  253. */
  254. public function getLocalizedUrlsForPage(BasePageDocument $page)
  255. {
  256. $localizedUrls = [];
  257. $webspaceKey = $this->getWebspace($page);
  258. /** @var Webspace $webspace */
  259. $webspace = $this->webspaceManager->findWebspaceByKey($webspaceKey);
  260. $node = $this->getNode($page);
  261. $structure = $this->getStructureMetadata($page);
  262. $resourceLocatorProperty = $structure->getPropertyByTagName('sulu.rlp');
  263. foreach ($webspace->getAllLocalizations() as $localization) {
  264. $resolvedLocale = $localization->getLocale();
  265. $locale = $resolvedLocale;
  266. $shadowEnabledName = $this->encoder->localizedSystemName(
  267. ShadowLocaleSubscriber::SHADOW_ENABLED_FIELD,
  268. $resolvedLocale
  269. );
  270. if (true === $node->getPropertyValueWithDefault($shadowEnabledName, false)) {
  271. $shadowLocaleName = $this->encoder->localizedSystemName(
  272. ShadowLocaleSubscriber::SHADOW_LOCALE_FIELD,
  273. $resolvedLocale
  274. );
  275. /** @var string $resolvedLocale */
  276. $resolvedLocale = $node->getPropertyValue($shadowLocaleName);
  277. }
  278. $stageName = $this->encoder->localizedSystemName(
  279. WorkflowStageSubscriber::WORKFLOW_STAGE_FIELD,
  280. $resolvedLocale
  281. );
  282. if (false === $node->hasProperty($stageName)) {
  283. continue;
  284. }
  285. $stage = $node->getProperty($stageName);
  286. if (WorkflowStage::PUBLISHED !== $stage->getValue()) {
  287. continue;
  288. }
  289. /** @var string $name */
  290. $name = $resourceLocatorProperty->getName();
  291. $url = $node->getPropertyValueWithDefault($this->encoder->localizedContentName($name, $locale), null);
  292. if (null === $url) {
  293. continue;
  294. }
  295. $localizedUrls[$locale] = $url;
  296. }
  297. return $localizedUrls;
  298. }
  299. /**
  300. * Extracts webspace key from given path.
  301. *
  302. * @param string $path path of node
  303. *
  304. * @return string|null
  305. */
  306. private function extractWebspaceFromPath($path)
  307. {
  308. $match = \preg_match(
  309. \sprintf(
  310. '/^\/%s\/([\w\.-]*?)\/.*$/',
  311. $this->pathSegmentRegistry->getPathSegment('base')
  312. ),
  313. $path,
  314. $matches
  315. );
  316. if ($match) {
  317. return $matches[1];
  318. }
  319. return null;
  320. }
  321. }