vendor/sulu/sulu/src/Sulu/Component/Content/Types/BlockContentType.php line 366

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\Types;
  11. use PHPCR\NodeInterface;
  12. use Sulu\Bundle\AudienceTargetingBundle\TargetGroup\TargetGroupStoreInterface;
  13. use Sulu\Bundle\ReferenceBundle\Application\Collector\ReferenceCollectorInterface;
  14. use Sulu\Bundle\ReferenceBundle\Infrastructure\Sulu\ContentType\ReferenceContentTypeInterface;
  15. use Sulu\Component\Content\Compat\Block\BlockPropertyInterface;
  16. use Sulu\Component\Content\Compat\Block\BlockPropertyWrapper;
  17. use Sulu\Component\Content\Compat\Property;
  18. use Sulu\Component\Content\Compat\PropertyInterface;
  19. use Sulu\Component\Content\ComplexContentType;
  20. use Sulu\Component\Content\ContentTypeExportInterface;
  21. use Sulu\Component\Content\ContentTypeInterface;
  22. use Sulu\Component\Content\ContentTypeManagerInterface;
  23. use Sulu\Component\Content\Document\Subscriber\PHPCR\SuluNode;
  24. use Sulu\Component\Content\Exception\UnexpectedPropertyType;
  25. use Sulu\Component\Content\PreResolvableContentTypeInterface;
  26. use Sulu\Component\Content\Types\Block\BlockVisitorInterface;
  27. use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface;
  28. /**
  29. * content type for block.
  30. */
  31. class BlockContentType extends ComplexContentType implements ContentTypeExportInterface, PreResolvableContentTypeInterface, ReferenceContentTypeInterface
  32. {
  33. /**
  34. * @param string $languageNamespace
  35. */
  36. public function __construct(
  37. private ContentTypeManagerInterface $contentTypeManager,
  38. private $languageNamespace,
  39. /**
  40. * @deprecated This property is not needed anymore and will be removed in Sulu 3.0
  41. */
  42. private RequestAnalyzerInterface $requestAnalyzer,
  43. /**
  44. * @deprecated This property is not needed anymore and will be removed in Sulu 3.0
  45. */
  46. private ?TargetGroupStoreInterface $targetGroupStore = null,
  47. /**
  48. * @var BlockVisitorInterface[]
  49. */
  50. private ?iterable $blockVisitors = null
  51. ) {
  52. if (null === $this->blockVisitors) {
  53. @trigger_deprecation('sulu/sulu', '2.3', 'Instantiating BlockContentType without the $blockVisitors argument is deprecated.');
  54. $this->blockVisitors = [];
  55. }
  56. }
  57. public function read(
  58. NodeInterface $node,
  59. PropertyInterface $property,
  60. $webspaceKey,
  61. $languageCode,
  62. $segmentKey
  63. ) {
  64. if ($property->getIsBlock()) {
  65. /** @var BlockPropertyInterface $blockProperty */
  66. $blockProperty = $property;
  67. while (!($blockProperty instanceof BlockPropertyInterface)) {
  68. $blockProperty = $blockProperty->getProperty();
  69. }
  70. // init properties
  71. $typeProperty = new Property('type', '', 'text_line');
  72. $settingsProperty = new Property('settings', '', 'text_line');
  73. $lengthProperty = new Property('length', '', 'text_line');
  74. // load length
  75. $contentType = $this->contentTypeManager->get($lengthProperty->getContentTypeName());
  76. $contentType->read(
  77. $node,
  78. new BlockPropertyWrapper($lengthProperty, $property),
  79. $webspaceKey,
  80. $languageCode,
  81. $segmentKey
  82. );
  83. $len = $lengthProperty->getValue();
  84. for ($i = 0; $i < $len; ++$i) {
  85. // load type
  86. $contentType = $this->contentTypeManager->get($typeProperty->getContentTypeName());
  87. $contentType->read(
  88. $node,
  89. new BlockPropertyWrapper($typeProperty, $property, $i),
  90. $webspaceKey,
  91. $languageCode,
  92. $segmentKey
  93. );
  94. if (!$blockProperty->hasType($typeProperty->getValue())) {
  95. continue;
  96. }
  97. $contentType = $this->contentTypeManager->get($settingsProperty->getContentTypeName());
  98. $contentType->read(
  99. $node,
  100. new BlockPropertyWrapper($settingsProperty, $property, $i),
  101. $webspaceKey,
  102. $languageCode,
  103. $segmentKey
  104. );
  105. $blockPropertyType = $blockProperty->initProperties($i, $typeProperty->getValue());
  106. $settings = \json_decode($settingsProperty->getValue(), true);
  107. $blockPropertyType->setSettings(!empty($settings) ? $settings : new \stdClass());
  108. /** @var PropertyInterface $subProperty */
  109. foreach ($blockPropertyType->getChildProperties() as $subProperty) {
  110. $contentType = $this->contentTypeManager->get($subProperty->getContentTypeName());
  111. $contentType->read(
  112. $node,
  113. new BlockPropertyWrapper($subProperty, $property, $i),
  114. $webspaceKey,
  115. $languageCode,
  116. $segmentKey
  117. );
  118. }
  119. }
  120. } else {
  121. throw new UnexpectedPropertyType($property, $this);
  122. }
  123. }
  124. public function hasValue(
  125. NodeInterface $node,
  126. PropertyInterface $property,
  127. $webspaceKey,
  128. $languageCode,
  129. $segmentKey
  130. ) {
  131. if ($property->getIsBlock()) {
  132. /** @var BlockPropertyInterface $blockProperty */
  133. $blockProperty = $property;
  134. while (!($blockProperty instanceof BlockPropertyInterface)) {
  135. $blockProperty = $blockProperty->getProperty();
  136. }
  137. // init properties
  138. $lengthProperty = new Property('length', '', 'text_line');
  139. $lengthBlockProperty = new BlockPropertyWrapper($lengthProperty, $property);
  140. $contentType = $this->contentTypeManager->get($lengthProperty->getContentTypeName());
  141. return $contentType->hasValue($node, $lengthBlockProperty, $webspaceKey, $languageCode, $segmentKey);
  142. }
  143. return false;
  144. }
  145. public function write(
  146. NodeInterface $node,
  147. PropertyInterface $property,
  148. $userId,
  149. $webspaceKey,
  150. $languageCode,
  151. $segmentKey
  152. ) {
  153. return $this->doWrite($node, $property, $userId, $webspaceKey, $languageCode, $segmentKey, false);
  154. }
  155. /**
  156. * Save the value from given property.
  157. *
  158. * @param string $userId
  159. * @param string $webspaceKey
  160. * @param string $languageCode
  161. * @param string|null $segmentKey
  162. * @param bool $isImport
  163. *
  164. * @throws UnexpectedPropertyType
  165. */
  166. private function doWrite(
  167. NodeInterface $node,
  168. PropertyInterface $property,
  169. $userId,
  170. $webspaceKey,
  171. $languageCode,
  172. $segmentKey,
  173. $isImport = false
  174. ) {
  175. if ($property->getIsBlock()) {
  176. /** @var BlockPropertyInterface $blockProperty */
  177. $blockProperty = $property;
  178. while (!($blockProperty instanceof BlockPropertyInterface)) {
  179. $blockProperty = $blockProperty->getProperty();
  180. }
  181. $data = $blockProperty->getValue();
  182. if (!$blockProperty->getIsMultiple()) {
  183. $data = [$data];
  184. }
  185. $data = \array_filter($data);
  186. $len = \count($data);
  187. // init properties
  188. $typeProperty = new Property('type', '', 'text_line');
  189. $settingsProperty = new Property('settings', '', 'text_line');
  190. $lengthProperty = new Property('length', '', 'text_line');
  191. //save length
  192. $lengthProperty->setValue($len);
  193. $contentType = $this->contentTypeManager->get($lengthProperty->getContentTypeName());
  194. $contentType->write(
  195. $node,
  196. new BlockPropertyWrapper($lengthProperty, $property),
  197. $userId,
  198. $webspaceKey,
  199. $languageCode,
  200. $segmentKey
  201. );
  202. for ($i = 0; $i < $len; ++$i) {
  203. $blockPropertyType = $blockProperty->getProperties($i);
  204. $this->writeProperty(
  205. $typeProperty,
  206. $property,
  207. $blockPropertyType->getName(),
  208. $i,
  209. $node,
  210. $userId,
  211. $webspaceKey,
  212. $languageCode,
  213. $segmentKey,
  214. $isImport
  215. );
  216. $blockSettings = $blockPropertyType->getSettings();
  217. if (\is_array($blockSettings)) {
  218. $this->writeProperty(
  219. $settingsProperty,
  220. $property,
  221. \json_encode($blockSettings),
  222. $i,
  223. $node,
  224. $userId,
  225. $webspaceKey,
  226. $languageCode,
  227. $segmentKey,
  228. $isImport
  229. );
  230. }
  231. foreach ($blockProperty->getProperties($i)->getChildProperties() as $subProperty) {
  232. $this->writeProperty(
  233. $subProperty,
  234. $property,
  235. $subProperty->getValue(),
  236. $i,
  237. $node,
  238. $userId,
  239. $webspaceKey,
  240. $languageCode,
  241. $segmentKey,
  242. $isImport
  243. );
  244. }
  245. }
  246. } else {
  247. throw new UnexpectedPropertyType($property, $this);
  248. }
  249. }
  250. /**
  251. * write a property to node.
  252. */
  253. private function writeProperty(
  254. PropertyInterface $property,
  255. PropertyInterface $blockProperty,
  256. $value,
  257. $index,
  258. NodeInterface $node,
  259. $userId,
  260. $webspaceKey,
  261. $languageCode,
  262. $segmentKey,
  263. $isImport = false
  264. ) {
  265. // save sub property
  266. $contentType = $this->contentTypeManager->get($property->getContentTypeName());
  267. $blockPropertyWrapper = new BlockPropertyWrapper($property, $blockProperty, $index);
  268. $blockPropertyWrapper->setValue($value);
  269. if ($isImport && $contentType instanceof ContentTypeExportInterface) {
  270. $contentType->importData(
  271. new SuluNode($node),
  272. $blockPropertyWrapper,
  273. $value,
  274. $userId,
  275. $webspaceKey,
  276. $languageCode,
  277. $segmentKey
  278. );
  279. return;
  280. }
  281. $contentType->write(
  282. new SuluNode($node),
  283. $blockPropertyWrapper,
  284. $userId,
  285. $webspaceKey,
  286. $languageCode,
  287. $segmentKey
  288. );
  289. }
  290. public function remove(
  291. NodeInterface $node,
  292. PropertyInterface $property,
  293. $webspaceKey,
  294. $languageCode,
  295. $segmentKey
  296. ) {
  297. foreach ($node->getProperties($property->getName() . '-*') as $nodeProperty) {
  298. $node->getProperty($nodeProperty->getName())->remove();
  299. }
  300. }
  301. public function getViewData(PropertyInterface $property)
  302. {
  303. return $this->prepareData(
  304. $property,
  305. function(ContentTypeInterface $contentType, $property) {
  306. return $contentType->getViewData($property);
  307. },
  308. false
  309. );
  310. }
  311. public function getContentData(PropertyInterface $property)
  312. {
  313. return $this->prepareData(
  314. $property,
  315. function(ContentTypeInterface $contentType, $property) {
  316. return $contentType->getContentData($property);
  317. }
  318. );
  319. }
  320. /**
  321. * Returns prepared data from property
  322. * use callback to prepare data foreach property function($contentType, $property).
  323. *
  324. * @param bool $returnType
  325. *
  326. * @return array
  327. */
  328. private function prepareData(PropertyInterface $property, callable $dataCallback, $returnType = true)
  329. {
  330. /** @var BlockPropertyInterface $blockProperty */
  331. $blockProperty = $property;
  332. while (!($blockProperty instanceof BlockPropertyInterface)) {
  333. $blockProperty = $blockProperty->getProperty();
  334. }
  335. $blockPropertyTypes = [];
  336. for ($i = 0; $i < $blockProperty->getLength(); ++$i) {
  337. $blockPropertyType = $blockProperty->getProperties($i);
  338. foreach ($this->blockVisitors as $blockVisitor) {
  339. $blockPropertyType = $blockVisitor->visit($blockPropertyType);
  340. if (!$blockPropertyType) {
  341. break;
  342. }
  343. }
  344. if ($blockPropertyType) {
  345. $blockPropertyTypes[] = $blockPropertyType;
  346. }
  347. }
  348. $data = [];
  349. foreach ($blockPropertyTypes as $blockPropertyType) {
  350. $blockPropertyTypeSettings = $blockPropertyType->getSettings();
  351. $blockData = [];
  352. if ($returnType) {
  353. $blockData['type'] = $blockPropertyType->getName();
  354. $blockData['settings'] = $blockPropertyTypeSettings;
  355. }
  356. foreach ($blockPropertyType->getChildProperties() as $childProperty) {
  357. $contentType = $this->contentTypeManager->get($childProperty->getContentTypeName());
  358. $blockData[$childProperty->getName()] = $dataCallback($contentType, $childProperty);
  359. }
  360. $data[] = $blockData;
  361. }
  362. if (!$property->getIsMultiple() && \count($data) > 0) {
  363. $data = $data[0];
  364. }
  365. return $data;
  366. }
  367. public function exportData($propertyValue)
  368. {
  369. return $propertyValue;
  370. }
  371. public function importData(
  372. NodeInterface $node,
  373. PropertyInterface $property,
  374. $value,
  375. $userId,
  376. $webspaceKey,
  377. $languageCode,
  378. $segmentKey = null
  379. ) {
  380. $property->setValue($value);
  381. $this->doWrite($node, $property, $userId, $webspaceKey, $languageCode, $segmentKey, true);
  382. }
  383. public function preResolve(PropertyInterface $property)
  384. {
  385. $this->prepareData(
  386. $property,
  387. function(ContentTypeInterface $contentType, $property) {
  388. if (!$contentType instanceof PreResolvableContentTypeInterface) {
  389. return;
  390. }
  391. return $contentType->preResolve($property);
  392. },
  393. false
  394. );
  395. }
  396. public function getReferences(PropertyInterface $property, ReferenceCollectorInterface $referenceCollector, string $propertyPrefix = ''): void
  397. {
  398. $values = $property->getValue();
  399. if (!\is_array($values)) {
  400. return;
  401. }
  402. foreach ($values as $index => $value) {
  403. $propertyType = $property->getType($value['type']);
  404. foreach ($propertyType->getChildProperties() as $child) {
  405. $contentType = $this->contentTypeManager->get($child->getContentTypeName());
  406. $childName = $child->getName();
  407. if (!$contentType instanceof ReferenceContentTypeInterface || !isset($value[$childName])) {
  408. continue;
  409. }
  410. $oldValue = $child->getValue();
  411. $child->setValue($value[$childName]);
  412. $contentType->getReferences(
  413. $child,
  414. $referenceCollector,
  415. $propertyPrefix . $property->getName() . '[' . $index . '].'
  416. );
  417. $child->setValue($oldValue);
  418. }
  419. }
  420. }
  421. }