<?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\Types;
use PHPCR\NodeInterface;
use Sulu\Bundle\AudienceTargetingBundle\TargetGroup\TargetGroupStoreInterface;
use Sulu\Component\Content\Compat\Block\BlockPropertyInterface;
use Sulu\Component\Content\Compat\Block\BlockPropertyWrapper;
use Sulu\Component\Content\Compat\Property;
use Sulu\Component\Content\Compat\PropertyInterface;
use Sulu\Component\Content\ComplexContentType;
use Sulu\Component\Content\ContentTypeExportInterface;
use Sulu\Component\Content\ContentTypeInterface;
use Sulu\Component\Content\ContentTypeManagerInterface;
use Sulu\Component\Content\Document\Subscriber\PHPCR\SuluNode;
use Sulu\Component\Content\Exception\UnexpectedPropertyType;
use Sulu\Component\Content\PreResolvableContentTypeInterface;
use Sulu\Component\Content\Types\Block\BlockVisitorInterface;
use Sulu\Component\Webspace\Analyzer\RequestAnalyzerInterface;
/**
* content type for block.
*/
class BlockContentType extends ComplexContentType implements ContentTypeExportInterface, PreResolvableContentTypeInterface
{
/**
* @var ContentTypeManagerInterface
*/
private $contentTypeManager;
/**
* @var string
*/
private $languageNamespace;
/**
* @var RequestAnalyzerInterface
*
* @deprecated This property is not needed anymore and will be removed in Sulu 3.0
*/
private $requestAnalyzer;
/**
* @var ?TargetGroupStoreInterface
*
* @deprecated This property is not needed anymore and will be removed in Sulu 3.0
*/
private $targetGroupStore;
/**
* @var BlockVisitorInterface[]
*/
private $blockVisitors;
public function __construct(
ContentTypeManagerInterface $contentTypeManager,
$languageNamespace,
RequestAnalyzerInterface $requestAnalyzer,
TargetGroupStoreInterface $targetGroupStore = null,
iterable $blockVisitors = null
) {
$this->contentTypeManager = $contentTypeManager;
$this->languageNamespace = $languageNamespace;
$this->requestAnalyzer = $requestAnalyzer;
$this->targetGroupStore = $targetGroupStore;
$this->blockVisitors = $blockVisitors;
if (null === $this->blockVisitors) {
@\trigger_error(
'Instantiating BlockContentType without the $blockVisitors argument is deprecated.',
\E_USER_DEPRECATED
);
$this->blockVisitors = [];
}
}
public function read(
NodeInterface $node,
PropertyInterface $property,
$webspaceKey,
$languageCode,
$segmentKey
) {
if ($property->getIsBlock()) {
/** @var BlockPropertyInterface $blockProperty */
$blockProperty = $property;
while (!($blockProperty instanceof BlockPropertyInterface)) {
$blockProperty = $blockProperty->getProperty();
}
// init properties
$typeProperty = new Property('type', '', 'text_line');
$settingsProperty = new Property('settings', '', 'text_line');
$lengthProperty = new Property('length', '', 'text_line');
// load length
$contentType = $this->contentTypeManager->get($lengthProperty->getContentTypeName());
$contentType->read(
$node,
new BlockPropertyWrapper($lengthProperty, $property),
$webspaceKey,
$languageCode,
$segmentKey
);
$len = $lengthProperty->getValue();
for ($i = 0; $i < $len; ++$i) {
// load type
$contentType = $this->contentTypeManager->get($typeProperty->getContentTypeName());
$contentType->read(
$node,
new BlockPropertyWrapper($typeProperty, $property, $i),
$webspaceKey,
$languageCode,
$segmentKey
);
if (!$blockProperty->hasType($typeProperty->getValue())) {
continue;
}
$contentType = $this->contentTypeManager->get($settingsProperty->getContentTypeName());
$contentType->read(
$node,
new BlockPropertyWrapper($settingsProperty, $property, $i),
$webspaceKey,
$languageCode,
$segmentKey
);
$blockPropertyType = $blockProperty->initProperties($i, $typeProperty->getValue());
$settings = \json_decode($settingsProperty->getValue(), true);
$blockPropertyType->setSettings(!empty($settings) ? $settings : new \stdClass());
/** @var PropertyInterface $subProperty */
foreach ($blockPropertyType->getChildProperties() as $subProperty) {
$contentType = $this->contentTypeManager->get($subProperty->getContentTypeName());
$contentType->read(
$node,
new BlockPropertyWrapper($subProperty, $property, $i),
$webspaceKey,
$languageCode,
$segmentKey
);
}
}
} else {
throw new UnexpectedPropertyType($property, $this);
}
}
public function hasValue(
NodeInterface $node,
PropertyInterface $property,
$webspaceKey,
$languageCode,
$segmentKey
) {
if ($property->getIsBlock()) {
/** @var BlockPropertyInterface $blockProperty */
$blockProperty = $property;
while (!($blockProperty instanceof BlockPropertyInterface)) {
$blockProperty = $blockProperty->getProperty();
}
// init properties
$lengthProperty = new Property('length', '', 'text_line');
$lengthBlockProperty = new BlockPropertyWrapper($lengthProperty, $property);
$contentType = $this->contentTypeManager->get($lengthProperty->getContentTypeName());
return $contentType->hasValue($node, $lengthBlockProperty, $webspaceKey, $languageCode, $segmentKey);
}
return false;
}
public function write(
NodeInterface $node,
PropertyInterface $property,
$userId,
$webspaceKey,
$languageCode,
$segmentKey
) {
return $this->doWrite($node, $property, $userId, $webspaceKey, $languageCode, $segmentKey, false);
}
/**
* Save the value from given property.
*
* @param string $userId
* @param string $webspaceKey
* @param string $languageCode
* @param string $segmentKey
* @param bool $isImport
*
* @throws UnexpectedPropertyType
*/
private function doWrite(
NodeInterface $node,
PropertyInterface $property,
$userId,
$webspaceKey,
$languageCode,
$segmentKey,
$isImport = false
) {
if ($property->getIsBlock()) {
/** @var BlockPropertyInterface $blockProperty */
$blockProperty = $property;
while (!($blockProperty instanceof BlockPropertyInterface)) {
$blockProperty = $blockProperty->getProperty();
}
$data = $blockProperty->getValue();
if (!$blockProperty->getIsMultiple()) {
$data = [$data];
}
$data = \array_filter($data);
$len = \count($data);
// init properties
$typeProperty = new Property('type', '', 'text_line');
$settingsProperty = new Property('settings', '', 'text_line');
$lengthProperty = new Property('length', '', 'text_line');
//save length
$lengthProperty->setValue($len);
$contentType = $this->contentTypeManager->get($lengthProperty->getContentTypeName());
$contentType->write(
$node,
new BlockPropertyWrapper($lengthProperty, $property),
$userId,
$webspaceKey,
$languageCode,
$segmentKey
);
for ($i = 0; $i < $len; ++$i) {
$blockPropertyType = $blockProperty->getProperties($i);
$this->writeProperty(
$typeProperty,
$property,
$blockPropertyType->getName(),
$i,
$node,
$userId,
$webspaceKey,
$languageCode,
$segmentKey,
$isImport
);
$this->writeProperty(
$settingsProperty,
$property,
\json_encode($blockPropertyType->getSettings()),
$i,
$node,
$userId,
$webspaceKey,
$languageCode,
$segmentKey,
$isImport
);
foreach ($blockProperty->getProperties($i)->getChildProperties() as $subProperty) {
$this->writeProperty(
$subProperty,
$property,
$subProperty->getValue(),
$i,
$node,
$userId,
$webspaceKey,
$languageCode,
$segmentKey,
$isImport
);
}
}
} else {
throw new UnexpectedPropertyType($property, $this);
}
}
/**
* write a property to node.
*/
private function writeProperty(
PropertyInterface $property,
PropertyInterface $blockProperty,
$value,
$index,
NodeInterface $node,
$userId,
$webspaceKey,
$languageCode,
$segmentKey,
$isImport = false
) {
// save sub property
$contentType = $this->contentTypeManager->get($property->getContentTypeName());
$blockPropertyWrapper = new BlockPropertyWrapper($property, $blockProperty, $index);
$blockPropertyWrapper->setValue($value);
if ($isImport && $contentType instanceof ContentTypeExportInterface) {
$contentType->importData(
new SuluNode($node),
$blockPropertyWrapper,
$value,
$userId,
$webspaceKey,
$languageCode,
$segmentKey
);
return;
}
$contentType->write(
new SuluNode($node),
$blockPropertyWrapper,
$userId,
$webspaceKey,
$languageCode,
$segmentKey
);
}
public function remove(
NodeInterface $node,
PropertyInterface $property,
$webspaceKey,
$languageCode,
$segmentKey
) {
foreach ($node->getProperties($property->getName() . '-*') as $nodeProperty) {
$node->getProperty($nodeProperty->getName())->remove();
}
}
public function getViewData(PropertyInterface $property)
{
return $this->prepareData(
$property,
function(ContentTypeInterface $contentType, $property) {
return $contentType->getViewData($property);
},
false
);
}
public function getContentData(PropertyInterface $property)
{
return $this->prepareData(
$property,
function(ContentTypeInterface $contentType, $property) {
return $contentType->getContentData($property);
}
);
}
/**
* Returns prepared data from property
* use callback to prepare data foreach property function($contentType, $property).
*
* @param bool $returnType
*
* @return array
*/
private function prepareData(PropertyInterface $property, callable $dataCallback, $returnType = true)
{
/** @var BlockPropertyInterface $blockProperty */
$blockProperty = $property;
while (!($blockProperty instanceof BlockPropertyInterface)) {
$blockProperty = $blockProperty->getProperty();
}
$blockPropertyTypes = [];
for ($i = 0; $i < $blockProperty->getLength(); ++$i) {
$blockPropertyType = $blockProperty->getProperties($i);
foreach ($this->blockVisitors as $blockVisitor) {
$blockPropertyType = $blockVisitor->visit($blockPropertyType);
if (!$blockPropertyType) {
break;
}
}
if ($blockPropertyType) {
$blockPropertyTypes[] = $blockPropertyType;
}
}
$data = [];
foreach ($blockPropertyTypes as $blockPropertyType) {
$blockPropertyTypeSettings = $blockPropertyType->getSettings();
$blockData = [];
if ($returnType) {
$blockData['type'] = $blockPropertyType->getName();
$blockData['settings'] = $blockPropertyTypeSettings;
}
foreach ($blockPropertyType->getChildProperties() as $childProperty) {
$contentType = $this->contentTypeManager->get($childProperty->getContentTypeName());
$blockData[$childProperty->getName()] = $dataCallback($contentType, $childProperty);
}
$data[] = $blockData;
}
if (!$property->getIsMultiple() && \count($data) > 0) {
$data = $data[0];
}
return $data;
}
public function exportData($propertyValue)
{
return $propertyValue;
}
public function importData(
NodeInterface $node,
PropertyInterface $property,
$value,
$userId,
$webspaceKey,
$languageCode,
$segmentKey = null
) {
$property->setValue($value);
$this->doWrite($node, $property, $userId, $webspaceKey, $languageCode, $segmentKey, true);
}
public function preResolve(PropertyInterface $property)
{
$this->prepareData(
$property,
function(ContentTypeInterface $contentType, $property) {
if (!$contentType instanceof PreResolvableContentTypeInterface) {
return;
}
return $contentType->preResolve($property);
},
false
);
}
}