diff --git a/UPGRADE.md b/UPGRADE.md index c9e93846..337aa159 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,8 @@ # Upgrade Notes +## 5.2.0 +- [NEW FEATURE] Add element hash to headless stack + ## 5.1.2 - [BUGFIX] Enriched injected JS `toolbox-wysiwyg-document-style.js` with toolbox document id param [#223](https://github.com/dachcom-digital/pimcore-toolbox/issues/223) diff --git a/src/Document/Editable/EditableJsonSubscriber.php b/src/Document/Editable/EditableJsonSubscriber.php index 9db2b5ad..40c6ec85 100644 --- a/src/Document/Editable/EditableJsonSubscriber.php +++ b/src/Document/Editable/EditableJsonSubscriber.php @@ -13,6 +13,7 @@ final class EditableJsonSubscriber implements EventSubscriberInterface protected const ELEMENTS_IDENTIFIER = 'elements'; protected const ELEMENT_TYPE_IDENTIFIER = 'elementType'; protected const ELEMENT_SUB_TYPE_IDENTIFIER = 'elementSubType'; + protected const ELEMENT_HASH = 'elementHash'; protected const ELEMENT_DATA_IDENTIFIER = 'elementContext'; protected array $jsonEditables = []; @@ -26,7 +27,7 @@ public static function getSubscribedEvents(): array public function onHeadlessElementAdd(HeadlessElementEvent $event): void { - $this->jsonEditables[$event->getElementNamespace()] = [$event->getElementType(), $event->getElementSubType(), $event->getData()]; + $this->jsonEditables[$event->getElementNamespace()] = [$event->getElementType(), $event->getElementSubType(), $event->getElementHash(), $event->getData()]; } public function getJsonEditables(): array @@ -59,7 +60,8 @@ private function convertNestedArray($flatArray): array $currentArray[self::ELEMENT_TYPE_IDENTIFIER] = $value[0]; $currentArray[self::ELEMENT_SUB_TYPE_IDENTIFIER] = $value[1]; - $currentArray[self::ELEMENT_DATA_IDENTIFIER] = $value[2]; + $currentArray[self::ELEMENT_HASH] = $value[2]; + $currentArray[self::ELEMENT_DATA_IDENTIFIER] = $value[3]; } /** @phpstan-ignore-next-line */ diff --git a/src/Document/Editable/EditableWorker.php b/src/Document/Editable/EditableWorker.php index e217dedd..32c0ec51 100644 --- a/src/Document/Editable/EditableWorker.php +++ b/src/Document/Editable/EditableWorker.php @@ -30,6 +30,7 @@ public function processBrick(HeadlessResponse $data, AreabrickInterface $areabri $this->dispatch([ 'elementType' => $data->getType(), 'elementSubType' => $areabrick->getId(), + 'elementHash' => $this->buildBrickHash(), 'elementNamespace' => $this->buildBrickNamespace(), 'data' => $this->processBrickData($data, $areabrick->getId()) ]); @@ -40,11 +41,38 @@ public function processEditable(HeadlessResponse $data, Editable $editable): voi $this->dispatch([ 'elementType' => $data->getType(), 'elementSubType' => $editable->getType(), + 'elementHash' => $this->buildEditableHash($editable), 'elementNamespace' => $this->buildEditableNamespace($editable), 'data' => $this->processEditableData($data) ]); } + public function processVirtualElement(string $type, string $subType, string $hash, string $namespace): void + { + $this->dispatch([ + 'elementType' => $type, + 'elementSubType' => $subType, + 'elementHash' => $hash, + 'elementNamespace' => $namespace, + 'data' => [] + ]); + } + + public function buildBrickHash(): string + { + return hash('xxh3', sprintf('element_hash_%s', str_replace([':', '.'], '_', $this->buildBrickNamespace()))); + } + + public function buildEditableHash(Editable $editable): string + { + return hash('xxh3', sprintf('element_hash_%s', str_replace([':', '.'], '_', $editable->getName()))); + } + + public function buildBlockHash(string $blockName, int $blockIndex): string + { + return hash('xxh3', sprintf('element_hash_%s_%d', $blockName, $blockIndex)); + } + private function dispatch(array $arguments): void { $this->eventDispatcher->dispatch( diff --git a/src/Document/Editable/HeadlessEditableRenderer.php b/src/Document/Editable/HeadlessEditableRenderer.php index 8b45e8c6..807c0bc4 100644 --- a/src/Document/Editable/HeadlessEditableRenderer.php +++ b/src/Document/Editable/HeadlessEditableRenderer.php @@ -21,12 +21,24 @@ public function __construct( public function renderBrickWithWrapper(array $contentBlocks): string { - return sprintf('
%s
', implode(PHP_EOL, $contentBlocks)); + $brickHash = $this->editableWorker->buildBrickHash(); + + return sprintf( + '
%s
', + $brickHash, + implode(PHP_EOL, $contentBlocks) + ); } - public function renderStandaloneEditableWithWrapper(string $contentBlock): string + public function renderStandaloneEditableWithWrapper(string $contentBlock, Editable $editable): string { - return sprintf('
%s
', $contentBlock); + $editableHash = $this->editableWorker->buildEditableHash($editable); + + return sprintf( + '
%s
', + $editableHash, + $contentBlock + ); } public function renderEditableWithWrapper(string $type, array $viewParameters): string @@ -52,7 +64,7 @@ public function buildEditable(HeadlessEditableInfo $headlessEditableInfo): Edita private function buildStandardEditable(HeadlessEditableInfo $headlessEditableInfo): Editable|string|array { - return $this->processEditable($headlessEditableInfo); + return $this->processEditable($headlessEditableInfo, $this->getEditable($headlessEditableInfo)); } private function buildColumnEditable(HeadlessEditableInfo $headlessEditableInfo): string|array @@ -72,13 +84,14 @@ private function buildColumnEditable(HeadlessEditableInfo $headlessEditableInfo) foreach ($headlessEditableInfo->getChildren() as $headlessColumnEditableInfo) { $areaBlockDataResponse = null; + $editable = $this->getEditable($headlessEditableInfo); ob_start(); - echo $this->processEditable($headlessColumnEditableInfo, true); + echo $this->processEditable($headlessColumnEditableInfo, $editable, true); if ($editMode === false) { - $areaBlockDataResponse = $this->processEditable($headlessColumnEditableInfo); + $areaBlockDataResponse = $this->processEditable($headlessColumnEditableInfo, $editable); } $areaBlockHtmlResponse = ob_get_clean(); @@ -100,13 +113,14 @@ private function buildAreaEditable(HeadlessEditableInfo $headlessEditableInfo): { $areaDataResponse = ''; $editMode = $headlessEditableInfo->isEditMode(); + $editable = $this->getEditable($headlessEditableInfo); ob_start(); - echo $this->processEditable($headlessEditableInfo, true); + echo $this->processEditable($headlessEditableInfo, $editable, true); if ($editMode === false) { - $areaDataResponse = $this->processEditable($headlessEditableInfo); + $areaDataResponse = $this->processEditable($headlessEditableInfo, $editable); } $areaHtmlResponse = ob_get_clean(); @@ -118,13 +132,14 @@ private function buildAreaBlockEditable(HeadlessEditableInfo $headlessEditableIn { $areaBlockDataResponse = ''; $editMode = $headlessEditableInfo->isEditMode(); + $editable = $this->getEditable($headlessEditableInfo); ob_start(); - echo $this->processEditable($headlessEditableInfo, true); + echo $this->processEditable($headlessEditableInfo, $editable, true); if ($editMode === false) { - $areaBlockDataResponse = $this->processEditable($headlessEditableInfo); + $areaBlockDataResponse = $this->processEditable($headlessEditableInfo, $editable); } $areaBlockHtmlResponse = ob_get_clean(); @@ -145,11 +160,23 @@ private function buildBlockEditable(HeadlessEditableInfo $headlessEditableInfo): $blockEditable = $this->editableRenderer->getEditable($document, 'block', $headlessEditableInfo->getName(), $config, $headlessEditableInfo->isEditMode()); foreach ($blockEditable->getIterator() as $blockIndex) { + + $blockHash = $this->editableWorker->buildBlockHash($headlessEditableInfo->getName(), $blockIndex); + $blockNamespace = sprintf('%s:%s', $headlessEditableInfo->getName(), $blockIndex); + + if ($editMode === true) { + echo sprintf('', $blockHash); + } + + $this->editableWorker->processVirtualElement(HeadlessResponse::TYPE_EDITABLE, 'block', $blockHash, $blockNamespace); + foreach ($headlessEditableInfo->getChildren() as $childHeadlessEditableInfo) { + $editable = $this->getEditable($childHeadlessEditableInfo); + ob_start(); - echo $this->processEditable($childHeadlessEditableInfo, true); + echo $this->processEditable($childHeadlessEditableInfo, $editable, true); $renderedBlockEditable = ob_get_clean(); @@ -162,7 +189,7 @@ private function buildBlockEditable(HeadlessEditableInfo $headlessEditableInfo): ]); if ($editMode === false) { - $data[] = $this->processEditable($childHeadlessEditableInfo); + $data[] = $this->processEditable($childHeadlessEditableInfo, $editable); } } } @@ -172,18 +199,12 @@ private function buildBlockEditable(HeadlessEditableInfo $headlessEditableInfo): return $editMode ? $areaBlockHtmlResponse : $data; } - private function processEditable(HeadlessEditableInfo $headlessEditableInfo, bool $forceRendering = false): mixed + private function processEditable(HeadlessEditableInfo $headlessEditableInfo, Editable $editable, bool $forceRendering = false): mixed { $editMode = $headlessEditableInfo->isEditMode(); $type = $headlessEditableInfo->getType(); - $name = $headlessEditableInfo->getName(); - $config = $headlessEditableInfo->getConfig(); - $document = $headlessEditableInfo->getDocument(); $isSimple = !$headlessEditableInfo->isBlockEditable(); - /** @var Editable $editable */ - $editable = $this->editableRenderer->getEditable($document, $type, $name, $config, $editMode); - if ($headlessEditableInfo->isStandAlone() === true) { if ($editMode === false) { @@ -217,4 +238,18 @@ private function processEditable(HeadlessEditableInfo $headlessEditableInfo, boo return ''; } + + public function getEditable(HeadlessEditableInfo $headlessEditableInfo): Editable + { + $editMode = $headlessEditableInfo->isEditMode(); + $type = $headlessEditableInfo->getType(); + $name = $headlessEditableInfo->getName(); + $config = $headlessEditableInfo->getConfig(); + $document = $headlessEditableInfo->getDocument(); + + /** @var Editable $editable */ + $editable = $this->editableRenderer->getEditable($document, $type, $name, $config, $editMode); + + return $editable; + } } diff --git a/src/Event/HeadlessElementEvent.php b/src/Event/HeadlessElementEvent.php index 79d6f601..7b4b4297 100644 --- a/src/Event/HeadlessElementEvent.php +++ b/src/Event/HeadlessElementEvent.php @@ -10,6 +10,7 @@ public function __construct( protected array $data, protected string $elementType, protected string $elementSubType, + protected ?string $elementHash, protected string $elementNamespace ) { } @@ -29,6 +30,11 @@ public function getElementSubType(): string return $this->elementSubType; } + public function getElementHash(): ?string + { + return $this->elementHash; + } + public function getElementNamespace(): string { return $this->elementNamespace; diff --git a/src/HeadlessDocument/HeadlessDocumentResolver.php b/src/HeadlessDocument/HeadlessDocumentResolver.php index 9e923d1d..14f3f4ed 100644 --- a/src/HeadlessDocument/HeadlessDocumentResolver.php +++ b/src/HeadlessDocument/HeadlessDocumentResolver.php @@ -67,6 +67,7 @@ private function buildEditModeOutput(Document $document, string $headlessDocumen $headlessInfo = $this->editableInfoFactory->createViaEditable($document, $itemName, true, $item); $renderedEditable = $this->headlessEditableRenderer->buildEditable($headlessInfo); + $editable = $this->headlessEditableRenderer->getEditable($headlessInfo); if (in_array($headlessInfo->getType(), ['areablock', 'area'])) { // will be rendered within brick process workflow @@ -76,7 +77,8 @@ private function buildEditModeOutput(Document $document, string $headlessDocumen $this->headlessEditableRenderer->renderEditableWithWrapper($item['type'], [ 'item' => $item, 'editable' => $renderedEditable - ]) + ]), + $editable ); }