[BUGFIX] Change finisher override display behavior 77/60577/10
authorRalf Zimmermann <rz@tritum.de>
Fri, 26 Apr 2019 14:37:48 +0000 (16:37 +0200)
committerSusanne Moog <look@susi.dev>
Sat, 27 Apr 2019 15:30:20 +0000 (17:30 +0200)
Show all possible finisher options which can be overridden within the
form plugin even if if they are not part of a form definition.

Resolves: #86635
Resolves: #85033
Releases: master, 9.5
Change-Id: Ie5ce8e2fb97e2c3fde92cbcb405d77818d1c7bda
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/60577
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Mathias Brodala <mbrodala@pagemachine.de>
Tested-by: Susanne Moog <look@susi.dev>
Reviewed-by: Mathias Brodala <mbrodala@pagemachine.de>
Reviewed-by: Susanne Moog <look@susi.dev>
typo3/sysext/form/Classes/Controller/FormFrontendController.php
typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/AbstractProcessor.php [new file with mode: 0644]
typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/FinisherOptionGenerator.php [new file with mode: 0644]
typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/ProcessorDto.php [new file with mode: 0644]
typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/ProcessorInterface.php [new file with mode: 0644]
typo3/sysext/form/Classes/Domain/Configuration/FormDefinition/Converters/FinisherOptionsFlexFormOverridesConverter.php [new file with mode: 0644]
typo3/sysext/form/Classes/Domain/Configuration/FormDefinition/Converters/FlexFormFinisherOverridesConverterDto.php [new file with mode: 0644]
typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php
typo3/sysext/form/Resources/Private/Language/locallang.xlf
typo3/sysext/form/Tests/Unit/Controller/FormFrontendControllerTest.php
typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php

index eedc464..d2eae2b 100644 (file)
@@ -16,8 +16,13 @@ namespace TYPO3\CMS\Form\Controller;
  */
 
 use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
+use TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessing;
+use TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessor;
 use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService;
+use TYPO3\CMS\Form\Domain\Configuration\FormDefinition\Converters\FinisherOptionsFlexFormOverridesConverter;
+use TYPO3\CMS\Form\Domain\Configuration\FormDefinition\Converters\FlexFormFinisherOverridesConverterDto;
 use TYPO3\CMS\Form\Mvc\Configuration\TypoScriptService;
 
 /**
@@ -87,24 +92,33 @@ class FormFrontendController extends ActionController
     protected function overrideByFlexFormSettings(array $formDefinition): array
     {
         if (isset($formDefinition['finishers'])) {
-            foreach ($formDefinition['finishers'] as &$finisherValue) {
-                $finisherIdentifier = $finisherValue['identifier'];
+            foreach ($formDefinition['finishers'] as $index => $formFinisherDefinition) {
+                $finisherIdentifier = $formFinisherDefinition['identifier'];
+                $prototypeName = $formDefinition['prototypeName'] ?? 'standard';
+
                 if ($this->settings['overrideFinishers'] && isset($this->settings['finishers'][$finisherIdentifier])) {
-                    $prototypeName = $formDefinition['prototypeName'] ?? 'standard';
                     $configurationService = $this->objectManager->get(ConfigurationService::class);
                     $prototypeConfiguration = $configurationService->getPrototypeConfiguration($prototypeName);
+                    $flexFormSheetSettings = $this->settings;
+                    $prototypeFinisherDefinition = $prototypeConfiguration['finishersDefinition'][$finisherIdentifier] ?? [];
+                    $converterDto = GeneralUtility::makeInstance(
+                        FlexFormFinisherOverridesConverterDto::class,
+                        $formFinisherDefinition,
+                        $finisherIdentifier,
+                        $flexFormSheetSettings
+                    );
+
+                    // Iterate over all `TYPO3.CMS.Form.prototypes.<prototypeName>.finishersDefinition.<finisherIdentifier>.FormEngine.elements` values
+                    GeneralUtility::makeInstance(ArrayProcessor::class, $prototypeFinisherDefinition['FormEngine']['elements'])->forEach(
+                        GeneralUtility::makeInstance(
+                            ArrayProcessing::class,
+                            'modifyFinisherOptionsFromFlexFormOverrides',
+                            '^(.*)\.config\.type$',
+                            GeneralUtility::makeInstance(FinisherOptionsFlexFormOverridesConverter::class, $converterDto)
+                        )
+                    );
 
-                    foreach ($finisherValue['options'] as $optionKey => $optionValue) {
-                        // If a previous overridden finisher property is excluded at some time
-                        // it is still present in the flexform database row.
-                        // To avoid a override from the time the property is excluded, this check is needed
-                        if (!isset($prototypeConfiguration['finishersDefinition'][$finisherIdentifier]['FormEngine']['elements'][$optionKey])) {
-                            continue;
-                        }
-                        if (isset($this->settings['finishers'][$finisherIdentifier][$optionKey])) {
-                            $finisherValue['options'][$optionKey] = $this->settings['finishers'][$finisherIdentifier][$optionKey];
-                        }
-                    }
+                    $formDefinition['finishers'][$index] = $converterDto->getFinisherDefinition();
                 }
             }
         }
diff --git a/typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/AbstractProcessor.php b/typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/AbstractProcessor.php
new file mode 100644 (file)
index 0000000..350db47
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Form\Domain\Configuration\FlexformConfiguration\Processors;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * @internal
+ */
+abstract class AbstractProcessor implements ProcessorInterface
+{
+    /**
+     * @var ProcessorDto
+     */
+    protected $converterDto;
+
+    /**
+     * @param ProcessorDto $converterDto
+     */
+    public function __construct(ProcessorDto $converterDto)
+    {
+        $this->converterDto = $converterDto;
+    }
+}
diff --git a/typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/FinisherOptionGenerator.php b/typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/FinisherOptionGenerator.php
new file mode 100644 (file)
index 0000000..0915a19
--- /dev/null
@@ -0,0 +1,94 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Form\Domain\Configuration\FlexformConfiguration\Processors;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
+/**
+ * Generate a FlexForm element for a finisher option
+ *
+ * @internal
+ */
+class FinisherOptionGenerator extends AbstractProcessor
+{
+    /**
+     * @var \TYPO3\CMS\Core\Localization\LanguageService
+     */
+    protected $languageService;
+
+    /**
+     * @param ProcessorDto $converterDto
+     */
+    public function __construct(ProcessorDto $converterDto)
+    {
+        parent::__construct($converterDto);
+
+        $this->languageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\LanguageService::class);
+        $this->languageService->includeLLFile('EXT:form/Resources/Private/Language/locallang.xlf');
+    }
+
+    /**
+     * @param string $_ unused in this context
+     * @param mixed $__ unused in this context
+     * @param array $matches the expression matches from the ArrayProcessor - for example matches of ^(.*)\.config\.type$
+     */
+    public function __invoke(string $_, $__, array $matches)
+    {
+        [, $optionKey] = $matches;
+
+        $finisherIdentifier = $this->converterDto->getFinisherIdentifier();
+        $finisherDefinitionFromSetup = $this->converterDto->getFinisherDefinitionFromSetup();
+        $finisherDefinitionFromFormDefinition = $this->converterDto->getFinisherDefinitionFromFormDefinition();
+
+        try {
+            $elementConfiguration = ArrayUtility::getValueByPath(
+                $finisherDefinitionFromSetup['FormEngine']['elements'],
+                $optionKey,
+                '.'
+            );
+        } catch (MissingArrayPathException $exception) {
+            return;
+        }
+
+        // use the option value from the ext:form setup from the current finisher as default value
+        try {
+            $optionValue = ArrayUtility::getValueByPath($finisherDefinitionFromSetup['options'], $optionKey, '.');
+        } catch (MissingArrayPathException $exception) {
+            $optionValue = null;
+        }
+
+        // use the option value from the form definition from the current finisher (if exists) as default value
+        try {
+            $optionValue = ArrayUtility::getValueByPath($finisherDefinitionFromFormDefinition['options'], $optionKey, '.');
+        } catch (MissingArrayPathException $exception) {
+        }
+
+        if (empty($optionValue)) {
+            $elementConfiguration['label'] .= sprintf(' (%s: "%s")', $this->languageService->getLL('default'), $this->languageService->getLL('empty'));
+        } else {
+            $elementConfiguration['label'] .= sprintf(' (%s: "' . $optionValue . '")', $this->languageService->getLL('default'));
+        }
+
+        $elementConfiguration['config']['default'] = $optionValue;
+
+        $sheetElements = $this->converterDto->getResult();
+        $sheetElements['settings.finishers.' . $finisherIdentifier . '.' . $optionKey] = $elementConfiguration;
+
+        $this->converterDto->setResult($sheetElements);
+    }
+}
diff --git a/typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/ProcessorDto.php b/typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/ProcessorDto.php
new file mode 100644 (file)
index 0000000..0c176ad
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Form\Domain\Configuration\FlexformConfiguration\Processors;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Data container for finisher FlexForm processing
+ *
+ * @internal
+ */
+class ProcessorDto
+{
+    /**
+     * @var string
+     */
+    protected $finisherIdentifier;
+
+    /**
+     * @var array
+     */
+    protected $finisherDefinitionFromSetup;
+
+    /**
+     * @var array
+     */
+    protected $finisherDefinitionFromFormDefinition;
+
+    /**
+     * @var array
+     */
+    protected $result = [];
+
+    /**
+     * @param string $finisherIdentifier
+     * @param array $finisherDefinitionFromSetup
+     * @param array $finisherDefinitionFromFormDefinition
+     */
+    public function __construct(
+        string $finisherIdentifier,
+        array $finisherDefinitionFromSetup,
+        array $finisherDefinitionFromFormDefinition
+    ) {
+        $this->finisherIdentifier = $finisherIdentifier;
+        $this->finisherDefinitionFromSetup = $finisherDefinitionFromSetup;
+        $this->finisherDefinitionFromFormDefinition = $finisherDefinitionFromFormDefinition;
+    }
+
+    /**
+     * @return string
+     */
+    public function getFinisherIdentifier(): string
+    {
+        return $this->finisherIdentifier;
+    }
+
+    /**
+     * @return array
+     */
+    public function getFinisherDefinitionFromSetup(): array
+    {
+        return $this->finisherDefinitionFromSetup;
+    }
+
+    /**
+     * @return array
+     */
+    public function getFinisherDefinitionFromFormDefinition(): array
+    {
+        return $this->finisherDefinitionFromFormDefinition;
+    }
+
+    /**
+     * @return array
+     */
+    public function getResult(): array
+    {
+        return $this->result;
+    }
+
+    /**
+     * @param array $result
+     * @return ProcessorDto
+     */
+    public function setResult(array $result): ProcessorDto
+    {
+        $this->result = $result;
+
+        return $this;
+    }
+}
diff --git a/typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/ProcessorInterface.php b/typo3/sysext/form/Classes/Domain/Configuration/FlexformConfiguration/Processors/ProcessorInterface.php
new file mode 100644 (file)
index 0000000..2875581
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Form\Domain\Configuration\FlexformConfiguration\Processors;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Interface for FlexForm processors
+ *
+ * @internal
+ */
+interface ProcessorInterface
+{
+
+    /**
+     * @param ProcessorDto $converterDto
+     */
+    public function __construct(ProcessorDto $converterDto);
+
+    /**
+     * @param string $key
+     * @param mixed $value
+     * @param array $matches
+     */
+    public function __invoke(string $key, $value, array $matches);
+}
diff --git a/typo3/sysext/form/Classes/Domain/Configuration/FormDefinition/Converters/FinisherOptionsFlexFormOverridesConverter.php b/typo3/sysext/form/Classes/Domain/Configuration/FormDefinition/Converters/FinisherOptionsFlexFormOverridesConverter.php
new file mode 100644 (file)
index 0000000..94077cd
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Form\Domain\Configuration\FormDefinition\Converters;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
+
+/**
+ * Apply FlexForm finisher option overrides
+ *
+ * @internal
+ */
+class FinisherOptionsFlexFormOverridesConverter
+{
+    /**
+     * @var FlexFormFinisherOverridesConverterDto
+     */
+    protected $converterDto;
+
+    /**
+     * @param FlexFormFinisherOverridesConverterDto $converterDto
+     */
+    public function __construct(FlexFormFinisherOverridesConverterDto $converterDto)
+    {
+        $this->converterDto = $converterDto;
+    }
+
+    /**
+     * Used for overriding finisher options with flexform settings
+     * Flexform settings "win": When a setting is set in the form
+     * definition and in flexform the one in flexform will overwrite the
+     * one defined in the form definition.
+     *
+     * Here we adjust the parsed configuration and apply the overrides.
+     *
+     * @param string $_ unused in this context
+     * @param mixed $__ unused in this context
+     * @param array $matches the expression matches from the ArrayProcessor - for example matches of ^(.*)\.config\.type$
+     */
+    public function __invoke(string $_, $__, array $matches): void
+    {
+        [, $optionKey] = $matches;
+        $finisherDefinition = $this->converterDto->getFinisherDefinition();
+        $finisherIdentifier = $this->converterDto->getFinisherIdentifier();
+        $flexFormSheetSettings = $this->converterDto->getFlexFormSheetSettings();
+
+        try {
+            $value = ArrayUtility::getValueByPath(
+                $flexFormSheetSettings['finishers'][$finisherIdentifier],
+                $optionKey,
+                '.'
+            );
+        } catch (MissingArrayPathException $exception) {
+            return;
+        }
+
+        $finisherDefinition = ArrayUtility::setValueByPath($finisherDefinition, 'options.' . $optionKey, $value, '.');
+
+        $this->converterDto->setFinisherDefinition($finisherDefinition);
+    }
+}
diff --git a/typo3/sysext/form/Classes/Domain/Configuration/FormDefinition/Converters/FlexFormFinisherOverridesConverterDto.php b/typo3/sysext/form/Classes/Domain/Configuration/FormDefinition/Converters/FlexFormFinisherOverridesConverterDto.php
new file mode 100644 (file)
index 0000000..e719e66
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Form\Domain\Configuration\FormDefinition\Converters;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * @internal
+ */
+class FlexFormFinisherOverridesConverterDto
+{
+    /**
+     * @var array
+     */
+    protected $finisherDefinition = [];
+
+    /**
+     * @var string
+     */
+    protected $finisherIdentifier = '';
+
+    /**
+     * @var array
+     */
+    protected $flexFormSheetSettings = [];
+
+    /**
+     * @param array $finisherDefinition
+     * @param string $finisherIdentifier
+     * @param array $flexFormSheetSettings
+     */
+    public function __construct(
+        array $finisherDefinition,
+        string $finisherIdentifier,
+        array $flexFormSheetSettings
+    ) {
+        $this->finisherDefinition = $finisherDefinition;
+        $this->finisherIdentifier = $finisherIdentifier;
+        $this->flexFormSheetSettings = $flexFormSheetSettings;
+    }
+
+    /**
+     * @return array
+     */
+    public function getFinisherDefinition(): array
+    {
+        return $this->finisherDefinition;
+    }
+
+    /**
+     * @param array $finisherDefinition
+     * @return FlexFormFinisherOverridesConverterDto
+     */
+    public function setFinisherDefinition(array $finisherDefinition): FlexFormFinisherOverridesConverterDto
+    {
+        $this->finisherDefinition = $finisherDefinition;
+
+        return $this;
+    }
+
+    /**
+     * @return string
+     */
+    public function getFinisherIdentifier(): string
+    {
+        return $this->finisherIdentifier;
+    }
+
+    /**
+     * @return array
+     */
+    public function getFlexFormSheetSettings(): array
+    {
+        return $this->flexFormSheetSettings;
+    }
+}
index 48ceff5..faf61c2 100644 (file)
@@ -20,10 +20,13 @@ use TYPO3\CMS\Core\Messaging\AbstractMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessage;
 use TYPO3\CMS\Core\Messaging\FlashMessageService;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
-use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
+use TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessing;
+use TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessor;
 use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService;
+use TYPO3\CMS\Form\Domain\Configuration\FlexformConfiguration\Processors\FinisherOptionGenerator;
+use TYPO3\CMS\Form\Domain\Configuration\FlexformConfiguration\Processors\ProcessorDto;
 use TYPO3\CMS\Form\Mvc\Configuration\Exception\NoSuchFileException;
 use TYPO3\CMS\Form\Mvc\Configuration\Exception\ParseErrorException;
 use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface;
@@ -189,69 +192,47 @@ class DataStructureIdentifierHook
         $finishersDefinition = $prototypeConfiguration['finishersDefinition'];
 
         $sheets = ['sheets' => []];
-        foreach ($formDefinition['finishers'] as $finisherValue) {
-            $finisherIdentifier = $finisherValue['identifier'];
+        foreach ($formDefinition['finishers'] as $formFinisherDefinition) {
+            $finisherIdentifier = $formFinisherDefinition['identifier'];
             if (!isset($finishersDefinition[$finisherIdentifier]['FormEngine']['elements'])) {
                 continue;
             }
-            $sheetIdentifier = md5(
-                implode('', [
-                    $persistenceIdentifier,
-                    $prototypeName,
-                    $formIdentifier,
-                    $finisherIdentifier
-                ])
+            $sheetIdentifier = $this->buildFlexformSheetIdentifier(
+                $persistenceIdentifier,
+                $prototypeName,
+                $formIdentifier,
+                $finisherIdentifier
             );
 
-            if (isset($finishersDefinition[$finisherIdentifier]['FormEngine']['translationFile'])) {
-                $translationFile = $finishersDefinition[$finisherIdentifier]['FormEngine']['translationFile'];
-            } else {
-                $translationFile = $prototypeConfiguration['formEngine']['translationFile'];
-            }
-
-            $finishersDefinition[$finisherIdentifier]['FormEngine'] = TranslationService::getInstance()->translateValuesRecursive(
-                $finishersDefinition[$finisherIdentifier]['FormEngine'],
-                $translationFile
+            $finishersDefinition = $this->translateFinisherDefinitionByIdentifier(
+                $finisherIdentifier,
+                $finishersDefinition,
+                $prototypeConfiguration
             );
-            $finisherLabel = $finishersDefinition[$finisherIdentifier]['FormEngine']['label'];
-            $sheet = $this->initializeNewSheetArray($sheetIdentifier, $finisherLabel);
 
-            $sheetElements = [];
-            foreach ($finisherValue['options'] as $optionKey => $optionValue) {
-                if (is_array($optionValue)) {
-                    $optionKey = $optionKey . '.' . $this->implodeArrayKeys($finisherValue['options'][$optionKey]);
-                    try {
-                        $elementConfiguration = ArrayUtility::getValueByPath(
-                            $finishersDefinition[$finisherIdentifier]['FormEngine']['elements'],
-                            $optionKey,
-                            '.'
-                        );
-                    } catch (MissingArrayPathException $exception) {
-                        $elementConfiguration = null;
-                    }
-                    try {
-                        $optionValue = ArrayUtility::getValueByPath($finisherValue['options'], $optionKey, '.');
-                    } catch (MissingArrayPathException $exception) {
-                        $optionValue = null;
-                    }
-                } else {
-                    $elementConfiguration = $finishersDefinition[$finisherIdentifier]['FormEngine']['elements'][$optionKey];
-                }
+            $prototypeFinisherDefinition = $finishersDefinition[$finisherIdentifier];
+            $finisherLabel = $prototypeFinisherDefinition['FormEngine']['label'] ?? '';
+            $sheet = $this->initializeNewSheetArray($sheetIdentifier, $finisherLabel);
 
-                if (empty($elementConfiguration)) {
-                    continue;
-                }
+            $converterDto = GeneralUtility::makeInstance(
+                ProcessorDto::class,
+                $finisherIdentifier,
+                $prototypeFinisherDefinition,
+                $formFinisherDefinition
+            );
 
-                if (empty($optionValue)) {
-                    $elementConfiguration['label'] .= ' (default: "[Empty]")';
-                } else {
-                    $elementConfiguration['label'] .= ' (default: "' . $optionValue . '")';
-                }
-                $elementConfiguration['config']['default'] = $optionValue;
-                $sheetElements['settings.finishers.' . $finisherIdentifier . '.' . $optionKey] = $elementConfiguration;
-            }
+            // Iterate over all `TYPO3.CMS.Form.prototypes.<prototypeName>.finishersDefinition.<finisherIdentifier>.FormEngine.elements` values
+            // and convert them to FlexForm elements
+            GeneralUtility::makeInstance(ArrayProcessor::class, $prototypeFinisherDefinition['FormEngine']['elements'])->forEach(
+                GeneralUtility::makeInstance(
+                    ArrayProcessing::class,
+                    'convertToFlexFormSheets',
+                    '^(.*)\.config\.type$',
+                    GeneralUtility::makeInstance(FinisherOptionGenerator::class, $converterDto)
+                )
+            );
 
-            $sheet[$sheetIdentifier]['ROOT']['el'] = $sheetElements;
+            $sheet[$sheetIdentifier]['ROOT']['el'] = $converterDto->getResult();
             ArrayUtility::mergeRecursiveWithOverrule($sheets['sheets'], $sheet);
         }
         if (empty($sheets['sheets'])) {
@@ -292,23 +273,6 @@ class DataStructureIdentifierHook
     }
 
     /**
-     * Recursive helper to implode a nested array to a dotted path notation
-     *
-     * ['a' => [ 'b' => 42 ] ] becomes 'a.b'
-     *
-     * @param array $nestedArray
-     * @return string
-     */
-    protected function implodeArrayKeys(array $nestedArray): string
-    {
-        $dottedPath = (string)key($nestedArray);
-        if (is_array($nestedArray[$dottedPath])) {
-            $dottedPath .= '.' . $this->implodeArrayKeys($nestedArray[$dottedPath]);
-        }
-        return $dottedPath;
-    }
-
-    /**
      * @param string $persistenceIdentifier
      * @param array $dataStructure
      * @return array
@@ -353,6 +317,54 @@ class DataStructureIdentifierHook
     }
 
     /**
+     * @param string $persistenceIdentifier
+     * @param string $prototypeName
+     * @param string $formIdentifier
+     * @param string $finisherIdentifier
+     * @return string
+     */
+    protected function buildFlexformSheetIdentifier(
+        string $persistenceIdentifier,
+        string $prototypeName,
+        string $formIdentifier,
+        string $finisherIdentifier
+    ): string {
+        return md5(
+            implode('', [
+                $persistenceIdentifier,
+                $prototypeName,
+                $formIdentifier,
+                $finisherIdentifier
+            ])
+        );
+    }
+
+    /**
+     * @param string $finisherIdentifier
+     * @param array $finishersDefinition
+     * @param array $prototypeConfiguration
+     * @return array
+     */
+    protected function translateFinisherDefinitionByIdentifier(
+        string $finisherIdentifier,
+        array $finishersDefinition,
+        array $prototypeConfiguration
+    ): array {
+        if (isset($finishersDefinition[$finisherIdentifier]['FormEngine']['translationFile'])) {
+            $translationFile = $finishersDefinition[$finisherIdentifier]['FormEngine']['translationFile'];
+        } else {
+            $translationFile = $prototypeConfiguration['formEngine']['translationFile'];
+        }
+
+        $finishersDefinition[$finisherIdentifier]['FormEngine'] = TranslationService::getInstance()->translateValuesRecursive(
+            $finishersDefinition[$finisherIdentifier]['FormEngine'],
+            $translationFile
+        );
+
+        return $finishersDefinition;
+    }
+
+    /**
      * @return LanguageService
      */
     protected function getLanguageService(): LanguageService
index 7cabb6d..a49094e 100644 (file)
@@ -3,6 +3,12 @@
     <file t3:id="1475977066" source-language="en" datatype="plaintext" original="messages" date="2016-10-09T03:38:32Z" product-name="form">
         <header/>
         <body>
+            <trans-unit id="default" xml:space="preserve">
+                <source>Default</source>
+            </trans-unit>
+             <trans-unit id="empty" xml:space="preserve">
+                <source>[Empty]</source>
+            </trans-unit>
             <trans-unit id="element.ImageUpload.properties.SummaryPage.altText" xml:space="preserve">
                 <source>Uploaded image</source>
             </trans-unit>
index 014e3ee..66a4656 100644 (file)
@@ -130,9 +130,9 @@ class FormFrontendControllerTest extends UnitTestCase
                 'EmailToReceiver' => [
                     'FormEngine' => [
                         'elements' => [
-                            'subject' => [],
-                            'recipientAddress' => [],
-                            'format' => [],
+                            'subject' => ['config' => ['type' => 'input']],
+                            'recipientAddress' => ['config' => ['type' => 'input']],
+                            'format' => ['config' => ['type' => 'input']],
                         ],
                     ],
                 ],
@@ -208,8 +208,8 @@ class FormFrontendControllerTest extends UnitTestCase
                 'EmailToReceiver' => [
                     'FormEngine' => [
                         'elements' => [
-                            'subject' => [],
-                            'recipientAddress' => [],
+                            'subject' => ['config' => ['type' => 'input']],
+                            'recipientAddress' => ['config' => ['type' => 'input']],
                         ],
                     ],
                 ],
index 1f8a974..1da37eb 100644 (file)
@@ -276,85 +276,4 @@ class DataStructureIdentifierHookTest extends UnitTestCase
             ],
         ];
     }
-
-    /**
-     * Data provider for implodeArrayKeysReturnsString
-     *
-     * @return array
-     */
-    public function implodeArrayKeysReturnsStringDataProvider(): array
-    {
-        return [
-            'One string' => [
-                [
-                    'a' => 'b',
-                ],
-                'a'
-            ],
-            'Two strings' => [
-                [
-                    'a' => [
-                        'b' => 'c'
-                    ],
-                ],
-                'a.b'
-            ],
-            'One integer' => [
-                [
-                    20 => 'a',
-                ],
-                '20'
-            ],
-            'Two integers' => [
-                [
-                    20 => [
-                        30 => 'a'
-                    ],
-                ],
-                '20.30'
-            ],
-            'Mixed' => [
-                [
-                    20 => [
-                        'a' => 'b'
-                    ],
-                ],
-                '20.a'
-            ],
-            'Multiple Entries' => [
-                [
-                    1 => [
-                        'a' => 'b',
-                        'b' => 'foo',
-                    ],
-                ],
-                '1.a'
-            ],
-            'four levels' => [
-                [
-                    1 => [
-                        'a' => [
-                            '2' => [
-                                42 => 'foo',
-                            ],
-                        ],
-                        'b' => 22,
-                    ],
-                ],
-                '1.a.2.42',
-            ],
-        ];
-    }
-
-    /**
-     * @dataProvider implodeArrayKeysReturnsStringDataProvider
-     * @test
-     * @param array $array
-     * @param string $expectation
-     */
-    public function implodeArrayKeysReturnsString(array $array, string $expectation): void
-    {
-        $hookMock = $this->getAccessibleMock(DataStructureIdentifierHook::class, [ 'dummy' ], [], '', false);
-        $this->assertEquals($expectation, $hookMock->_call('implodeArrayKeys', $array));
-    }
 }