Skip to content

Commit

Permalink
Add shadow functionality to publishing process
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-schranz committed Mar 23, 2023
1 parent 34f3fad commit 60e7c7f
Show file tree
Hide file tree
Showing 23 changed files with 494 additions and 85 deletions.
22 changes: 16 additions & 6 deletions Content/Application/ContentCopier/ContentCopier.php
Expand Up @@ -59,29 +59,39 @@ public function copy(
ContentRichEntityInterface $sourceContentRichEntity,
array $sourceDimensionAttributes,
ContentRichEntityInterface $targetContentRichEntity,
array $targetDimensionAttributes
array $targetDimensionAttributes,
array $data = [],
array $ignoredAttributes = []
): DimensionContentInterface {
$sourceDimensionContent = $this->contentResolver->resolve($sourceContentRichEntity, $sourceDimensionAttributes);

return $this->copyFromDimensionContent($sourceDimensionContent, $targetContentRichEntity, $targetDimensionAttributes);
return $this->copyFromDimensionContent($sourceDimensionContent, $targetContentRichEntity, $targetDimensionAttributes, $data, $ignoredAttributes);
}

public function copyFromDimensionContentCollection(
DimensionContentCollectionInterface $dimensionContentCollection,
ContentRichEntityInterface $targetContentRichEntity,
array $targetDimensionAttributes
array $targetDimensionAttributes,
array $data = [],
array $ignoredAttributes = []
): DimensionContentInterface {
$sourceDimensionContent = $this->contentMerger->merge($dimensionContentCollection);

return $this->copyFromDimensionContent($sourceDimensionContent, $targetContentRichEntity, $targetDimensionAttributes);
return $this->copyFromDimensionContent($sourceDimensionContent, $targetContentRichEntity, $targetDimensionAttributes, $data, $ignoredAttributes);
}

public function copyFromDimensionContent(
DimensionContentInterface $dimensionContent,
ContentRichEntityInterface $targetContentRichEntity,
array $targetDimensionAttributes
array $targetDimensionAttributes,
array $data = [],
array $ignoredAttributes = []
): DimensionContentInterface {
$data = $this->contentNormalizer->normalize($dimensionContent);
$data = \array_replace($this->contentNormalizer->normalize($dimensionContent), $data);

foreach ($ignoredAttributes as $ignoredAttribute) {
unset($data[$ignoredAttribute]);
}

return $this->contentPersister->persist($targetContentRichEntity, $data, $targetDimensionAttributes);
}
Expand Down
18 changes: 15 additions & 3 deletions Content/Application/ContentCopier/ContentCopierInterface.php
Expand Up @@ -26,14 +26,18 @@ interface ContentCopierInterface
* @param mixed[] $sourceDimensionAttributes
* @param ContentRichEntityInterface<T> $targetContentRichEntity
* @param mixed[] $targetDimensionAttributes
* @param mixed[] $data This data is merged with the data of the source content before set on the target content
* @param string[] $ignoredAttributes This attributes stayed untouched
*
* @return T
*/
public function copy(
ContentRichEntityInterface $sourceContentRichEntity,
array $sourceDimensionAttributes,
ContentRichEntityInterface $targetContentRichEntity,
array $targetDimensionAttributes
array $targetDimensionAttributes,
array $data = [],
array $ignoredAttributes = []
): DimensionContentInterface;

/**
Expand All @@ -42,13 +46,17 @@ public function copy(
* @param DimensionContentCollectionInterface<T> $dimensionContentCollection
* @param ContentRichEntityInterface<T> $targetContentRichEntity
* @param mixed[] $targetDimensionAttributes
* @param mixed[] $data This data is merged with the data of the source content before set on the target content
* @param string[] $ignoredAttributes This attributes stayed untouched
*
* @return T
*/
public function copyFromDimensionContentCollection(
DimensionContentCollectionInterface $dimensionContentCollection,
ContentRichEntityInterface $targetContentRichEntity,
array $targetDimensionAttributes
array $targetDimensionAttributes,
array $data = [],
array $ignoredAttributes = []
): DimensionContentInterface;

/**
Expand All @@ -57,12 +65,16 @@ public function copyFromDimensionContentCollection(
* @param T $dimensionContent
* @param ContentRichEntityInterface<T> $targetContentRichEntity
* @param mixed[] $targetDimensionAttributes
* @param mixed[] $data This data is merged with the data of the source content before set on the target content
* @param string[] $ignoredAttributes This attributes stayed untouched
*
* @return T
*/
public function copyFromDimensionContent(
DimensionContentInterface $dimensionContent,
ContentRichEntityInterface $targetContentRichEntity,
array $targetDimensionAttributes
array $targetDimensionAttributes,
array $data = [],
array $ignoredAttributes = []
): DimensionContentInterface;
}
Expand Up @@ -108,6 +108,12 @@ public function map(

/** @var string $name */
$name = $property->getName();
if ('url' !== $name) {
throw new \RuntimeException(\sprintf(
'Expected a property with the name "url" but "%s" given.',
$name
)); // TODO move this validation to a compiler pass see also direct access of 'url' in PublishTransitionSubscriber class.
}

$currentRoutePath = $localizedDimensionContent->getTemplateData()[$name] ?? null;
if (!\array_key_exists($name, $data) && null !== $currentRoutePath) {
Expand Down Expand Up @@ -194,6 +200,7 @@ public function map(
private function getRouteProperty(StructureMetadata $metadata): ?PropertyMetadata
{
foreach ($metadata->getProperties() as $property) {
// TODO add support for page_tree_route field type: https://github.com/sulu/SuluContentBundle/issues/242
if ('route' === $property->getType()) {
return $property;
}
Expand Down
Expand Up @@ -23,7 +23,9 @@ public function map(
DimensionContentInterface $localizedDimensionContent,
array $data
): void {
if (!$localizedDimensionContent instanceof ShadowInterface) {
if (!$unlocalizedDimensionContent instanceof ShadowInterface
|| !$localizedDimensionContent instanceof ShadowInterface
) {
return;
}

Expand All @@ -33,11 +35,19 @@ public function map(
/** @var string|null $shadowLocale */
$shadowLocale = $data['shadowLocale'] ?? null;

$locale = $localizedDimensionContent->getLocale();

$localizedDimensionContent->setShadowLocale(
$shadowOn
$shadowOn && $locale
? $shadowLocale
: null
);

if ($locale && $shadowLocale) {
$unlocalizedDimensionContent->addShadowLocale($locale, $shadowLocale);
} elseif ($locale) {
$unlocalizedDimensionContent->removeShadowLocale($locale);
}
}
}
}
Expand Up @@ -62,10 +62,12 @@ public function map(
throw new \RuntimeException('Expected "template" to be set in the data array.');
}

list($unlocalizedData, $localizedData, $hasAnyValue) = $this->getTemplateData(
[$unlocalizedData, $localizedData, $hasAnyValue] = $this->getTemplateData(
$data,
$type,
$template
$template,
$unlocalizedDimensionContent->getTemplateData(),
$localizedDimensionContent->getTemplateData()
);

if (!isset($data['template']) && !$hasAnyValue) {
Expand All @@ -75,44 +77,47 @@ public function map(

$localizedDimensionContent->setTemplateKey($template);
$localizedDimensionContent->setTemplateData($localizedData);

$unlocalizedDimensionContent->setTemplateData(\array_merge(
$unlocalizedDimensionContent->getTemplateData(),
$unlocalizedData
));
$unlocalizedDimensionContent->setTemplateData($unlocalizedData);
}

/**
* @param mixed[] $data
* @param mixed[] $unlocalizedData
* @param mixed[] $localizedData
*
* @return array{
* 0: mixed[],
* 1: mixed[],
* 2: bool,
* 0: mixed[],
* 1: mixed[],
* 2: bool,
* }
*/
private function getTemplateData(array $data, string $type, string $template): array
{
private function getTemplateData(
array $data,
string $type,
string $template,
array $unlocalizedData,
array $localizedData
): array {
$metadata = $this->factory->getStructureMetadata($type, $template);

if (!$metadata) {
throw new \RuntimeException(\sprintf('Could not find structure "%s" of type "%s".', $template, $type));
}

$unlocalizedData = [];
$localizedData = [];
$hasAnyValue = false;

$defaultLocalizedData = $localizedData; // use existing localizedData only as default to remove not longer existing properties of the template
$localizedData = [];
foreach ($metadata->getProperties() as $property) {
$value = null;
$name = $property->getName();

// Float are converted to ints in php array as key so we need convert it to string
if (\is_float($name)) {
$name = (string) $name;
}

if (\array_key_exists($name, $data)) {
$value = $property->isLocalized() ? $defaultLocalizedData[$name] ?? null : $defaultLocalizedData[$name] ?? null;
if (\array_key_exists($name, $data)) { // values not explicitly given need to stay untouched for e.g. for shadow pages urls
$hasAnyValue = true;
$value = $data[$name];
}
Expand Down
2 changes: 1 addition & 1 deletion Content/Application/ContentMerger/Merger/ShadowMerger.php
Expand Up @@ -35,7 +35,7 @@ public function merge(object $targetObject, object $sourceObject): void
$targetObject->setShadowLocale($shadowLocale);
}

foreach ($sourceObject->getShadowLocales() ?: [] as $locale => $shadowLocale) {
foreach (($sourceObject->getShadowLocales() ?? []) as $locale => $shadowLocale) {
$targetObject->addShadowLocale($locale, $shadowLocale);
}
}
Expand Down
Expand Up @@ -18,10 +18,17 @@
use Sulu\Bundle\ContentBundle\Content\Domain\Model\ContentRichEntityInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentCollectionInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\ShadowInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\TemplateInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\WorkflowInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Workflow\Event\TransitionEvent;

/**
* @final
*
* @internal this class is internal and should not be extended from or used in another context
*/
class PublishTransitionSubscriber implements EventSubscriberInterface
{
/**
Expand Down Expand Up @@ -66,12 +73,68 @@ public function onPublish(TransitionEvent $transitionEvent): void
throw new \RuntimeException('No "contentRichEntity" given.');
}

$dimensionAttributes['stage'] = DimensionContentInterface::STAGE_LIVE;
$sourceDimensionAttributes = $dimensionAttributes;
$targetDimensionAttributes = $dimensionAttributes;
$targetDimensionAttributes['stage'] = DimensionContentInterface::STAGE_LIVE;

$shadowLocale = $dimensionContent instanceof ShadowInterface
? $dimensionContent->getShadowLocale()
: null;

/** @var string $locale */
$locale = $dimensionContent->getLocale();

if (!$shadowLocale) {
$publishedDimensionContent = $this->contentCopier->copyFromDimensionContentCollection(
$dimensionContentCollection,
$contentRichEntity,
$targetDimensionAttributes
);

if (!$publishedDimensionContent instanceof ShadowInterface) {
return;
}

$shadowLocales = $publishedDimensionContent->getShadowLocalesForLocale($locale);

foreach ($shadowLocales as $shadowLocale) {
$targetDimensionAttributes['locale'] = $shadowLocale;

$this->contentCopier->copyFromDimensionContentCollection(
$dimensionContentCollection,
$contentRichEntity,
$targetDimensionAttributes,
[],
[
'shadowOn',
'shadowLocale',
'url',
]
);
}

return;
}

$sourceDimensionAttributes['locale'] = $shadowLocale;
$sourceDimensionAttributes['stage'] = DimensionContentInterface::STAGE_LIVE;

$this->contentCopier->copyFromDimensionContentCollection(
$dimensionContentCollection,
$data = [
// @see \Sulu\Bundle\ContentBundle\Content\Application\ContentDataMapper\DataMapper\ShadowDataMapper::map
'shadowOn' => true,
'shadowLocale' => $shadowLocale,
];

if ($dimensionContent instanceof TemplateInterface) {
$data['url'] = $dimensionContent->getTemplateData()['url'] ?? null; // TODO get correct route property
}

$this->contentCopier->copy(
$contentRichEntity,
$sourceDimensionAttributes,
$contentRichEntity,
$dimensionAttributes
$targetDimensionAttributes,
$data
);
}

Expand Down
Expand Up @@ -21,6 +21,11 @@
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Workflow\Event\TransitionEvent;

/**
* @final
*
* @internal this class is internal and should not be extended from or used in another context
*/
class RemoveDraftTransitionSubscriber implements EventSubscriberInterface
{
/**
Expand Down
Expand Up @@ -18,11 +18,17 @@
use Sulu\Bundle\ContentBundle\Content\Domain\Exception\ContentNotFoundException;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\ContentRichEntityInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\DimensionContentInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\ShadowInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Model\WorkflowInterface;
use Sulu\Bundle\ContentBundle\Content\Domain\Repository\DimensionContentRepositoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Workflow\Event\TransitionEvent;

/**
* @final
*
* @internal this class is internal and should not be extended from or used in another context
*/
class UnpublishTransitionSubscriber implements EventSubscriberInterface
{
/**
Expand Down Expand Up @@ -83,6 +89,10 @@ public function onUnpublish(TransitionEvent $transitionEvent): void
/** @var DimensionContentInterface $unlocalizedLiveDimensionContent */
$unlocalizedLiveDimensionContent = $dimensionContentCollection->getDimensionContent($unlocalizedLiveDimensionAttributes); // @phpstan-ignore-line we can not define the generic of DimensionContentInterface here
$unlocalizedLiveDimensionContent->removeAvailableLocale($locale);

if ($unlocalizedLiveDimensionContent instanceof ShadowInterface) {
$unlocalizedLiveDimensionContent->removeShadowLocale($locale);
}
}

$this->entityManager->remove($localizedLiveDimensionContent);
Expand Down
3 changes: 0 additions & 3 deletions Content/Domain/Model/RoutableInterface.php
Expand Up @@ -13,9 +13,6 @@

namespace Sulu\Bundle\ContentBundle\Content\Domain\Model;

/**
* Marker interface for autoloading the doctrine metadata for routables.
*/
interface RoutableInterface
{
public static function getResourceKey(): string;
Expand Down

0 comments on commit 60e7c7f

Please sign in to comment.