Revert "[FEATURE] EXT:form - introduce YAML "imports"" 22/55422/5
authorHelmut Hummel <typo3@helhum.io>
Sun, 21 Jan 2018 17:40:15 +0000 (18:40 +0100)
committerOliver Hader <oliver.hader@typo3.org>
Tue, 30 Jan 2018 12:04:07 +0000 (13:04 +0100)
While the intended feature is great (having the possiblity
to extract and re-use parts of the form definition),
implementation wise it needs some rework.

To be able to release 9.1 as stable as possible,
we now revert this implementation for now
and bring it in for 9.2 again.

This reverts commit a52a99220ca24251186ea88d11b3db43436a8156.

Reverts: #82089
Resolves: #83729
Releases: master
Change-Id: I850401d232b642e712801613d3780816f873966a
Reviewed-on: https://review.typo3.org/55422
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
42 files changed:
typo3/sysext/core/Classes/Configuration/Loader/FalYamlFileLoader.php [deleted file]
typo3/sysext/core/Classes/Configuration/Loader/YamlFileLoader.php
typo3/sysext/core/Classes/Configuration/Loader/YamlFileLoader/Configuration.php [deleted file]
typo3/sysext/core/Classes/Configuration/Loader/YamlFileLoaderInterface.php [deleted file]
typo3/sysext/core/Classes/Configuration/Writer/Exception/FileWriteException.php [deleted file]
typo3/sysext/core/Classes/Configuration/Writer/YamlFileWriter.php [deleted file]
typo3/sysext/core/Classes/Resource/ResourceFactory.php
typo3/sysext/core/Documentation/Changelog/master/Deprecation-82089-ExtFormYamlConfigurationsTyposcriptOption.rst [deleted file]
typo3/sysext/core/Documentation/Changelog/master/Feature-82089-ExtFormSupportsYamlImports.rst [deleted file]
typo3/sysext/core/Tests/Unit/Configuration/Loader/YamlFileLoaderTest.php
typo3/sysext/form/Classes/Controller/FormEditorController.php
typo3/sysext/form/Classes/Controller/FormFrontendController.php
typo3/sysext/form/Classes/Controller/FormManagerController.php
typo3/sysext/form/Classes/Domain/Exception/FormDefinitionNotValidException.php [deleted file]
typo3/sysext/form/Classes/Domain/Factory/ArrayFormFactory.php
typo3/sysext/form/Classes/Mvc/Configuration/ConfigurationManager.php
typo3/sysext/form/Classes/Mvc/Configuration/Exception/NoConfigurationFoundException.php [deleted file]
typo3/sysext/form/Classes/Mvc/Configuration/YamlSource.php [new file with mode: 0644]
typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManager.php
typo3/sysext/form/Classes/Property/TypeConverter/FormDefinitionArrayConverter.php
typo3/sysext/form/Classes/ViewHelpers/RenderViewHelper.php
typo3/sysext/form/Configuration/TypoScript/setup.txt
typo3/sysext/form/Configuration/Yaml/BaseSetup.yaml [new file with mode: 0644]
typo3/sysext/form/Configuration/Yaml/FormEditorSetup.yaml [new file with mode: 0644]
typo3/sysext/form/Configuration/Yaml/FormEngineSetup.yaml [new file with mode: 0644]
typo3/sysext/form/Configuration/Yaml/FormSetup.yaml [deleted file]
typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Inspector/EmailSelectEditor.html [deleted file]
typo3/sysext/form/Tests/Unit/Mvc/Configuration/ConfigurationManagerTest.php [deleted file]
typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File1.yaml [deleted file]
typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File2.yaml [deleted file]
typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File3.yaml [deleted file]
typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File4.yaml [deleted file]
typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/Header.yaml [new file with mode: 0644]
typo3/sysext/form/Tests/Unit/Mvc/Configuration/YamlSourceTest.php [new file with mode: 0644]
typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/ConfigurationManagerTest.php [deleted file]
typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File1.yaml [deleted file]
typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File2.yaml [deleted file]
typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File3.yaml [deleted file]
typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File4.yaml [deleted file]
typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/Header.yaml [deleted file]
typo3/sysext/form/ext_localconf.php
typo3/sysext/form/ext_typoscript_setup.txt [new file with mode: 0644]

diff --git a/typo3/sysext/core/Classes/Configuration/Loader/FalYamlFileLoader.php b/typo3/sysext/core/Classes/Configuration/Loader/FalYamlFileLoader.php
deleted file mode 100644 (file)
index 9aea9f2..0000000
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Configuration\Loader;
-
-/*
- * 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\Configuration\Loader\YamlFileLoader\Configuration;
-use TYPO3\CMS\Core\Resource\File;
-use TYPO3\CMS\Core\Resource\ResourceFactory;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-
-/**
- * YAML loader for FAL files
- */
-class FalYamlFileLoader extends YamlFileLoader
-{
-    /**
-     * @var \TYPO3\CMS\Core\Resource\ResourceFactory
-     */
-    protected $resourceFactory;
-
-    /**
-     * @param Configuration|null $configuration
-     */
-    public function __construct(Configuration $configuration = null, ResourceFactory $resourceFactory = null)
-    {
-        parent::__construct($configuration);
-        $this->resourceFactory = $resourceFactory ?: GeneralUtility::makeInstance(ResourceFactory::class);
-    }
-
-    /**
-     * Loads and parses a YAML file, returns an array with the data found
-     *
-     * @param File|string $fileName either relative to PATH_site or prefixed with EXT:... or File object
-     * @return array the configuration as array
-     */
-    public function load($fileName): array
-    {
-        return $this->loadFromContent($this->getFileContents($fileName));
-    }
-
-    /**
-     * @param File|string $fileName either relative to PATH_site or prefixed with EXT:... or File object
-     * @return string the contents of the file
-     * @throws \RuntimeException when the file was not accessible
-     */
-    protected function getFileContents($fileName): string
-    {
-        $file = null;
-
-        if (is_string($fileName)) {
-            $file = $this->resourceFactory->retrieveFileOrFolderObject($fileName);
-        } elseif (is_object($fileName)) {
-            $file = $fileName;
-        }
-
-        if ($file instanceof File) {
-            $content = $file->getContents();
-
-            if (!$content) {
-                throw new \RuntimeException('YAML file "' . $file->getIdentifier() . '" could not be loaded', 1512561127);
-            }
-
-            return $content;
-        }
-
-        return parent::getFileContents($fileName);
-    }
-}
index 4651ea4..eb05f5f 100644 (file)
@@ -15,7 +15,6 @@ namespace TYPO3\CMS\Core\Configuration\Loader;
  */
 
 use Symfony\Component\Yaml\Yaml;
-use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 
 /**
@@ -26,64 +25,35 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
  * - A special "imports" key in the YAML file allows to include other YAML files recursively
  *   where the actual YAML file gets loaded after the import statements, which are interpreted at the very beginning
  *
- * - Merging configuration options of import files when having simple "lists" will add items to the list by default
- *   instead of overwriting them.
+ * - Merging configuration options of import files when having simple "lists" will add items to the list instead
+ *   of overwriting them.
  *
  * - Special placeholder values set via %optionA.suboptionB% replace the value with the named path of the configuration
  *   The placeholders will act as a full replacement of this value.
  */
-class YamlFileLoader implements YamlFileLoaderInterface
+class YamlFileLoader
 {
-    /**
-     * @var Configuration
-     */
-    protected $configuration;
-
-    /**
-     * @param Configuration|null $configuration
-     */
-    public function __construct(Configuration $configuration = null)
-    {
-        $this->configuration = $configuration ?: GeneralUtility::makeInstance(Configuration::class);
-    }
 
     /**
-     * Loads and parses a YAML file, returns an array with the data found
+     * Loads and parses a YAML file, and returns an array with the found data
      *
      * @param string $fileName either relative to PATH_site or prefixed with EXT:...
      * @return array the configuration as array
-     */
-    public function load($fileName): array
-    {
-        if (!is_string($fileName)) {
-            throw new \InvalidArgumentException('The argument "$fileName" must be a string ("' . gettype($fileName) . '" given)', 1512558206);
-        }
-        return $this->loadFromContent($this->getFileContents($fileName));
-    }
-
-    /**
-     * Parses a string as YAML, returns an array with the data found
-     *
-     * @param string $content
-     * @return array the configuration as array
      * @throws \RuntimeException when the file is empty or is of invalid format
      */
-    public function loadFromContent(string $content): array
+    public function load(string $fileName): array
     {
+        $content = $this->getFileContents($fileName);
         $content = Yaml::parse($content);
 
         if (!is_array($content)) {
-            throw new \RuntimeException('YAML content could not be parsed into valid syntax, probably empty?', 1497332874);
+            throw new \RuntimeException('YAML file "' . $fileName . '" could not be parsed into valid syntax, probably empty?', 1497332874);
         }
 
-        if ($this->configuration->getProcessImports()) {
-            $content = $this->processImports($content);
-        }
+        $content = $this->processImports($content);
 
         // Check for "%" placeholders
-        if ($this->configuration->getProcessPlaceholders()) {
-            $content = $this->processPlaceholders($content, $content);
-        }
+        $content = $this->processPlaceholders($content, $content);
 
         return $content;
     }
@@ -96,14 +66,11 @@ class YamlFileLoader implements YamlFileLoaderInterface
      * @return string the contents of the file
      * @throws \RuntimeException when the file was not accessible
      */
-    protected function getFileContents($fileName): string
+    protected function getFileContents(string $fileName): string
     {
-        if (!is_string($fileName)) {
-            throw new \InvalidArgumentException('The argument "$fileName" must be a string ("' . gettype($fileName) . '" given)', 1512558207);
-        }
         $streamlinedFileName = GeneralUtility::getFileAbsFileName($fileName);
         if (!$streamlinedFileName) {
-            throw new \RuntimeException('YAML file "' . $fileName . '" could not be loaded', 1485784246);
+            throw new \RuntimeException('YAML File "' . $fileName . '" could not be loaded', 1485784246);
         }
         return file_get_contents($streamlinedFileName);
     }
@@ -123,9 +90,7 @@ class YamlFileLoader implements YamlFileLoaderInterface
                 // override the imported content with the one from the current file
                 $content = $this->merge($importedContent, $content);
             }
-            if ($this->configuration->getRemoveImportsProperty()) {
-                unset($content['imports']);
-            }
+            unset($content['imports']);
         }
         return $content;
     }
@@ -190,8 +155,8 @@ class YamlFileLoader implements YamlFileLoaderInterface
     }
 
     /**
-     * Same as array_replace_recursive except that when in simple arrays (= YAML lists),
-     * the entries are appended (array_merge) configured accordingly
+     * Same as array_replace_recursive except that when in simple arrays (= YAML lists), the entries are
+     * appended (array_merge)
      *
      * @param array $val1
      * @param array $val2
@@ -201,10 +166,8 @@ class YamlFileLoader implements YamlFileLoaderInterface
     protected function merge(array $val1, array $val2): array
     {
         // Simple lists get merged / added up
-        if ($this->configuration->getMergeLists()) {
-            if (count(array_filter(array_keys($val1), 'is_int')) === count($val1)) {
-                return array_merge($val1, $val2);
-            }
+        if (count(array_filter(array_keys($val1), 'is_int')) === count($val1)) {
+            return array_merge($val1, $val2);
         }
         foreach ($val1 as $k => $v) {
             // The key also exists in second array, if it is a simple value
diff --git a/typo3/sysext/core/Classes/Configuration/Loader/YamlFileLoader/Configuration.php b/typo3/sysext/core/Classes/Configuration/Loader/YamlFileLoader/Configuration.php
deleted file mode 100644 (file)
index 711e058..0000000
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-declare(strict_types = 1);
-namespace TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
-
-/*
- * 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!
- */
-
-/**
- * Configuration for YAML file loading
- */
-class Configuration
-{
-    /**
-     * @var bool
-     */
-    protected $processImports = true;
-
-    /**
-     * @var bool
-     */
-    protected $removeImportsProperty = true;
-
-    /**
-     * @var bool
-     */
-    protected $mergeLists = true;
-
-    /**
-     * @var bool
-     */
-    protected $processPlaceholders = true;
-
-    /**
-     * @return bool
-     */
-    public function getProcessImports(): bool
-    {
-        return $this->processImports;
-    }
-
-    /**
-     * @param bool $processImports
-     * @return Configuration
-     */
-    public function setProcessImports(bool $processImports): self
-    {
-        $this->processImports = $processImports;
-        return $this;
-    }
-
-    /**
-     * @return bool
-     */
-    public function getRemoveImportsProperty(): bool
-    {
-        return $this->removeImportsProperty;
-    }
-
-    /**
-     * @param bool $removeImportsProperty
-     * @return Configuration
-     */
-    public function setRemoveImportsProperty(bool $removeImportsProperty): self
-    {
-        $this->removeImportsProperty = $removeImportsProperty;
-        return $this;
-    }
-
-    /**
-     * @return bool
-     */
-    public function getProcessPlaceholders(): bool
-    {
-        return $this->processPlaceholders;
-    }
-
-    /**
-     * @param bool $processPlaceholders
-     * @return Configuration
-     */
-    public function setProcessPlaceholders(bool $processPlaceholders): self
-    {
-        $this->processPlaceholders = $processPlaceholders;
-        return $this;
-    }
-
-    /**
-     * @return bool
-     */
-    public function getMergeLists(): bool
-    {
-        return $this->mergeLists;
-    }
-
-    /**
-     * @param bool $mergeLists
-     * @return Configuration
-     */
-    public function setMergeLists(bool $mergeLists): self
-    {
-        $this->mergeLists = $mergeLists;
-        return $this;
-    }
-}
diff --git a/typo3/sysext/core/Classes/Configuration/Loader/YamlFileLoaderInterface.php b/typo3/sysext/core/Classes/Configuration/Loader/YamlFileLoaderInterface.php
deleted file mode 100644 (file)
index 3748a4d..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Configuration\Loader;
-
-/*
- * 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 YAML file loaders
- */
-interface YamlFileLoaderInterface
-{
-    /**
-     * Loads and parses a YAML file, returns an array with the data found
-     *
-     * @param mixed $file
-     * @return array the configuration as array
-     */
-    public function load($file): array;
-
-    /**
-     * Parses a string as YAML, returns an array with the data found
-     *
-     * @param string $content
-     * @return array the configuration as array
-     */
-    public function loadFromContent(string $content): array;
-}
diff --git a/typo3/sysext/core/Classes/Configuration/Writer/Exception/FileWriteException.php b/typo3/sysext/core/Classes/Configuration/Writer/Exception/FileWriteException.php
deleted file mode 100644 (file)
index 245a1c5..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Configuration\Writer\Exception;
-
-/*
- * 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!
- */
-
-/**
- * Exception for file write errors
- */
-class FileWriteException extends \TYPO3\CMS\Core\Exception
-{
-}
diff --git a/typo3/sysext/core/Classes/Configuration/Writer/YamlFileWriter.php b/typo3/sysext/core/Classes/Configuration/Writer/YamlFileWriter.php
deleted file mode 100644 (file)
index 8c3eb78..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-namespace TYPO3\CMS\Core\Configuration\Writer;
-
-/*
- * 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 Symfony\Component\Yaml\Yaml;
-use TYPO3\CMS\Core\Configuration\Writer\Exception\FileWriteException;
-use TYPO3\CMS\Core\Resource\File;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-
-/**
- * A YAML file writer that allows to write YAML files, based on the Symfony/Yaml component
- */
-class YamlFileWriter
-{
-
-    /**
-     * Write a YAML file
-     *
-     * @param FILE|string $fileName either relative to PATH_site or prefixed with EXT:... or FILE object
-     * @param array $content The content
-     * @param int $inlineLevel The level where you switch to inline YAML
-     * @param int $indent The amount of spaces to use for indentation of nested nodes
-     * @param int $flags A bit field of Yaml::DUMP_* constants to customize the dumped YAML string
-     * @throws FileWriteException if the file could not be written
-     */
-    public function save(
-        $fileName,
-        array $content,
-        int $inlineLevel = 99,
-        int $indent = 2,
-        int $flags = 0
-    ) {
-        $content = Yaml::dump($content, $inlineLevel, $indent, $flags);
-
-        if ($fileName instanceof File) {
-            try {
-                $fileName->setContents($content);
-            } catch (\RuntimeException $e) {
-                throw new FileWriteException($e->getMessage(), 1512582753, $e);
-            }
-        } else {
-            $streamlinedFileName = GeneralUtility::getFileAbsFileName($fileName);
-            if (!$streamlinedFileName) {
-                throw new \FileWriteException('YAML File "' . $fileName . '" could not be loaded', 1485784248);
-            }
-            if (!GeneralUtility::writeFile($streamlinedFileName, $content)) {
-                $error = error_get_last();
-                throw new FileWriteException($error['message'], 1512582929);
-            }
-        }
-    }
-}
index eab6f69..d1fd31b 100644 (file)
@@ -459,7 +459,7 @@ class ResourceFactory implements ResourceFactoryInterface, \TYPO3\CMS\Core\Singl
      * - "file:23"
      *
      * @param string $input
-     * @return File|Folder|null
+     * @return File|Folder
      */
     public function retrieveFileOrFolderObject($input)
     {
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-82089-ExtFormYamlConfigurationsTyposcriptOption.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-82089-ExtFormYamlConfigurationsTyposcriptOption.rst
deleted file mode 100644 (file)
index 9341ee6..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-.. include:: ../../Includes.txt
-
-===================================================================
-Deprecation: #82089 - EXT:form yamlConfigurations TypoScript option
-===================================================================
-
-See :issue:`82089`
-See Feature-82089-ExtFormSupportsYamlImports.rst
-
-Description
-===========
-
-The registration of YAML configuration paths for the `form` extension
-via :typoscript:`<module|plugin>.tx_form.settings.yamlConfigurations`
-is deprecated and will be removed in TYPO3 v10.
-
-Instead, a single configuration file must be registered via
-:typoscript:`<module|plugin>.tx_form.settings.configurationFile`.
-
-
-Impact
-======
-
-If paths are added to the TypoScript option
-:typoscript:`<module|plugin>.tx_form.settings.yamlConfigurations`
-a deprecation log entry will be triggered.
-
-
-Affected Installations
-======================
-
-All installations that add paths to the TypoScript option
-:typoscript:`<module|plugin>.tx_form.settings.yamlConfigurations`.
-
-
-Migration
-=========
-
-All occurrences of :typoscript:`<module|plugin>.tx_form.settings.yamlConfigurations`
-must be migrated to :typoscript:`plugin.tx_form.settings.configurationFile`.
-
-
-Form frontend setup
--------------------
-
-The registration of custom frontend `form` configuration was previously done like this:
-
-.. code-block:: typoscript
-
-   plugin.tx_form {
-       settings {
-           yamlConfigurations {
-               100 = EXT:my_site_package/Configuration/Yaml/CustomFormSetup.yaml
-           }
-       }
-   }
-
-This must be changed to use the new :typoscript:`plugin.tx_form.settings.configurationFile` option:
-
-.. code-block:: typoscript
-
-   plugin.tx_form {
-       settings {
-           configurationFile = EXT:my_site_package/Configuration/Yaml/CustomFormSetup.yaml
-       }
-   }
-
-:file:`EXT:my_site_package/Configuration/Yaml/CustomFormSetup.yaml` should look like this:
-
-.. code-block:: yaml
-
-   imports:
-     - { resource: "EXT:form/Configuration/Yaml/FormSetup.yaml" }
-
-   # Custom form setup configuration
-
-
-Form backend setup (form editor)
---------------------------------
-
-The registration for custom backend `form` editor configuration was previously done like this:
-
-.. code-block:: typoscript
-
-   module.tx_form {
-       settings {
-           yamlConfigurations {
-               100 = EXT:my_site_package/Configuration/Yaml/CustomFormEditorSetup.yaml
-           }
-       }
-   }
-
-This must be changed to use the new :typoscript:`module.tx_form.settings.configurationFile` option:
-
-.. code-block:: typoscript
-
-   module.tx_form {
-       settings {
-           configurationFile = EXT:my_site_package/Configuration/Yaml/CustomFormEditorSetup.yaml
-       }
-   }
-
-:file:`EXT:my_site_package/Configuration/Yaml/CustomFormEditorSetup.yaml` should look like this:
-
-.. code-block:: yaml
-
-   imports:
-     - { resource: "EXT:form/Configuration/Yaml/FormSetup.yaml" }
-
-   # Custom form editor setup configuration
-
-.. index:: ext:form, Frontend, Backend, NotScanned
\ No newline at end of file
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-82089-ExtFormSupportsYamlImports.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-82089-ExtFormSupportsYamlImports.rst
deleted file mode 100644 (file)
index 87067d0..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-.. include:: ../../Includes.txt
-
-================================================
-Feature: #82089 - EXT:form supports YAML imports
-================================================
-
-See :issue:`82089`
-
-Description
-===========
-
-The `form` extension now features imports in YAML configuration files via the special toplevel
-:yaml:`imports` option. With the help of this feature, form setup and especially form definitions
-can be reused without copying.
-
-
-Form setup configuration
-------------------------
-
-The :yaml:`imports` option can now be used to load the form setup of the `form` extension followed
-by a custom configuration.
-
-For example a :file:`EXT:my_site_package/Configuration/Yaml/FormSetup.yaml` could look like this:
-
-.. code-block:: yaml
-
-    imports:
-      - { resource: "EXT:form/Configuration/Yaml/FormSetup.yaml" }
-      - { resource: "EXT:my_site_package/Configuration/Yaml/FormSetup/Prototypes.yaml" }
-
-You can also combine imports with configuration:
-
-.. code-block:: yaml
-
-    imports:
-      - { resource: "EXT:form/Configuration/Yaml/FormSetup.yaml" }
-
-    TYPO3:
-      CMS:
-        Form:
-          prototypes:
-            # custom configuration
-            # ...
-
-
-Form definitions
-----------------
-
-Imports are also possible within form definitions but must be added manually to the YAML files.
-Currently, the form editor does not have a graphical interface for imports.
-
-The following example shows a basic contact form definition (e.g. in
-:file:`fileadmin/form_definitions/contact.yaml`):
-
-.. code-block:: yaml
-
-    identifier: contact
-    label: 'Contact us'
-    type: Form
-    prototypeName: standard
-    finishers:
-      EmailToReceiver:
-        identifier: EmailToReceiver
-        sorting: 10
-        options:
-          # ...
-    renderables:
-      page-1:
-        identifier: page-1
-        type: Page
-        sorting: 10
-        label: 'Contact Form'
-        renderables:
-          name:
-            identifier: name
-            type: Text
-            label: Name
-            sorting: 10
-            validators:
-              NotEmpty:
-                identifier: NotEmpty
-                sorting: 10
-          subject:
-            identifier: subject
-            type: Text
-            sorting: 20
-            label: Subject
-            validators:
-              NotEmpty:
-                identifier: NotEmpty
-                sorting: 10
-
-Other form definitions can import :file:`fileadmin/form_definitions/contact.yaml` to inherit the
-definitions. Additional form definitions can then be added to extend or change existing definitions.
-
-.. important::
-
-   The form :yaml:`identifier` **must** be changed when importing other form definitions.
-
-You have to do the following in oder to change the form label and to move the :yaml:`subject` field
-before the :yaml:`name` field (see aforementioned
-:file:`fileadmin/form_definitions/another-contact.yaml`):
-
-.. code-block:: yaml
-
-    imports:
-      - { resource: fileadmin/form_definitions/contact.yaml }
-
-    # The identifier MUST be changed
-    identifier: inquiry
-
-    label: Inquiry
-    renderables:
-      page-1:
-        renderables:
-          name:
-            sorting: 20
-          subject:
-            sorting: 10
-
-The key of every section with an :yaml:`identifier` property must be named exactly like the
-:yaml:`identifier` property. This way it is ensured that form definitions importing other form
-definitions and form definitions which are imported are properly merged.
-
-For example, before this feature was introduced a list of :yaml:`finishers` was defined like this:
-
-.. code-block:: yaml
-
-    finishers:
-      -
-        identifier: EmailToReceiver
-        options:
-          # ...
-      -
-        identifier: EmailToSender
-        options:
-          # ...
-      # ...
-
-To guarantee imports work properly this must be rewritten slightly. Please use the :yaml:`identifier`
-value as section key:
-
-.. code-block:: yaml
-
-    finishers:
-      EmailToReceiver:
-        identifier: EmailToReceiver
-        options:
-          # ...
-      EmailToSender:
-        identifier: EmailToSender
-        options:
-          # ...
-      # ...
-
-Aside from this, every section with an :yaml:`identifier` must have a :yaml:`sorting` property. This
-property is essential to detect differences in sortings between the form definition you import and
-the imported form definition.
-
-.. tip::
-
-   Form definitions managed with the form editor are migrated automatically once opened and saved.
-
-
-.. index:: Frontend, Backend, ext:form
\ No newline at end of file
index fbf88a4..783e7d9 100644 (file)
@@ -190,17 +190,4 @@ betterthanbefore: %firstset.myinitialversion%
         $output = $subject->_call('isPlaceholder', $placeholderValue);
         $this->assertSame($expected, $output);
     }
-
-    /**
-     * @test
-     */
-    public function loadFromContentThrowsExceptionIfContentIsInvalid()
-    {
-        $this->expectException(\RuntimeException::class);
-        $this->expectExceptionCode(1497332874);
-
-        $subject = $this->getAccessibleMock(YamlFileLoader::class, ['dummy']);
-        $input = 'foo bar';
-        $subject->_call('loadFromContent', $input);
-    }
 }
index ac92795..6f94d94 100644 (file)
@@ -18,17 +18,13 @@ namespace TYPO3\CMS\Form\Controller;
 use TYPO3\CMS\Backend\Template\Components\ButtonBar;
 use TYPO3\CMS\Backend\View\BackendTemplateView;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
-use TYPO3\CMS\Core\Configuration\Loader\FalYamlFileLoader;
-use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Localization\LanguageService;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Core\Utility\MathUtility;
 use TYPO3\CMS\Extbase\Mvc\View\JsonView;
 use TYPO3\CMS\Fluid\View\TemplateView;
 use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService;
-use TYPO3\CMS\Form\Domain\Exception\InvalidFormDefinitionException;
 use TYPO3\CMS\Form\Domain\Exception\RenderingException;
 use TYPO3\CMS\Form\Domain\Factory\ArrayFormFactory;
 use TYPO3\CMS\Form\Mvc\Persistence\Exception\PersistenceManagerException;
@@ -76,14 +72,11 @@ class FormEditorController extends AbstractBackendController
             throw new PersistenceManagerException('Edit a extension formDefinition is not allowed.', 1478265661);
         }
 
-        $prototypeName = $prototypeName ?? $formDefinition['prototypeName'] ?? 'standard';
-        /** @var \TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration */
-        $configuration = GeneralUtility::makeInstance(Configuration::class)
-            ->setRemoveImportsProperty(false)
-            ->setMergeLists(false);
-        $formDefinition = $this->formPersistenceManager->load($formPersistenceIdentifier, $configuration);
+        $formDefinition = $this->formPersistenceManager->load($formPersistenceIdentifier);
         $formDefinition = ArrayUtility::stripTagsFromValuesRecursive($formDefinition);
-        $formDefinition = $this->transformFormDefinitionWithImportsForFormEditor($formDefinition, is_array($formDefinition['imports']));
+        if (empty($prototypeName)) {
+            $prototypeName = $formDefinition['prototypeName'] ?? 'standard';
+        }
         $formDefinition['prototypeName'] = $prototypeName;
 
         $configurationService = $this->objectManager->get(ConfigurationService::class);
@@ -159,7 +152,6 @@ class FormEditorController extends AbstractBackendController
     public function saveFormAction(string $formPersistenceIdentifier, FormDefinitionArray $formDefinition)
     {
         $formDefinition = $formDefinition->getArrayCopy();
-
         foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormSave'] ?? [] as $className) {
             $hookObj = GeneralUtility::makeInstance($className);
             if (method_exists($hookObj, 'beforeFormSave')) {
@@ -170,12 +162,6 @@ class FormEditorController extends AbstractBackendController
             }
         }
 
-        $formDefinition = $this->transformFormDefinitionWithImportsForFormFramework(
-            $formPersistenceIdentifier,
-            $formDefinition,
-            is_array($formDefinition['imports'])
-        );
-
         $response = [
             'status' => 'success',
         ];
@@ -210,13 +196,15 @@ class FormEditorController extends AbstractBackendController
      */
     public function renderFormPageAction(FormDefinitionArray $formDefinition, int $pageIndex, string $prototypeName = null): string
     {
-        $prototypeName = $prototypeName ?? $formDefinition['prototypeName'] ?? 'standard';
+        if (empty($prototypeName)) {
+            $prototypeName = $formDefinition['prototypeName'] ?? 'standard';
+        }
+
         $formFactory = $this->objectManager->get(ArrayFormFactory::class);
         $formDefinition = $formFactory->build($formDefinition->getArrayCopy(), $prototypeName);
         $formDefinition->setRenderingOption('previewMode', true);
         $form = $formDefinition->bind($this->request, $this->response);
         $form->overrideCurrentPage($pageIndex);
-
         return $form->render();
     }
 
@@ -229,6 +217,7 @@ class FormEditorController extends AbstractBackendController
      */
     protected function getInsertRenderablesPanelConfiguration(array $formElementsDefinition): array
     {
+        $formElementGroups = $this->prototypeConfiguration['formEditor']['formElementGroups'] ?? [];
         $formElementsByGroup = [];
 
         foreach ($formElementsDefinition as $formElementName => $formElementConfiguration) {
@@ -254,7 +243,7 @@ class FormEditorController extends AbstractBackendController
         }
 
         $formGroups = [];
-        foreach ($this->prototypeConfiguration['formEditor']['formElementGroups'] ?? [] as $groupName => $groupConfiguration) {
+        foreach ($formElementGroups as $groupName => $groupConfiguration) {
             if (!isset($formElementsByGroup[$groupName])) {
                 continue;
             }
@@ -384,232 +373,6 @@ class FormEditorController extends AbstractBackendController
     }
 
     /**
-     * @param array $array
-     * @param bool $hasImports
-     * @return array
-     * @throws PropertyException
-     */
-    protected function transformFormDefinitionWithImportsForFormEditor(array $array, bool $hasImports): array
-    {
-        $result = $array;
-        foreach ($result as $key => $value) {
-            if (is_array($value)) {
-                if (
-                    $key === 'renderables'
-                    || $key === 'validators'
-                    || $key === 'finishers'
-                ) {
-                    if ($hasImports) {
-                        foreach ($value as $itemKey => $item) {
-                            if (is_int($itemKey)) {
-                                throw new InvalidFormDefinitionException(
-                                    'All array keys within "' . $key . '" must be strings.',
-                                    1505505524
-                                );
-                            }
-
-                            if ($itemKey !== $item['identifier']) {
-                                throw new InvalidFormDefinitionException(
-                                    'All items keys within "' . $key . '" must be equal to the "identifier" property.',
-                                    1505505525
-                                );
-                            }
-
-                            if (!isset($item['sorting'])) {
-                                throw new InvalidFormDefinitionException(
-                                    'All items within "' . $key . '" must have a "sorting" property.',
-                                    1505505526
-                                );
-                            }
-                        }
-                    }
-                    // transform string keys to integer keys
-                    $value = array_values($value);
-
-                    if ($hasImports) {
-                        // sort by "sorting"
-                        usort($value, function ($a, $b) {
-                            return (float)$a['sorting'] - (float)$b['sorting'];
-                        });
-                    }
-                }
-                $result[$key] = $this->transformFormDefinitionWithImportsForFormEditor($value, $hasImports);
-            }
-        }
-        return $result;
-    }
-
-    /**
-     * @param string $formPersistenceIdentifier
-     * @param array $formDefinition
-     * @param bool $hasImports
-     * @return array
-     */
-    protected function transformFormDefinitionWithImportsForFormFramework(
-        string $formPersistenceIdentifier,
-        array $formDefinition,
-        bool $hasImports
-    ): array {
-        if ($hasImports) {
-            $fakeYaml = $this->generateFakeYamlFromImports(
-                $formDefinition['imports']
-            );
-            /** @var \TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration */
-            $configuration = GeneralUtility::makeInstance(Configuration::class)
-                ->setMergeLists(false);
-            $importsFormDefinition = $this->objectManager->get(FalYamlFileLoader::class, $configuration)
-                ->loadFromContent($fakeYaml);
-            $importsFormDefinition = $this->castValuesToNumbers($importsFormDefinition);
-        }
-
-        $formDefinition = $this->castValuesToNumbers($formDefinition);
-        $formDefinition = $this->setIdentifiersAsKeys($formDefinition);
-        $formDefinition = $this->setNewSortings($formDefinition);
-
-        if ($hasImports) {
-            $this->makeFormDefinitionWithImportsDiff($formDefinition, $importsFormDefinition);
-        }
-        return $formDefinition;
-    }
-
-    /**
-     * @param array $array
-     * @return array
-     */
-    public static function castValuesToNumbers(array $array): array
-    {
-        $result = $array;
-        foreach ($result as $key => $value) {
-            if (is_array($value)) {
-                $result[$key] = self::castValuesToNumbers($value);
-            } elseif (MathUtility::canBeInterpretedAsInteger($value)) {
-                $result[$key] = (int)$value;
-            } elseif (MathUtility::canBeInterpretedAsFloat($value)) {
-                $result[$key] = (float)$value;
-            }
-        }
-        return $result;
-    }
-
-    /**
-     * @param array $formDefinition
-     * @return array
-     */
-    protected function setNewSortings(array $formDefinition): array
-    {
-        $result = $formDefinition;
-
-        foreach ($result as $key => $value) {
-            if (is_array($value)) {
-                if (
-                    $key === 'renderables'
-                    || $key === 'validators'
-                    || $key === 'finishers'
-                ) {
-                    $sorting = 10;
-                    foreach ($value as $identifier => $item) {
-                        $value[$identifier]['sorting'] = $sorting;
-                        $sorting += 10;
-                    }
-                }
-                $result[$key] = $this->setNewSortings($value);
-            }
-        }
-        return $result;
-    }
-
-    /**
-     * @param array $array
-     * @return array
-     */
-    protected function setIdentifiersAsKeys(array $array): array
-    {
-        $result = $array;
-        foreach ($result as $key => $value) {
-            if (is_array($value)) {
-                if (
-                    $key === 'renderables'
-                    || $key === 'validators'
-                    || $key === 'finishers'
-                ) {
-                    $newValue = [];
-                    foreach ($value as $itemKey => $item) {
-                        $newValue[$item['identifier']] = $item;
-                    }
-                    $value = $newValue;
-                }
-                $result[$key] = $this->setIdentifiersAsKeys($value);
-            }
-        }
-        return $result;
-    }
-
-    /**
-     * @param array $imports
-     * @return string
-     */
-    protected function generateFakeYamlFromImports(array $imports): string
-    {
-        $fakeYaml = 'imports:' . LF;
-        foreach ($imports as $import) {
-            foreach ($import as $resource) {
-                $fakeYaml .= '  - { resource: "' . $resource . '" }' . LF;
-            }
-        }
-        return $fakeYaml;
-    }
-
-    /**
-     * @param array &$newFullFormDefinition
-     * @param array $importsFormDefinition
-     * @param array $path
-     */
-    protected function makeFormDefinitionWithImportsDiff(
-        array &$newFullFormDefinition,
-        array $importsFormDefinition,
-        array $path = []
-    ) {
-        foreach ($importsFormDefinition as $key => $valueFromImportsFormDefinition) {
-            $currentPath = $path;
-            $currentPath[] = $key;
-            $currentPathString = implode('/', $currentPath);
-            if (is_array($valueFromImportsFormDefinition)) {
-                if (!ArrayUtility::isValidPath($newFullFormDefinition, $currentPathString)) {
-                    // Overwrite the value within the new formDefinition with null
-                    // because the value exists within one of the imports
-                    // but not within the new formDefinition which means
-                    // that the value should be deleted.
-                    $newFullFormDefinition = ArrayUtility::setValueByPath($newFullFormDefinition, $currentPathString, null);
-                } else {
-                    $this->makeFormDefinitionWithImportsDiff($newFullFormDefinition, $valueFromImportsFormDefinition, $currentPath);
-                    $value = ArrayUtility::getValueByPath($newFullFormDefinition, $currentPathString);
-                    // If values are deleted within deeper nestings, the array
-                    // keys still exists. If empty arrays exists within the new formDefinition
-                    // then they should be removed.
-                    if (is_array($value) && empty($value)) {
-                        $newFullFormDefinition = ArrayUtility::removeByPath($newFullFormDefinition, $currentPathString);
-                    }
-                }
-            } else {
-                if (
-                    ArrayUtility::isValidPath($newFullFormDefinition, $currentPathString)
-                    && ArrayUtility::getValueByPath($newFullFormDefinition, $currentPathString) === $valueFromImportsFormDefinition
-                ) {
-                    // Remove the value within the new formDefinition
-                    // because the value already exists within one of the imports.
-                    $newFullFormDefinition = ArrayUtility::removeByPath($newFullFormDefinition, $currentPathString);
-                } elseif (!ArrayUtility::isValidPath($newFullFormDefinition, $currentPathString)) {
-                    // Overwrite the value within the new formDefinition with null
-                    // because the value exists within one of the imports
-                    // but not within the new formDefinition which means
-                    // that the value should be deleted.
-                    $newFullFormDefinition = ArrayUtility::setValueByPath($newFullFormDefinition, $currentPathString, null);
-                }
-            }
-        }
-    }
-
-    /**
      * Render the "text/x-formeditor-template" templates.
      *
      * @param array $formEditorDefinitions
index e069569..ee40afc 100644 (file)
@@ -15,7 +15,6 @@ namespace TYPO3\CMS\Form\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
 use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService;
@@ -56,10 +55,7 @@ class FormFrontendController extends ActionController
     {
         $formDefinition = [];
         if (!empty($this->settings['persistenceIdentifier'])) {
-            /** @var \TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration */
-            $configuration = $this->objectManager->get(Configuration::class)
-                ->setRemoveImportsProperty(false);
-            $formDefinition = $this->formPersistenceManager->load($this->settings['persistenceIdentifier'], $configuration);
+            $formDefinition = $this->formPersistenceManager->load($this->settings['persistenceIdentifier']);
             $formDefinition['persistenceIdentifier'] = $this->settings['persistenceIdentifier'];
             $formDefinition = $this->overrideByTypoScriptSettings($formDefinition);
             $formDefinition = $this->overrideByFlexFormSettings($formDefinition);
index be48176..0aed1a7 100644 (file)
@@ -15,12 +15,11 @@ namespace TYPO3\CMS\Form\Controller;
  * The TYPO3 project - inspiring people to share!
  */
 
+use Symfony\Component\Yaml\Yaml;
 use TYPO3\CMS\Backend\Template\Components\ButtonBar;
 use TYPO3\CMS\Backend\Utility\BackendUtility;
 use TYPO3\CMS\Backend\View\BackendTemplateView;
 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
-use TYPO3\CMS\Core\Configuration\Loader\FalYamlFileLoader;
-use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration;
 use TYPO3\CMS\Core\Database\ConnectionPool;
 use TYPO3\CMS\Core\Imaging\Icon;
 use TYPO3\CMS\Core\Imaging\IconFactory;
@@ -99,10 +98,8 @@ class FormManagerController extends AbstractBackendController
             throw new FormException(sprintf('No form name', $templatePath), 1472312204);
         }
 
-        /** @var \TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration */
-        $configuration = GeneralUtility::makeInstance(Configuration::class)
-            ->setMergeLists(false);
-        $form = $this->objectManager->get(FalYamlFileLoader::class, $configuration)->load($templatePath);
+        $templatePath = GeneralUtility::getFileAbsFileName($templatePath);
+        $form = Yaml::parse(file_get_contents($templatePath));
         $form['label'] = $formName;
         $form['identifier'] = $this->formPersistenceManager->getUniqueIdentifier($this->convertFormNameToIdentifier($formName));
         $form['prototypeName'] = $prototypeName;
@@ -163,12 +160,7 @@ class FormManagerController extends AbstractBackendController
      */
     public function duplicateAction(string $formName, string $formPersistenceIdentifier, string $savePath)
     {
-        /** @var \TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration */
-        $configuration = GeneralUtility::makeInstance(Configuration::class)
-            ->setProcessImports(false)
-            ->setRemoveImportsProperty(false)
-            ->setMergeLists(false);
-        $formToDuplicate = $this->formPersistenceManager->load($formPersistenceIdentifier, $configuration);
+        $formToDuplicate = $this->formPersistenceManager->load($formPersistenceIdentifier);
         $formToDuplicate['label'] = $formName;
         $formToDuplicate['identifier'] = $this->formPersistenceManager->getUniqueIdentifier($this->convertFormNameToIdentifier($formName));
 
diff --git a/typo3/sysext/form/Classes/Domain/Exception/FormDefinitionNotValidException.php b/typo3/sysext/form/Classes/Domain/Exception/FormDefinitionNotValidException.php
deleted file mode 100644 (file)
index 5d75301..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-declare(strict_types = 1);
-namespace TYPO3\CMS\Form\Domain\Exception;
-
-/*
- * 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\Form\Domain\Exception;
-
-/**
- * @api
- */
-class FormDefinitionNotValidException extends Exception
-{
-}
index 38fa584..9baf7f2 100644 (file)
@@ -17,7 +17,6 @@ namespace TYPO3\CMS\Form\Domain\Factory;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
 use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService;
@@ -50,11 +49,6 @@ class ArrayFormFactory extends AbstractFormFactory
         }
         $persistenceIdentifier = $configuration['persistenceIdentifier'] ?? null;
 
-        $configuration = $this->transformFormDefinitionWithImportsForFormFramework($configuration);
-        unset($configuration['imports']);
-        $configuration = ArrayUtility::convertBooleanStringsToBooleanRecursive($configuration);
-        $configuration = ArrayUtility::removeNullValuesRecursive($configuration);
-
         $prototypeConfiguration = GeneralUtility::makeInstance(ObjectManager::class)
             ->get(ConfigurationService::class)
             ->getPrototypeConfiguration($prototypeName);
@@ -124,34 +118,4 @@ class ArrayFormFactory extends AbstractFormFactory
 
         return $renderable;
     }
-
-    /**
-     * @param array $array
-     * @return array
-     * @internal
-     */
-    protected function transformFormDefinitionWithImportsForFormFramework(array $array): array
-    {
-        $result = $array;
-        foreach ($result as $key => $value) {
-            if (is_array($value)) {
-                if (
-                    $key === 'renderables'
-                    || $key === 'validators'
-                    || $key === 'finishers'
-                ) {
-                    // sort by "sorting"
-                    usort($value, function ($a, $b) {
-                        return (float)$a['sorting'] - (float)$b['sorting'];
-                    });
-
-                    foreach ($value as $itemKey => $item) {
-                        unset($value[$itemKey]['sorting']);
-                    }
-                }
-                $result[$key] = $this->transformFormDefinitionWithImportsForFormFramework($value);
-            }
-        }
-        return $result;
-    }
 }
index 8dcda81..6927498 100644 (file)
@@ -17,14 +17,10 @@ namespace TYPO3\CMS\Form\Mvc\Configuration;
 
 use TYPO3\CMS\Core\Cache\CacheManager;
 use TYPO3\CMS\Core\Cache\Frontend\FrontendInterface;
-use TYPO3\CMS\Core\Configuration\Loader\FalYamlFileLoader;
-use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
-use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration;
 use TYPO3\CMS\Core\Utility\ArrayUtility;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Extbase\Configuration\ConfigurationManager as ExtbaseConfigurationManager;
 use TYPO3\CMS\Form\Mvc\Configuration\Exception\ExtensionNameRequiredException;
-use TYPO3\CMS\Form\Mvc\Configuration\Exception\NoConfigurationFoundException;
 
 /**
  * Extend the ExtbaseConfigurationManager to read YAML configurations.
@@ -41,6 +37,20 @@ class ConfigurationManager extends ExtbaseConfigurationManager implements Config
     protected $cache;
 
     /**
+     * @var \TYPO3\CMS\Form\Mvc\Configuration\YamlSource
+     */
+    protected $yamlSource;
+
+    /**
+     * @param \TYPO3\CMS\Form\Mvc\Configuration\YamlSource $yamlSource
+     * @internal
+     */
+    public function injectYamlSource(\TYPO3\CMS\Form\Mvc\Configuration\YamlSource $yamlSource)
+    {
+        $this->yamlSource = $yamlSource;
+    }
+
+    /**
      * @param string $configurationType The kind of configuration to fetch - must be one of the CONFIGURATION_TYPE_* constants
      * @param string $extensionName if specified, the configuration for the given extension will be returned.
      * @param string $pluginName if specified, the configuration for the given plugin will be returned.
@@ -58,14 +68,14 @@ class ConfigurationManager extends ExtbaseConfigurationManager implements Config
     }
 
     /**
-     * Load and parse a YAML configuration which is configured within
-     * plugin.tx_form.settings.configurationFile
+     * Load and parse YAML files which are configured within the TypoScript
+     * path plugin.tx_extensionkey.settings.yamlConfigurations
      *
      * The following steps will be done:
      *
-     * * load a YAML file into an array
+     * * Convert each singe YAML file into an array
+     * * merge this arrays together
      * * resolve all declared inheritances
-     * * convert all boolean strings ('true' / 'false') into boolean values
      * * remove all keys if their values are NULL
      * * return all configuration paths within TYPO3.CMS
      * * sort by array keys, if all keys within the current nesting level are numerical keys
@@ -74,7 +84,6 @@ class ConfigurationManager extends ExtbaseConfigurationManager implements Config
      * @param string $extensionName
      * @return array
      * @throws ExtensionNameRequiredException
-     * @throws NoConfigurationFoundException
      */
     protected function getConfigurationFromYamlFile(string $extensionName): array
     {
@@ -88,48 +97,25 @@ class ConfigurationManager extends ExtbaseConfigurationManager implements Config
 
         $typoscriptSettings = $this->getTypoScriptSettings($extensionName);
 
-        $cacheKeySuffix = $extensionName;
-        if (isset($typoscriptSettings['configurationFile'])) {
-            $cacheKeySuffix .= md5($typoscriptSettings['configurationFile']);
-        } elseif (isset($typoscriptSettings['yamlConfigurations'])) {
-            $cacheKeySuffix .= md5(json_encode($typoscriptSettings['yamlConfigurations']));
-        }
+        $yamlSettingsFilePaths = isset($typoscriptSettings['yamlConfigurations'])
+            ? ArrayUtility::sortArrayWithIntegerKeys($typoscriptSettings['yamlConfigurations'])
+            : [];
+
+        $cacheKeySuffix = $extensionName . md5(json_encode($yamlSettingsFilePaths));
 
         $yamlSettings = $this->getYamlSettingsFromCache($cacheKeySuffix);
         if (!empty($yamlSettings)) {
             return $this->overrideConfigurationByTypoScript($yamlSettings, $extensionName);
         }
 
-        $configuration = $this->objectManager->get(Configuration::class)
-            ->setMergeLists(false);
-        if (isset($typoscriptSettings['configurationFile'])) {
-            $yamlSettings = $this->objectManager->get(FalYamlFileLoader::class, $configuration)
-                ->load($typoscriptSettings['configurationFile']);
-        } elseif (isset($typoscriptSettings['yamlConfigurations'])) {
-            trigger_error('EXT:form configuration registration via "<module|plugin>.tx_form.settings.yamlConfigurations" has been deprecated in v9 and will be removed in v10. Use "<module|plugin>.tx_form.settings.configurationFile" instead.', E_USER_DEPRECATED);
-            $yamlContent = $this->generateYamlFromLegacyYamlConfigurations(
-                $typoscriptSettings['yamlConfigurations']
-            );
-
-            $yamlSettings = $this->objectManager->get(YamlFileLoader::class, $configuration)
-                ->loadFromContent($yamlContent);
-        } else {
-            throw new NoConfigurationFoundException(
-                'No YAML configurations could be found for extension ' . $extensionName,
-                1471473378
-            );
-        }
-
-        $yamlSettings = ArrayUtility::convertBooleanStringsToBooleanRecursive($yamlSettings);
-        $yamlSettings = ArrayUtility::removeNullValuesRecursive($yamlSettings);
-        $yamlSettings = InheritancesResolverService::create($yamlSettings)
+        $yamlSettings = InheritancesResolverService::create($this->yamlSource->load($yamlSettingsFilePaths))
             ->getResolvedConfiguration();
 
+        $yamlSettings = ArrayUtility::removeNullValuesRecursive($yamlSettings);
         $yamlSettings = is_array($yamlSettings['TYPO3']['CMS'][$ucFirstExtensioName])
             ? $yamlSettings['TYPO3']['CMS'][$ucFirstExtensioName]
             : [];
         $yamlSettings = ArrayUtility::sortArrayWithIntegerKeysRecursive($yamlSettings);
-
         $this->setYamlSettingsIntoCache($cacheKeySuffix, $yamlSettings);
 
         return $this->overrideConfigurationByTypoScript($yamlSettings, $extensionName);
@@ -213,48 +199,4 @@ class ConfigurationManager extends ExtbaseConfigurationManager implements Config
             $extensionName
         );
     }
-
-    /**
-     * Compatibility layer for the deprecated TypoScript option
-     * "plugin.tx_form.settings.yamlConfigurations"
-     *
-     * @param array $yamlConfigurations
-     * @return string
-     * @internal
-     */
-    protected function generateYamlFromLegacyYamlConfigurations(array $yamlConfigurations): string
-    {
-        $yamlConfigurations = ArrayUtility::sortArrayWithIntegerKeys($yamlConfigurations);
-        $yamlContent = 'imports:' . LF;
-
-        $baseExtFormConfigurations = [
-            'EXT:form/Configuration/Yaml/BaseSetup.yaml',
-            'EXT:form/Configuration/Yaml/FormEditorSetup.yaml',
-            'EXT:form/Configuration/Yaml/FormEngineSetup.yaml',
-            'EXT:form/Configuration/Yaml/FormSetup.yaml',
-        ];
-
-        $imports = '';
-        $baseExtFormConfigurationsExists = false;
-        foreach ($yamlConfigurations as $yamlConfiguration) {
-            if (in_array($yamlConfiguration, $baseExtFormConfigurations)) {
-                $baseExtFormConfigurationsExists = true;
-            } else {
-                $imports .= '  - { resource: "' . $yamlConfiguration . '" }' . LF;
-            }
-        }
-
-        // We assume that if one of the files defined within $baseExtFormConfigurations exists
-        // within plugin.tx_form.settings.yamlConfigurations, someone wants to load
-        // the base EXT:form setup files (old or new) and afterwards extend it with his own configuration.
-        // In this case, we define the new EXT:form/Configuration/Yaml/FormSetup.yaml file as the
-        // first file to import from.
-        if ($baseExtFormConfigurationsExists) {
-            $yamlContent .= '  - { resource: "EXT:form/Configuration/Yaml/FormSetup.yaml" }' . LF . $imports;
-        } else {
-            $yamlContent .= $imports;
-        }
-
-        return $yamlContent;
-    }
 }
diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/Exception/NoConfigurationFoundException.php b/typo3/sysext/form/Classes/Mvc/Configuration/Exception/NoConfigurationFoundException.php
deleted file mode 100644 (file)
index e46394d..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-namespace TYPO3\CMS\Form\Mvc\Configuration\Exception;
-
-/*
- * 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\Form\Mvc\Configuration\Exception;
-
-/**
- * A No Such File exception
- */
-class NoConfigurationFoundException extends Exception
-{
-}
diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/YamlSource.php b/typo3/sysext/form/Classes/Mvc/Configuration/YamlSource.php
new file mode 100644 (file)
index 0000000..abce0d4
--- /dev/null
@@ -0,0 +1,185 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Form\Mvc\Configuration;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It originated from the Neos.Form package (www.neos.io)
+ *
+ * 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 Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Yaml;
+use TYPO3\CMS\Core\Resource\Exception\InsufficientFileAccessPermissionsException;
+use TYPO3\CMS\Core\Resource\File;
+use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Form\Mvc\Configuration\Exception\FileWriteException;
+use TYPO3\CMS\Form\Mvc\Configuration\Exception\NoSuchFileException;
+use TYPO3\CMS\Form\Mvc\Configuration\Exception\ParseErrorException;
+
+/**
+ * Configuration source based on YAML files
+ *
+ * Scope: frontend / backend
+ * @internal
+ */
+class YamlSource
+{
+    /**
+     * Will be set if the PHP YAML Extension is installed.
+     * Having this installed massively improves YAML parsing performance.
+     *
+     * @var bool
+     * @see http://pecl.php.net/package/yaml
+     */
+    protected $usePhpYamlExtension = false;
+
+    /**
+     * Use PHP YAML Extension if installed.
+     * @internal
+     */
+    public function __construct()
+    {
+        if (extension_loaded('yaml')) {
+            $this->usePhpYamlExtension = true;
+        }
+    }
+
+    /**
+     * Loads the specified configuration files and returns its merged content
+     * as an array.
+     *
+     * @param array $filesToLoad
+     * @return array
+     * @throws ParseErrorException
+     * @throws NoSuchFileException
+     * @internal
+     */
+    public function load(array $filesToLoad): array
+    {
+        $configuration = [];
+        foreach ($filesToLoad as $fileToLoad) {
+            if ($fileToLoad instanceof File) {
+                $fileIdentifier = $fileToLoad->getIdentifier();
+                $rawYamlContent = $fileToLoad->getContents();
+                if ($rawYamlContent === false) {
+                    throw new NoSuchFileException(
+                        'The file "' . $fileToLoad . '" does not exist.',
+                        1498802253
+                    );
+                }
+            } else {
+                $fileIdentifier = $fileToLoad;
+                $fileToLoad = GeneralUtility::getFileAbsFileName($fileToLoad);
+                if (is_file($fileToLoad)) {
+                    $rawYamlContent = file_get_contents($fileToLoad);
+                } else {
+                    throw new NoSuchFileException(
+                        'The file "' . $fileToLoad . '" does not exist.',
+                        1471473378
+                    );
+                }
+            }
+
+            try {
+                if ($this->usePhpYamlExtension) {
+                    $loadedConfiguration = @yaml_parse($rawYamlContent);
+                    if ($loadedConfiguration === false) {
+                        throw new ParseErrorException(
+                            'A parse error occurred while parsing file "' . $fileIdentifier . '".',
+                            1391894094
+                        );
+                    }
+                } else {
+                    $loadedConfiguration = Yaml::parse($rawYamlContent);
+                }
+
+                if (is_array($loadedConfiguration)) {
+                    ArrayUtility::mergeRecursiveWithOverrule($configuration, $loadedConfiguration);
+                }
+            } catch (ParseException $exception) {
+                throw new ParseErrorException(
+                    'An error occurred while parsing file "' . $fileIdentifier . '": ' . $exception->getMessage(),
+                    1480195405
+                );
+            }
+        }
+
+        $configuration = ArrayUtility::convertBooleanStringsToBooleanRecursive($configuration);
+        return $configuration;
+    }
+
+    /**
+     * Save the specified configuration array to the given file in YAML format.
+     *
+     * @param File|string $fileToSave The file to write to.
+     * @param array $configuration The configuration to save
+     * @throws FileWriteException if the file could not be written
+     * @internal
+     */
+    public function save($fileToSave, array $configuration)
+    {
+        try {
+            $header = $this->getHeaderFromFile($fileToSave);
+        } catch (InsufficientFileAccessPermissionsException  $e) {
+            throw new FileWriteException($e->getMessage(), 1512584488, $e);
+        }
+
+        $yaml = Yaml::dump($configuration, 99, 2);
+
+        if ($fileToSave instanceof File) {
+            try {
+                $fileToSave->setContents($header . LF . $yaml);
+            } catch (InsufficientFileAccessPermissionsException $e) {
+                throw new FileWriteException($e->getMessage(), 1512582753, $e);
+            }
+        } else {
+            $byteCount = @file_put_contents($fileToSave, $header . LF . $yaml);
+
+            if ($byteCount === false) {
+                $error = error_get_last();
+                throw new FileWriteException($error['message'], 1512582929);
+            }
+        }
+
+        return $return;
+    }
+
+    /**
+     * Read the header part from the given file. That means, every line
+     * until the first non comment line is found.
+     *
+     * @param File|string $file
+     * @return string The header of the given YAML file
+     */
+    protected function getHeaderFromFile($file): string
+    {
+        $header = '';
+        if ($file instanceof File) {
+            $fileLines = explode(LF, $file->getContents());
+        } elseif (is_file($file)) {
+            $fileLines = file($file);
+        } else {
+            return '';
+        }
+
+        foreach ($fileLines as $line) {
+            if (preg_match('/^#/', $line)) {
+                $header .= $line;
+            } else {
+                break;
+            }
+        }
+        return $header;
+    }
+}
index 36704f0..f0eb150 100644 (file)
@@ -17,10 +17,6 @@ namespace TYPO3\CMS\Form\Mvc\Persistence;
  * The TYPO3 project - inspiring people to share!
  */
 
-use TYPO3\CMS\Core\Configuration\Loader\FalYamlFileLoader;
-use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration as LoadConfiguration;
-use TYPO3\CMS\Core\Configuration\Writer\Exception\FileWriteException;
-use TYPO3\CMS\Core\Configuration\Writer\YamlFileWriter;
 use TYPO3\CMS\Core\Resource\Exception\FolderDoesNotExistException;
 use TYPO3\CMS\Core\Resource\Exception\InsufficientFolderAccessPermissionsException;
 use TYPO3\CMS\Core\Resource\File;
@@ -31,6 +27,7 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Core\Utility\PathUtility;
 use TYPO3\CMS\Extbase\Object\ObjectManager;
 use TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManagerInterface;
+use TYPO3\CMS\Form\Mvc\Configuration\Exception\FileWriteException;
 use TYPO3\CMS\Form\Mvc\Persistence\Exception\NoUniqueIdentifierException;
 use TYPO3\CMS\Form\Mvc\Persistence\Exception\NoUniquePersistenceIdentifierException;
 use TYPO3\CMS\Form\Mvc\Persistence\Exception\PersistenceManagerException;
@@ -44,6 +41,11 @@ class FormPersistenceManager implements FormPersistenceManagerInterface
 {
 
     /**
+     * @var \TYPO3\CMS\Form\Mvc\Configuration\YamlSource
+     */
+    protected $yamlSource;
+
+    /**
      * @var \TYPO3\CMS\Core\Resource\StorageRepository
      */
     protected $storageRepository;
@@ -54,6 +56,15 @@ class FormPersistenceManager implements FormPersistenceManagerInterface
     protected $formSettings;
 
     /**
+     * @param \TYPO3\CMS\Form\Mvc\Configuration\YamlSource $yamlSource
+     * @internal
+     */
+    public function injectYamlSource(\TYPO3\CMS\Form\Mvc\Configuration\YamlSource $yamlSource)
+    {
+        $this->yamlSource = $yamlSource;
+    }
+
+    /**
      * @param \TYPO3\CMS\Core\Resource\StorageRepository $storageRepository
      * @internal
      */
@@ -77,12 +88,11 @@ class FormPersistenceManager implements FormPersistenceManagerInterface
      * Only files with the extension .yaml are loaded.
      *
      * @param string $persistenceIdentifier
-     * @param LoadConfiguration $loadConfiguration
      * @return array
      * @throws PersistenceManagerException
      * @internal
      */
-    public function load(string $persistenceIdentifier, LoadConfiguration $loadConfiguration = null): array
+    public function load(string $persistenceIdentifier): array
     {
         if (pathinfo($persistenceIdentifier, PATHINFO_EXTENSION) !== 'yaml') {
             throw new PersistenceManagerException(sprintf('The file "%s" could not be loaded.', $persistenceIdentifier), 1477679819);
@@ -98,9 +108,7 @@ class FormPersistenceManager implements FormPersistenceManagerInterface
         }
 
         try {
-            $yaml = GeneralUtility::makeInstance(ObjectManager::class)
-                ->get(FalYamlFileLoader::class, $loadConfiguration)
-                ->load($file);
+            $yaml = $this->yamlSource->load([$file]);
         } catch (\Exception $e) {
             $yaml = [
                 'identifier' => $file->getCombinedIdentifier(),
@@ -142,15 +150,13 @@ class FormPersistenceManager implements FormPersistenceManagerInterface
             if (!array_key_exists(pathinfo($persistenceIdentifier, PATHINFO_DIRNAME) . '/', $this->getAccessibleExtensionFolders())) {
                 throw new PersistenceManagerException(sprintf('The file "%s" could not be saved.', $persistenceIdentifier), 1484073571);
             }
-            $fileToSave = $persistenceIdentifier;
+            $fileToSave = GeneralUtility::getFileAbsFileName($persistenceIdentifier);
         } else {
             $fileToSave = $this->getOrCreateFile($persistenceIdentifier);
         }
 
         try {
-            GeneralUtility::makeInstance(ObjectManager::class)
-                ->get(YamlFileWriter::class)
-                ->save($fileToSave, $formDefinition);
+            $this->yamlSource->save($fileToSave, $formDefinition);
         } catch (FileWriteException $e) {
             throw new PersistenceManagerException(sprintf(
                 'The file "%s" could not be saved: %s',
index 4376c0d..d7412b8 100644 (file)
@@ -63,7 +63,7 @@ class FormDefinitionArrayConverter extends AbstractTypeConverter
         }
 
         $rawFormDefinitionArray = ArrayUtility::stripTagsFromValuesRecursive($rawFormDefinitionArray);
-        $rawFormDefinitionArray = $this->transformMultivalueElementsForFormFramework($rawFormDefinitionArray);
+        $rawFormDefinitionArray = $this->convertJsonArrayToAssociativeArray($rawFormDefinitionArray);
         $formDefinitionArray = new FormDefinitionArray($rawFormDefinitionArray);
 
         return $formDefinitionArray;
@@ -88,20 +88,23 @@ class FormDefinitionArrayConverter extends AbstractTypeConverter
      * @param array $input
      * @return array
      */
-    protected function transformMultivalueElementsForFormFramework(array $input): array
+    protected function convertJsonArrayToAssociativeArray(array $input): array
     {
         $output = [];
+
         foreach ($input as $key => $value) {
             if (is_int($key) && is_array($value) && isset($value['_label']) && isset($value['_value'])) {
                 $key = $value['_value'];
                 $value = $value['_label'];
             }
+
             if (is_array($value)) {
-                $output[$key] = $this->transformMultivalueElementsForFormFramework($value);
+                $output[$key] = $this->convertJsonArrayToAssociativeArray($value);
             } else {
                 $output[$key] = $value;
             }
         }
+
         return $output;
     }
 }
index a32c2a8..03f58c9 100644 (file)
@@ -81,7 +81,7 @@ class RenderViewHelper extends AbstractViewHelper
         $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
         if (!empty($persistenceIdentifier)) {
             $formPersistenceManager = $objectManager->get(FormPersistenceManagerInterface::class);
-            $formConfiguration = $formPersistenceManager->load($persistenceIdentifier, true, false);
+            $formConfiguration = $formPersistenceManager->load($persistenceIdentifier);
             ArrayUtility::mergeRecursiveWithOverrule(
                 $formConfiguration,
                 $overrideConfiguration
index 73de4a9..eb81df0 100644 (file)
@@ -17,7 +17,10 @@ plugin.tx_form {
     }
 
     settings {
-        configurationFile = EXT:form/Configuration/Yaml/FormSetup.yaml
+        yamlConfigurations {
+            10 = EXT:form/Configuration/Yaml/BaseSetup.yaml
+            20 = EXT:form/Configuration/Yaml/FormEngineSetup.yaml
+        }
     }
 }
 
@@ -27,4 +30,4 @@ lib.tx_form.contentElementRendering {
     tables = tt_content
     source.current = 1
     dontCheckPid = 1
-}
+}
\ No newline at end of file
diff --git a/typo3/sysext/form/Configuration/Yaml/BaseSetup.yaml b/typo3/sysext/form/Configuration/Yaml/BaseSetup.yaml
new file mode 100644 (file)
index 0000000..bb445df
--- /dev/null
@@ -0,0 +1,455 @@
+TYPO3:
+  CMS:
+    Form:
+      persistenceManager:
+        allowedFileMounts:
+          10: 1:/form_definitions/
+          20: 1:/user_upload/
+        allowSaveToExtensionPaths: false
+        allowDeleteFromExtensionPaths: false
+        #allowedExtensionPaths:
+          #10: EXT:example/Resources/Private/Forms/
+
+      prototypes:
+        standard:
+
+          ########### DEFAULT FORM ELEMENT DEFINITIONS ###########
+          formElementsDefinition:
+
+            ### BASE ELEMENTS ###
+            Form:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin'
+              rendererClassName: 'TYPO3\CMS\Form\Domain\Renderer\FluidFormRenderer'
+              renderingOptions:
+                __inheritances:
+                  10: 'TYPO3.CMS.Form.mixins.translationSettingsMixin'
+                templateRootPaths:
+                  10: 'EXT:form/Resources/Private/Frontend/Templates/'
+                partialRootPaths:
+                  10: 'EXT:form/Resources/Private/Frontend/Partials/'
+                layoutRootPaths:
+                  10: 'EXT:form/Resources/Private/Frontend/Layouts/'
+                addQueryString: false
+                argumentsToBeExcludedFromQueryString: []
+                additionalParams: []
+                controllerAction: perform
+                httpMethod: post
+                httpEnctype: 'multipart/form-data'
+                _isCompositeFormElement: false
+                _isTopLevelFormElement: true
+
+                honeypot:
+                  enable: true
+                  formElementToUse: 'Honeypot'
+
+                submitButtonLabel: 'Submit'
+
+                # set this to TRUE if you want to avoid exceptions for FormElements without definitions
+                skipUnknownElements: true
+
+            ### FORM ELEMENTS: CONTAINER ###
+            Page:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin'
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\Page'
+              renderingOptions:
+                _isTopLevelFormElement: true
+                _isCompositeFormElement: true
+                nextButtonLabel: 'next Page'
+                previousButtonLabel: 'previous Page'
+
+            SummaryPage:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.prototypes.standard.formElementsDefinition.Page'
+              renderingOptions:
+                _isTopLevelFormElement: true
+                _isCompositeFormElement: false
+                nextButtonLabel: 'next Page'
+                previousButtonLabel: 'previous Page'
+
+            Fieldset:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\Section'
+              renderingOptions:
+                _isCompositeFormElement: true
+
+            GridContainer:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\GridContainer'
+              renderingOptions:
+                _isCompositeFormElement: true
+                _isGridContainerFormElement: true
+              properties:
+                elementClassAttribute: 'container'
+                # overrules 'GridRow.properties.gridColumnClassAutoConfiguration'
+                gridColumnClassAutoConfiguration:
+                  gridSize: 12
+                  viewPorts:
+                    xs:
+                      classPattern: 'col-xs-{@numbersOfColumnsToUse}'
+                    sm:
+                      classPattern: 'col-sm-{@numbersOfColumnsToUse}'
+                    md:
+                      classPattern: 'col-md-{@numbersOfColumnsToUse}'
+                    lg:
+                      classPattern: 'col-lg-{@numbersOfColumnsToUse}'
+
+            GridRow:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\GridRow'
+              properties:
+                elementClassAttribute: 'row'
+                gridColumnClassAutoConfiguration:
+                  gridSize: 12
+                  viewPorts:
+                    xs:
+                      classPattern: 'col-xs-{@numbersOfColumnsToUse}'
+                    sm:
+                      classPattern: 'col-sm-{@numbersOfColumnsToUse}'
+                    md:
+                      classPattern: 'col-md-{@numbersOfColumnsToUse}'
+                    lg:
+                      classPattern: 'col-lg-{@numbersOfColumnsToUse}'
+              renderingOptions:
+                _isCompositeFormElement: true
+                _isGridRowFormElement: true
+
+            ### FORM ELEMENTS: INPUT ###
+            Text:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin'
+
+            Password:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin'
+
+            AdvancedPassword:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.prototypes.standard.formElementsDefinition.Password'
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement'
+              properties:
+                elementClassAttribute: 'input-medium'
+                confirmationLabel: ''
+                # Optional description (hint) for the first password input element
+                #passwordDescription: ''
+                confirmationClassAttribute: 'input-medium'
+
+            Textarea:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin'
+              properties:
+                elementClassAttribute: 'xxlarge'
+
+            Honeypot:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin'
+              properties:
+                renderAsHiddenField: false
+                styleAttribute: 'position:absolute; margin:0 0 0 -999em;'
+              renderingOptions:
+                _isHiddenFormElement: true
+
+            Hidden:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
+              renderingOptions:
+                _isHiddenFormElement: true
+
+            ### FORM ELEMENTS: HTML5 ###
+            Email:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin'
+              validators:
+                -
+                  identifier: EmailAddress
+
+            Telephone:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin'
+              validators:
+                -
+                  identifier: RegularExpression
+                  options:
+                    regularExpression: '/^.*$/'
+
+            Url:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin'
+              validators:
+                -
+                  identifier: RegularExpression
+                  options:
+                    regularExpression: '/^.*$/'
+
+            Number:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin'
+              validators:
+                -
+                  identifier: Number
+
+            ### FORM ELEMENTS: SELECT ###
+            Checkbox:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
+              properties:
+                elementClassAttribute: 'add-on'
+                containerClassAttribute: 'input checkbox'
+                value: 1
+
+            MultiCheckbox:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.MultiSelectionMixin'
+              properties:
+                containerClassAttribute: 'input checkbox'
+
+            MultiSelect:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.MultiSelectionMixin'
+              properties:
+                elementClassAttribute: 'xlarge'
+
+            RadioButton:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.SingleSelectionMixin'
+              properties:
+                elementClassAttribute: 'xlarge'
+
+            SingleSelect:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.SingleSelectionMixin'
+
+            ### FORM ELEMENTS: CUSTOM ###
+            DatePicker:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\DatePicker'
+              properties:
+                elementClassAttribute: 'small form-control'
+                timeSelectorClassAttribute: 'mini'
+                timeSelectorHourLabel: ''
+                timeSelectorMinuteLabel: ''
+                dateFormat: 'Y-m-d'
+                enableDatePicker: true
+                displayTimeSelector: false
+
+            StaticText:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.ReadOnlyFormElementMixin'
+              properties:
+                text: ''
+
+            ContentElement:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.ReadOnlyFormElementMixin'
+              properties:
+                contentElementUid: ''
+
+            ### FORM ELEMENTS: UPLOADS ###
+            FileUpload:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.FileUploadMixin'
+              properties:
+                allowedMimeTypes: ['application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.oasis.opendocument.text', 'application/pdf']
+
+            ImageUpload:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.FileUploadMixin'
+              properties:
+                allowedMimeTypes: ['image/jpeg', 'image/png', 'image/bmp']
+                elementClassAttribute: 'lightbox'
+                imageLinkMaxWidth: 500
+                imageMaxWidth: 500
+                imageMaxHeight: 500
+
+          ### FINISHERS ###
+
+          finishersDefinition:
+            Closure:
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\ClosureFinisher'
+              options:
+                #closure:
+
+            Confirmation:
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\ConfirmationFinisher'
+              #options:
+                #message: ''
+                #contentElementUid: 0
+                #typoscriptObjectPath: 'lib.tx_form.contentElementRendering'
+
+            EmailToSender:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.finishersEmailMixin'
+
+            EmailToReceiver:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.finishersEmailMixin'
+
+            DeleteUploads:
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\DeleteUploadsFinisher'
+
+            FlashMessage:
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\FlashMessageFinisher'
+              #options:
+                #messageBody: ''
+                #messageTitle: ''
+                #messageArguments: {}
+                #messageCode: 0
+                #severity: 0
+
+            Redirect:
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\RedirectFinisher'
+              #options:
+                #pageUid: 1
+                #additionalParameters: ''
+                #delay: 0
+                #statusCode: 303
+
+            SaveToDatabase:
+              implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\SaveToDatabaseFinisher'
+              #options:
+                #table: ''
+                #mode: 'insert'
+                #whereClause: []
+                #elements:
+                #  <elementIdentifier>:
+                #    mapOnDatabaseColumn: <databaseColumnName>
+                #    saveFileIdentifierInsteadOfUid: false
+                #    skipIfValueIsEmpty: false
+                #databaseColumnMappings:
+                #  <databaseColumnName>:
+                #    value: 'someValue'
+                #    skipIfValueIsEmpty: false
+
+          ### VALIDATORS ###
+          validatorsDefinition:
+            NotEmpty:
+              implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator'
+            DateTime:
+              implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\DateTimeValidator'
+            Alphanumeric:
+              implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\AlphanumericValidator'
+            Text:
+              implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\TextValidator'
+            StringLength:
+              implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\StringLengthValidator'
+              #options:
+                #minimum: 0
+                #maximum: 0
+            EmailAddress:
+              implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\EmailAddressValidator'
+            Integer:
+              implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\IntegerValidator'
+            Float:
+              implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\FloatValidator'
+            Number:
+              implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\NumberValidator'
+            NumberRange:
+              implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\NumberRangeValidator'
+              #options:
+                #minimum: 0
+                #maximum: 0
+            RegularExpression:
+              implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\RegularExpressionValidator'
+              #options:
+                #regularExpression: '/^.*$/'
+            Count:
+              implementationClassName: 'TYPO3\CMS\Form\Mvc\Validation\CountValidator'
+              #options:
+                #minimum: 0
+                #maximum: 0
+            FileSize:
+              implementationClassName: 'TYPO3\CMS\Form\Mvc\Validation\FileSizeValidator'
+              #options:
+                #minimum: '0B'
+                #maximum: '10M'
+
+      ########### MIXINS ###########
+      mixins:
+        translationSettingsMixin:
+          translation:
+            translationFile: 'EXT:form/Resources/Private/Language/locallang.xlf'
+            #translatePropertyValueIfEmpty: true
+
+        ########### FORM ELEMENT MIXINS ###########
+        formElementMixins:
+          BaseFormElementMixin: []
+              # The form element type is chosen as the template name by default.
+              # If you want another name you can set it with 'templateName'
+              #templateName: 'CustomTemplateName'
+
+          ReadOnlyFormElementMixin:
+            __inheritances:
+              10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin'
+            implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement'
+            renderingOptions:
+              _isReadOnlyFormElement: true
+
+          FormElementMixin:
+            __inheritances:
+              10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin'
+            implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement'
+            properties:
+              containerClassAttribute: 'input'
+              elementClassAttribute: ''
+              elementErrorClassAttribute: 'error'
+              #gridColumnClassAutoConfiguration:
+              #  viewPorts:
+              #    xs:
+              #      numbersOfColumnsToUse: ''
+              #    sm:
+              #      numbersOfColumnsToUse: ''
+              #    md:
+              #      numbersOfColumnsToUse: ''
+              #    lg:
+              #      numbersOfColumnsToUse: ''
+
+          TextMixin:
+            __inheritances:
+              10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
+
+          SelectionMixin:
+            __inheritances:
+              10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
+
+          SingleSelectionMixin:
+            __inheritances:
+              10: 'TYPO3.CMS.Form.mixins.formElementMixins.SelectionMixin'
+
+          MultiSelectionMixin:
+            __inheritances:
+              10: 'TYPO3.CMS.Form.mixins.formElementMixins.SelectionMixin'
+
+          FileUploadMixin:
+            __inheritances:
+              10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin'
+            implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\FileUpload'
+            properties:
+              saveToFileMount: '1:/user_upload/'
+
+        finishersEmailMixin:
+          implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\EmailFinisher'
+          options:
+            #subject: ''
+            #recipientAddress: ''
+            #recipientName: ''
+            #senderAddress:
+            #senderName: ''
+            #replyToAddress: ''
+            #carbonCopyAddress: ''
+            #blindCarbonCopyAddress: ''
+            #format: 'html'
+            #attachUploads: true
+            #translation:
+            #  language: 'default'
+            # {@format} depends the 'format' value
+            templateName: '{@format}.html'
+            templateRootPaths:
+              10: 'EXT:form/Resources/Private/Frontend/Templates/Finishers/Email/'
+            #partialRootPaths: []
+            #layoutRootPaths: []
+            #variables: {}
diff --git a/typo3/sysext/form/Configuration/Yaml/FormEditorSetup.yaml b/typo3/sysext/form/Configuration/Yaml/FormEditorSetup.yaml
new file mode 100644 (file)
index 0000000..f9ce552
--- /dev/null
@@ -0,0 +1,1410 @@
+TYPO3:
+  CMS:
+    Form:
+      ########### FORM MANAGER CONFIGURATION ###########
+      formManager:
+        dynamicRequireJsModules:
+          app: 'TYPO3/CMS/Form/Backend/FormManager'
+          viewModel: 'TYPO3/CMS/Form/Backend/FormManager/ViewModel'
+        stylesheets:
+          100: 'EXT:form/Resources/Public/Css/form.css'
+        translationFile: 'EXT:form/Resources/Private/Language/Database.xlf'
+        javaScriptTranslationFile: 'EXT:form/Resources/Private/Language/locallang_formManager_javascript.xlf'
+        selectablePrototypesConfiguration:
+          100:
+            identifier: 'standard'
+            label: 'formManager.selectablePrototypesConfiguration.standard.label'
+            newFormTemplates:
+              100:
+                templatePath: 'EXT:form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/BlankForm.yaml'
+                label: 'formManager.selectablePrototypesConfiguration.standard.newFormTemplates.blankForm.label'
+              200:
+                templatePath: 'EXT:form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/SimpleContactForm.yaml'
+                label: 'formManager.selectablePrototypesConfiguration.standard.newFormTemplates.simpleContactForm.label'
+        controller:
+          deleteAction:
+            errorTitle: 'formManagerController.deleteAction.error.title'
+            errorMessage: 'formManagerController.deleteAction.error.body'
+
+      ########### FORMEDITOR CONFIGURATION ###########
+      prototypes:
+        standard:
+          formEditor:
+            translationFile: 'EXT:form/Resources/Private/Language/Database.xlf'
+            dynamicRequireJsModules:
+              app: 'TYPO3/CMS/Form/Backend/FormEditor'
+              mediator: 'TYPO3/CMS/Form/Backend/FormEditor/Mediator'
+              viewModel: 'TYPO3/CMS/Form/Backend/FormEditor/ViewModel'
+              additionalViewModelModules:
+
+            addInlineSettings: []
+            maximumUndoSteps: 10
+
+            stylesheets:
+              200: 'EXT:form/Resources/Public/Css/form.css'
+
+            formEditorFluidConfiguration:
+              templatePathAndFilename: 'EXT:form/Resources/Private/Backend/Templates/FormEditor/InlineTemplates.html'
+              partialRootPaths:
+                10: 'EXT:form/Resources/Private/Backend/Partials/FormEditor/'
+              layoutRootPaths:
+                10: 'EXT:form/Resources/Private/Backend/Layouts/FormEditor/'
+
+            formEditorPartials:
+              # abstract form element partials
+              FormElement-_ElementToolbar: 'Stage/_ElementToolbar'
+              FormElement-_UnknownElement: 'Stage/_UnknownElement'
+              FormElement-Page: 'Stage/Page'
+              FormElement-SummaryPage: 'Stage/SummaryPage'
+              FormElement-Fieldset: 'Stage/Fieldset'
+              FormElement-GridContainer: 'Stage/Fieldset'
+              FormElement-GridRow: 'Stage/Fieldset'
+              FormElement-Text: 'Stage/SimpleTemplate'
+              FormElement-Password: 'Stage/SimpleTemplate'
+              FormElement-AdvancedPassword: 'Stage/SimpleTemplate'
+              FormElement-Textarea: 'Stage/SimpleTemplate'
+              FormElement-Checkbox: 'Stage/SimpleTemplate'
+              FormElement-MultiCheckbox: 'Stage/SelectTemplate'
+              FormElement-MultiSelect: 'Stage/SelectTemplate'
+              FormElement-RadioButton: 'Stage/SelectTemplate'
+              FormElement-SingleSelect: 'Stage/SelectTemplate'
+              FormElement-DatePicker: 'Stage/SimpleTemplate'
+              FormElement-StaticText: 'Stage/StaticText'
+              FormElement-Hidden: 'Stage/SimpleTemplate'
+              FormElement-ContentElement: 'Stage/ContentElement'
+              FormElement-FileUpload: 'Stage/FileUploadTemplate'
+              FormElement-ImageUpload: 'Stage/FileUploadTemplate'
+              FormElement-Email: 'Stage/SimpleTemplate'
+              FormElement-Telephone: 'Stage/SimpleTemplate'
+              FormElement-Url: 'Stage/SimpleTemplate'
+              FormElement-Number: 'Stage/SimpleTemplate'
+
+              # modals
+              Modal-InsertElements: 'Modals/InsertElements'
+              Modal-InsertPages: 'Modals/InsertPages'
+              Modal-ValidationErrors: 'Modals/ValidationErrors'
+
+              # inspector editors
+              Inspector-FormElementHeaderEditor: 'Inspector/FormElementHeaderEditor'
+              Inspector-CollectionElementHeaderEditor: 'Inspector/CollectionElementHeaderEditor'
+              Inspector-TextEditor: 'Inspector/TextEditor'
+              Inspector-PropertyGridEditor: 'Inspector/PropertyGridEditor'
+              Inspector-SingleSelectEditor: 'Inspector/SingleSelectEditor'
+              Inspector-MultiSelectEditor: 'Inspector/MultiSelectEditor'
+              Inspector-GridColumnViewPortConfigurationEditor: 'Inspector/GridColumnViewPortConfigurationEditor'
+              Inspector-TextareaEditor: 'Inspector/TextareaEditor'
+              Inspector-RemoveElementEditor: 'Inspector/RemoveElementEditor'
+              Inspector-FinishersEditor: 'Inspector/FinishersEditor'
+              Inspector-ValidatorsEditor: 'Inspector/ValidatorsEditor'
+              Inspector-RequiredValidatorEditor: 'Inspector/RequiredValidatorEditor'
+              Inspector-CheckboxEditor: 'Inspector/CheckboxEditor'
+              Inspector-Typo3WinBrowserEditor: 'Inspector/Typo3WinBrowserEditor'
+
+            formElementPropertyValidatorsDefinition:
+              NotEmpty:
+                errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.NotEmpty.label'
+              Integer:
+                errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.Integer.label'
+              NaiveEmail:
+                errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.NaiveEmail.label'
+              NaiveEmailOrEmpty:
+                errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.NaiveEmail.label'
+              FormElementIdentifierWithinCurlyBracesInclusive:
+                errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.FormElementIdentifierWithinCurlyBraces.label'
+              FormElementIdentifierWithinCurlyBracesExclusive:
+                errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.FormElementIdentifierWithinCurlyBraces.label'
+              FileSize:
+                errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.FileSize.label'
+
+            formElementGroups:
+              input:
+                label: 'formEditor.formElementGroups.input.label'
+              html5:
+                label: 'formEditor.formElementGroups.html5.label'
+              select:
+                label: 'formEditor.formElementGroups.select.label'
+              custom:
+                label: 'formEditor.formElementGroups.custom.label'
+              container:
+                label: 'formEditor.formElementGroups.container.label'
+              page:
+                label: 'formEditor.formElementGroups.page.label'
+
+          ########### DEFAULT FORM ELEMENT DEFINITIONS ###########
+          formElementsDefinition:
+            Form:
+              formEditor:
+                _isCompositeFormElement: false
+                _isTopLevelFormElement: true
+
+                saveSuccessFlashMessageTitle: 'formEditor.elements.Form.saveSuccessFlashMessageTitle'
+                saveSuccessFlashMessageMessage: 'formEditor.elements.Form.saveSuccessFlashMessageMessage'
+                saveErrorFlashMessageTitle: 'formEditor.elements.Form.saveErrorFlashMessageTitle'
+                saveErrorFlashMessageMessage: 'formEditor.elements.Form.saveErrorFlashMessageMessage'
+
+                modalValidationErrorsDialogTitle: 'formEditor.modals.validationErrors.dialogTitle'
+                modalValidationErrorsConfirmButton: 'formEditor.modals.validationErrors.confirmButton'
+
+                modalInsertElementsDialogTitle: 'formEditor.modals.insertElements.dialogTitle'
+                modalInsertPagesDialogTitle: 'formEditor.modals.newPages.dialogTitle'
+
+                modalCloseDialogMessage: 'formEditor.modals.close.dialogMessage'
+                modalCloseDialogTitle: 'formEditor.modals.close.dialogTitle'
+                modalCloseConfirmButton: 'formEditor.modals.close.confirmButton'
+                modalCloseCancleButton: 'formEditor.modals.close.cancleButton'
+
+                modalRemoveElementDialogTitle: 'formEditor.modals.removeElement.dialogTitle'
+                modalRemoveElementDialogMessage: 'formEditor.modals.removeElement.dialogMessage'
+                modalRemoveElementConfirmButton: 'formEditor.modals.removeElement.confirmButton'
+                modalRemoveElementCancleButton: 'formEditor.modals.removeElement.cancleButton'
+                modalRemoveElementLastAvailablePageFlashMessageTitle: 'formEditor.modals.removeElement.lastAvailablePageFlashMessageTitle'
+                modalRemoveElementLastAvailablePageFlashMessageMessage: 'formEditor.modals.removeElement.lastAvailablePageFlashMessageMessage'
+
+                inspectorEditorFormElementSelectorNoElements: 'formEditor.inspector.editor.formelement_selector.no_elements'
+
+                paginationTitle: 'formEditor.pagination.title'
+
+                iconIdentifier: 'content-elements-mailform'
+                predefinedDefaults:
+                  renderingOptions:
+                    submitButtonLabel: 'formEditor.elements.Form.editor.submitButtonLabel.value'
+                editors:
+                  300:
+                    identifier: 'submitButtonLabel'
+                    templateName: 'Inspector-TextEditor'
+                    label: 'formEditor.elements.Form.editor.submitButtonLabel.label'
+                    propertyPath: 'renderingOptions.submitButtonLabel'
+                  900:
+                    identifier: 'finishers'
+                    templateName: 'Inspector-FinishersEditor'
+                    label: 'formEditor.elements.Form.editor.finishers.label'
+                    selectOptions:
+                      10:
+                        value: ''
+                        label: 'formEditor.elements.Form.editor.finishers.EmptyValue.label'
+                      20:
+                        value: 'EmailToSender'
+                        label: 'formEditor.elements.Form.editor.finishers.EmailToSender.label'
+                      30:
+                        value: 'EmailToReceiver'
+                        label: 'formEditor.elements.Form.editor.finishers.EmailToReceiver.label'
+                      40:
+                        value: 'Redirect'
+                        label: 'formEditor.elements.Form.editor.finishers.Redirect.label'
+                      50:
+                        value: 'DeleteUploads'
+                        label: 'formEditor.elements.Form.editor.finishers.DeleteUploads.label'
+                      60:
+                        value: 'Confirmation'
+                        label: 'formEditor.elements.Form.editor.finishers.Confirmation.label'
+
+                propertyCollections:
+                  finishers:
+                    10:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.formEmailFinisherMixin'
+                      identifier: 'EmailToSender'
+
+                    20:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.formEmailFinisherMixin'
+                      identifier: 'EmailToReceiver'
+                      editors:
+                        100:
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.header.label'
+                        200:
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.subject.label'
+                        300:
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientAddress.label'
+                          fieldExplanationText: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientAddress.fieldExplanationText'
+                        400:
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientName.label'
+                          fieldExplanationText: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientName.fieldExplanationText'
+                        500:
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.senderAddress.label'
+                          fieldExplanationText: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.senderAddress.fieldExplanationText'
+                        600:
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.senderName.label'
+                          fieldExplanationText: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.senderName.fieldExplanationText'
+                        700:
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.replyToAddress.label'
+                        800:
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.carbonCopyAddress.label'
+                        900:
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.blindCarbonCopyAddress.label'
+                        1000:
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.format.label'
+                        1100:
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.attachUploads.label'
+                        1200:
+                          identifier: 'language'
+                          templateName: 'Inspector-SingleSelectEditor'
+                          label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.language.label'
+                          propertyPath: 'options.translation.language'
+                          selectOptions:
+                            10:
+                              value: 'default'
+                              label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.language.1'
+
+                    30:
+                      identifier: 'Redirect'
+                      editors:
+                        __inheritances:
+                          10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        100:
+                          label: 'formEditor.elements.Form.finisher.Redirect.editor.header.label'
+                        200:
+                          identifier: 'pageUid'
+                          templateName: 'Inspector-Typo3WinBrowserEditor'
+                          label: 'formEditor.elements.Form.finisher.Redirect.editor.pageUid.label'
+                          buttonLabel: 'formEditor.elements.Form.finisher.Redirect.editor.pageUid.buttonLabel'
+                          browsableType: pages
+                          propertyPath: 'options.pageUid'
+                          propertyValidatorsMode: 'OR'
+                          propertyValidators:
+                            10: 'Integer'
+                            20: 'FormElementIdentifierWithinCurlyBracesExclusive'
+
+                        300:
+                          identifier: 'additionalParameters'
+                          templateName: 'Inspector-TextEditor'
+                          label: 'formEditor.elements.Form.finisher.Redirect.editor.additionalParameters.label'
+                          propertyPath: 'options.additionalParameters'
+
+                    40:
+                      identifier: 'DeleteUploads'
+                      editors:
+                        __inheritances:
+                          10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        100:
+                          label: 'formEditor.elements.Form.finisher.DeleteUploads.editor.header.label'
+
+                    50:
+                      identifier: 'Confirmation'
+                      editors:
+                        __inheritances:
+                          10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        100:
+                          label: 'formEditor.elements.Form.finisher.Confirmation.editor.header.label'
+                        200:
+                          identifier: 'contentElement'
+                          templateName: 'Inspector-Typo3WinBrowserEditor'
+                          label: 'formEditor.elements.Form.finisher.Confirmation.editor.contentElement.label'
+                          buttonLabel: 'formEditor.elements.Form.finisher.Confirmation.editor.contentElement.buttonLabel'
+                          browsableType: tt_content
+                          propertyPath: 'options.contentElementUid'
+                          propertyValidatorsMode: 'OR'
+                          propertyValidators:
+                            10: 'IntegerOrEmpty'
+                            20: 'FormElementIdentifierWithinCurlyBracesExclusive'
+                        300:
+                          identifier: 'message'
+                          templateName: 'Inspector-TextareaEditor'
+                          label: 'formEditor.elements.Form.finisher.Confirmation.editor.message.label'
+                          propertyPath: 'options.message'
+                          fieldExplanationText: 'formEditor.elements.Form.finisher.Confirmation.editor.message.fieldExplanationText'
+
+                    60:
+                      identifier: 'Closure'
+                      editors:
+                        __inheritances:
+                          10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        100:
+                          label: 'formEditor.elements.Form.finisher.Closure.editor.header.label'
+
+                    70:
+                      identifier: 'FlashMessage'
+                      editors:
+                        __inheritances:
+                          10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        100:
+                          label: 'formEditor.elements.Form.finisher.FlashMessage.editor.header.label'
+
+                    80:
+                      identifier: 'SaveToDatabase'
+                      editors:
+                        __inheritances:
+                          10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        100:
+                          label: 'formEditor.elements.Form.finisher.SaveToDatabase.editor.header.label'
+
+            ### FORM ELEMENTS: CONTAINER ###
+            Fieldset:
+              formEditor:
+                label: 'formEditor.elements.Fieldset.label'
+                group: container
+                groupSorting: 100
+                _isCompositeFormElement: true
+                iconIdentifier: 't3-form-icon-fieldset'
+                editors:
+                  200:
+                    label: 'formEditor.elements.Fieldset.editor.label.label'
+                  800: null
+
+            GridContainer:
+              formEditor:
+                label: 'formEditor.elements.GridContainer.label'
+                _isCompositeFormElement: true
+                _isGridContainerFormElement: true
+                iconIdentifier: 't3-form-icon-gridcontainer'
+                editors:
+                  200:
+                    label: 'formEditor.elements.GridContainer.editor.label.label'
+                  800: null
+
+            GridRow:
+              formEditor:
+                label: 'formEditor.elements.GridRow.label'
+                group: container
+                groupSorting: 300
+                _isCompositeFormElement: true
+                _isGridRowFormElement: true
+                iconIdentifier: 't3-form-icon-gridrow'
+                editors:
+                  200:
+                    label: 'formEditor.elements.GridRow.editor.label.label'
+                  800: null
+
+            ### FORM ELEMENTS: PAGE TYPES ###
+            Page:
+              formEditor:
+                __inheritances:
+                  10: 'TYPO3.CMS.Form.mixins.formElementMixins.RemovableFormElementMixin'
+                predefinedDefaults:
+                  renderingOptions:
+                    previousButtonLabel: 'formEditor.elements.Page.editor.previousButtonLabel.value'
+                    nextButtonLabel: 'formEditor.elements.Page.editor.nextButtonLabel.value'
+                label: 'formEditor.elements.Page.label'
+                group: page
+                groupSorting: 100
+                _isTopLevelFormElement: true
+                _isCompositeFormElement: true
+                iconIdentifier: 't3-form-icon-page'
+                editors:
+                  200:
+                    label: 'formEditor.elements.Page.editor.label.label'
+                  300:
+                    identifier: 'previousButtonLabel'
+                    templateName: 'Inspector-TextEditor'
+                    label: 'formEditor.elements.Page.editor.previousButtonLabel.label'
+                    propertyPath: 'renderingOptions.previousButtonLabel'
+                  400:
+                    identifier: 'nextButtonLabel'
+                    templateName: 'Inspector-TextEditor'
+                    label: 'formEditor.elements.Page.editor.nextButtonLabel.label'
+                    propertyPath: 'renderingOptions.nextButtonLabel'
+
+            SummaryPage:
+              formEditor:
+                predefinedDefaults:
+                  renderingOptions:
+                    previousButtonLabel: 'formEditor.elements.SummaryPage.editor.previousButtonLabel.value'
+                    nextButtonLabel: 'formEditor.elements.SummaryPage.editor.nextButtonLabel.value'
+                label: 'formEditor.elements.SummaryPage.label'
+                group: page
+                groupSorting: 200
+                _isTopLevelFormElement: true
+                _isCompositeFormElement: false
+                iconIdentifier: 't3-form-icon-summary-page'
+                editors:
+                  200:
+                    label: 'formEditor.elements.SummaryPage.editor.label.label'
+                  300:
+                    identifier: 'previousButtonLabel'
+                    templateName: 'Inspector-TextEditor'
+                    label: 'formEditor.elements.SummaryPage.editor.previousButtonLabel.label'
+                    propertyPath: 'renderingOptions.previousButtonLabel'
+                  400:
+                    identifier: 'nextButtonLabel'
+                    templateName: 'Inspector-TextEditor'
+                    label: 'formEditor.elements.SummaryPage.editor.nextButtonLabel.label'
+                    propertyPath: 'renderingOptions.nextButtonLabel'
+
+            ### FORM ELEMENTS: INPUT ###
+
+            Text:
+              formEditor:
+                label: 'formEditor.elements.Text.label'
+                group: input
+                groupSorting: 100
+                iconIdentifier: 't3-form-icon-text'
+
+            Password:
+              formEditor:
+                label: 'formEditor.elements.Password.label'
+                group: input
+                groupSorting: 300
+                iconIdentifier: 't3-form-icon-password'
+
+            AdvancedPassword:
+              formEditor:
+                label: 'formEditor.elements.AdvancedPassword.label'
+                group: custom
+                groupSorting: 500
+                predefinedDefaults:
+                  properties:
+                    confirmationLabel: 'formEditor.element.AdvancedPassword.editor.confirmationLabel.predefinedDefaults'
+                  defaultValue: null
+                iconIdentifier: 't3-form-icon-advanced-password'
+                editors:
+                  300:
+                    identifier: 'confirmationLabel'
+                    templateName: 'Inspector-TextEditor'
+                    label: 'formEditor.elements.AdvancedPassword.editor.confirmationLabel.label'
+                    propertyPath: 'properties.confirmationLabel'
+                  500: null
+
+            Hidden:
+              formEditor:
+                label: 'formEditor.elements.Hidden.label'
+                group: custom
+                groupSorting: 300
+                iconIdentifier: 't3-form-icon-hidden'
+                predefinedDefaults:
+                  defaultValue: ''
+                editors:
+                  300:
+                    identifier: 'defaultValue'
+                    templateName: 'Inspector-TextEditor'
+                    label: 'formEditor.elements.Hidden.editor.defaultValue.label'
+                    propertyPath: 'defaultValue'
+                  800: null
+
+            Textarea:
+              formEditor:
+                label: 'formEditor.elements.Textarea.label'
+                group: input
+                groupSorting: 200
+                iconIdentifier: 't3-form-icon-textarea'
+                editors:
+                  900:
+                    selectOptions:
+                      # remove email validator
+                      50: null
+
+            ### FORM ELEMENTS: HTML5 ###
+            Email:
+              formEditor:
+                label: 'formEditor.elements.Email.label'
+                group: html5
+                groupSorting: 100
+                iconIdentifier: 't3-form-icon-email'
+                predefinedDefaults:
+                  validators:
+                    -
+                      identifier: EmailAddress
+                editors:
+                  500:
+                    propertyValidators:
+                      10: 'NaiveEmailOrEmpty'
+                  900:
+                    selectOptions:
+                      20: null
+                      30: null
+                      40: null
+                      60: null
+                      70: null
+                      80: null
+                      90: null
+
+                propertyCollections:
+                  validators:
+                    40:
+                      editors:
+                        9999: null
+
+            Telephone:
+              formEditor:
+                label: 'formEditor.elements.Telephone.label'
+                group: html5
+                groupSorting: 200
+                iconIdentifier: 't3-form-icon-telephone'
+
+                editors:
+                  900:
+                    selectOptions:
+                      20: null
+                      30: null
+                      40: null
+                      50: null
+                      60: null
+                      70: null
+                      80: null
+
+                propertyCollections:
+                  validators:
+                    80:
+                      editors:
+                        9999: null
+
+            Url:
+              formEditor:
+                label: 'formEditor.elements.Url.label'
+                group: html5
+                groupSorting: 300
+                iconIdentifier: 't3-form-icon-url'
+
+                editors:
+                  900:
+                    selectOptions:
+                      20: null
+                      30: null
+                      40: null
+                      50: null
+                      60: null
+                      70: null
+                      80: null
+
+                propertyCollections:
+                  validators:
+                    80:
+                      editors:
+                        9999: null
+
+            Number:
+              formEditor:
+                label: 'formEditor.elements.Number.label'
+                group: html5
+                groupSorting: 400
+                iconIdentifier: 't3-form-icon-number'
+                predefinedDefaults:
+                  properties:
+                    fluidAdditionalAttributes:
+                      step: 1
+                  validators:
+                    -
+                      identifier: Number
+                editors:
+                  500:
+                   propertyValidators:
+                      10: 'IntegerOrEmpty'
+                  700:
+                    identifier: 'step'
+                    templateName: 'Inspector-TextEditor'
+                    label: 'formEditor.elements.TextMixin.editor.step.label'
+                    propertyPath: 'properties.fluidAdditionalAttributes.step'
+                    propertyValidators:
+                      10: 'Integer'
+
+                  900:
+                    selectOptions:
+                      20: null
+                      30: null
+                      40: null
+                      50: null
+                      60:
+                        value: 'Number'
+                        label: 'formEditor.elements.Number.editor.validators.Number.label'
+                      70: null
+                      90: null
+
+                propertyCollections:
+                  validators:
+                    60:
+                      identifier: 'Number'
+                      editors:
+                        __inheritances:
+                          10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        100:
+                          label: 'formEditor.elements.TextMixin.validators.Number.editor.header.label'
+                        9999: null
+
+            ### FORM ELEMENTS: SELECT ###
+            Checkbox:
+              formEditor:
+                label: 'formEditor.elements.Checkbox.label'
+                group: select
+                groupSorting: 100
+                iconIdentifier: 't3-form-icon-checkbox'
+
+            MultiCheckbox:
+              formEditor:
+                label: 'formEditor.elements.MultiCheckbox.label'
+                group: select
+                groupSorting: 400
+                iconIdentifier: 't3-form-icon-multi-checkbox'
+                editors:
+                  800:
+                    propertyPath: null
+                    propertyValue: null
+
+            MultiSelect:
+              formEditor:
+                label: 'formEditor.elements.MultiSelect.label'
+                group: select
+                groupSorting: 500
+                iconIdentifier: 't3-form-icon-multi-select'
+                editors:
+                  250:
+                    identifier: 'inactiveOption'
+                    templateName: 'Inspector-TextEditor'
+                    label: 'formEditor.elements.SelectionMixin.editor.inactiveOption.label'
+                    propertyPath: 'properties.prependOptionLabel'
+                    fieldExplanationText: 'formEditor.elements.SelectionMixin.editor.inactiveOption.fieldExplanationText'
+                    doNotSetIfPropertyValueIsEmpty: true
+
+            RadioButton:
+              formEditor:
+                label: 'formEditor.elements.RadioButton.label'
+                group: select
+                groupSorting: 300
+                iconIdentifier: 't3-form-icon-radio-button'
+
+            SingleSelect:
+              formEditor:
+                label: 'formEditor.elements.SingleSelect.label'
+                group: select
+                groupSorting: 200
+                iconIdentifier: 't3-form-icon-single-select'
+                editors:
+                  250:
+                    identifier: 'inactiveOption'
+                    templateName: 'Inspector-TextEditor'
+                    label: 'formEditor.elements.SelectionMixin.editor.inactiveOption.label'
+                    propertyPath: 'properties.prependOptionLabel'
+                    fieldExplanationText: 'formEditor.elements.SelectionMixin.editor.inactiveOption.fieldExplanationText'
+                    doNotSetIfPropertyValueIsEmpty: true
+
+            ### FORM ELEMENTS: CUSTOM ###
+            DatePicker:
+              formEditor:
+                label: 'formEditor.elements.DatePicker.label'
+                group: custom
+                groupSorting: 200
+                predefinedDefaults:
+                  properties:
+                    dateFormat: 'Y-m-d'
+                    enableDatePicker: true
+                    displayTimeSelector: false
+                iconIdentifier: 't3-form-icon-date-picker'
+                editors:
+                  300:
+                    identifier: 'dateFormat'
+                    templateName: 'Inspector-TextEditor'
+                    label: 'formEditor.elements.DatePicker.editor.dateFormat.label'
+                    propertyPath: 'properties.dateFormat'
+                  400:
+                    identifier: 'enableDatePicker'
+                    templateName: 'Inspector-CheckboxEditor'
+                    label: 'formEditor.elements.DatePicker.editor.enableDatePicker.label'
+                    propertyPath: 'properties.enableDatePicker'
+                  500:
+                    identifier: 'displayTimeSelector'
+                    templateName: 'Inspector-CheckboxEditor'
+                    label: 'formEditor.elements.DatePicker.editor.displayTimeSelector.label'
+                    propertyPath: 'properties.displayTimeSelector'
+                  900:
+                    identifier: 'validators'
+                    templateName: 'Inspector-ValidatorsEditor'
+                    label: 'formEditor.elements.DatePicker.editor.validators.label'
+                    selectOptions:
+                      10:
+                        value: ''
+                        label: 'formEditor.elements.DatePicker.editor.validators.EmptyValue.label'
+                      20:
+                        value: 'DateTime'
+                        label: 'formEditor.elements.DatePicker.editor.validators.DateTime.label'
+
+                propertyCollections:
+                  validators:
+                    10:
+                      identifier: 'DateTime'
+                      editors:
+                        __inheritances:
+                          10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        100:
+                          label: 'formEditor.elements.DatePicker.validators.DateTime.editor.header.label'
+
+            StaticText:
+              formEditor:
+                label: 'formEditor.elements.StaticText.label'
+                group: custom
+                groupSorting: 600
+                predefinedDefaults:
+                  properties:
+                    text: ''
+                iconIdentifier: 't3-form-icon-static-text'
+                editors:
+                  300:
+                    identifier: 'staticText'
+                    templateName: 'Inspector-TextareaEditor'
+                    label: 'formEditor.elements.StaticText.editor.staticText.label'
+                    propertyPath: 'properties.text'
+
+            ContentElement:
+              formEditor:
+                label: 'formEditor.elements.ContentElement.label'
+                group: custom
+                groupSorting: 700
+                predefinedDefaults:
+                  properties:
+                    contentElementUid: ''
+                iconIdentifier: 't3-form-icon-content-element'
+                editors:
+                  200: null
+                  300:
+                    identifier: 'contentElement'
+                    templateName: 'Inspector-Typo3WinBrowserEditor'
+                    label: 'formEditor.elements.ContentElement.editor.contentElement.label'
+                    buttonLabel: 'formEditor.elements.ContentElement.editor.contentElement.buttonLabel'
+                    browsableType: tt_content
+                    propertyPath: 'properties.contentElementUid'
+                    propertyValidatorsMode: 'OR'
+                    propertyValidators:
+                      10: 'Integer'
+                      20: 'FormElementIdentifierWithinCurlyBracesExclusive'
+
+            ### FORM ELEMENTS: UPLOADS ###
+            FileUpload:
+              formEditor:
+                label: 'formEditor.elements.FileUpload.label'
+                group: custom
+                groupSorting: 100
+                predefinedDefaults:
+                  properties:
+                    allowedMimeTypes: ['application/pdf']
+                iconIdentifier: 't3-form-icon-file-upload'
+                editors:
+                  300:
+                    identifier: 'allowedMimeTypes'
+                    templateName: 'Inspector-MultiSelectEditor'
+                    label: 'formEditor.elements.FileUpload.editor.allowedMimeTypes.label'
+                    propertyPath: 'properties.allowedMimeTypes'
+                    selectOptions:
+                      10:
+                        value: 'application/msword'
+                        label: 'formEditor.elements.FileUpload.editor.allowedMimeTypes.doc'
+                      20:
+                        value: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
+                        label: 'formEditor.elements.FileUpload.editor.allowedMimeTypes.docx'
+                      30:
+                        value: 'application/msexcel'
+                        label: 'formEditor.elements.FileUpload.editor.allowedMimeTypes.xls'
+                      40:
+                        value: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
+                        label: 'formEditor.elements.FileUpload.editor.allowedMimeTypes.xlsx'
+                      50:
+                        value: 'application/pdf'
+                        label: 'formEditor.elements.FileUpload.editor.allowedMimeTypes.pdf'
+                      60:
+                        value: 'application/vnd.oasis.opendocument.text'
+                        label: 'formEditor.elements.FileUpload.editor.allowedMimeTypes.odt'
+                      70:
+                        value: 'application/vnd.oasis.opendocument.spreadsheet-template'
+                        label: 'formEditor.elements.FileUpload.editor.allowedMimeTypes.ods'
+
+            ImageUpload:
+              formEditor:
+                label: 'formEditor.elements.ImageUpload.label'
+                group: custom
+                groupSorting: 400
+                predefinedDefaults:
+                  properties:
+                    allowedMimeTypes: ['image/jpeg']
+                iconIdentifier: 't3-form-icon-image-upload'
+                editors:
+                  300:
+                    identifier: 'allowedMimeTypes'
+                    templateName: 'Inspector-MultiSelectEditor'
+                    label: 'formEditor.elements.ImageUpload.editor.allowedMimeTypes.label'
+                    propertyPath: 'properties.allowedMimeTypes'
+                    selectOptions:
+                      10:
+                        value: 'image/jpeg'
+                        label: 'formEditor.elements.ImageUpload.editor.allowedMimeTypes.jpg'
+                      20:
+                        value: 'image/png'
+                        label: 'formEditor.elements.ImageUpload.editor.allowedMimeTypes.png'
+                      30:
+                        value: 'image/bmp'
+                        label: 'formEditor.elements.ImageUpload.editor.allowedMimeTypes.bmp'
+
+          ### FINISHERS ###
+          finishersDefinition:
+            EmailToSender:
+              formEditor:
+                iconIdentifier: 't3-form-icon-finisher'
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.header.label'
+                predefinedDefaults:
+                  options:
+                    subject: ''
+                    recipientAddress: ''
+                    recipientName: ''
+                    senderAddress: ''
+                    senderName: ''
+                    replyToAddress: ''
+                    carbonCopyAddress: ''
+                    blindCarbonCopyAddress: ''
+                    format: 'html'
+                    attachUploads: true
+
+            EmailToReceiver:
+              formEditor:
+                iconIdentifier: 't3-form-icon-finisher'
+                label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.header.label'
+                predefinedDefaults:
+                  options:
+                    subject: ''
+                    recipientAddress: ''
+                    recipientName: ''
+                    senderAddress: ''
+                    senderName: ''
+                    replyToAddress: ''
+                    carbonCopyAddress: ''
+                    blindCarbonCopyAddress: ''
+                    format: 'html'
+                    attachUploads: true
+                    translation:
+                      language: ''
+
+            Redirect:
+              formEditor:
+                iconIdentifier: 't3-form-icon-finisher'
+                label: 'formEditor.elements.Form.finisher.Redirect.editor.header.label'
+                predefinedDefaults:
+                  options:
+                    pageUid: ''
+                    additionalParameters: ''
+
+            Closure:
+              formEditor:
+                iconIdentifier: 't3-form-icon-finisher'
+                label: 'formEditor.elements.Form.finisher.Closure.editor.header.label'
+                predefinedDefaults:
+                  options:
+                    closure: ''
+
+            Confirmation:
+              formEditor:
+                iconIdentifier: 't3-form-icon-finisher'
+                label: 'formEditor.elements.Form.finisher.Confirmation.editor.header.label'
+                predefinedDefaults:
+                  options:
+                    message: ''
+                    contentElementUid: ''
+
+            FlashMessage:
+              formEditor:
+                iconIdentifier: 't3-form-icon-finisher'
+                label: 'formEditor.elements.Form.finisher.FlashMessage.editor.header.label'
+                predefinedDefaults:
+                  options:
+                    messageBody: ''
+                    messageTitle: ''
+                    messageArguments: ''
+                    messageCode: 0
+                    severity: 0
+
+            SaveToDatabase:
+              formEditor:
+                iconIdentifier: 't3-form-icon-finisher'
+                label: 'formEditor.elements.Form.finisher.SaveToDatabase.editor.header.label'
+                predefinedDefaults:
+                  options: []
+
+            DeleteUploads:
+              formEditor:
+                iconIdentifier: 't3-form-icon-finisher'
+                label: 'formEditor.elements.Form.finisher.DeleteUploads.editor.header.label'
+
+          ### VALIDATORS ###
+          validatorsDefinition:
+            NotEmpty:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label : 'formEditor.elements.FormElement.editor.requiredValidator.label'
+            DateTime:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.DatePicker.validators.DateTime.editor.header.label'
+            Alphanumeric:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.TextMixin.editor.validators.Alphanumeric.label'
+            Text:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.TextMixin.editor.validators.Text.label'
+            StringLength:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.TextMixin.editor.validators.StringLength.label'
+                predefinedDefaults:
+                  options:
+                    minimum: ''
+                    maximum: ''
+            EmailAddress:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.TextMixin.editor.validators.EmailAddress.label'
+            Integer:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.TextMixin.editor.validators.Integer.label'
+            Float:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.TextMixin.editor.validators.Float.label'
+            Number:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.TextMixin.editor.validators.Number.label'
+            NumberRange:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.TextMixin.editor.validators.NumberRange.label'
+                predefinedDefaults:
+                  options:
+                    minimum: ''
+                    maximum: ''
+            RegularExpression:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.TextMixin.editor.validators.RegularExpression.label'
+                predefinedDefaults:
+                  options:
+                    regularExpression: ''
+            Count:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.MultiSelectionMixin.validators.Count.editor.header.label'
+                predefinedDefaults:
+                  options:
+                    minimum: ''
+                    maximum: ''
+            FileSize:
+              formEditor:
+                iconIdentifier: 't3-form-icon-validator'
+                label: 'formEditor.elements.FileUploadMixin.validators.FileSize.editor.header.label'
+                predefinedDefaults:
+                  options:
+                    minimum: '0B'
+                    maximum: '10M'
+
+      ########### MIXINS ###########
+      mixins:
+        ########### FORM ELEMENT MIXINS ###########
+        formElementMixins:
+          BaseFormElementMixin:
+            formEditor:
+              predefinedDefaults: []
+              editors:
+                100:
+                  identifier: 'header'
+                  templateName: 'Inspector-FormElementHeaderEditor'
+                200:
+                  identifier: 'label'
+                  templateName: 'Inspector-TextEditor'
+                  label: 'formEditor.elements.BaseFormElementMixin.editor.label.label'
+                  propertyPath: 'label'
+
+          RemoveButtonMixin:
+            9999:
+              identifier: 'removeButton'
+              templateName: 'Inspector-RemoveElementEditor'
+
+          RemovableFormElementMixin:
+            editors:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.RemoveButtonMixin'
+
+          BaseCollectionEditorsMixin:
+            __inheritances:
+              10: 'TYPO3.CMS.Form.mixins.formElementMixins.RemoveButtonMixin'
+            100:
+              identifier: 'header'
+              templateName: 'Inspector-CollectionElementHeaderEditor'
+              label: ''
+
+          MinimumMaximumEditorsMixin:
+            200:
+              identifier: 'minimum'
+              templateName: 'Inspector-TextEditor'
+              label: 'formEditor.elements.MinimumMaximumEditorsMixin.editor.minimum.label'
+              propertyPath: 'options.minimum'
+              propertyValidators:
+                10: 'Integer'
+            300:
+              identifier: 'maximum'
+              templateName: 'Inspector-TextEditor'
+              label: 'formEditor.elements.MinimumMaximumEditorsMixin.editor.maximum.label'
+              propertyPath: 'options.maximum'
+              propertyValidators:
+                10: 'Integer'
+
+          ReadOnlyFormElementMixin:
+            formEditor:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.RemovableFormElementMixin'
+              editors:
+                200:
+                  label: 'formEditor.elements.ReadOnlyFormElement.editor.label.label'
+
+          FormElementMixin:
+            formEditor:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.RemovableFormElementMixin'
+              editors:
+                200:
+                  label: 'formEditor.elements.FormElement.editor.label.label'
+
+                700:
+                  identifier: 'gridColumnViewPortConfiguration'
+                  templateName: 'Inspector-GridColumnViewPortConfigurationEditor'
+                  label: 'formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.label'
+                  configurationOptions:
+                    viewPorts:
+                      10:
+                        viewPortIdentifier: 'xs'
+                        label: 'formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.xs.label'
+                      20:
+                        viewPortIdentifier: 'sm'
+                        label: 'formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.sm.label'
+                      30:
+                        viewPortIdentifier: 'md'
+                        label: 'formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.md.label'
+                      40:
+                        viewPortIdentifier: 'lg'
+                        label: 'formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.lg.label'
+                    numbersOfColumnsToUse:
+                      label: 'formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.numbersOfColumnsToUse.label'
+                      propertyPath: 'properties.gridColumnClassAutoConfiguration.viewPorts.{@viewPortIdentifier}.numbersOfColumnsToUse'
+                      fieldExplanationText: 'formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.numbersOfColumnsToUse.fieldExplanationText'
+
+                800:
+                  identifier: 'requiredValidator'
+                  templateName: 'Inspector-RequiredValidatorEditor'
+                  label: 'formEditor.elements.FormElement.editor.requiredValidator.label'
+                  validatorIdentifier: 'NotEmpty'
+                  propertyPath: 'properties.fluidAdditionalAttributes.required'
+                  propertyValue: 'required'
+
+          TextMixin:
+            formEditor:
+              predefinedDefaults:
+                defaultValue: ''
+              editors:
+                400:
+                  identifier: 'placeholder'
+                  templateName: 'Inspector-TextEditor'
+                  label: 'formEditor.elements.TextMixin.editor.placeholder.label'
+                  propertyPath: 'properties.fluidAdditionalAttributes.placeholder'
+                  doNotSetIfPropertyValueIsEmpty: true
+                500:
+                  identifier: 'defaultValue'
+                  templateName: 'Inspector-TextEditor'
+                  label: 'formEditor.elements.TextMixin.editor.defaultValue.label'
+                  propertyPath: 'defaultValue'
+                900:
+                  identifier: 'validators'
+                  templateName: 'Inspector-ValidatorsEditor'
+                  label: 'formEditor.elements.TextMixin.editor.validators.label'
+                  selectOptions:
+                    10:
+                      value: ''
+                      label: 'formEditor.elements.TextMixin.editor.validators.EmptyValue.label'
+                    20:
+                      value: 'Alphanumeric'
+                      label: 'formEditor.elements.TextMixin.editor.validators.Alphanumeric.label'
+                    30:
+                      value: 'Text'
+                      label: 'formEditor.elements.TextMixin.editor.validators.Text.label'
+                    40:
+                      value: 'StringLength'
+                      label: 'formEditor.elements.TextMixin.editor.validators.StringLength.label'
+                    50:
+                      value: 'EmailAddress'
+                      label: 'formEditor.elements.TextMixin.editor.validators.EmailAddress.label'
+                    60:
+                      value: 'Integer'
+                      label: 'formEditor.elements.TextMixin.editor.validators.Integer.label'
+                    70:
+                      value: 'Float'
+                      label: 'formEditor.elements.TextMixin.editor.validators.Float.label'
+                    80:
+                      value: 'NumberRange'
+                      label: 'formEditor.elements.TextMixin.editor.validators.NumberRange.label'
+                    90:
+                      value: 'RegularExpression'
+                      label: 'formEditor.elements.TextMixin.editor.validators.RegularExpression.label'
+
+              propertyCollections:
+                validators:
+                  10:
+                    identifier: 'Alphanumeric'
+                    editors:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                      100:
+                        label: 'formEditor.elements.TextMixin.validators.Alphanumeric.editor.header.label'
+                  20:
+                    identifier: 'Text'
+                    editors:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                      100:
+                        label: 'formEditor.elements.TextMixin.validators.Text.editor.header.label'
+                  30:
+                    identifier: 'StringLength'
+                    editors:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        20: 'TYPO3.CMS.Form.mixins.formElementMixins.MinimumMaximumEditorsMixin'
+                      100:
+                        label: 'formEditor.elements.TextMixin.validators.StringLength.editor.header.label'
+                      200:
+                        additionalElementPropertyPaths:
+                          10: 'properties.fluidAdditionalAttributes.minlength'
+                      300:
+                        additionalElementPropertyPaths:
+                          10: 'properties.fluidAdditionalAttributes.maxlength'
+                  40:
+                    identifier: 'EmailAddress'
+                    editors:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                      100:
+                        label: 'formEditor.elements.TextMixin.validators.EmailAddress.editor.header.label'
+                  50:
+                    identifier: 'Integer'
+                    editors:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                      100:
+                        label: 'formEditor.elements.TextMixin.validators.Integer.editor.header.label'
+                  60:
+                    identifier: 'Float'
+                    editors:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                      100:
+                        label: 'formEditor.elements.TextMixin.validators.Float.editor.header.label'
+                  70:
+                    identifier: 'NumberRange'
+                    editors:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        20: 'TYPO3.CMS.Form.mixins.formElementMixins.MinimumMaximumEditorsMixin'
+                      100:
+                        label: 'formEditor.elements.TextMixin.validators.NumberRange.editor.header.label'
+                      200:
+                        additionalElementPropertyPaths:
+                          10: 'properties.fluidAdditionalAttributes.min'
+                      300:
+                        additionalElementPropertyPaths:
+                          10: 'properties.fluidAdditionalAttributes.max'
+                  80:
+                    identifier: 'RegularExpression'
+                    editors:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                      100:
+                        label: 'formEditor.elements.TextMixin.validators.RegularExpression.editor.header.label'
+                      200:
+                        identifier: 'regex'
+                        templateName: 'Inspector-TextEditor'
+                        label: 'formEditor.elements.TextMixin.validators.RegularExpression.editor.regex.label'
+                        fieldExplanationText: 'formEditor.elements.TextMixin.validators.RegularExpression.editor.regex.fieldExplanationText'
+                        propertyPath: 'options.regularExpression'
+                        propertyValidators:
+                          10: 'NotEmpty'
+
+          SelectionMixin:
+            formEditor:
+              predefinedDefaults:
+                properties:
+                  options: []
+              editors:
+                300:
+                  identifier: 'options'
+                  templateName: 'Inspector-PropertyGridEditor'
+                  label: 'formEditor.elements.SelectionMixin.editor.options.label'
+                  propertyPath: 'properties.options'
+                  isSortable: true
+                  enableAddRow: true
+                  enableDeleteRow: true
+                  removeLastAvailableRowFlashMessageTitle: 'formEditor.elements.SelectionMixin.editor.options.removeLastAvailableRowFlashMessageTitle'
+                  removeLastAvailableRowFlashMessageMessage: 'formEditor.elements.SelectionMixin.editor.options.removeLastAvailableRowFlashMessageMessage'
+
+          SingleSelectionMixin:
+            formEditor:
+              editors:
+                300:
+                  shouldShowPreselectedValueColumn: 'single'
+                  multiSelection: false
+
+          MultiSelectionMixin:
+            formEditor:
+              editors:
+                300:
+                  shouldShowPreselectedValueColumn: 'multiple'
+                  multiSelection: true
+                900:
+                  identifier: 'validators'
+                  templateName: 'Inspector-ValidatorsEditor'
+                  label: 'formEditor.elements.MultiSelectionMixin.editor.validators.label'
+                  selectOptions:
+                    10:
+                      value: ''
+                      label: 'formEditor.elements.MultiSelectionMixin.editor.validators.EmptyValue.label'
+                    20:
+                      value: 'Count'
+                      label: 'formEditor.elements.MultiSelectionMixin.editor.validators.Count.label'
+
+              propertyCollections:
+                validators:
+                  10:
+                    identifier: 'Count'
+                    editors:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        20: 'TYPO3.CMS.Form.mixins.formElementMixins.MinimumMaximumEditorsMixin'
+                      100:
+                        label: 'formEditor.elements.MultiSelectionMixin.validators.Count.editor.header.label'
+
+          FileUploadMixin:
+            formEditor:
+              predefinedDefaults:
+                properties:
+                  saveToFileMount: '1:/user_upload/'
+              editors:
+                400:
+                  identifier: 'saveToFileMount'
+                  templateName: 'Inspector-SingleSelectEditor'
+                  label: 'formEditor.elements.FileUploadMixin.editor.saveToFileMount.label'
+                  propertyPath: 'properties.saveToFileMount'
+                  selectOptions:
+                    10:
+                      value: '1:/user_upload/'
+                      label: '1:/user_upload/'
+                900:
+                  identifier: 'validators'
+                  templateName: 'Inspector-ValidatorsEditor'
+                  label: 'formEditor.elements.FileUploadMixin.editor.validators.label'
+                  selectOptions:
+                    10:
+                      value: ''
+                      label: 'formEditor.elements.FileUploadMixin.editor.validators.EmptyValue.label'
+                    20:
+                      value: 'FileSize'
+                      label: 'formEditor.elements.FileUploadMixin.editor.validators.FileSize.label'
+
+              propertyCollections:
+                validators:
+                  10:
+                    identifier: 'FileSize'
+                    editors:
+                      __inheritances:
+                        10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+                        20: 'TYPO3.CMS.Form.mixins.formElementMixins.MinimumMaximumEditorsMixin'
+                      100:
+                        label: 'formEditor.elements.FileUploadMixin.validators.FileSize.editor.header.label'
+                      200:
+                        propertyValidators:
+                          10: 'FileSize'
+                      300:
+                        propertyValidators:
+                          10: 'FileSize'
+
+          formEmailFinisherMixin:
+            editors:
+              __inheritances:
+                10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin'
+              100:
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.header.label'
+              200:
+                identifier: 'subject'
+                templateName: 'Inspector-TextEditor'
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.subject.label'
+                propertyPath: 'options.subject'
+                enableFormelementSelectionButton: true
+                propertyValidators:
+                  10: 'NotEmpty'
+                  20: 'FormElementIdentifierWithinCurlyBracesInclusive'
+              300:
+                identifier: 'recipientAddress'
+                templateName: 'Inspector-TextEditor'
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.recipientAddress.label'
+                propertyPath: 'options.recipientAddress'
+                enableFormelementSelectionButton: true
+                propertyValidatorsMode: 'OR'
+                propertyValidators:
+                  10: 'NaiveEmail'
+                  20: 'FormElementIdentifierWithinCurlyBracesExclusive'
+                fieldExplanationText: 'formEditor.elements.Form.finisher.EmailToSender.editor.recipientAddress.fieldExplanationText'
+              400:
+                identifier: 'recipientName'
+                templateName: 'Inspector-TextEditor'
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.recipientName.label'
+                propertyPath: 'options.recipientName'
+                enableFormelementSelectionButton: true
+                propertyValidators:
+                  10: 'FormElementIdentifierWithinCurlyBracesInclusive'
+                fieldExplanationText: 'formEditor.elements.Form.finisher.EmailToSender.editor.recipientName.fieldExplanationText'
+              500:
+                identifier: 'senderAddress'
+                templateName: 'Inspector-TextEditor'
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.senderAddress.label'
+                propertyPath: 'options.senderAddress'
+                enableFormelementSelectionButton: true
+                propertyValidatorsMode: 'OR'
+                propertyValidators:
+                  10: 'NaiveEmail'
+                  20: 'FormElementIdentifierWithinCurlyBracesExclusive'
+                fieldExplanationText: 'formEditor.elements.Form.finisher.EmailToSender.editor.senderAddress.fieldExplanationText'
+              600:
+                identifier: 'senderName'
+                templateName: 'Inspector-TextEditor'
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.senderName.label'
+                propertyPath: 'options.senderName'
+                enableFormelementSelectionButton: true
+                propertyValidators:
+                  10: 'FormElementIdentifierWithinCurlyBracesInclusive'
+                fieldExplanationText: 'formEditor.elements.Form.finisher.EmailToSender.editor.senderName.fieldExplanationText'
+              700:
+                identifier: 'replyToAddress'
+                templateName: 'Inspector-TextEditor'
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.replyToAddress.label'
+                propertyPath: 'options.replyToAddress'
+                enableFormelementSelectionButton: true
+                propertyValidatorsMode: 'OR'
+                propertyValidators:
+                  10: 'NaiveEmailOrEmpty'
+                  20: 'FormElementIdentifierWithinCurlyBracesExclusive'
+              800:
+                identifier: 'carbonCopyAddress'
+                templateName: 'Inspector-TextEditor'
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.carbonCopyAddress.label'
+                propertyPath: 'options.carbonCopyAddress'
+                enableFormelementSelectionButton: true
+                propertyValidatorsMode: 'OR'
+                propertyValidators:
+                  10: 'NaiveEmailOrEmpty'
+                  20: 'FormElementIdentifierWithinCurlyBracesExclusive'
+              900:
+                identifier: 'blindCarbonCopyAddress'
+                templateName: 'Inspector-TextEditor'
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.blindCarbonCopyAddress.label'
+                propertyPath: 'options.blindCarbonCopyAddress'
+                enableFormelementSelectionButton: true
+                propertyValidatorsMode: 'OR'
+                propertyValidators:
+                  10: 'NaiveEmailOrEmpty'
+                  20: 'FormElementIdentifierWithinCurlyBracesExclusive'
+              1000:
+                identifier: 'format'
+                templateName: 'Inspector-SingleSelectEditor'
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.format.label'
+                propertyPath: 'options.format'
+                selectOptions:
+                  10:
+                    value: 'plaintext'
+                    label: 'formEditor.elements.Form.finisher.EmailToSender.editor.format.1'
+                  20:
+                    value: 'html'
+                    label: 'formEditor.elements.Form.finisher.EmailToSender.editor.format.2'
+              1100:
+                identifier: 'attachUploads'
+                templateName: 'Inspector-CheckboxEditor'
+                label: 'formEditor.elements.Form.finisher.EmailToSender.editor.attachUploads.label'
+                propertyPath: 'options.attachUploads'
diff --git a/typo3/sysext/form/Configuration/Yaml/FormEngineSetup.yaml b/typo3/sysext/form/Configuration/Yaml/FormEngineSetup.yaml
new file mode 100644 (file)
index 0000000..89221a1
--- /dev/null
@@ -0,0 +1,131 @@
+TYPO3:
+  CMS:
+    Form:
+      prototypes:
+        standard:
+          formEngine:
+            translationFile: 'EXT:form/Resources/Private/Language/Database.xlf'
+
+          ########### TCE Forms CONFIGURATION ###########
+
+          ### FINISHERS ###
+          finishersDefinition:
+            EmailToSender:
+              FormEngine:
+                __inheritances:
+                  10: 'TYPO3.CMS.Form.mixins.FormEngineEmailMixin'
+
+            EmailToReceiver:
+              FormEngine:
+                __inheritances:
+                  10: 'TYPO3.CMS.Form.mixins.FormEngineEmailMixin'
+                label: 'tt_content.finishersDefinition.EmailToReceiver.label'
+                elements:
+                  subject:
+                    label: 'tt_content.finishersDefinition.EmailToReceiver.subject.label'
+                  recipientAddress:
+                    label: 'tt_content.finishersDefinition.EmailToReceiver.recipientAddress.label'
+                  recipientName:
+                    label: 'tt_content.finishersDefinition.EmailToReceiver.recipientName.label'
+                  senderAddress:
+                    label: 'tt_content.finishersDefinition.EmailToReceiver.senderAddress.label'
+                  senderName:
+                    label: 'tt_content.finishersDefinition.EmailToReceiver.senderName.label'
+                  replyToAddress:
+                    label: 'tt_content.finishersDefinition.EmailToReceiver.replyToAddress.label'
+                  carbonCopyAddress:
+                    label: 'tt_content.finishersDefinition.EmailToReceiver.carbonCopyAddress.label'
+                  blindCarbonCopyAddress:
+                    label: 'tt_content.finishersDefinition.EmailToReceiver.blindCarbonCopyAddress.label'
+                  format:
+                    label: 'tt_content.finishersDefinition.EmailToReceiver.format.label'
+                  translation:
+                    language:
+                      label: 'tt_content.finishersDefinition.EmailToReceiver.language.label'
+                      config:
+                        type: select
+                        renderType: 'selectSingle'
+                        minitems: 1
+                        maxitems: 1
+                        size: 1
+                        items:
+                          10:
+                            0: 'tt_content.finishersDefinition.EmailToReceiver.language.1'
+                            1: 'default'
+
+            Redirect:
+              FormEngine:
+                label: 'tt_content.finishersDefinition.Redirect.label'
+                elements:
+                  pageUid:
+                    label: 'tt_content.finishersDefinition.Redirect.pageUid.label'
+                    config:
+                      type: 'group'
+                      internal_type: 'db'
+                      allowed: 'pages'
+                      size: 1
+                      minitems: 1
+                      maxitems: 1
+                      fieldWizard:
+                        recordsOverview:
+                          disabled: 1
+                  additionalParameters:
+                    label: 'tt_content.finishersDefinition.Redirect.additionalParameters.label'
+                    config:
+                      type: 'input'
+
+      ########### MIXINS ###########
+      mixins:
+        FormEngineEmailMixin:
+          label: 'tt_content.finishersDefinition.EmailToSender.label'
+          elements:
+            subject:
+              label: 'tt_content.finishersDefinition.EmailToSender.subject.label'
+              config:
+                type: 'input'
+                eval: 'required'
+            recipientAddress:
+              label: 'tt_content.finishersDefinition.EmailToSender.recipientAddress.label'
+              config:
+                type: 'input'
+                eval: 'required'
+            recipientName:
+              label: 'tt_content.finishersDefinition.EmailToSender.recipientName.label'
+              config:
+                type: 'input'
+            senderAddress:
+              label: 'tt_content.finishersDefinition.EmailToSender.senderAddress.label'
+              config:
+                type: 'input'
+                eval: 'required'
+            senderName:
+              label: 'tt_content.finishersDefinition.EmailToSender.senderName.label'
+              config:
+                type: 'input'
+            replyToAddress:
+              label: 'tt_content.finishersDefinition.EmailToSender.replyToAddress.label'
+              config:
+                type: 'input'
+            carbonCopyAddress:
+              label: 'tt_content.finishersDefinition.EmailToSender.carbonCopyAddress.label'
+              config:
+                type: 'input'
+            blindCarbonCopyAddress:
+              label: 'tt_content.finishersDefinition.EmailToSender.blindCarbonCopyAddress.label'
+              config:
+                type: 'input'
+            format:
+              label: 'tt_content.finishersDefinition.EmailToSender.format.label'
+              config:
+                type: select
+                renderType: 'selectSingle'
+                minitems: 1
+                maxitems: 1
+                size: 1
+                items:
+                  10:
+                    0: 'tt_content.finishersDefinition.EmailToSender.format.1'
+                    1: 'html'
+                  20:
+                    0: 'tt_content.finishersDefinition.EmailToSender.format.2'
+                    1: 'plaintext'
diff --git a/typo3/sysext/form/Configuration/Yaml/FormSetup.yaml b/typo3/sysext/form/Configuration/Yaml/FormSetup.yaml
deleted file mode 100644 (file)
index 339002c..0000000
+++ /dev/null
@@ -1,1747 +0,0 @@
-TYPO3:
-  CMS:
-    Form:
-
-      ########### FORM MANAGER CONFIGURATION (backend) ###########
-      formManager:
-        controller:
-          deleteAction:
-            errorMessage: formManagerController.deleteAction.error.body
-            errorTitle: formManagerController.deleteAction.error.title
-        dynamicRequireJsModules:
-          app: TYPO3/CMS/Form/Backend/FormManager
-          viewModel: TYPO3/CMS/Form/Backend/FormManager/ViewModel
-        javaScriptTranslationFile: 'EXT:form/Resources/Private/Language/locallang_formManager_javascript.xlf'
-        selectablePrototypesConfiguration:
-          100:
-            identifier: standard
-            label: formManager.selectablePrototypesConfiguration.standard.label
-            newFormTemplates:
-              100:
-                label: formManager.selectablePrototypesConfiguration.standard.newFormTemplates.blankForm.label
-                templatePath: 'EXT:form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/BlankForm.yaml'
-              200:
-                label: formManager.selectablePrototypesConfiguration.standard.newFormTemplates.simpleContactForm.label
-                templatePath: 'EXT:form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/SimpleContactForm.yaml'
-        stylesheets:
-          100: 'EXT:form/Resources/Public/Css/form.css'
-        translationFile: 'EXT:form/Resources/Private/Language/Database.xlf'
-
-      ########### MIXINS (frontend / backend) ###########
-      mixins:
-
-        ########### FORM ENGINE MIXINS (backend) ###########
-        FormEngineEmailMixin:
-          elements:
-            blindCarbonCopyAddress:
-              config:
-                type: input
-              label: tt_content.finishersDefinition.EmailToSender.blindCarbonCopyAddress.label
-            carbonCopyAddress:
-              config:
-                type: input
-              label: tt_content.finishersDefinition.EmailToSender.carbonCopyAddress.label
-            format:
-              config:
-                items:
-                  10:
-                    - tt_content.finishersDefinition.EmailToSender.format.1
-                    - html
-                  20:
-                    - tt_content.finishersDefinition.EmailToSender.format.2
-                    - plaintext
-                maxitems: 1
-                minitems: 1
-                renderType: selectSingle
-                size: 1
-                type: select
-              label: tt_content.finishersDefinition.EmailToSender.format.label
-            recipientAddress:
-              config:
-                eval: required
-                type: input
-              label: tt_content.finishersDefinition.EmailToSender.recipientAddress.label
-            recipientName:
-              config:
-                type: input
-              label: tt_content.finishersDefinition.EmailToSender.recipientName.label
-            replyToAddress:
-              config:
-                type: input
-              label: tt_content.finishersDefinition.EmailToSender.replyToAddress.label
-            senderAddress:
-              config:
-                eval: required
-                type: input
-              label: tt_content.finishersDefinition.EmailToSender.senderAddress.label
-            senderName:
-              config:
-                type: input
-              label: tt_content.finishersDefinition.EmailToSender.senderName.label
-            subject:
-              config:
-                eval: required
-                type: input
-              label: tt_content.finishersDefinition.EmailToSender.subject.label
-          label: tt_content.finishersDefinition.EmailToSender.label
-
-        ########### FINISHER MIXINS (frontend) ###########
-        finishersEmailMixin:
-          implementationClassName: TYPO3\CMS\Form\Domain\Finishers\EmailFinisher
-          options:
-            templateName: '{@format}.html'
-            templateRootPaths:
-              10: 'EXT:form/Resources/Private/Frontend/Templates/Finishers/Email/'
-
-        ########### FORM ELEMENT MIXINS (frontend / backend) ###########
-        formElementMixins:
-          # (backend)
-          BaseCollectionEditorsMixin:
-            __inheritances:
-              10: TYPO3.CMS.Form.mixins.formElementMixins.RemoveButtonMixin
-            100:
-              identifier: header
-              label: ''
-              templateName: Inspector-CollectionElementHeaderEditor
-
-          # (backend)
-          BaseFormElementMixin:
-            formEditor:
-              editors:
-                100:
-                  identifier: header
-                  templateName: Inspector-FormElementHeaderEditor
-                200:
-                  identifier: label
-                  label: formEditor.elements.BaseFormElementMixin.editor.label.label
-                  propertyPath: label
-                  templateName: Inspector-TextEditor
-              predefinedDefaults: {  }
-
-          # (frontend / backend)
-          FileUploadMixin:
-            __inheritances:
-              10: TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin
-            formEditor:
-              editors:
-                400:
-                  identifier: saveToFileMount
-                  label: formEditor.elements.FileUploadMixin.editor.saveToFileMount.label
-                  propertyPath: properties.saveToFileMount
-                  selectOptions:
-                    10:
-                      label: '1:/user_upload/'
-                      value: '1:/user_upload/'
-                  templateName: Inspector-SingleSelectEditor
-              predefinedDefaults:
-                properties:
-                  saveToFileMount: '1:/user_upload/'
-            implementationClassName: TYPO3\CMS\Form\Domain\Model\FormElements\FileUpload
-            properties:
-              saveToFileMount: '1:/user_upload/'
-
-          # (frontend / backend)
-          FormElementMixin:
-            __inheritances:
-              10: TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin
-            formEditor:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.RemovableFormElementMixin
-              editors:
-                200:
-                  label: formEditor.elements.FormElement.editor.label.label
-                700:
-                  configurationOptions:
-                    numbersOfColumnsToUse:
-                      fieldExplanationText: formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.numbersOfColumnsToUse.fieldExplanationText
-                      label: formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.numbersOfColumnsToUse.label
-                      propertyPath: 'properties.gridColumnClassAutoConfiguration.viewPorts.{@viewPortIdentifier}.numbersOfColumnsToUse'
-                    viewPorts:
-                      10:
-                        label: formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.xs.label
-                        viewPortIdentifier: xs
-                      20:
-                        label: formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.sm.label
-                        viewPortIdentifier: sm
-                      30:
-                        label: formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.md.label
-                        viewPortIdentifier: md
-                      40:
-                        label: formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.lg.label
-                        viewPortIdentifier: lg
-                  identifier: gridColumnViewPortConfiguration
-                  label: formEditor.elements.FormElement.editor.gridColumnViewPortConfiguration.label
-                  templateName: Inspector-GridColumnViewPortConfigurationEditor
-                800:
-                  identifier: requiredValidator
-                  label: formEditor.elements.FormElement.editor.requiredValidator.label
-                  propertyPath: properties.fluidAdditionalAttributes.required
-                  propertyValue: required
-                  templateName: Inspector-RequiredValidatorEditor
-                  validatorIdentifier: NotEmpty
-            implementationClassName: TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement
-            properties:
-              containerClassAttribute: input
-              elementClassAttribute: ''
-              elementErrorClassAttribute: error
-
-          # (backend)
-          MinimumMaximumEditorsMixin:
-            200:
-              identifier: minimum
-              label: formEditor.elements.MinimumMaximumEditorsMixin.editor.minimum.label
-              propertyPath: options.minimum
-              propertyValidators:
-                10: Integer
-              templateName: Inspector-TextEditor
-            300:
-              identifier: maximum
-              label: formEditor.elements.MinimumMaximumEditorsMixin.editor.maximum.label
-              propertyPath: options.maximum
-              propertyValidators:
-                10: Integer
-              templateName: Inspector-TextEditor
-
-          # (frontend / backend)
-          MultiSelectionMixin:
-            __inheritances:
-              10: TYPO3.CMS.Form.mixins.formElementMixins.SelectionMixin
-            formEditor:
-              editors:
-                300:
-                  multiSelection: true
-                  shouldShowPreselectedValueColumn: multiple
-                900:
-                  identifier: validators
-                  label: formEditor.elements.MultiSelectionMixin.editor.validators.label
-                  selectOptions:
-                    10:
-                      label: formEditor.elements.MultiSelectionMixin.editor.validators.EmptyValue.label
-                      value: ''
-                    20:
-                      label: formEditor.elements.MultiSelectionMixin.editor.validators.Count.label
-                      value: Count
-                  templateName: Inspector-ValidatorsEditor
-              propertyCollections:
-                validators:
-                  10:
-                    editors:
-                      __inheritances:
-                        10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                        20: TYPO3.CMS.Form.mixins.formElementMixins.MinimumMaximumEditorsMixin
-                      100:
-                        label: formEditor.elements.MultiSelectionMixin.validators.Count.editor.header.label
-                    identifier: Count
-
-          # (frontend / backend)
-          ReadOnlyFormElementMixin:
-            __inheritances:
-              10: TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin
-            formEditor:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.RemovableFormElementMixin
-              editors:
-                200:
-                  label: formEditor.elements.ReadOnlyFormElement.editor.label.label
-            implementationClassName: TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement
-            renderingOptions:
-              _isReadOnlyFormElement: true
-
-          # (backend)
-          RemovableFormElementMixin:
-            editors:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.RemoveButtonMixin
-
-          # (backend)
-          RemoveButtonMixin:
-            9999:
-              identifier: removeButton
-              templateName: Inspector-RemoveElementEditor
-
-          # (frontend / backend)
-          SelectionMixin:
-            __inheritances:
-              10: TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin
-            formEditor:
-              editors:
-                300:
-                  enableAddRow: true
-                  enableDeleteRow: true
-                  identifier: options
-                  isSortable: true
-                  label: formEditor.elements.SelectionMixin.editor.options.label
-                  propertyPath: properties.options
-                  removeLastAvailableRowFlashMessageMessage: formEditor.elements.SelectionMixin.editor.options.removeLastAvailableRowFlashMessageMessage
-                  removeLastAvailableRowFlashMessageTitle: formEditor.elements.SelectionMixin.editor.options.removeLastAvailableRowFlashMessageTitle
-                  templateName: Inspector-PropertyGridEditor
-              predefinedDefaults:
-                properties:
-                  options: {  }
-
-          # (frontend / backend)
-          SingleSelectionMixin:
-            __inheritances:
-              10: TYPO3.CMS.Form.mixins.formElementMixins.SelectionMixin
-            formEditor:
-              editors:
-                300:
-                  multiSelection: false
-                  shouldShowPreselectedValueColumn: single
-
-          # (frontend / backend)
-          TextMixin:
-            __inheritances:
-              10: TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin
-            formEditor:
-              editors:
-                400:
-                  doNotSetIfPropertyValueIsEmpty: true
-                  identifier: placeholder
-                  label: formEditor.elements.TextMixin.editor.placeholder.label
-                  propertyPath: properties.fluidAdditionalAttributes.placeholder
-                  templateName: Inspector-TextEditor
-                500:
-                  identifier: defaultValue
-                  label: formEditor.elements.TextMixin.editor.defaultValue.label
-                  propertyPath: defaultValue
-                  templateName: Inspector-TextEditor
-                900:
-                  identifier: validators
-                  label: formEditor.elements.TextMixin.editor.validators.label
-                  selectOptions:
-                    10:
-                      label: formEditor.elements.TextMixin.editor.validators.EmptyValue.label
-                      value: ''
-                    20:
-                      label: formEditor.elements.TextMixin.editor.validators.Alphanumeric.label
-                      value: Alphanumeric
-                    30:
-                      label: formEditor.elements.TextMixin.editor.validators.Text.label
-                      value: Text
-                    40:
-                      label: formEditor.elements.TextMixin.editor.validators.StringLength.label
-                      value: StringLength
-                    50:
-                      label: formEditor.elements.TextMixin.editor.validators.EmailAddress.label
-                      value: EmailAddress
-                    60:
-                      label: formEditor.elements.TextMixin.editor.validators.Integer.label
-                      value: Integer
-                    70:
-                      label: formEditor.elements.TextMixin.editor.validators.Float.label
-                      value: Float
-                    80:
-                      label: formEditor.elements.TextMixin.editor.validators.NumberRange.label
-                      value: NumberRange
-                    90:
-                      label: formEditor.elements.TextMixin.editor.validators.RegularExpression.label
-                      value: RegularExpression
-                  templateName: Inspector-ValidatorsEditor
-              predefinedDefaults:
-                defaultValue: ''
-              propertyCollections:
-                validators:
-                  10:
-                    editors:
-                      __inheritances:
-                        10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                      100:
-                        label: formEditor.elements.TextMixin.validators.Alphanumeric.editor.header.label
-                    identifier: Alphanumeric
-                  20:
-                    editors:
-                      __inheritances:
-                        10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                      100:
-                        label: formEditor.elements.TextMixin.validators.Text.editor.header.label
-                    identifier: Text
-                  30:
-                    editors:
-                      __inheritances:
-                        10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                        20: TYPO3.CMS.Form.mixins.formElementMixins.MinimumMaximumEditorsMixin
-                      100:
-                        label: formEditor.elements.TextMixin.validators.StringLength.editor.header.label
-                      200:
-                        additionalElementPropertyPaths:
-                          10: properties.fluidAdditionalAttributes.minlength
-                      300:
-                        additionalElementPropertyPaths:
-                          10: properties.fluidAdditionalAttributes.maxlength
-                    identifier: StringLength
-                  40:
-                    editors:
-                      __inheritances:
-                        10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                      100:
-                        label: formEditor.elements.TextMixin.validators.EmailAddress.editor.header.label
-                    identifier: EmailAddress
-                  50:
-                    editors:
-                      __inheritances:
-                        10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                      100:
-                        label: formEditor.elements.TextMixin.validators.Integer.editor.header.label
-                    identifier: Integer
-                  60:
-                    editors:
-                      __inheritances:
-                        10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                      100:
-                        label: formEditor.elements.TextMixin.validators.Float.editor.header.label
-                    identifier: Float
-                  70:
-                    editors:
-                      __inheritances:
-                        10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                        20: TYPO3.CMS.Form.mixins.formElementMixins.MinimumMaximumEditorsMixin
-                      100:
-                        label: formEditor.elements.TextMixin.validators.NumberRange.editor.header.label
-                      200:
-                        additionalElementPropertyPaths:
-                          10: properties.fluidAdditionalAttributes.min
-                      300:
-                        additionalElementPropertyPaths:
-                          10: properties.fluidAdditionalAttributes.max
-                    identifier: NumberRange
-                  80:
-                    editors:
-                      __inheritances:
-                        10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                      100:
-                        label: formEditor.elements.TextMixin.validators.RegularExpression.editor.header.label
-                      200:
-                        fieldExplanationText: formEditor.elements.TextMixin.validators.RegularExpression.editor.regex.fieldExplanationText
-                        identifier: regex
-                        label: formEditor.elements.TextMixin.validators.RegularExpression.editor.regex.label
-                        propertyPath: options.regularExpression
-                        propertyValidators:
-                          10: NotEmpty
-                        templateName: Inspector-TextEditor
-                    identifier: RegularExpression
-
-          # (backend)
-          formEmailFinisherMixin:
-            editors:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-              100:
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.header.label
-              200:
-                enableFormelementSelectionButton: true
-                identifier: subject
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.subject.label
-                propertyPath: options.subject
-                propertyValidators:
-                  10: NotEmpty
-                  20: FormElementIdentifierWithinCurlyBracesInclusive
-                templateName: Inspector-TextEditor
-              300:
-                enableFormelementSelectionButton: true
-                fieldExplanationText: formEditor.elements.Form.finisher.EmailToSender.editor.recipientAddress.fieldExplanationText
-                identifier: recipientAddress
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.recipientAddress.label
-                propertyPath: options.recipientAddress
-                propertyValidators:
-                  10: NaiveEmail
-                  20: FormElementIdentifierWithinCurlyBracesExclusive
-                propertyValidatorsMode: OR
-                templateName: Inspector-TextEditor
-              400:
-                enableFormelementSelectionButton: true
-                fieldExplanationText: formEditor.elements.Form.finisher.EmailToSender.editor.recipientName.fieldExplanationText
-                identifier: recipientName
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.recipientName.label
-                propertyPath: options.recipientName
-                propertyValidators:
-                  10: FormElementIdentifierWithinCurlyBracesInclusive
-                templateName: Inspector-TextEditor
-              500:
-                enableFormelementSelectionButton: true
-                fieldExplanationText: formEditor.elements.Form.finisher.EmailToSender.editor.senderAddress.fieldExplanationText
-                identifier: senderAddress
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.senderAddress.label
-                propertyPath: options.senderAddress
-                propertyValidators:
-                  10: NaiveEmail
-                  20: FormElementIdentifierWithinCurlyBracesExclusive
-                propertyValidatorsMode: OR
-                templateName: Inspector-TextEditor
-              600:
-                enableFormelementSelectionButton: true
-                fieldExplanationText: formEditor.elements.Form.finisher.EmailToSender.editor.senderName.fieldExplanationText
-                identifier: senderName
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.senderName.label
-                propertyPath: options.senderName
-                propertyValidators:
-                  10: FormElementIdentifierWithinCurlyBracesInclusive
-                templateName: Inspector-TextEditor
-              700:
-                enableFormelementSelectionButton: true
-                identifier: replyToAddress
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.replyToAddress.label
-                propertyPath: options.replyToAddress
-                propertyValidators:
-                  10: NaiveEmailOrEmpty
-                  20: FormElementIdentifierWithinCurlyBracesExclusive
-                propertyValidatorsMode: OR
-                templateName: Inspector-TextEditor
-              800:
-                enableFormelementSelectionButton: true
-                identifier: carbonCopyAddress
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.carbonCopyAddress.label
-                propertyPath: options.carbonCopyAddress
-                propertyValidators:
-                  10: NaiveEmailOrEmpty
-                  20: FormElementIdentifierWithinCurlyBracesExclusive
-                propertyValidatorsMode: OR
-                templateName: Inspector-TextEditor
-              900:
-                enableFormelementSelectionButton: true
-                identifier: blindCarbonCopyAddress
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.blindCarbonCopyAddress.label
-                propertyPath: options.blindCarbonCopyAddress
-                propertyValidators:
-                  10: NaiveEmailOrEmpty
-                  20: FormElementIdentifierWithinCurlyBracesExclusive
-                propertyValidatorsMode: OR
-                templateName: Inspector-TextEditor
-              1000:
-                identifier: format
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.format.label
-                propertyPath: options.format
-                selectOptions:
-                  10:
-                    label: formEditor.elements.Form.finisher.EmailToSender.editor.format.1
-                    value: plaintext
-                  20:
-                    label: formEditor.elements.Form.finisher.EmailToSender.editor.format.2
-                    value: html
-                templateName: Inspector-SingleSelectEditor
-              1100:
-                identifier: attachUploads
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.attachUploads.label
-                propertyPath: options.attachUploads
-                templateName: Inspector-CheckboxEditor
-
-        ########### TRANSLATION MIXINS (frontend) ###########
-        translationSettingsMixin:
-          translation:
-            translationFile: 'EXT:form/Resources/Private/Language/locallang.xlf'
-
-      ########### PERSISTENCE MANAGER CONFIGURATION (frontend / backend) ###########
-      persistenceManager:
-        allowDeleteFromExtensionPaths: false
-        allowSaveToExtensionPaths: false
-        allowedFileMounts:
-          10: '1:/form_definitions/'
-          20: '1:/user_upload/'
-
-      ########### PROTOTYPES (frontend / backend) ###########
-      prototypes:
-        standard:
-
-          ########### FINISHERS ###########
-          finishersDefinition:
-            Closure:
-              formEditor:
-                iconIdentifier: t3-form-icon-finisher
-                label: formEditor.elements.Form.finisher.Closure.editor.header.label
-                predefinedDefaults:
-                  options:
-                    closure: ''
-              implementationClassName: TYPO3\CMS\Form\Domain\Finishers\ClosureFinisher
-
-            Confirmation:
-              formEditor:
-                iconIdentifier: t3-form-icon-finisher
-                label: formEditor.elements.Form.finisher.Confirmation.editor.header.label
-                predefinedDefaults:
-                  options:
-                    contentElementUid: ''
-                    message: ''
-              implementationClassName: TYPO3\CMS\Form\Domain\Finishers\ConfirmationFinisher
-
-            DeleteUploads:
-              formEditor:
-                iconIdentifier: t3-form-icon-finisher
-                label: formEditor.elements.Form.finisher.DeleteUploads.editor.header.label
-              implementationClassName: TYPO3\CMS\Form\Domain\Finishers\DeleteUploadsFinisher
-
-            EmailToReceiver:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.finishersEmailMixin
-
-              # (backend)
-              FormEngine:
-                __inheritances:
-                  10: TYPO3.CMS.Form.mixins.FormEngineEmailMixin
-                elements:
-                  blindCarbonCopyAddress:
-                    label: tt_content.finishersDefinition.EmailToReceiver.blindCarbonCopyAddress.label
-                  carbonCopyAddress:
-                    label: tt_content.finishersDefinition.EmailToReceiver.carbonCopyAddress.label
-                  format:
-                    label: tt_content.finishersDefinition.EmailToReceiver.format.label
-                  recipientAddress:
-                    label: tt_content.finishersDefinition.EmailToReceiver.recipientAddress.label
-                  recipientName:
-                    label: tt_content.finishersDefinition.EmailToReceiver.recipientName.label
-                  replyToAddress:
-                    label: tt_content.finishersDefinition.EmailToReceiver.replyToAddress.label
-                  senderAddress:
-                    label: tt_content.finishersDefinition.EmailToReceiver.senderAddress.label
-                  senderName:
-                    label: tt_content.finishersDefinition.EmailToReceiver.senderName.label
-                  subject:
-                    label: tt_content.finishersDefinition.EmailToReceiver.subject.label
-                  translation:
-                    language:
-                      config:
-                        items:
-                          10:
-                            - tt_content.finishersDefinition.EmailToReceiver.language.1
-                            - default
-                        maxitems: 1
-                        minitems: 1
-                        renderType: selectSingle
-                        size: 1
-                        type: select
-                      label: tt_content.finishersDefinition.EmailToReceiver.language.label
-                label: tt_content.finishersDefinition.EmailToReceiver.label
-
-              # (backend)
-              formEditor:
-                iconIdentifier: t3-form-icon-finisher
-                label: formEditor.elements.Form.finisher.EmailToReceiver.editor.header.label
-                predefinedDefaults:
-                  options:
-                    attachUploads: true
-                    blindCarbonCopyAddress: ''
-                    carbonCopyAddress: ''
-                    format: html
-                    recipientAddress: ''
-                    recipientName: ''
-                    replyToAddress: ''
-                    senderAddress: ''
-                    senderName: ''
-                    subject: ''
-                    translation:
-                      language: ''
-
-            EmailToSender:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.finishersEmailMixin
-              # (backend)
-              FormEngine:
-                __inheritances:
-                  10: TYPO3.CMS.Form.mixins.FormEngineEmailMixin
-
-              # (backend)
-              formEditor:
-                iconIdentifier: t3-form-icon-finisher
-                label: formEditor.elements.Form.finisher.EmailToSender.editor.header.label
-                predefinedDefaults:
-                  options:
-                    attachUploads: true
-                    blindCarbonCopyAddress: ''
-                    carbonCopyAddress: ''
-                    format: html
-                    recipientAddress: ''
-                    recipientName: ''
-                    replyToAddress: ''
-                    senderAddress: ''
-                    senderName: ''
-                    subject: ''
-
-            FlashMessage:
-              # (backend)
-              formEditor:
-                iconIdentifier: t3-form-icon-finisher
-                label: formEditor.elements.Form.finisher.FlashMessage.editor.header.label
-                predefinedDefaults:
-                  options:
-                    messageArguments: ''
-                    messageBody: ''
-                    messageCode: 0
-                    messageTitle: ''
-                    severity: 0
-              implementationClassName: TYPO3\CMS\Form\Domain\Finishers\FlashMessageFinisher
-
-            Redirect:
-              # (backend)
-              FormEngine:
-                elements:
-                  additionalParameters:
-                    config:
-                      type: input
-                    label: tt_content.finishersDefinition.Redirect.additionalParameters.label
-                  pageUid:
-                    config:
-                      allowed: pages
-                      fieldWizard:
-                        recordsOverview:
-                          disabled: 1
-                      internal_type: db
-                      maxitems: 1
-                      minitems: 1
-                      size: 1
-                      type: group
-                    label: tt_content.finishersDefinition.Redirect.pageUid.label
-                label: tt_content.finishersDefinition.Redirect.label
-
-              # (backend)
-              formEditor:
-                iconIdentifier: t3-form-icon-finisher
-                label: formEditor.elements.Form.finisher.Redirect.editor.header.label
-                predefinedDefaults:
-                  options:
-                    additionalParameters: ''
-                    pageUid: ''
-              implementationClassName: TYPO3\CMS\Form\Domain\Finishers\RedirectFinisher
-
-            SaveToDatabase:
-              # (backend)
-              formEditor:
-                iconIdentifier: t3-form-icon-finisher
-                label: formEditor.elements.Form.finisher.SaveToDatabase.editor.header.label
-                predefinedDefaults:
-                  options: {  }
-              implementationClassName: TYPO3\CMS\Form\Domain\Finishers\SaveToDatabaseFinisher
-
-          ########### FORM EDITOR CONFIGURATION (backend) ###########
-          formEditor:
-            addInlineSettings: {  }
-            dynamicRequireJsModules:
-              app: TYPO3/CMS/Form/Backend/FormEditor
-              mediator: TYPO3/CMS/Form/Backend/FormEditor/Mediator
-              viewModel: TYPO3/CMS/Form/Backend/FormEditor/ViewModel
-
-            formEditorFluidConfiguration:
-              layoutRootPaths:
-                10: 'EXT:form/Resources/Private/Backend/Layouts/FormEditor/'
-              partialRootPaths:
-                10: 'EXT:form/Resources/Private/Backend/Partials/FormEditor/'
-              templatePathAndFilename: 'EXT:form/Resources/Private/Backend/Templates/FormEditor/InlineTemplates.html'
-
-            formEditorPartials:
-              FormElement-AdvancedPassword: Stage/SimpleTemplate
-              FormElement-Checkbox: Stage/SimpleTemplate
-              FormElement-ContentElement: Stage/ContentElement
-              FormElement-DatePicker: Stage/SimpleTemplate
-              FormElement-Email: Stage/SimpleTemplate
-              FormElement-Fieldset: Stage/Fieldset
-              FormElement-FileUpload: Stage/FileUploadTemplate
-              FormElement-GridContainer: Stage/Fieldset
-              FormElement-GridRow: Stage/Fieldset
-              FormElement-Hidden: Stage/SimpleTemplate
-              FormElement-ImageUpload: Stage/FileUploadTemplate
-              FormElement-MultiCheckbox: Stage/SelectTemplate
-              FormElement-MultiSelect: Stage/SelectTemplate
-              FormElement-Number: Stage/SimpleTemplate
-              FormElement-Page: Stage/Page
-              FormElement-Password: Stage/SimpleTemplate
-              FormElement-RadioButton: Stage/SelectTemplate
-              FormElement-SingleSelect: Stage/SelectTemplate
-              FormElement-StaticText: Stage/StaticText
-              FormElement-SummaryPage: Stage/SummaryPage
-              FormElement-Telephone: Stage/SimpleTemplate
-              FormElement-Text: Stage/SimpleTemplate
-              FormElement-Textarea: Stage/SimpleTemplate
-              FormElement-Url: Stage/SimpleTemplate
-              FormElement-_ElementToolbar: Stage/_ElementToolbar
-              FormElement-_UnknownElement: Stage/_UnknownElement
-              Inspector-CheckboxEditor: Inspector/CheckboxEditor
-              Inspector-CollectionElementHeaderEditor: Inspector/CollectionElementHeaderEditor
-              Inspector-FinishersEditor: Inspector/FinishersEditor
-              Inspector-FormElementHeaderEditor: Inspector/FormElementHeaderEditor
-              Inspector-GridColumnViewPortConfigurationEditor: Inspector/GridColumnViewPortConfigurationEditor
-              Inspector-MultiSelectEditor: Inspector/MultiSelectEditor
-              Inspector-PropertyGridEditor: Inspector/PropertyGridEditor
-              Inspector-RemoveElementEditor: Inspector/RemoveElementEditor
-              Inspector-RequiredValidatorEditor: Inspector/RequiredValidatorEditor
-              Inspector-SingleSelectEditor: Inspector/SingleSelectEditor
-              Inspector-TextEditor: Inspector/TextEditor
-              Inspector-TextareaEditor: Inspector/TextareaEditor
-              Inspector-Typo3WinBrowserEditor: Inspector/Typo3WinBrowserEditor
-              Inspector-ValidatorsEditor: Inspector/ValidatorsEditor
-              Modal-InsertElements: Modals/InsertElements
-              Modal-InsertPages: Modals/InsertPages
-              Modal-ValidationErrors: Modals/ValidationErrors
-
-            formElementGroups:
-              container:
-                label: formEditor.formElementGroups.container.label
-              custom:
-                label: formEditor.formElementGroups.custom.label
-              html5:
-                label: formEditor.formElementGroups.html5.label
-              input:
-                label: formEditor.formElementGroups.input.label
-              page:
-                label: formEditor.formElementGroups.page.label
-              select:
-                label: formEditor.formElementGroups.select.label
-
-            formElementPropertyValidatorsDefinition:
-              FormElementIdentifierWithinCurlyBracesExclusive:
-                errorMessage: formEditor.formElementPropertyValidatorsDefinition.FormElementIdentifierWithinCurlyBraces.label
-              FormElementIdentifierWithinCurlyBracesInclusive:
-                errorMessage: formEditor.formElementPropertyValidatorsDefinition.FormElementIdentifierWithinCurlyBraces.label
-              Integer:
-                errorMessage: formEditor.formElementPropertyValidatorsDefinition.Integer.label
-              NaiveEmail:
-                errorMessage: formEditor.formElementPropertyValidatorsDefinition.NaiveEmail.label
-              NaiveEmailOrEmpty:
-                errorMessage: formEditor.formElementPropertyValidatorsDefinition.NaiveEmail.label
-              NotEmpty:
-                errorMessage: formEditor.formElementPropertyValidatorsDefinition.NotEmpty.label
-
-            maximumUndoSteps: 10
-            stylesheets:
-              200: 'EXT:form/Resources/Public/Css/form.css'
-            translationFile: 'EXT:form/Resources/Private/Language/Database.xlf'
-
-          ########### FORM ELEMENT DEFINITIONS (frontend / backend) ###########
-          formElementsDefinition:
-
-            ########### AdvancedPassword ###########
-            AdvancedPassword:
-              __inheritances:
-                10: TYPO3.CMS.Form.prototypes.standard.formElementsDefinition.Password
-              formEditor:
-                editors:
-                  300:
-                    identifier: confirmationLabel
-                    label: formEditor.elements.AdvancedPassword.editor.confirmationLabel.label
-                    propertyPath: properties.confirmationLabel
-                    templateName: Inspector-TextEditor
-                group: custom
-                groupSorting: 500
-                iconIdentifier: t3-form-icon-advanced-password
-                label: formEditor.elements.AdvancedPassword.label
-                predefinedDefaults:
-                  properties:
-                    confirmationLabel: formEditor.element.AdvancedPassword.editor.confirmationLabel.predefinedDefaults
-              implementationClassName: TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement
-              properties:
-                confirmationClassAttribute: input-medium
-                confirmationLabel: ''
-                elementClassAttribute: input-medium
-
-            ########### Checkbox ###########
-            Checkbox:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin
-              formEditor:
-                group: select
-                groupSorting: 100
-                iconIdentifier: t3-form-icon-checkbox
-                label: formEditor.elements.Checkbox.label
-              properties:
-                containerClassAttribute: 'input checkbox'
-                elementClassAttribute: add-on
-                value: 1
-
-            ########### ContentElement ###########
-            ContentElement:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.ReadOnlyFormElementMixin
-              formEditor:
-                editors:
-                  300:
-                    browsableType: tt_content
-                    buttonLabel: formEditor.elements.ContentElement.editor.contentElement.buttonLabel
-                    identifier: contentElement
-                    label: formEditor.elements.ContentElement.editor.contentElement.label
-                    propertyPath: properties.contentElementUid
-                    propertyValidators:
-                      10: Integer
-                      20: FormElementIdentifierWithinCurlyBracesExclusive
-                    propertyValidatorsMode: OR
-                    templateName: Inspector-Typo3WinBrowserEditor
-                group: custom
-                groupSorting: 700
-                iconIdentifier: t3-form-icon-content-element
-                label: formEditor.elements.ContentElement.label
-                predefinedDefaults:
-                  properties:
-                    contentElementUid: ''
-              properties:
-                contentElementUid: ''
-
-            ########### DatePicker ###########
-            DatePicker:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin
-              formEditor:
-                editors:
-                  300:
-                    identifier: dateFormat
-                    label: formEditor.elements.DatePicker.editor.dateFormat.label
-                    propertyPath: properties.dateFormat
-                    templateName: Inspector-TextEditor
-                  400:
-                    identifier: enableDatePicker
-                    label: formEditor.elements.DatePicker.editor.enableDatePicker.label
-                    propertyPath: properties.enableDatePicker
-                    templateName: Inspector-CheckboxEditor
-                  500:
-                    identifier: displayTimeSelector
-                    label: formEditor.elements.DatePicker.editor.displayTimeSelector.label
-                    propertyPath: properties.displayTimeSelector
-                    templateName: Inspector-CheckboxEditor
-                  900:
-                    identifier: validators
-                    label: formEditor.elements.DatePicker.editor.validators.label
-                    selectOptions:
-                      10:
-                        label: formEditor.elements.DatePicker.editor.validators.EmptyValue.label
-                        value: ''
-                      20:
-                        label: formEditor.elements.DatePicker.editor.validators.DateTime.label
-                        value: DateTime
-                    templateName: Inspector-ValidatorsEditor
-                group: custom
-                groupSorting: 200
-                iconIdentifier: t3-form-icon-date-picker
-                label: formEditor.elements.DatePicker.label
-                predefinedDefaults:
-                  properties:
-                    dateFormat: Y-m-d
-                    displayTimeSelector: false
-                    enableDatePicker: true
-                propertyCollections:
-                  validators:
-                    10:
-                      editors:
-                        __inheritances:
-                          10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                        100:
-                          label: formEditor.elements.DatePicker.validators.DateTime.editor.header.label
-                      identifier: DateTime
-              implementationClassName: TYPO3\CMS\Form\Domain\Model\FormElements\DatePicker
-              properties:
-                dateFormat: Y-m-d
-                displayTimeSelector: false
-                elementClassAttribute: 'small form-control'
-                enableDatePicker: true
-                timeSelectorClassAttribute: mini
-                timeSelectorHourLabel: ''
-                timeSelectorMinuteLabel: ''
-
-            ########### Email ###########
-            Email:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.TextMixin
-              formEditor:
-                editors:
-                  500:
-                    propertyValidators:
-                      10: NaiveEmailOrEmpty
-                  900:
-                    selectOptions: {  }
-                group: html5
-                groupSorting: 100
-                iconIdentifier: t3-form-icon-email
-                label: formEditor.elements.Email.label
-                predefinedDefaults:
-                  validators:
-                    -
-                      identifier: EmailAddress
-                propertyCollections:
-                  validators:
-                    40:
-                      editors: {  }
-              validators:
-                -
-                  identifier: EmailAddress
-
-            ########### Fieldset ###########
-            Fieldset:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin
-              formEditor:
-                _isCompositeFormElement: true
-                editors:
-                  200:
-                    label: formEditor.elements.Fieldset.editor.label.label
-                group: container
-                groupSorting: 100
-                iconIdentifier: t3-form-icon-fieldset
-                label: formEditor.elements.Fieldset.label
-              implementationClassName: TYPO3\CMS\Form\Domain\Model\FormElements\Section
-              renderingOptions:
-                _isCompositeFormElement: true
-
-            ########### FileUpload ###########
-            FileUpload:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.FileUploadMixin
-              formEditor:
-                editors:
-                  300:
-                    identifier: allowedMimeTypes
-                    label: formEditor.elements.FileUpload.editor.allowedMimeTypes.label
-                    propertyPath: properties.allowedMimeTypes
-                    selectOptions:
-                      10:
-                        label: formEditor.elements.FileUpload.editor.allowedMimeTypes.doc
-                        value: application/msword
-                      20:
-                        label: formEditor.elements.FileUpload.editor.allowedMimeTypes.docx
-                        value: application/vnd.openxmlformats-officedocument.wordprocessingml.document
-                      30:
-                        label: formEditor.elements.FileUpload.editor.allowedMimeTypes.xls
-                        value: application/msexcel
-                      40:
-                        label: formEditor.elements.FileUpload.editor.allowedMimeTypes.xlsx
-                        value: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
-                      50:
-                        label: formEditor.elements.FileUpload.editor.allowedMimeTypes.pdf
-                        value: application/pdf
-                      60:
-                        label: formEditor.elements.FileUpload.editor.allowedMimeTypes.odt
-                        value: application/vnd.oasis.opendocument.text
-                      70:
-                        label: formEditor.elements.FileUpload.editor.allowedMimeTypes.ods
-                        value: application/vnd.oasis.opendocument.spreadsheet-template
-                    templateName: Inspector-MultiSelectEditor
-                group: custom
-                groupSorting: 100
-                iconIdentifier: t3-form-icon-file-upload
-                label: formEditor.elements.FileUpload.label
-                predefinedDefaults:
-                  properties:
-                    allowedMimeTypes:
-                      - application/pdf
-              properties:
-                allowedMimeTypes:
-                  - application/msword
-                  - application/vnd.openxmlformats-officedocument.wordprocessingml.document
-                  - application/vnd.oasis.opendocument.text
-                  - application/pdf
-
-            ########### Form ###########
-            Form:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin
-              formEditor:
-                _isCompositeFormElement: false
-                _isTopLevelFormElement: true
-                editors:
-                  300:
-                    identifier: submitButtonLabel
-                    label: formEditor.elements.Form.editor.submitButtonLabel.label
-                    propertyPath: renderingOptions.submitButtonLabel
-                    templateName: Inspector-TextEditor
-                  900:
-                    identifier: finishers
-                    label: formEditor.elements.Form.editor.finishers.label
-                    selectOptions:
-                      10:
-                        label: formEditor.elements.Form.editor.finishers.EmptyValue.label
-                        value: ''
-                      20:
-                        label: formEditor.elements.Form.editor.finishers.EmailToSender.label
-                        value: EmailToSender
-                      30:
-                        label: formEditor.elements.Form.editor.finishers.EmailToReceiver.label
-                        value: EmailToReceiver
-                      40:
-                        label: formEditor.elements.Form.editor.finishers.Redirect.label
-                        value: Redirect
-                      50:
-                        label: formEditor.elements.Form.editor.finishers.DeleteUploads.label
-                        value: DeleteUploads
-                      60:
-                        label: formEditor.elements.Form.editor.finishers.Confirmation.label
-                        value: Confirmation
-                    templateName: Inspector-FinishersEditor
-                iconIdentifier: content-elements-mailform
-                inspectorEditorFormElementSelectorNoElements: formEditor.inspector.editor.formelement_selector.no_elements
-                modalCloseCancleButton: formEditor.modals.close.cancleButton
-                modalCloseConfirmButton: formEditor.modals.close.confirmButton
-                modalCloseDialogMessage: formEditor.modals.close.dialogMessage
-                modalCloseDialogTitle: formEditor.modals.close.dialogTitle
-                modalInsertElementsDialogTitle: formEditor.modals.insertElements.dialogTitle
-                modalInsertPagesDialogTitle: formEditor.modals.newPages.dialogTitle
-                modalRemoveElementCancleButton: formEditor.modals.removeElement.cancleButton
-                modalRemoveElementConfirmButton: formEditor.modals.removeElement.confirmButton
-                modalRemoveElementDialogMessage: formEditor.modals.removeElement.dialogMessage
-                modalRemoveElementDialogTitle: formEditor.modals.removeElement.dialogTitle
-                modalRemoveElementLastAvailablePageFlashMessageMessage: formEditor.modals.removeElement.lastAvailablePageFlashMessageMessage
-                modalRemoveElementLastAvailablePageFlashMessageTitle: formEditor.modals.removeElement.lastAvailablePageFlashMessageTitle
-                modalValidationErrorsConfirmButton: formEditor.modals.validationErrors.confirmButton
-                modalValidationErrorsDialogTitle: formEditor.modals.validationErrors.dialogTitle
-                paginationTitle: formEditor.pagination.title
-                predefinedDefaults:
-                  renderingOptions:
-                    submitButtonLabel: formEditor.elements.Form.editor.submitButtonLabel.value
-                propertyCollections:
-                  finishers:
-                    10:
-                      __inheritances:
-                        10: TYPO3.CMS.Form.mixins.formElementMixins.formEmailFinisherMixin
-                      identifier: EmailToSender
-                    20:
-                      __inheritances:
-                        10: TYPO3.CMS.Form.mixins.formElementMixins.formEmailFinisherMixin
-                      editors:
-                        100:
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.header.label
-                        200:
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.subject.label
-                        300:
-                          fieldExplanationText: formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientAddress.fieldExplanationText
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientAddress.label
-                        400:
-                          fieldExplanationText: formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientName.fieldExplanationText
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientName.label
-                        500:
-                          fieldExplanationText: formEditor.elements.Form.finisher.EmailToReceiver.editor.senderAddress.fieldExplanationText
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.senderAddress.label
-                        600:
-                          fieldExplanationText: formEditor.elements.Form.finisher.EmailToReceiver.editor.senderName.fieldExplanationText
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.senderName.label
-                        700:
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.replyToAddress.label
-                        800:
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.carbonCopyAddress.label
-                        900:
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.blindCarbonCopyAddress.label
-                        1000:
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.format.label
-                        1100:
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.attachUploads.label
-                        1200:
-                          identifier: language
-                          label: formEditor.elements.Form.finisher.EmailToReceiver.editor.language.label
-                          propertyPath: options.translation.language
-                          selectOptions:
-                            10:
-                              label: formEditor.elements.Form.finisher.EmailToReceiver.editor.language.1
-                              value: default
-                          templateName: Inspector-SingleSelectEditor
-                      identifier: EmailToReceiver
-                    30:
-                      editors:
-                        __inheritances:
-                          10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                        100:
-                          label: formEditor.elements.Form.finisher.Redirect.editor.header.label
-                        200:
-                          browsableType: pages
-                          buttonLabel: formEditor.elements.Form.finisher.Redirect.editor.pageUid.buttonLabel
-                          identifier: pageUid
-                          label: formEditor.elements.Form.finisher.Redirect.editor.pageUid.label
-                          propertyPath: options.pageUid
-                          propertyValidators:
-                            10: Integer
-                            20: FormElementIdentifierWithinCurlyBracesExclusive
-                          propertyValidatorsMode: OR
-                          templateName: Inspector-Typo3WinBrowserEditor
-                        300:
-                          identifier: additionalParameters
-                          label: formEditor.elements.Form.finisher.Redirect.editor.additionalParameters.label
-                          propertyPath: options.additionalParameters
-                          templateName: Inspector-TextEditor
-                      identifier: Redirect
-                    40:
-                      editors:
-                        __inheritances:
-                          10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                        100:
-                          label: formEditor.elements.Form.finisher.DeleteUploads.editor.header.label
-                      identifier: DeleteUploads
-                    50:
-                      editors:
-                        __inheritances:
-                          10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                        100:
-                          label: formEditor.elements.Form.finisher.Confirmation.editor.header.label
-                        200:
-                          browsableType: tt_content
-                          buttonLabel: formEditor.elements.Form.finisher.Confirmation.editor.contentElement.buttonLabel
-                          identifier: contentElement
-                          label: formEditor.elements.Form.finisher.Confirmation.editor.contentElement.label
-                          propertyPath: options.contentElementUid
-                          propertyValidators:
-                            10: IntegerOrEmpty
-                            20: FormElementIdentifierWithinCurlyBracesExclusive
-                          propertyValidatorsMode: OR
-                          templateName: Inspector-Typo3WinBrowserEditor
-                        300:
-                          fieldExplanationText: formEditor.elements.Form.finisher.Confirmation.editor.message.fieldExplanationText
-                          identifier: message
-                          label: formEditor.elements.Form.finisher.Confirmation.editor.message.label
-                          propertyPath: options.message
-                          templateName: Inspector-TextareaEditor
-                      identifier: Confirmation
-                    60:
-                      editors:
-                        __inheritances:
-                          10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                        100:
-                          label: formEditor.elements.Form.finisher.Closure.editor.header.label
-                      identifier: Closure
-                    70:
-                      editors:
-                        __inheritances:
-                          10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                        100:
-                          label: formEditor.elements.Form.finisher.FlashMessage.editor.header.label
-                      identifier: FlashMessage
-                    80:
-                      editors:
-                        __inheritances:
-                          10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                        100:
-                          label: formEditor.elements.Form.finisher.SaveToDatabase.editor.header.label
-                      identifier: SaveToDatabase
-                saveErrorFlashMessageMessage: formEditor.elements.Form.saveErrorFlashMessageMessage
-                saveErrorFlashMessageTitle: formEditor.elements.Form.saveErrorFlashMessageTitle
-                saveSuccessFlashMessageMessage: formEditor.elements.Form.saveSuccessFlashMessageMessage
-                saveSuccessFlashMessageTitle: formEditor.elements.Form.saveSuccessFlashMessageTitle
-
-              rendererClassName: TYPO3\CMS\Form\Domain\Renderer\FluidFormRenderer
-              renderingOptions:
-                __inheritances:
-                  10: TYPO3.CMS.Form.mixins.translationSettingsMixin
-                _isCompositeFormElement: false
-                _isTopLevelFormElement: true
-                addQueryString: false
-                additionalParams: {  }
-                argumentsToBeExcludedFromQueryString: {  }
-                controllerAction: perform
-                honeypot:
-                  enable: true
-                  formElementToUse: Honeypot
-                httpEnctype: multipart/form-data
-                httpMethod: post
-                layoutRootPaths:
-                  10: 'EXT:form/Resources/Private/Frontend/Layouts/'
-                partialRootPaths:
-                  10: 'EXT:form/Resources/Private/Frontend/Partials/'
-                skipUnknownElements: true
-                submitButtonLabel: Submit
-                templateRootPaths:
-                  10: 'EXT:form/Resources/Private/Frontend/Templates/'
-
-            ########### GridContainer ###########
-            GridContainer:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin
-              formEditor:
-                _isCompositeFormElement: true
-                _isGridContainerFormElement: true
-                editors:
-                  200:
-                    label: formEditor.elements.GridContainer.editor.label.label
-                iconIdentifier: t3-form-icon-gridcontainer
-                label: formEditor.elements.GridContainer.label
-              implementationClassName: TYPO3\CMS\Form\Domain\Model\FormElements\GridContainer
-              properties:
-                elementClassAttribute: container
-                gridColumnClassAutoConfiguration:
-                  gridSize: 12
-                  viewPorts:
-                    lg:
-                      classPattern: 'col-lg-{@numbersOfColumnsToUse}'
-                    md:
-                      classPattern: 'col-md-{@numbersOfColumnsToUse}'
-                    sm:
-                      classPattern: 'col-sm-{@numbersOfColumnsToUse}'
-                    xs:
-                      classPattern: 'col-xs-{@numbersOfColumnsToUse}'
-              renderingOptions:
-                _isCompositeFormElement: true
-                _isGridContainerFormElement: true
-
-            ########### GridRow ###########
-            GridRow:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin
-              formEditor:
-                _isCompositeFormElement: true
-                _isGridRowFormElement: true
-                editors:
-                  200:
-                    label: formEditor.elements.GridRow.editor.label.label
-                group: container
-                groupSorting: 300
-                iconIdentifier: t3-form-icon-gridrow
-                label: formEditor.elements.GridRow.label
-              implementationClassName: TYPO3\CMS\Form\Domain\Model\FormElements\GridRow
-              properties:
-                elementClassAttribute: row
-                gridColumnClassAutoConfiguration:
-                  gridSize: 12
-                  viewPorts:
-                    lg:
-                      classPattern: 'col-lg-{@numbersOfColumnsToUse}'
-                    md:
-                      classPattern: 'col-md-{@numbersOfColumnsToUse}'
-                    sm:
-                      classPattern: 'col-sm-{@numbersOfColumnsToUse}'
-                    xs:
-                      classPattern: 'col-xs-{@numbersOfColumnsToUse}'
-              renderingOptions:
-                _isCompositeFormElement: true
-                _isGridRowFormElement: true
-
-            ########### Hidden ###########
-            Hidden:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin
-              formEditor:
-                editors:
-                  300:
-                    identifier: defaultValue
-                    label: formEditor.elements.Hidden.editor.defaultValue.label
-                    propertyPath: defaultValue
-                    templateName: Inspector-TextEditor
-                group: custom
-                groupSorting: 300
-                iconIdentifier: t3-form-icon-hidden
-                label: formEditor.elements.Hidden.label
-                predefinedDefaults:
-                  defaultValue: ''
-              renderingOptions:
-                _isHiddenFormElement: true
-
-            ########### Honeypot ###########
-            Honeypot:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.TextMixin
-              properties:
-                renderAsHiddenField: false
-                styleAttribute: 'position:absolute; margin:0 0 0 -999em;'
-              renderingOptions:
-                _isHiddenFormElement: true
-
-            ########### ImageUpload ###########
-            ImageUpload:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.FileUploadMixin
-              formEditor:
-                editors:
-                  300:
-                    identifier: allowedMimeTypes
-                    label: formEditor.elements.ImageUpload.editor.allowedMimeTypes.label
-                    propertyPath: properties.allowedMimeTypes
-                    selectOptions:
-                      10:
-                        label: formEditor.elements.ImageUpload.editor.allowedMimeTypes.jpg
-                        value: image/jpeg
-                      20:
-                        label: formEditor.elements.ImageUpload.editor.allowedMimeTypes.png
-                        value: image/png
-                      30:
-                        label: formEditor.elements.ImageUpload.editor.allowedMimeTypes.bmp
-                        value: image/bmp
-                    templateName: Inspector-MultiSelectEditor
-                group: custom
-                groupSorting: 400
-                iconIdentifier: t3-form-icon-image-upload
-                label: formEditor.elements.ImageUpload.label
-                predefinedDefaults:
-                  properties:
-                    allowedMimeTypes:
-                      - image/jpeg
-              properties:
-                allowedMimeTypes:
-                  - image/jpeg
-                  - image/png
-                  - image/bmp
-                elementClassAttribute: lightbox
-                imageLinkMaxWidth: 500
-                imageMaxHeight: 500
-                imageMaxWidth: 500
-
-            ########### MultiCheckbox ###########
-            MultiCheckbox:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.MultiSelectionMixin
-              formEditor:
-                editors:
-                  800: {  }
-                group: select
-                groupSorting: 400
-                iconIdentifier: t3-form-icon-multi-checkbox
-                label: formEditor.elements.MultiCheckbox.label
-              properties:
-                containerClassAttribute: 'input checkbox'
-
-            ########### MultiSelect ###########
-            MultiSelect:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.MultiSelectionMixin
-              formEditor:
-                editors:
-                  250:
-                    doNotSetIfPropertyValueIsEmpty: true
-                    fieldExplanationText: formEditor.elements.SelectionMixin.editor.inactiveOption.fieldExplanationText
-                    identifier: inactiveOption
-                    label: formEditor.elements.SelectionMixin.editor.inactiveOption.label
-                    propertyPath: properties.prependOptionLabel
-                    templateName: Inspector-TextEditor
-                group: select
-                groupSorting: 500
-                iconIdentifier: t3-form-icon-multi-select
-                label: formEditor.elements.MultiSelect.label
-              properties:
-                elementClassAttribute: xlarge
-
-            ########### Number ###########
-            Number:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.TextMixin
-              formEditor:
-                editors:
-                  500:
-                    propertyValidators:
-                      10: IntegerOrEmpty
-                  700:
-                    identifier: step
-                    label: formEditor.elements.TextMixin.editor.step.label
-                    propertyPath: properties.fluidAdditionalAttributes.step
-                    propertyValidators:
-                      10: Integer
-                    templateName: Inspector-TextEditor
-                  900:
-                    selectOptions:
-                      60:
-                        label: formEditor.elements.Number.editor.validators.Number.label
-                        value: Number
-                group: html5
-                groupSorting: 400
-                iconIdentifier: t3-form-icon-number
-                label: formEditor.elements.Number.label
-                predefinedDefaults:
-                  properties:
-                    fluidAdditionalAttributes:
-                      step: 1
-                  validators:
-                    -
-                      identifier: Number
-                propertyCollections:
-                  validators:
-                    60:
-                      editors:
-                        __inheritances:
-                          10: TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin
-                        100:
-                          label: formEditor.elements.TextMixin.validators.Number.editor.header.label
-                      identifier: Number
-              validators:
-                -
-                  identifier: Number
-
-            ########### Page ###########
-            Page:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin
-              formEditor:
-                __inheritances:
-                  10: TYPO3.CMS.Form.mixins.formElementMixins.RemovableFormElementMixin
-                _isCompositeFormElement: true
-                _isTopLevelFormElement: true
-                editors:
-                  200:
-                    label: formEditor.elements.Page.editor.label.label
-                  300:
-                    identifier: previousButtonLabel
-                    label: formEditor.elements.Page.editor.previousButtonLabel.label
-                    propertyPath: renderingOptions.previousButtonLabel
-                    templateName: Inspector-TextEditor
-                  400:
-                    identifier: nextButtonLabel
-                    label: formEditor.elements.Page.editor.nextButtonLabel.label
-                    propertyPath: renderingOptions.nextButtonLabel
-                    templateName: Inspector-TextEditor
-                group: page
-                groupSorting: 100
-                iconIdentifier: t3-form-icon-page
-                label: formEditor.elements.Page.label
-                predefinedDefaults:
-                  renderingOptions:
-                    nextButtonLabel: formEditor.elements.Page.editor.nextButtonLabel.value
-                    previousButtonLabel: formEditor.elements.Page.editor.previousButtonLabel.value
-              implementationClassName: TYPO3\CMS\Form\Domain\Model\FormElements\Page
-              renderingOptions:
-                _isCompositeFormElement: true
-                _isTopLevelFormElement: true
-                nextButtonLabel: 'next Page'
-                previousButtonLabel: 'previous Page'
-
-            ########### Password ###########
-            Password:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.TextMixin
-              formEditor:
-                group: input
-                groupSorting: 300
-                iconIdentifier: t3-form-icon-password
-                label: formEditor.elements.Password.label
-
-            ########### RadioButton ###########
-            RadioButton:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.SingleSelectionMixin
-              formEditor:
-                group: select
-                groupSorting: 300
-                iconIdentifier: t3-form-icon-radio-button
-                label: formEditor.elements.RadioButton.label
-              properties:
-                elementClassAttribute: xlarge
-
-            ########### SingleSelect ###########
-            SingleSelect:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.SingleSelectionMixin
-              formEditor:
-                editors:
-                  250:
-                    doNotSetIfPropertyValueIsEmpty: true
-                    fieldExplanationText: formEditor.elements.SelectionMixin.editor.inactiveOption.fieldExplanationText
-                    identifier: inactiveOption
-                    label: formEditor.elements.SelectionMixin.editor.inactiveOption.label
-                    propertyPath: properties.prependOptionLabel
-                    templateName: Inspector-TextEditor
-                group: select
-                groupSorting: 200
-                iconIdentifier: t3-form-icon-single-select
-                label: formEditor.elements.SingleSelect.label
-
-            ########### StaticText ###########
-            StaticText:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.ReadOnlyFormElementMixin
-              formEditor:
-                editors:
-                  300:
-                    identifier: staticText
-                    label: formEditor.elements.StaticText.editor.staticText.label
-                    propertyPath: properties.text
-                    templateName: Inspector-TextareaEditor
-                group: custom
-                groupSorting: 600
-                iconIdentifier: t3-form-icon-static-text
-                label: formEditor.elements.StaticText.label
-                predefinedDefaults:
-                  properties:
-                    text: ''
-              properties:
-                text: ''
-
-            ########### SummaryPage ###########
-            SummaryPage:
-              __inheritances:
-                10: TYPO3.CMS.Form.prototypes.standard.formElementsDefinition.Page
-              formEditor:
-                _isCompositeFormElement: false
-                _isTopLevelFormElement: true
-                editors:
-                  200:
-                    label: formEditor.elements.SummaryPage.editor.label.label
-                  300:
-                    identifier: previousButtonLabel
-                    label: formEditor.elements.SummaryPage.editor.previousButtonLabel.label
-                    propertyPath: renderingOptions.previousButtonLabel
-                    templateName: Inspector-TextEditor
-                  400:
-                    identifier: nextButtonLabel
-                    label: formEditor.elements.SummaryPage.editor.nextButtonLabel.label
-                    propertyPath: renderingOptions.nextButtonLabel
-                    templateName: Inspector-TextEditor
-                group: page
-                groupSorting: 200
-                iconIdentifier: t3-form-icon-summary-page
-                label: formEditor.elements.SummaryPage.label
-                predefinedDefaults:
-                  renderingOptions:
-                    nextButtonLabel: formEditor.elements.SummaryPage.editor.nextButtonLabel.value
-                    previousButtonLabel: formEditor.elements.SummaryPage.editor.previousButtonLabel.value
-              renderingOptions:
-                _isCompositeFormElement: false
-                _isTopLevelFormElement: true
-                nextButtonLabel: 'next Page'
-                previousButtonLabel: 'previous Page'
-
-            ########### Telephone ###########
-            Telephone:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.TextMixin
-              formEditor:
-                editors:
-                  900:
-                    selectOptions: {  }
-                group: html5
-                groupSorting: 200
-                iconIdentifier: t3-form-icon-telephone
-                label: formEditor.elements.Telephone.label
-                propertyCollections:
-                  validators:
-                    80:
-                      editors: {  }
-              validators:
-                -
-                  identifier: RegularExpression
-                  options:
-                    regularExpression: '/^.*$/'
-
-            ########### Text ###########
-            Text:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.TextMixin
-              formEditor:
-                group: input
-                groupSorting: 100
-                iconIdentifier: t3-form-icon-text
-                label: formEditor.elements.Text.label
-
-            ########### Textarea ###########
-            Textarea:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.TextMixin
-              formEditor:
-                editors:
-                  900:
-                    selectOptions: {  }
-                group: input
-                groupSorting: 200
-                iconIdentifier: t3-form-icon-textarea
-                label: formEditor.elements.Textarea.label
-              properties:
-                elementClassAttribute: xxlarge
-
-            ########### Url ###########
-            Url:
-              __inheritances:
-                10: TYPO3.CMS.Form.mixins.formElementMixins.TextMixin
-              formEditor:
-                editors:
-                  900:
-                    selectOptions: {  }
-                group: html5
-                groupSorting: 300
-                iconIdentifier: t3-form-icon-url
-                label: formEditor.elements.Url.label
-                propertyCollections:
-                  validators:
-                    80:
-                      editors: {  }
-              validators:
-                -
-                  identifier: RegularExpression
-                  options:
-                    regularExpression: '/^.*$/'
-
-          ########### FORM ENGINE CONFIGURATION (backend) ###########
-          formEngine:
-            translationFile: 'EXT:form/Resources/Private/Language/Database.xlf'
-
-          ########### VALIDATOR DEFINITIONS (frontend / backend) ###########
-          validatorsDefinition:
-
-            ########### Alphanumeric ###########
-            Alphanumeric:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.TextMixin.editor.validators.Alphanumeric.label
-              implementationClassName: TYPO3\CMS\Extbase\Validation\Validator\AlphanumericValidator
-
-            ########### Count ###########
-            Count:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.MultiSelectionMixin.validators.Count.editor.header.label
-                predefinedDefaults:
-                  options:
-                    maximum: ''
-                    minimum: ''
-              implementationClassName: TYPO3\CMS\Form\Mvc\Validation\CountValidator
-
-            ########### DateTime ###########
-            DateTime:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.DatePicker.validators.DateTime.editor.header.label
-              implementationClassName: TYPO3\CMS\Extbase\Validation\Validator\DateTimeValidator
-
-            ########### EmailAddress ###########
-            EmailAddress:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.TextMixin.editor.validators.EmailAddress.label
-              implementationClassName: TYPO3\CMS\Extbase\Validation\Validator\EmailAddressValidator
-
-            ########### Float ###########
-            Float:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.TextMixin.editor.validators.Float.label
-              implementationClassName: TYPO3\CMS\Extbase\Validation\Validator\FloatValidator
-
-            ########### Integer ###########
-            Integer:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.TextMixin.editor.validators.Integer.label
-              implementationClassName: TYPO3\CMS\Extbase\Validation\Validator\IntegerValidator
-
-            ########### NotEmpty ###########
-            NotEmpty:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.FormElement.editor.requiredValidator.label
-              implementationClassName: TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator
-
-            ########### Number ###########
-            Number:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.TextMixin.editor.validators.Number.label
-              implementationClassName: TYPO3\CMS\Extbase\Validation\Validator\NumberValidator
-
-            ########### NumberRange ###########
-            NumberRange:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.TextMixin.editor.validators.NumberRange.label
-                predefinedDefaults:
-                  options:
-                    maximum: ''
-                    minimum: ''
-              implementationClassName: TYPO3\CMS\Extbase\Validation\Validator\NumberRangeValidator
-
-            ########### RegularExpression ###########
-            RegularExpression:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.TextMixin.editor.validators.RegularExpression.label
-                predefinedDefaults:
-                  options:
-                    regularExpression: ''
-              implementationClassName: TYPO3\CMS\Extbase\Validation\Validator\RegularExpressionValidator
-
-            ########### StringLength ###########
-            StringLength:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.TextMixin.editor.validators.StringLength.label
-                predefinedDefaults:
-                  options:
-                    maximum: ''
-                    minimum: ''
-              implementationClassName: TYPO3\CMS\Extbase\Validation\Validator\StringLengthValidator
-
-            ########### Text ###########
-            Text:
-              formEditor:
-                iconIdentifier: t3-form-icon-validator
-                label: formEditor.elements.TextMixin.editor.validators.Text.label
-              implementationClassName: TYPO3\CMS\Extbase\Validation\Validator\TextValidator
diff --git a/typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Inspector/EmailSelectEditor.html b/typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Inspector/EmailSelectEditor.html
deleted file mode 100644 (file)
index 136ee03..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-<html xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" xmlns:formvh="http://typo3.org/ns/TYPO3/CMS/Form/ViewHelpers" data-namespace-typo3-fluid="true">
-<div class="form-editor">
-       <div class="t3-form-control-group form-group property-grid">
-               <label><span data-template-property="label" /></label>
-               <div data-editor="new-property-grid" data-template-property="newPropertyPath">
-                       <table class="table table-hover" data-identifier="propertyGridContainer">
-                               <thead>
-                                       <tr>
-                                               <th></th>
-                                               <th>Label</th>
-                                               <th>Value</th>
-                                               <th></th>
-                                       </tr>
-                               </thead>
-                               <tbody>
-                                       <tr data-identifier="rowItem">
-                                               <td><span class="sort-row-field" data-identifier="sortRow"><core:icon identifier="actions-move-move" /></span></td>
-                                               <td data-identifier="labelContainer">
-                                                       <input type="text" class="form-control" value="" data-identifier="label" />
-                                                       <span class="input-group-btn" role="group" data-identifier="inspectorEditorFormElementSelectorControlsWrapper">
-                                                               <span class="btn-group t3-form-dropdown-buttons" data-identifier="inspectorEditorFormElementSelectorSplitButtonContainer">
-                                                                       <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.inspector.editor.formelement_selector.title')}">
-                                                                               <core:icon identifier="t3-form-icon-form-element-selector" />
-                                                                               <span class="caret"></span>
-                                                                               <span class="sr-only">Toggle Dropdown</span>
-                                                                       </button>
-                                                                       <ul class="dropdown-menu dropdown-menu-right" data-identifier="inspectorEditorFormElementSelectorSplitButtonListContainer"></ul>
-                                                               </span>
-                                                       </span>
-                                               </td>
-                                               <td data-identifier="valueContainer">
-                                                       <input type="text" class="form-control" value="" data-identifier="value" />
-                                                       <span class="input-group-btn" role="group" data-identifier="inspectorEditorFormElementSelectorControlsWrapper">
-                                                               <span class="btn-group t3-form-dropdown-buttons" data-identifier="inspectorEditorFormElementSelectorSplitButtonContainer">
-                                                                       <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.inspector.editor.formelement_selector.title')}">
-                                                                               <core:icon identifier="t3-form-icon-form-element-selector" />
-                                                                               <span class="caret"></span>
-                                                                               <span class="sr-only">Toggle Dropdown</span>
-                                                                       </button>
-                                                                       <ul class="dropdown-menu dropdown-menu-right" data-identifier="inspectorEditorFormElementSelectorSplitButtonListContainer"></ul>
-                                                               </span>
-                                                       </span>
-                                               </td>
-                                               <td>
-                                                       <div class="btn-group btn-group-sm" role="group">
-                                                               <button class="btn btn-default" title="Remove this row" data-identifier="deleteRow"><core:icon identifier="actions-delete" /></button>
-                                                       </div>
-                                               </td>
-                                       </tr>
-                                       <tr data-identifier="addRowItem">
-                                               <td>
-                                                       <div class="btn-group btn-group-sm" role="group">
-                                                               <button class="btn btn-default" title="Add a new row" data-identifier="addRow"><core:icon identifier="actions-add" /></button>
-                                                       </div>
-                                               </td>
-                                               <td></td>
-                                               <td></td>
-                                               <td></td>
-                                               <td></td>
-                                       </tr>
-                               </tbody>
-                       </table>
-               </div>
-               <div data-editor="property-grid" data-template-property="propertyPath" class="t3-form-grid" />
-       </div>
-</div>
-</html>
diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/ConfigurationManagerTest.php b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/ConfigurationManagerTest.php
deleted file mode 100644 (file)
index 5f21422..0000000
+++ /dev/null
@@ -1,220 +0,0 @@
-<?php
-namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Configuration;
-
-/*
- * 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 Prophecy\Argument;
-use TYPO3\CMS\Core\Configuration\Loader\FalYamlFileLoader;
-use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration;
-use TYPO3\CMS\Core\Resource\File;
-use TYPO3\CMS\Core\Resource\ResourceFactory;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Extbase\Object\ObjectManager;
-use TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManager;
-use TYPO3\CMS\Form\Mvc\Configuration\Exception\ExtensionNameRequiredException;
-use TYPO3\CMS\Form\Mvc\Configuration\Exception\NoConfigurationFoundException;
-use TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService;
-
-/**
- * Test case
- */
-class ConfigurationManagerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
-{
-
-    /**
-     * @var array A backup of registered singleton instances
-     */
-    protected $singletonInstances = [];
-
-    /**
-     * Set up
-     */
-    public function setUp()
-    {
-        $this->singletonInstances = GeneralUtility::getSingletonInstances();
-    }
-
-    /**
-     * Tear down
-     */
-    public function tearDown()
-    {
-        GeneralUtility::resetSingletonInstances($this->singletonInstances);
-        parent::tearDown();
-    }
-
-    /**
-     * @test
-     */
-    public function getConfigurationFromYamlFileThrowsExceptionIfExtensionNameIsNotGiven()
-    {
-        $this->expectException(ExtensionNameRequiredException::class);
-        $this->expectExceptionCode(1471473377);
-
-        $mockConfigurationManager = $this->getAccessibleMock(ConfigurationManager::class, [
-            'dummy',
-        ], [], '', false);
-
-        $mockConfigurationManager->_call('getConfigurationFromYamlFile', '');
-    }
-
-    /**
-     * @test
-     */
-    public function getConfigurationFromYamlFileThrowsExceptionIfNoConfigurationIsFound()
-    {
-        $this->expectException(NoConfigurationFoundException::class);
-        $this->expectExceptionCode(1471473378);
-
-        $mockConfigurationManager = $this->getAccessibleMock(ConfigurationManager::class, [
-            'getYamlSettingsFromCache',
-            'getTypoScriptSettings',
-        ], [], '', false);
-
-        $mockConfigurationManager
-            ->expects($this->any())
-            ->method('getYamlSettingsFromCache')
-            ->willReturn([]);
-
-        $mockConfigurationManager
-            ->expects($this->any())
-            ->method('getTypoScriptSettings')
-            ->willReturn([]);
-
-        $objectManagerProphecy = $this->prophesize(ObjectManager::class);
-        $objectManagerProphecy
-            ->get(FalYamlFileLoader::class)
-            ->willReturn(new FalYamlFileLoader);
-
-        $configuration = new Configuration();
-        $objectManagerProphecy
-            ->get(Configuration::class)
-            ->willReturn($configuration);
-
-        $mockConfigurationManager->_set('objectManager', $objectManagerProphecy->reveal());
-
-        $input = 'form';
-        $expected = [];
-
-        $this->assertSame($expected, $mockConfigurationManager->_call('getConfigurationFromYamlFile', 'form'));
-    }
-
-    /**
-     * @test
-     */
-    public function getConfigurationFromYamlFile()
-    {
-        $mockConfigurationManager = $this->getAccessibleMock(ConfigurationManager::class, [
-            'getYamlSettingsFromCache',
-            'setYamlSettingsIntoCache',
-            'getTypoScriptSettings',
-            'overrideConfigurationByTypoScript',
-        ], [], '', false);
-
-        $objectManagerProphecy = $this->prophesize(ObjectManager::class);
-        GeneralUtility::setSingletonInstance(ObjectManager::class, $objectManagerProphecy->reveal());
-
-        /** @var File|\Prophecy\Prophecy\ObjectProphecy */
-        $file1 = $this->prophesize(File::class);
-        $file1->getContents()->willReturn(file_get_contents(__DIR__ . '/Fixtures/File1.yaml'));
-        /** @var File|\Prophecy\Prophecy\ObjectProphecy */
-        $file2 = $this->prophesize(File::class);
-        $file2->getContents()->willReturn(file_get_contents(__DIR__ . '/Fixtures/File2.yaml'));
-        /** @var File|\Prophecy\Prophecy\ObjectProphecy */
-        $file3 = $this->prophesize(File::class);
-        $file3->getContents()->willReturn(file_get_contents(__DIR__ . '/Fixtures/File3.yaml'));
-        /** @var File|\Prophecy\Prophecy\ObjectProphecy */
-        $file4 = $this->prophesize(File::class);
-        $file4->getContents()->willReturn(file_get_contents(__DIR__ . '/Fixtures/File4.yaml'));
-
-        /** @var ResourceFactory|\Prophecy\Prophecy\ObjectProphecy */
-        $resourceFactory = $this->prophesize(ResourceFactory::class);
-        $resourceFactory->retrieveFileOrFolderObject('EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File1.yaml')->willReturn($file1->reveal());
-        $resourceFactory->retrieveFileOrFolderObject('EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File2.yaml')->willReturn($file2->reveal());
-        $resourceFactory->retrieveFileOrFolderObject('EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File3.yaml')->willReturn($file3->reveal());
-        $resourceFactory->retrieveFileOrFolderObject('EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File4.yaml')->willReturn($file4->reveal());
-
-        $configuration = new Configuration();
-        $objectManagerProphecy
-            ->get(Configuration::class)
-            ->willReturn($configuration);
-
-        $objectManagerProphecy
-            ->get(FalYamlFileLoader::class, Argument::type(Configuration::class))
-            ->willReturn(new FalYamlFileLoader($configuration, $resourceFactory->reveal()));
-
-        $objectManagerProphecy
-            ->get(InheritancesResolverService::class)
-            ->willReturn(new InheritancesResolverService);
-
-        $mockConfigurationManager->_set('objectManager', $objectManagerProphecy->reveal());
-
-        $mockConfigurationManager
-            ->expects($this->any())
-            ->method('getYamlSettingsFromCache')
-            ->willReturn([]);
-
-        $mockConfigurationManager
-            ->expects($this->any())
-            ->method('getTypoScriptSettings')
-            ->willReturn([
-                'configurationFile' => 'EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File1.yaml',
-            ]);
-
-        $mockConfigurationManager
-            ->expects($this->any())
-            ->method('setYamlSettingsIntoCache')
-            ->willReturn(null);
-
-        $mockConfigurationManager
-            ->expects($this->any())
-            ->method('overrideConfigurationByTypoScript')
-            ->willReturnArgument(0);
-
-        $input = 'form';
-        $expected = [
-            'config' => [
-                'value9' => 'File 3',
-                'value10' => 'File 4',
-                'value8' => 'File 3',
-                'value1' => 'File 1',
-                'value4' => 'File 1',
-                'value5' => 'File 1',
-                'value7' => 'File 2',
-                'value11' => [
-                    'key1' => 'File 1',
-                    'key2' => 'File 1',
-                ],
-                'value12' => [
-                    'key1' => 'File 2',
-                ],
-                'value3' => 'File 1',
-            ],
-            'mixins' => [
-                'value11Mixin' => [
-                    'key1' => 'File 1',
-                    'key2' => 'File 1',
-                ],
-                'value12Mixin1' => [
-                    'key1' => 'File 2',
-                ],
-                'value12Mixin2' => [
-                    'key2' => 'File 2',
-                ],
-            ],
-        ];
-
-        $this->assertSame($expected, $mockConfigurationManager->_call('getConfigurationFromYamlFile', 'form'));
-    }
-}
diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File1.yaml b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File1.yaml
deleted file mode 100644 (file)
index 112c691..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-imports:
-  - { resource: "EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File2.yaml" }
-  - { resource: "EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File3.yaml" }
-
-TYPO3:
-  CMS:
-    Form:
-      config:
-        value1: File 1
-        value2: null
-        value3: File 1
-        value4: File 1
-        value5: File 1
-        value6: null
-        value12:
-          __inheritances:
-            20: null
-      mixins:
-        value11Mixin:
-          key1: File 1
-          key2: File 1
diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File2.yaml b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File2.yaml
deleted file mode 100644 (file)
index 4b5a563..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-TYPO3:
-  CMS:
-    Form:
-      config:
-        value1: File2 (should be overridden by File1)
-        value2: File2 (should be removed by File1)
-        value4:
-          key1: File2 (the whole array should be overridden by File1)
-        value5: File2 (should be an array through File1)
-        value6:
-          key1: File2 (the whole array should be removed by File1)
-        value7: File 2
-        value11:
-          __inheritances:
-            10: 'TYPO3.CMS.Form.mixins.value11Mixin'
-        value12:
-          __inheritances:
-            10: 'TYPO3.CMS.Form.mixins.value12Mixin1'
-            20: 'TYPO3.CMS.Form.mixins.value12Mixin2'
-      mixins:
-        value11Mixin:
-          key1: File 2 (should be overridden by File1)
-          key2: File 2
-        value12Mixin1:
-          key1: File 2
-        value12Mixin2:
-          key2: File 2
diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File3.yaml b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File3.yaml
deleted file mode 100644 (file)
index 7dc85f8..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-imports:
-  - { resource: "EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File4.yaml" }
-
-TYPO3:
-  CMS:
-    Form:
-      config:
-        value8: File 3
-        value9: File 3
diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File4.yaml b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/File4.yaml
deleted file mode 100644 (file)
index ace3064..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-TYPO3:
-  CMS:
-    Form:
-      config:
-        value9: File 4 (should be overridden by File3)
-        value10: File 4
\ No newline at end of file
diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/Header.yaml b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/Header.yaml
new file mode 100644 (file)
index 0000000..ab19ab6
--- /dev/null
@@ -0,0 +1,6 @@
+# Header 1
+# Header 2
+
+yaml
+
+# Comment
\ No newline at end of file
diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/YamlSourceTest.php b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/YamlSourceTest.php
new file mode 100644 (file)
index 0000000..ccc114f
--- /dev/null
@@ -0,0 +1,106 @@
+<?php
+namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Configuration;
+
+/*
+ * 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\GeneralUtility;
+use TYPO3\CMS\Form\Mvc\Configuration\Exception\NoSuchFileException;
+use TYPO3\CMS\Form\Mvc\Configuration\Exception\ParseErrorException;
+use TYPO3\CMS\Form\Mvc\Configuration\YamlSource;
+
+/**
+ * Test case
+ */
+class YamlSourceTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
+{
+
+    /**
+     * @test
+     */
+    public function loadThrowsExceptionIfFileToLoadNotExists()
+    {
+        $this->expectException(NoSuchFileException::class);
+        $this->expectExceptionCode(1471473378);
+
+        $mockYamlSource = $this->getAccessibleMock(YamlSource::class, [
+            'dummy',
+        ], [], '', false);
+
+        $input = [
+            'EXT:form/Resources/Forms/_example.yaml'
+        ];
+
+        $mockYamlSource->_call('load', $input);
+    }
+
+    /**
+     * @test
+     */
+    public function loadThrowsExceptionIfFileToLoadIsNotValidYamlUseSymfonyParser()
+    {
+        if (!extension_loaded('yaml')) {
+            $this->expectException(ParseErrorException::class);
+            $this->expectExceptionCode(1480195405);
+
+            $mockYamlSource = $this->getAccessibleMock(YamlSource::class, [
+                'dummy',
+            ], [], '', false);
+
+            $input = [
+                'EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/Invalid.yaml'
+            ];
+
+            $mockYamlSource->_call('load', $input);
+        }
+    }
+
+    /**
+     * @test
+     */
+    public function loadThrowsExceptionIfFileToLoadIsNotValidYamlUsePhpExtensionParser()
+    {
+        if (extension_loaded('yaml')) {
+            $this->expectException(ParseErrorException::class);
+            $this->expectExceptionCode(1391894094);
+
+            $mockYamlSource = $this->getAccessibleMock(YamlSource::class, [
+                'dummy',
+            ], [], '', false);
+
+            $input = [
+                'EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/Invalid.yaml'
+            ];
+
+            $mockYamlSource->_call('load', $input);
+        }
+    }
+
+    /**
+     * @test
+     */
+    public function getHeaderFromFileReturnsHeaderPart()
+    {
+        $mockYamlSource = $this->getAccessibleMock(YamlSource::class, [
+            'dummy',
+        ], [], '', false);
+
+        $input = GeneralUtility::getFileAbsFileName('EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/Header.yaml');
+        $expected =
+'# Header 1
+# Header 2
+';
+
+        $this->assertSame($expected, $mockYamlSource->_call('getHeaderFromFile', $input));
+    }
+}
diff --git a/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/ConfigurationManagerTest.php b/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/ConfigurationManagerTest.php
deleted file mode 100644 (file)
index 5582780..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Configuration;
-
-/*
- * 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 Prophecy\Argument;
-use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader;
-use TYPO3\CMS\Core\Configuration\Loader\YamlFileLoader\Configuration;
-use TYPO3\CMS\Core\Utility\GeneralUtility;
-use TYPO3\CMS\Extbase\Object\ObjectManager;
-use TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManager;
-use TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService;
-
-/**
- * Test case
- */
-class ConfigurationManagerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
-{
-
-    /**
-     * @var array A backup of registered singleton instances
-     */
-    protected $singletonInstances = [];
-
-    /**
-     * Set up
-     */
-    public function setUp()
-    {
-        $this->singletonInstances = GeneralUtility::getSingletonInstances();
-    }
-
-    /**
-     * Tear down
-     */
-    public function tearDown()
-    {
-        GeneralUtility::resetSingletonInstances($this->singletonInstances);
-        parent::tearDown();
-    }
-
-    /**
-     * @test
-     */
-    public function getConfigurationFromYamlFile()
-    {
-        $mockConfigurationManager = $this->getAccessibleMock(ConfigurationManager::class, [
-            'getYamlSettingsFromCache',
-            'setYamlSettingsIntoCache',
-            'getTypoScriptSettings',
-            'overrideConfigurationByTypoScript',
-        ], [], '', false);
-
-        $objectManagerProphecy = $this->prophesize(ObjectManager::class);
-        GeneralUtility::setSingletonInstance(ObjectManager::class, $objectManagerProphecy->reveal());
-
-        $configuration = new Configuration();
-        $objectManagerProphecy
-            ->get(Configuration::class)
-            ->willReturn($configuration);
-
-        $objectManagerProphecy
-            ->get(YamlFileLoader::class, Argument::type(Configuration::class))
-            ->willReturn(new YamlFileLoader($configuration));
-
-        $objectManagerProphecy
-            ->get(InheritancesResolverService::class)
-            ->willReturn(new InheritancesResolverService);
-
-        $mockConfigurationManager->_set('objectManager', $objectManagerProphecy->reveal());
-
-        $mockConfigurationManager
-            ->expects($this->any())
-            ->method('getYamlSettingsFromCache')
-            ->willReturn([]);
-
-        $mockConfigurationManager
-            ->expects($this->any())
-            ->method('getTypoScriptSettings')
-            ->willReturn([
-                'yamlConfigurations' => [
-                    10 => 'EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File1.yaml'
-                ]
-            ]);
-
-        $mockConfigurationManager
-            ->expects($this->any())
-            ->method('setYamlSettingsIntoCache')
-            ->willReturn(null);
-
-        $mockConfigurationManager
-            ->expects($this->any())
-            ->method('overrideConfigurationByTypoScript')
-            ->willReturnArgument(0);
-
-        $input = 'form';
-        $expected = [
-            'config' => [
-                'value9' => 'File 3',
-                'value10' => 'File 4',
-                'value8' => 'File 3',
-                'value1' => 'File 1',
-                'value4' => 'File 1',
-                'value5' => 'File 1',
-                'value7' => 'File 2',
-                'value11' => [
-                    'key1' => 'File 1',
-                    'key2' => 'File 1',
-                ],
-                'value12' => [
-                    'key1' => 'File 2',
-                ],
-                'value3' => 'File 1',
-            ],
-            'mixins' => [
-                'value11Mixin' => [
-                    'key1' => 'File 1',
-                    'key2' => 'File 1',
-                ],
-                'value12Mixin1' => [
-                    'key1' => 'File 2',
-                ],
-                'value12Mixin2' => [
-                    'key2' => 'File 2',
-                ],
-            ],
-        ];
-
-        $this->assertSame($expected, $mockConfigurationManager->_call('getConfigurationFromYamlFile', 'form'));
-    }
-}
diff --git a/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File1.yaml b/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File1.yaml
deleted file mode 100644 (file)
index 112c691..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-imports:
-  - { resource: "EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File2.yaml" }
-  - { resource: "EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File3.yaml" }
-
-TYPO3:
-  CMS:
-    Form:
-      config:
-        value1: File 1
-        value2: null
-        value3: File 1
-        value4: File 1
-        value5: File 1
-        value6: null
-        value12:
-          __inheritances:
-            20: null
-      mixins:
-        value11Mixin:
-          key1: File 1
-          key2: File 1
diff --git a/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File2.yaml b/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File2.yaml
deleted file mode 100644 (file)
index 4b5a563..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-TYPO3:
-  CMS:
-    Form:
-      config:
-        value1: File2 (should be overridden by File1)
-        value2: File2 (should be removed by File1)
-        value4:
-          key1: File2 (the whole array should be overridden by File1)
-        value5: File2 (should be an array through File1)
-        value6:
-          key1: File2 (the whole array should be removed by File1)
-        value7: File 2
-        value11:
-          __inheritances:
-            10: 'TYPO3.CMS.Form.mixins.value11Mixin'
-        value12:
-          __inheritances:
-            10: 'TYPO3.CMS.Form.mixins.value12Mixin1'
-            20: 'TYPO3.CMS.Form.mixins.value12Mixin2'
-      mixins:
-        value11Mixin:
-          key1: File 2 (should be overridden by File1)
-          key2: File 2
-        value12Mixin1:
-          key1: File 2
-        value12Mixin2:
-          key2: File 2
diff --git a/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File3.yaml b/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File3.yaml
deleted file mode 100644 (file)
index 7dc85f8..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-imports:
-  - { resource: "EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/File4.yaml" }
-
-TYPO3:
-  CMS:
-    Form:
-      config:
-        value8: File 3
-        value9: File 3
diff --git a/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File4.yaml b/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/File4.yaml
deleted file mode 100644 (file)
index ace3064..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-TYPO3:
-  CMS:
-    Form:
-      config:
-        value9: File 4 (should be overridden by File3)
-        value10: File 4
\ No newline at end of file
diff --git a/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/Header.yaml b/typo3/sysext/form/Tests/UnitDeprecated/Mvc/Configuration/Fixtures/Header.yaml
deleted file mode 100644 (file)
index ab19ab6..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-# Header 1
-# Header 2
-
-yaml
-
-# Comment
\ No newline at end of file
index 4672c3a..ee892ad 100644 (file)
@@ -62,20 +62,9 @@ call_user_func(function () {
         '<INCLUDE_TYPOSCRIPT: source="FILE:EXT:form/Configuration/PageTS/modWizards.ts">'
     );
 
-    // Add module configuration
-    \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptSetup(
-        trim('
-            module.tx_form {
-                view {
-                    templateRootPaths.10 = EXT:form/Resources/Private/Backend/Templates/
-                    partialRootPaths.10 = EXT:form/Resources/Private/Backend/Partials/
-                    layoutRootPaths.10 = EXT:form/Resources/Private/Backend/Layouts/
-                }
-                settings {
-                    configurationFile = EXT:form/Configuration/Yaml/FormSetup.yaml
-                }
-            }
-        ')
+    // Add new content element wizard entry
+    \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSConfig(
+        '<INCLUDE_TYPOSCRIPT: source="FILE:EXT:form/Configuration/PageTS/modWizards.ts">'
     );
 
     $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterSubmit'][1489772699]
diff --git a/typo3/sysext/form/ext_typoscript_setup.txt b/typo3/sysext/form/ext_typoscript_setup.txt
new file mode 100644 (file)
index 0000000..ec0c566
--- /dev/null
@@ -0,0 +1,14 @@
+module.tx_form {
+    settings {
+        yamlConfigurations {
+            10 = EXT:form/Configuration/Yaml/BaseSetup.yaml
+            20 = EXT:form/Configuration/Yaml/FormEditorSetup.yaml
+            30 = EXT:form/Configuration/Yaml/FormEngineSetup.yaml
+        }
+    }
+    view {
+        templateRootPaths.10 = EXT:form/Resources/Private/Backend/Templates/
+        partialRootPaths.10 = EXT:form/Resources/Private/Backend/Partials/
+        layoutRootPaths.10 = EXT:form/Resources/Private/Backend/Layouts/
+    }
+}