From 1bb856c47400641aaf64ae8442e423ffd690959d Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 16 Nov 2023 21:06:17 +0100 Subject: [PATCH 1/6] TASK: Adjust to split up references and properties --- Classes/Domain/Model/Changes/Property.php | 42 +++++----- .../Service/NodePropertyConversionService.php | 2 - .../Service/NodePropertyConverterService.php | 79 ++++++++----------- 3 files changed, 54 insertions(+), 69 deletions(-) diff --git a/Classes/Domain/Model/Changes/Property.php b/Classes/Domain/Model/Changes/Property.php index fbf016ec48..4065cc9674 100644 --- a/Classes/Domain/Model/Changes/Property.php +++ b/Classes/Domain/Model/Changes/Property.php @@ -134,9 +134,8 @@ public function canApply(): bool } $nodeType = $this->subject->nodeType; $propertyName = $this->getPropertyName(); - $nodeTypeProperties = $nodeType->getProperties(); - return isset($nodeTypeProperties[$propertyName]); + return $nodeType->hasProperty($propertyName) || $nodeType->hasReference($propertyName); } /** @@ -153,8 +152,6 @@ public function apply(): void if ($this->canApply() && !is_null($subject) && !is_null($propertyName)) { $contentRepository = $this->contentRepositoryRegistry->get($subject->subgraphIdentity->contentRepositoryId); - $propertyType = $this->getNodeType($subject)->getPropertyType($propertyName); - $workspace = $this->contentRepositoryRegistry->get($this->subject->subgraphIdentity->contentRepositoryId) ->getWorkspaceFinder()->findOneByCurrentContentStreamId($subject->subgraphIdentity->contentStreamId); if (!$workspace) { @@ -164,24 +161,16 @@ public function apply(): void ); } - // Use extra commands for reference handling - if ($propertyType === 'reference' || $propertyType === 'references') { + if ($this->getNodeType($subject)->hasReference($propertyName)) { + // Use extra commands for reference handling $value = $this->getValue(); + $destinationNodeAggregateIds = []; - if ($propertyType === 'reference') { - if (is_string($value) && !empty($value)) { - $destinationNodeAggregateIds[] = $value; - } - } - if ($propertyType === 'references') { - /** @var array $values */ - $values = $value; - if (is_array($values)) { - foreach ($values as $singleNodeAggregateId) { - $destinationNodeAggregateIds[] = $singleNodeAggregateId; - } - } + if (is_string($value) && !empty($value)) { + $destinationNodeAggregateIds = [$value]; + } elseif (is_array($value)) { + $destinationNodeAggregateIds = $value; } $commandResult = $contentRepository->handle( @@ -286,8 +275,12 @@ public function apply(): void $updateNodeInfo->setNode($node); $this->feedbackCollection->add($updateNodeInfo); - $reloadIfChangedConfigurationPath = sprintf('properties.%s.ui.reloadIfChanged', $propertyName); - if (!$this->getIsInline() && $this->getNodeType($node)->getConfiguration($reloadIfChangedConfigurationPath)) { + if (!$this->getIsInline() + && ( + $this->getNodeType($node)->hasConfiguration(sprintf('properties.%s.ui.reloadIfChanged', $propertyName)) + || $this->getNodeType($node)->hasConfiguration(sprintf('references.%s.ui.reloadIfChanged', $propertyName)) + ) + ) { if ($this->getNodeDomAddress() && $this->getNodeDomAddress()->getFusionPath() && $parentNode && $this->getNodeType($parentNode)->isOfType('Neos.Neos:ContentCollection')) { @@ -300,9 +293,12 @@ public function apply(): void } } - $reloadPageIfChangedConfigurationPath = sprintf('properties.%s.ui.reloadPageIfChanged', $propertyName); if (!$this->getIsInline() - && $this->getNodeType($node)->getConfiguration($reloadPageIfChangedConfigurationPath)) { + && ( + $this->getNodeType($node)->hasConfiguration(sprintf('properties.%s.ui.reloadPageIfChanged', $propertyName)) + || $this->getNodeType($node)->hasConfiguration(sprintf('references.%s.ui.reloadPageIfChanged', $propertyName)) + ) + ) { $this->reloadDocument($node); } } diff --git a/Classes/Domain/Service/NodePropertyConversionService.php b/Classes/Domain/Service/NodePropertyConversionService.php index 356404b1b5..4b7b74f603 100644 --- a/Classes/Domain/Service/NodePropertyConversionService.php +++ b/Classes/Domain/Service/NodePropertyConversionService.php @@ -57,8 +57,6 @@ public function convert(string $propertyType, string|array|null $rawValue): mixe return $rawValue; case 'reference': - throw new \Exception("not implemented here, must be handled outside."); - case 'references': throw new \Exception("not implemented here, must be handled outside."); diff --git a/Classes/Domain/Service/NodePropertyConverterService.php b/Classes/Domain/Service/NodePropertyConverterService.php index 3451c477d8..fb0597607b 100644 --- a/Classes/Domain/Service/NodePropertyConverterService.php +++ b/Classes/Domain/Service/NodePropertyConverterService.php @@ -18,7 +18,6 @@ use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindReferencesFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; -use Neos\ContentRepository\Core\Projection\ContentGraph\References; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Flow\Log\ThrowableStorageInterface; @@ -101,49 +100,51 @@ public function injectThrowableStorage(ThrowableStorageInterface $throwableStora } /** - * Get a single property reduced to a simple type (no objects) representation - * - * @param Node $node - * @param string $propertyName - * @return mixed + * @return list|string|null */ - public function getProperty(Node $node, $propertyName) + private function getReference(Node $node, string $referenceName): array|string|null { - if ($propertyName === '_hidden') { - return $node->tags->contain(SubtreeTag::fromString('disabled')); + $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); + $references = $subgraph->findReferences( + $node->nodeAggregateId, + FindReferencesFilter::create(referenceName: $referenceName) + ); + + $referenceIdentifiers = []; + foreach ($references as $reference) { + $referenceIdentifiers[] = $reference->node->nodeAggregateId->value; } - $propertyType = $this->getNodeType($node)->getPropertyType($propertyName); - // We handle "reference" and "references" differently than other properties; - // because we need to use another API for querying these references. - if ($propertyType === 'reference') { - $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); - $referenceIdentifiers = $this->toNodeIdentifierStrings( - $subgraph->findReferences( - $node->nodeAggregateId, - FindReferencesFilter::create(referenceName: $propertyName) - ) - ); + $maxItems = $this->getNodeType($node)->getReferences()[$referenceName]['constraints']['maxItems'] ?? null; + + if ($maxItems === 1) { + // special handling to simulate old single reference behaviour. + // todo should be adjusted in the ui if (count($referenceIdentifiers) === 0) { return null; } else { return reset($referenceIdentifiers); } - } elseif ($propertyType === 'references') { - $subgraph = $this->contentRepositoryRegistry->subgraphForNode($node); - $references = $subgraph->findReferences( - $node->nodeAggregateId, - FindReferencesFilter::create(referenceName: $propertyName) - ); + } + return $referenceIdentifiers; + } + + /** + * Get a single property reduced to a simple type (no objects) representation + */ + private function getProperty(Node $node, string $propertyName): mixed + { + if ($propertyName === '_hidden') { + return $node->tags->contain(SubtreeTag::fromString('disabled')); + } - return $this->toNodeIdentifierStrings($references); - // Here, the normal property access logic starts. - } elseif ($propertyName[0] === '_' && $propertyName !== '_hiddenInIndex') { + if ($propertyName[0] === '_' && $propertyName !== '_hiddenInIndex') { $propertyValue = ObjectAccess::getProperty($node, ltrim($propertyName, '_')); } else { $propertyValue = $node->getProperty($propertyName); } + $propertyType = $this->getNodeType($node)->getPropertyType($propertyName); try { $convertedValue = $this->convertValue($propertyValue, $propertyType); } catch (PropertyException $exception) { @@ -169,19 +170,7 @@ public function getProperty(Node $node, $propertyName) } /** - * @return array - */ - private function toNodeIdentifierStrings(References $references): array - { - $identifiers = []; - foreach ($references as $reference) { - $identifiers[] = $reference->node->nodeAggregateId->value; - } - return $identifiers; - } - - /** - * Get all properties reduced to simple type (no objects) representations in an array + * Get all properties and references stuff reduced to simple type (no objects) representations in an array * * @param Node $node * @return array @@ -189,7 +178,7 @@ private function toNodeIdentifierStrings(References $references): array public function getPropertiesArray(Node $node) { $properties = []; - foreach ($this->getNodeType($node)->getProperties() as $propertyName => $propertyConfiguration) { + foreach ($this->getNodeType($node)->getProperties() as $propertyName => $_) { if ($propertyName[0] === '_' && $propertyName[1] === '_') { // skip fully-private properties continue; @@ -197,7 +186,9 @@ public function getPropertiesArray(Node $node) $properties[$propertyName] = $this->getProperty($node, $propertyName); } - + foreach ($this->getNodeType($node)->getReferences() as $referenceName => $_) { + $properties[$referenceName] = $this->getReference($node, $referenceName); + } return $properties; } From b309d47996e59d273e1c9670af90f7bc570dbdde Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Thu, 15 Feb 2024 13:50:31 +0100 Subject: [PATCH 2/6] TASK: Remove obsolete reference handing logic --- Classes/Domain/Service/NodePropertyConverterService.php | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Classes/Domain/Service/NodePropertyConverterService.php b/Classes/Domain/Service/NodePropertyConverterService.php index fb0597607b..a031f0f6ba 100644 --- a/Classes/Domain/Service/NodePropertyConverterService.php +++ b/Classes/Domain/Service/NodePropertyConverterService.php @@ -271,14 +271,7 @@ protected function createConfiguration($dataType) $propertyMappingConfiguration = new PropertyMappingConfiguration(); $propertyMappingConfiguration->allowAllProperties(); - $parsedType = [ - 'elementType' => null, - 'type' => $dataType - ]; - // Special handling for "reference(s)", should be deprecated and normlized to array - if ($dataType !== 'references' && $dataType !== 'reference') { - $parsedType = TypeHandling::parseType($dataType); - } + $parsedType = TypeHandling::parseType($dataType); if ($this->setTypeConverterForType($propertyMappingConfiguration, $dataType) === false) { $this->setTypeConverterForType($propertyMappingConfiguration, $parsedType['type']); From 60b3b104276d4b51c34a7bc6948e2bc9c823c1e9 Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Fri, 16 Feb 2024 23:49:03 +0100 Subject: [PATCH 3/6] TASK: Fix `reloadPageIfChanged` config --- Classes/Domain/Model/Changes/Property.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/Domain/Model/Changes/Property.php b/Classes/Domain/Model/Changes/Property.php index 4065cc9674..166273cc44 100644 --- a/Classes/Domain/Model/Changes/Property.php +++ b/Classes/Domain/Model/Changes/Property.php @@ -295,8 +295,8 @@ public function apply(): void if (!$this->getIsInline() && ( - $this->getNodeType($node)->hasConfiguration(sprintf('properties.%s.ui.reloadPageIfChanged', $propertyName)) - || $this->getNodeType($node)->hasConfiguration(sprintf('references.%s.ui.reloadPageIfChanged', $propertyName)) + $this->getNodeType($node)->getConfiguration(sprintf('properties.%s.ui.reloadPageIfChanged', $propertyName)) + || $this->getNodeType($node)->getConfiguration(sprintf('references.%s.ui.reloadPageIfChanged', $propertyName)) ) ) { $this->reloadDocument($node); From a7c94e3b002a08e17fae0659ab81c62a6104a7cf Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Mon, 18 Mar 2024 20:54:24 +0100 Subject: [PATCH 4/6] TASK: Fix `PromotedElementsCreationHandler` to work with new references api --- .../PromotedElementsCreationHandlerFactory.php | 8 +++----- Tests/Unit/CreationDialogNodeTypePostprocessorTest.php | 7 +++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Classes/Infrastructure/ContentRepository/CreationDialog/PromotedElementsCreationHandlerFactory.php b/Classes/Infrastructure/ContentRepository/CreationDialog/PromotedElementsCreationHandlerFactory.php index 64993e8359..5dababedaa 100644 --- a/Classes/Infrastructure/ContentRepository/CreationDialog/PromotedElementsCreationHandlerFactory.php +++ b/Classes/Infrastructure/ContentRepository/CreationDialog/PromotedElementsCreationHandlerFactory.php @@ -40,8 +40,7 @@ public function handle(NodeCreationCommands $commands, NodeCreationElements $ele $setReferencesCommands = []; foreach ($elements as $elementName => $elementValue) { // handle properties - // todo this will be simplified once hasProperty does not return true for references - if ($nodeType->hasProperty($elementName) && ($nodeType->getPropertyType($elementName) !== 'references' && $nodeType->getPropertyType($elementName) !== 'reference')) { + if ($nodeType->hasProperty($elementName)) { $propertyConfiguration = $nodeType->getProperties()[$elementName]; if ( ($propertyConfiguration['ui']['showInCreationDialog'] ?? false) === true @@ -52,10 +51,9 @@ public function handle(NodeCreationCommands $commands, NodeCreationElements $ele } // handle references - // todo this will be replaced by $nodeType->hasReference() - if ($nodeType->hasProperty($elementName) && ($nodeType->getPropertyType($elementName) === 'references' || $nodeType->getPropertyType($elementName) === 'reference')) { + if ($nodeType->hasReference($elementName)) { assert($elementValue instanceof NodeAggregateIds); - $referenceConfiguration = $nodeType->getProperties()[$elementName]; + $referenceConfiguration = $nodeType->getReferences()[$elementName]; if ( ($referenceConfiguration['ui']['showInCreationDialog'] ?? false) === true ) { diff --git a/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php b/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php index 38bf059b00..8228b78b38 100644 --- a/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php +++ b/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php @@ -33,6 +33,7 @@ public function setUp(): void public function processCopiesInspectorConfigurationToCreationDialogElements(): void { $configuration = [ + 'references' => [], 'properties' => [ 'somePropertyName' => [ 'ui' => [ @@ -215,6 +216,8 @@ public function processRespectsEditorDefaultConfiguration(): void public function processConvertsCreationDialogConfiguration(): void { $configuration = [ + 'references' => [], + 'properties' => [], 'ui' => [ 'creationDialog' => [ 'elements' => [ @@ -307,6 +310,8 @@ public function processConvertsCreationDialogConfiguration(): void ]); $expectedResult = [ + 'references' => [], + 'properties' => [], 'ui' => [ 'creationDialog' => [ 'elements' => [ @@ -403,6 +408,8 @@ public function processConvertsCreationDialogConfiguration(): void public function processDoesNotThrowExceptionIfNoCreationDialogEditorCanBeResolved(): void { $configuration = [ + 'references' => [], + 'properties' => [], 'ui' => [ 'creationDialog' => [ 'elements' => [ From 377c5f0d8ae5138c97d60e0b2e8e1a3d78eab11e Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sat, 6 Apr 2024 13:29:16 +0200 Subject: [PATCH 5/6] TASK: Refactor CreationDialogNodeTypePostprocessorTest --- ...reationDialogNodeTypePostprocessorTest.php | 101 ++++++++++-------- 1 file changed, 56 insertions(+), 45 deletions(-) diff --git a/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php b/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php index 8228b78b38..d09de2d2bc 100644 --- a/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php +++ b/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php @@ -5,26 +5,11 @@ use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\Flow\Tests\UnitTestCase; +use Neos\Neos\NodeTypePostprocessor\DefaultPropertyEditorPostprocessor; use Neos\Neos\Ui\Infrastructure\ContentRepository\CreationDialog\CreationDialogNodeTypePostprocessor; class CreationDialogNodeTypePostprocessorTest extends UnitTestCase { - /** - * @var CreationDialogNodeTypePostprocessor - */ - private $postprocessor; - - /** - * @var NodeType - */ - private $mockNodeType; - - public function setUp(): void - { - $this->postprocessor = new CreationDialogNodeTypePostprocessor(); - $this->mockNodeType = new NodeType(NodeTypeName::fromString('Foo:Bar'), [], [], new DefaultNodeLabelGeneratorFactory()); - } - /** * promoted elements (showInCreationDialog: true) * @@ -56,8 +41,6 @@ public function processCopiesInspectorConfigurationToCreationDialogElements(): v ], ]; - $this->postprocessor->process($this->mockNodeType, $configuration, []); - $expectedElements = [ 'somePropertyName' => [ 'type' => 'string', @@ -78,7 +61,9 @@ public function processCopiesInspectorConfigurationToCreationDialogElements(): v ], ]; - self::assertSame($expectedElements, $configuration['ui']['creationDialog']['elements']); + $result = $this->processConfigurationLegacyOnlyOnce($configuration, [], []); + + self::assertSame($expectedElements, $result['ui']['creationDialog']['elements']); } /** @@ -98,11 +83,11 @@ public function processDoesNotCreateEmptyCreationDialogs(): void ], ], ]; - $originalConfiguration = $configuration; - $this->postprocessor->process($this->mockNodeType, $configuration, []); + $result = $this->processConfigurationLegacyOnlyOnce($configuration, [], []); + + self::assertSame($configuration, $result); - self::assertSame($originalConfiguration, $configuration); } /** @@ -126,7 +111,7 @@ public function processRespectsDataTypeDefaultConfiguration(): void ], ], ]; - $this->inject($this->postprocessor, 'dataTypesDefaultConfiguration', [ + $dataTypesDefaultConfiguration = [ 'SomeType' => [ 'editor' => 'Some\Default\Editor', 'editorOptions' => [ @@ -134,9 +119,7 @@ public function processRespectsDataTypeDefaultConfiguration(): void 'someDefault' => 'option', ] ] - ]); - - $this->postprocessor->process($this->mockNodeType, $configuration, []); + ]; $expectedElements = [ 'somePropertyName' => [ @@ -149,7 +132,9 @@ public function processRespectsDataTypeDefaultConfiguration(): void ], ]; - self::assertEquals($expectedElements, $configuration['ui']['creationDialog']['elements']); + $result = $this->processConfigurationLegacyOnlyOnce($configuration, $dataTypesDefaultConfiguration, []); + + self::assertEquals($expectedElements, $result['ui']['creationDialog']['elements']); } /** @@ -172,7 +157,7 @@ public function processRespectsEditorDefaultConfiguration(): void ], ], ]; - $this->inject($this->postprocessor, 'editorDefaultConfiguration', [ + $editorDefaultConfiguration = [ 'Some\Editor' => [ 'editorOptions' => [ 'some' => 'editorDefault', @@ -180,8 +165,8 @@ public function processRespectsEditorDefaultConfiguration(): void 'someEditorDefault' => 'fromEditor', ] ] - ]); - $this->inject($this->postprocessor, 'dataTypesDefaultConfiguration', [ + ]; + $dataTypesDefaultConfiguration = [ 'SomeType' => [ 'editor' => 'Some\Editor', 'editorOptions' => [ @@ -189,10 +174,7 @@ public function processRespectsEditorDefaultConfiguration(): void 'someDefault' => 'fromDataType', ] ] - ]); - - - $this->postprocessor->process($this->mockNodeType, $configuration, []); + ]; $expectedElements = [ 'somePropertyName' => [ @@ -205,7 +187,9 @@ public function processRespectsEditorDefaultConfiguration(): void ], ]; - self::assertEquals($expectedElements, $configuration['ui']['creationDialog']['elements']); + $result = $this->processConfigurationLegacyOnlyOnce($configuration, $dataTypesDefaultConfiguration, $editorDefaultConfiguration); + + self::assertEquals($expectedElements, $result['ui']['creationDialog']['elements']); } /** @@ -285,14 +269,14 @@ public function processConvertsCreationDialogConfiguration(): void ], ]; - $this->inject($this->postprocessor, 'editorDefaultConfiguration', [ + $editorDefaultConfiguration = [ 'EditorWithDefaultConfig' => [ 'value' => 'fromEditorDefaultConfig', 'editorDefaultValue' => 'fromEditorDefaultConfig', ], - ]); + ]; - $this->inject($this->postprocessor, 'dataTypesDefaultConfiguration', [ + $dataTypesDefaultConfiguration = [ 'TypeWithDataTypeConfig' => [ 'editor' => 'EditorFromDataTypeConfig', 'value' => 'fromDataTypeConfig', @@ -307,7 +291,7 @@ public function processConvertsCreationDialogConfiguration(): void 'editor' => 'EditorWithDefaultConfig', 'dataTypeValue' => 'fromDataTypeConfig', ], - ]); + ]; $expectedResult = [ 'references' => [], @@ -397,9 +381,7 @@ public function processConvertsCreationDialogConfiguration(): void ], ]; - $this->postprocessor->process($this->mockNodeType, $configuration, []); - - self::assertSame($expectedResult, $configuration); + self::assertSame($expectedResult, $this->processConfigurationLegacyOnlyOnce($configuration, $dataTypesDefaultConfiguration, $editorDefaultConfiguration)); } /** @@ -421,10 +403,39 @@ public function processDoesNotThrowExceptionIfNoCreationDialogEditorCanBeResolve ], ], ]; - $expected = $configuration; - $this->postprocessor->process($this->mockNodeType, $configuration, []); + self::assertSame($configuration, $this->processConfigurationFully($configuration, [], [])); + } + + private function processConfigurationFully(array $configuration, array $dataTypesDefaultConfiguration, array $editorDefaultConfiguration): array + { + $mockNodeType = new NodeType(NodeTypeName::fromString('Some.NodeType:Name'), [], [], new DefaultNodeLabelGeneratorFactory()); + + $firstProcessor = new DefaultPropertyEditorPostprocessor(); + $this->inject($firstProcessor, 'dataTypesDefaultConfiguration', $dataTypesDefaultConfiguration); + $this->inject($firstProcessor, 'editorDefaultConfiguration', $editorDefaultConfiguration); + + $firstProcessor->process($mockNodeType, $configuration, []); + + + $secondProcessor = new CreationDialogNodeTypePostprocessor(); + $this->inject($secondProcessor, 'dataTypesDefaultConfiguration', $dataTypesDefaultConfiguration); + $this->inject($secondProcessor, 'editorDefaultConfiguration', $editorDefaultConfiguration); + + $secondProcessor->process($mockNodeType, $configuration, []); + + return $configuration; + } + + private function processConfigurationLegacyOnlyOnce(array $configuration, array $dataTypesDefaultConfiguration, array $editorDefaultConfiguration): array + { + $mockNodeType = new NodeType(NodeTypeName::fromString('Some.NodeType:Name'), [], [], new DefaultNodeLabelGeneratorFactory()); + + $postprocessor = new CreationDialogNodeTypePostprocessor(); + $this->inject($postprocessor, 'dataTypesDefaultConfiguration', $dataTypesDefaultConfiguration); + $this->inject($postprocessor, 'editorDefaultConfiguration', $editorDefaultConfiguration); - self::assertSame($expected, $configuration); + $postprocessor->process($mockNodeType, $configuration, []); + return $configuration; } } From 3266433e376fbbe8e4ebf9024abe0a91f9e9c8db Mon Sep 17 00:00:00 2001 From: mhsdesign <85400359+mhsdesign@users.noreply.github.com> Date: Sat, 6 Apr 2024 19:45:34 +0200 Subject: [PATCH 6/6] TASK: Allow promoting new references into creation dialog --- .../NodeCreation/NodeCreationElements.php | 4 + .../CreationDialogNodeTypePostprocessor.php | 77 ++++++++++++++++++- ...reationDialogNodeTypePostprocessorTest.php | 63 +++++++++++++++ 3 files changed, 143 insertions(+), 1 deletion(-) diff --git a/Classes/Domain/NodeCreation/NodeCreationElements.php b/Classes/Domain/NodeCreation/NodeCreationElements.php index 8148c2112f..b89d29ac58 100644 --- a/Classes/Domain/NodeCreation/NodeCreationElements.php +++ b/Classes/Domain/NodeCreation/NodeCreationElements.php @@ -46,6 +46,10 @@ * type: string * ui: * showInCreationDialog: true + * references: + * myReferences: + * ui: + * showInCreationDialog: true * * @implements \IteratorAggregate * @internal Especially the constructor and the serialized data diff --git a/Classes/Infrastructure/ContentRepository/CreationDialog/CreationDialogNodeTypePostprocessor.php b/Classes/Infrastructure/ContentRepository/CreationDialog/CreationDialogNodeTypePostprocessor.php index d0edd45d52..3d92d1de50 100644 --- a/Classes/Infrastructure/ContentRepository/CreationDialog/CreationDialogNodeTypePostprocessor.php +++ b/Classes/Infrastructure/ContentRepository/CreationDialog/CreationDialogNodeTypePostprocessor.php @@ -17,6 +17,7 @@ use Neos\ContentRepository\Core\NodeType\NodeType; use Neos\ContentRepository\Core\NodeType\NodeTypePostprocessorInterface; use Neos\Flow\Annotations as Flow; +use Neos\Neos\Ui\Domain\NodeCreation\NodeCreationElements; use Neos\Utility\Arrays; use Neos\Utility\PositionalArraySorter; @@ -88,10 +89,14 @@ public function process(NodeType $nodeType, array &$configuration, array $option { $creationDialogElements = $configuration['ui']['creationDialog']['elements'] ?? []; - if (!empty($configuration['properties'] ?? null)) { + if (!empty($configuration['properties'])) { $creationDialogElements = $this->promotePropertiesIntoCreationDialog($configuration['properties'], $creationDialogElements); } + if (!empty($configuration['references'])) { + $creationDialogElements = $this->promoteReferencesIntoCreationDialog($configuration['references'], $creationDialogElements); + } + $this->mergeDefaultCreationDialogElementEditors($creationDialogElements); if ($creationDialogElements !== []) { @@ -224,4 +229,74 @@ private function promotePropertyIntoCreationDialog(string $propertyName, array $ $convertedConfiguration['ui']['editorOptions'] = $editorOptions; return $convertedConfiguration; } + + /** + * @param array $references + * @param array $explicitCreationDialogElements + * @return array + */ + private function promoteReferencesIntoCreationDialog(array $references, array $explicitCreationDialogElements): array + { + foreach ($references as $referenceName => $referenceConfiguration) { + if ( + !isset($referenceConfiguration['ui']['showInCreationDialog']) + || $referenceConfiguration['ui']['showInCreationDialog'] !== true + ) { + continue; + } + + $creationDialogElement = $this->promoteReferenceIntoCreationDialog($referenceConfiguration); + if (isset($explicitCreationDialogElements[$referenceName])) { + $creationDialogElement = Arrays::arrayMergeRecursiveOverrule( + $creationDialogElement, + $explicitCreationDialogElements[$referenceName] + ); + } + $explicitCreationDialogElements[$referenceName] = $creationDialogElement; + } + return $explicitCreationDialogElements; + } + + /** + * Converts a NodeType reference configuration to the corresponding creationDialog "element" configuration + * + * @param array $referenceConfiguration + * @return array + */ + private function promoteReferenceIntoCreationDialog(array $referenceConfiguration): array + { + $maxAllowedItems = $referenceConfiguration['constraints']['maxItems'] ?? null; + $referenceMagicType = $maxAllowedItems === 1 ? 'reference' : 'references'; + /** + * For references, we add this magic type to the elements configuration {@see NodeCreationElements} + */ + $convertedConfiguration = [ + 'type' => $referenceMagicType, + ]; + if (isset($referenceConfiguration['ui']['label'])) { + $convertedConfiguration['ui']['label'] = $referenceConfiguration['ui']['label']; + } + if (isset($referenceConfiguration['defaultValue'])) { + $convertedConfiguration['defaultValue'] = $referenceConfiguration['defaultValue']; + } + if (isset($referenceConfiguration['ui']['help'])) { + $convertedConfiguration['ui']['help'] = $referenceConfiguration['ui']['help']; + } + if (isset($referenceConfiguration['ui']['inspector']['position'])) { + $convertedConfiguration['position'] = $referenceConfiguration['ui']['inspector']['position']; + } + if (isset($referenceConfiguration['ui']['inspector']['hidden'])) { + $convertedConfiguration['ui']['hidden'] = $referenceConfiguration['ui']['inspector']['hidden']; + } + /** + * the editor will be set based on the type above via {@see self::mergeDefaultCreationDialogElementEditors} + */ + if (isset($referenceConfiguration['ui']['inspector']['editor'])) { + $convertedConfiguration['ui']['editor'] = $referenceConfiguration['ui']['inspector']['editor']; + } + if (isset($referenceConfiguration['ui']['inspector']['editorOptions'])) { + $convertedConfiguration['ui']['editorOptions'] = $referenceConfiguration['ui']['inspector']['editorOptions']; + } + return $convertedConfiguration; + } } diff --git a/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php b/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php index d09de2d2bc..b5f19d3ced 100644 --- a/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php +++ b/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php @@ -7,9 +7,72 @@ use Neos\Flow\Tests\UnitTestCase; use Neos\Neos\NodeTypePostprocessor\DefaultPropertyEditorPostprocessor; use Neos\Neos\Ui\Infrastructure\ContentRepository\CreationDialog\CreationDialogNodeTypePostprocessor; +use Symfony\Component\Yaml\Yaml; class CreationDialogNodeTypePostprocessorTest extends UnitTestCase { + public function examples(): iterable + { + yield 'multiple references' => [ + 'nodeTypeDefinition' => <<<'YAML' + references: + someReferences: + ui: + showInCreationDialog: true + YAML, + 'expectedCreationDialog' => <<<'YAML' + elements: + someReferences: + type: references + ui: + editor: ReferencesEditor + YAML + ]; + + yield 'singular reference' => [ + 'nodeTypeDefinition' => <<<'YAML' + references: + someReference: + constraints: + maxItems: 1 + ui: + showInCreationDialog: true + YAML, + 'expectedCreationDialog' => <<<'YAML' + elements: + someReference: + type: reference + ui: + editor: SingularReferenceEditor + YAML + ]; + } + + /** + * @test + * @dataProvider examples + */ + public function processExamples(string $nodeTypeDefinition, string $expectedCreationDialog) + { + $configuration = array_merge([ + 'references' => [], + 'properties' => [] + ], Yaml::parse($nodeTypeDefinition)); + + $dataTypesDefaultConfiguration = [ + 'reference' => [ + 'editor' => 'SingularReferenceEditor', + ], + 'references' => [ + 'editor' => 'ReferencesEditor', + ], + ]; + + $result = $this->processConfigurationFully($configuration, $dataTypesDefaultConfiguration, []); + + self::assertEquals(Yaml::parse($expectedCreationDialog), $result['ui']['creationDialog'] ?? null); + } + /** * promoted elements (showInCreationDialog: true) *