diff --git a/Classes/Infrastructure/ContentRepository/CreationDialog/CreationDialogNodeTypePostprocessor.php b/Classes/Infrastructure/ContentRepository/CreationDialog/CreationDialogNodeTypePostprocessor.php index 9c7557bd97..d0edd45d52 100644 --- a/Classes/Infrastructure/ContentRepository/CreationDialog/CreationDialogNodeTypePostprocessor.php +++ b/Classes/Infrastructure/ContentRepository/CreationDialog/CreationDialogNodeTypePostprocessor.php @@ -57,6 +57,12 @@ * 'someProperty': * # ... * + * + * Sets default editor configurations for elements + * ----------------------------------------------- + * We add default editor configurations for elements based on type and editor. + * Similar to the process for regular properties in {@see \Neos\Neos\NodeTypePostprocessor\DefaultPropertyEditorPostprocessor} + * */ class CreationDialogNodeTypePostprocessor implements NodeTypePostprocessorInterface { @@ -80,18 +86,68 @@ class CreationDialogNodeTypePostprocessor implements NodeTypePostprocessorInterf */ public function process(NodeType $nodeType, array &$configuration, array $options): void { - if (!isset($configuration['properties'])) { - return; - } $creationDialogElements = $configuration['ui']['creationDialog']['elements'] ?? []; - $creationDialogElements = $this->promotePropertiesIntoCreationDialog($configuration['properties'], $creationDialogElements); + if (!empty($configuration['properties'] ?? null)) { + $creationDialogElements = $this->promotePropertiesIntoCreationDialog($configuration['properties'], $creationDialogElements); + } + + $this->mergeDefaultCreationDialogElementEditors($creationDialogElements); if ($creationDialogElements !== []) { $configuration['ui']['creationDialog']['elements'] = (new PositionalArraySorter($creationDialogElements))->toArray(); } } + /** + * @param array $creationDialogElements + */ + private function mergeDefaultCreationDialogElementEditors(array &$creationDialogElements): void + { + foreach ($creationDialogElements as &$elementConfiguration) { + if (!isset($elementConfiguration['type'])) { + continue; + } + + $type = $elementConfiguration['type']; + $defaultConfigurationFromDataType = $this->dataTypesDefaultConfiguration[$type] ?? []; + + // FIRST STEP: Figure out which editor should be used + // - Default: editor as configured from the data type + // - Override: editor as configured from the property configuration. + if (isset($elementConfiguration['ui']['editor'])) { + $editor = $elementConfiguration['ui']['editor']; + } elseif (isset($defaultConfigurationFromDataType['editor'])) { + $editor = $defaultConfigurationFromDataType['editor']; + } else { + // No exception since the configuration could be a partial configuration overriding a property + // with showInCreationDialog flag set + continue; + } + + // SECOND STEP: Build up the full UI configuration by merging: + // - take configuration from editor defaults + // - take configuration from dataType + // - take configuration from creationDialog elements (NodeTypes) + $mergedUiConfiguration = $this->editorDefaultConfiguration[$editor] ?? []; + $mergedUiConfiguration = Arrays::arrayMergeRecursiveOverrule( + $mergedUiConfiguration, + $defaultConfigurationFromDataType + ); + $mergedUiConfiguration = Arrays::arrayMergeRecursiveOverrule( + $mergedUiConfiguration, + $elementConfiguration['ui'] ?? [] + ); + $elementConfiguration['ui'] = $mergedUiConfiguration; + $elementConfiguration['ui']['editor'] = $editor; + } + } + + /** + * @param array $properties + * @param array $explicitCreationDialogElements + * @return array + */ private function promotePropertiesIntoCreationDialog(array $properties, array $explicitCreationDialogElements): array { foreach ($properties as $propertyName => $propertyConfiguration) { @@ -146,6 +202,7 @@ private function promotePropertyIntoCreationDialog(string $propertyName, array $ $convertedConfiguration['ui']['hidden'] = $propertyConfiguration['ui']['inspector']['hidden']; } + // todo maybe duplicated due to mergeDefaultCreationDialogElementEditors $editor = $propertyConfiguration['ui']['inspector']['editor'] ?? $dataTypeDefaultConfiguration['editor'] ?? 'Neos.Neos/Inspector/Editors/TextFieldEditor'; diff --git a/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php b/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php index e15fb7b0ba..38bf059b00 100644 --- a/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php +++ b/Tests/Unit/CreationDialogNodeTypePostprocessorTest.php @@ -26,6 +26,8 @@ public function setUp(): void } /** + * promoted elements (showInCreationDialog: true) + * * @test */ public function processCopiesInspectorConfigurationToCreationDialogElements(): void @@ -103,6 +105,8 @@ public function processDoesNotCreateEmptyCreationDialogs(): void } /** + * promoted elements (showInCreationDialog: true) + * * @test */ public function processRespectsDataTypeDefaultConfiguration(): void @@ -144,10 +148,12 @@ public function processRespectsDataTypeDefaultConfiguration(): void ], ]; - self::assertSame($expectedElements, $configuration['ui']['creationDialog']['elements']); + self::assertEquals($expectedElements, $configuration['ui']['creationDialog']['elements']); } /** + * promoted elements (showInCreationDialog: true) + * * @test */ public function processRespectsEditorDefaultConfiguration(): void @@ -198,6 +204,220 @@ public function processRespectsEditorDefaultConfiguration(): void ], ]; - self::assertSame($expectedElements, $configuration['ui']['creationDialog']['elements']); + self::assertEquals($expectedElements, $configuration['ui']['creationDialog']['elements']); + } + + /** + * default editor + * + * @test + */ + public function processConvertsCreationDialogConfiguration(): void + { + $configuration = [ + 'ui' => [ + 'creationDialog' => [ + 'elements' => [ + 'elementWithoutType' => [ + 'ui' => [ + 'label' => 'Some Label' + ] + ], + 'elementWithUnknownType' => [ + 'type' => 'TypeWithoutDataTypeConfig', + 'ui' => [ + 'label' => 'Some Label', + 'editor' => 'EditorFromPropertyConfig', + ] + ], + 'elementWithEditorFromDataTypeConfig' => [ + 'type' => 'TypeWithDataTypeConfig', + 'ui' => [ + 'value' => 'fromPropertyConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + 'elementWithEditorFromDataTypeConfigWithoutUiConfig' => [ + 'type' => 'TypeWithDataTypeConfig' + ], + 'elementWithOverriddenEditorConfig' => [ + 'type' => 'TypeWithDataTypeConfig', + 'ui' => [ + 'editor' => 'EditorFromPropertyConfig', + 'value' => 'fromPropertyConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + 'elementWithOverriddenEditorConfigAndEditorDefaultConfig' => [ + 'type' => 'TypeWithDataTypeConfig', + 'ui' => [ + 'editor' => 'EditorWithDefaultConfig', + 'value' => 'fromPropertyConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + 'elementWithEditorDefaultConfig' => [ + 'type' => 'TypeWithDefaultEditorConfig', + 'ui' => [ + 'value' => 'fromPropertyConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + 'elementWithOverriddenEditorConfigAndEditorDefaultConfig2' => [ + 'type' => 'TypeWithDefaultEditorConfig', + 'ui' => [ + 'editor' => 'EditorWithoutDefaultConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + 'elementWithOverriddenEditorConfigAndEditorDefaultConfig3' => [ + 'type' => 'TypeWithDefaultEditorConfig2', + 'ui' => [ + 'editor' => 'EditorWithDefaultConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + ], + ], + ], + ]; + + $this->inject($this->postprocessor, 'editorDefaultConfiguration', [ + 'EditorWithDefaultConfig' => [ + 'value' => 'fromEditorDefaultConfig', + 'editorDefaultValue' => 'fromEditorDefaultConfig', + ], + ]); + + $this->inject($this->postprocessor, 'dataTypesDefaultConfiguration', [ + 'TypeWithDataTypeConfig' => [ + 'editor' => 'EditorFromDataTypeConfig', + 'value' => 'fromDataTypeConfig', + 'dataTypeValue' => 'fromDataTypeConfig', + ], + 'TypeWithDefaultEditorConfig' => [ + 'editor' => 'EditorWithDefaultConfig', + 'value' => 'fromDataTypeConfig', + 'dataTypeValue' => 'fromDataTypeConfig', + ], + 'TypeWithDefaultEditorConfig2' => [ + 'editor' => 'EditorWithDefaultConfig', + 'dataTypeValue' => 'fromDataTypeConfig', + ], + ]); + + $expectedResult = [ + 'ui' => [ + 'creationDialog' => [ + 'elements' => [ + 'elementWithoutType' => [ + 'ui' => [ + 'label' => 'Some Label' + ] + ], + 'elementWithUnknownType' => [ + 'type' => 'TypeWithoutDataTypeConfig', + 'ui' => [ + 'label' => 'Some Label', + 'editor' => 'EditorFromPropertyConfig', + ] + ], + 'elementWithEditorFromDataTypeConfig' => [ + 'type' => 'TypeWithDataTypeConfig', + 'ui' => [ + 'editor' => 'EditorFromDataTypeConfig', + 'value' => 'fromPropertyConfig', + 'dataTypeValue' => 'fromDataTypeConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + 'elementWithEditorFromDataTypeConfigWithoutUiConfig' => [ + 'type' => 'TypeWithDataTypeConfig', + 'ui' => [ + 'editor' => 'EditorFromDataTypeConfig', + 'value' => 'fromDataTypeConfig', + 'dataTypeValue' => 'fromDataTypeConfig', + ] + ], + 'elementWithOverriddenEditorConfig' => [ + 'type' => 'TypeWithDataTypeConfig', + 'ui' => [ + 'editor' => 'EditorFromPropertyConfig', + 'value' => 'fromPropertyConfig', + 'dataTypeValue' => 'fromDataTypeConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + 'elementWithOverriddenEditorConfigAndEditorDefaultConfig' => [ + 'type' => 'TypeWithDataTypeConfig', + 'ui' => [ + 'value' => 'fromPropertyConfig', + 'editorDefaultValue' => 'fromEditorDefaultConfig', + 'editor' => 'EditorWithDefaultConfig', + 'dataTypeValue' => 'fromDataTypeConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + 'elementWithEditorDefaultConfig' => [ + 'type' => 'TypeWithDefaultEditorConfig', + 'ui' => [ + 'value' => 'fromPropertyConfig', + 'editorDefaultValue' => 'fromEditorDefaultConfig', + 'editor' => 'EditorWithDefaultConfig', + 'dataTypeValue' => 'fromDataTypeConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + 'elementWithOverriddenEditorConfigAndEditorDefaultConfig2' => [ + 'type' => 'TypeWithDefaultEditorConfig', + 'ui' => [ + 'editor' => 'EditorWithoutDefaultConfig', + 'value' => 'fromDataTypeConfig', + 'dataTypeValue' => 'fromDataTypeConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + 'elementWithOverriddenEditorConfigAndEditorDefaultConfig3' => [ + 'type' => 'TypeWithDefaultEditorConfig2', + 'ui' => [ + 'value' => 'fromEditorDefaultConfig', + 'editorDefaultValue' => 'fromEditorDefaultConfig', + 'editor' => 'EditorWithDefaultConfig', + 'dataTypeValue' => 'fromDataTypeConfig', + 'elementValue' => 'fromPropertyConfig', + ] + ], + ], + ], + ], + ]; + + $this->postprocessor->process($this->mockNodeType, $configuration, []); + + self::assertSame($expectedResult, $configuration); + } + + /** + * @test + */ + public function processDoesNotThrowExceptionIfNoCreationDialogEditorCanBeResolved(): void + { + $configuration = [ + 'ui' => [ + 'creationDialog' => [ + 'elements' => [ + 'someElement' => [ + 'type' => 'string', + 'ui' => ['label' => 'Foo'] + ], + ], + ], + ], + ]; + $expected = $configuration; + + $this->postprocessor->process($this->mockNodeType, $configuration, []); + + self::assertSame($expected, $configuration); } }