Skip to content

Commit

Permalink
Add shadow functionality to publishing process (#243)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-schranz authored Mar 27, 2023
1 parent 34f3fad commit 70f3f85
Show file tree
Hide file tree
Showing 25 changed files with 1,074 additions and 85 deletions.
19 changes: 13 additions & 6 deletions Content/Application/ContentCopier/ContentCopier.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,29 +59,36 @@ public function copy(
ContentRichEntityInterface $sourceContentRichEntity,
array $sourceDimensionAttributes,
ContentRichEntityInterface $targetContentRichEntity,
array $targetDimensionAttributes
array $targetDimensionAttributes,
array $options = []
): DimensionContentInterface {
$sourceDimensionContent = $this->contentResolver->resolve($sourceContentRichEntity, $sourceDimensionAttributes);

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

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

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

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

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

return $this->contentPersister->persist($targetContentRichEntity, $data, $targetDimensionAttributes);
}
Expand Down
12 changes: 9 additions & 3 deletions Content/Application/ContentCopier/ContentCopierInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@ interface ContentCopierInterface
* @param mixed[] $sourceDimensionAttributes
* @param ContentRichEntityInterface<T> $targetContentRichEntity
* @param mixed[] $targetDimensionAttributes
* @param array{data?: mixed[], ignoredAttributes?: string[]} $options the "data" allows given custom data to the target and "ignoredAttributes" avoids specific attributes to be copied
*
* @return T
*/
public function copy(
ContentRichEntityInterface $sourceContentRichEntity,
array $sourceDimensionAttributes,
ContentRichEntityInterface $targetContentRichEntity,
array $targetDimensionAttributes
array $targetDimensionAttributes,
array $options = []
): DimensionContentInterface;

/**
Expand All @@ -42,13 +44,15 @@ public function copy(
* @param DimensionContentCollectionInterface<T> $dimensionContentCollection
* @param ContentRichEntityInterface<T> $targetContentRichEntity
* @param mixed[] $targetDimensionAttributes
* @param array{data?: mixed[], ignoredAttributes?: string[]} $options the "data" allows given custom data to the target and "ignoredAttributes" avoids specific attributes to be copied
*
* @return T
*/
public function copyFromDimensionContentCollection(
DimensionContentCollectionInterface $dimensionContentCollection,
ContentRichEntityInterface $targetContentRichEntity,
array $targetDimensionAttributes
array $targetDimensionAttributes,
array $options = []
): DimensionContentInterface;

/**
Expand All @@ -57,12 +61,14 @@ public function copyFromDimensionContentCollection(
* @param T $dimensionContent
* @param ContentRichEntityInterface<T> $targetContentRichEntity
* @param mixed[] $targetDimensionAttributes
* @param array{data?: mixed[], ignoredAttributes?: string[]} $options the "data" allows given custom data to the target and "ignoredAttributes" avoids specific attributes to be copied
*
* @return T
*/
public function copyFromDimensionContent(
DimensionContentInterface $dimensionContent,
ContentRichEntityInterface $targetContentRichEntity,
array $targetDimensionAttributes
array $targetDimensionAttributes,
array $options = []
): DimensionContentInterface;
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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);
}
}
}
}
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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,69 @@ 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,
[
'ignoredAttributes' => [
'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' => $data]
);
}

Expand Down
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
11 changes: 11 additions & 0 deletions Content/Domain/Model/ShadowInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,18 @@ public function addShadowLocale(string $locale, string $shadowLocale): void;
public function removeShadowLocale(string $locale): void;

/**
* Returns the locales which shadow the given locale.
*
* @return array<string, string>|null
*/
public function getShadowLocales(): ?array;

/**
* @internal should only be set by content bundle services not from outside
*
* Returns the locales which shadow the given locale
*
* @return string[]
*/
public function getShadowLocalesForLocale(string $shadowLocale): array;
}
Loading

0 comments on commit 70f3f85

Please sign in to comment.