[BUGFIX] EXT:form - handle boolean finisher options 90/54990/8
authorRalf Zimmermann <ralf.zimmermann@tritum.de>
Thu, 28 Sep 2017 07:53:05 +0000 (09:53 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Mon, 5 Feb 2018 13:21:24 +0000 (14:21 +0100)
Properly store boolean form definition values. Also
boolean finisher options are returned as boolean now.

Resolves: #82569
Releases: master, 8.7
Change-Id: I1eee0c6d8eaf07534d31622025f3c954d608627b
Reviewed-on: https://review.typo3.org/54990
Reviewed-by: Alexander Opitz <opitz.alexander@googlemail.com>
Tested-by: Alexander Opitz <opitz.alexander@googlemail.com>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Mathias Brodala <mbrodala@pagemachine.de>
Tested-by: Mathias Brodala <mbrodala@pagemachine.de>
Reviewed-by: Tobi Kretschmann <tobi@tobishome.de>
Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/form/Classes/Controller/FormEditorController.php
typo3/sysext/form/Classes/Domain/Finishers/AbstractFinisher.php
typo3/sysext/form/Classes/Property/TypeConverter/FormDefinitionArrayConverter.php [new file with mode: 0644]
typo3/sysext/form/Classes/Type/FormDefinitionArray.php [new file with mode: 0644]
typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Core.js
typo3/sysext/form/Tests/Unit/Controller/FormEditorControllerTest.php
typo3/sysext/form/Tests/Unit/Domain/Finishers/AbstractFinisherTest.php
typo3/sysext/form/Tests/Unit/Property/TypeConverter/FormDefinitionArrayConverterTest.php [new file with mode: 0644]
typo3/sysext/form/ext_localconf.php

index d83a854..be7baed 100644 (file)
@@ -29,6 +29,7 @@ use TYPO3\CMS\Form\Domain\Exception\RenderingException;
 use TYPO3\CMS\Form\Domain\Factory\ArrayFormFactory;
 use TYPO3\CMS\Form\Mvc\Persistence\Exception\PersistenceManagerException;
 use TYPO3\CMS\Form\Service\TranslationService;
+use TYPO3\CMS\Form\Type\FormDefinitionArray;
 use TYPO3\CMS\Lang\LanguageService;
 
 /**
@@ -145,14 +146,12 @@ class FormEditorController extends AbstractBackendController
      * Save a formDefinition which was build by the form editor.
      *
      * @param string $formPersistenceIdentifier
-     * @param array $formDefinition
+     * @param FormDefinitionArray $formDefinition
      * @internal
      */
-    public function saveFormAction(string $formPersistenceIdentifier, array $formDefinition)
+    public function saveFormAction(string $formPersistenceIdentifier, FormDefinitionArray $formDefinition)
     {
-        $formDefinition = ArrayUtility::stripTagsFromValuesRecursive($formDefinition);
-        $formDefinition = $this->convertJsonArrayToAssociativeArray($formDefinition);
-
+        $formDefinition = $formDefinition->getArrayCopy();
         if (
             isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormSave'])
             && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeFormSave'])
@@ -194,22 +193,20 @@ class FormEditorController extends AbstractBackendController
      * Render a page from the formDefinition which was build by the form editor.
      * Use the frontend rendering and set the form framework to preview mode.
      *
-     * @param array $formDefinition
+     * @param FormDefinitionArray $formDefinition
      * @param int $pageIndex
      * @param string $prototypeName
      * @return string
      * @internal
      */
-    public function renderFormPageAction(array $formDefinition, int $pageIndex, string $prototypeName = null): string
+    public function renderFormPageAction(FormDefinitionArray $formDefinition, int $pageIndex, string $prototypeName = null): string
     {
-        $formDefinition = ArrayUtility::stripTagsFromValuesRecursive($formDefinition);
-        $formDefinition = $this->convertJsonArrayToAssociativeArray($formDefinition);
         if (empty($prototypeName)) {
             $prototypeName = isset($formDefinition['prototypeName']) ? $formDefinition['prototypeName'] : 'standard';
         }
 
         $formFactory = $this->objectManager->get(ArrayFormFactory::class);
-        $formDefinition = $formFactory->build($formDefinition, $prototypeName);
+        $formDefinition = $formFactory->build($formDefinition->getArrayCopy(), $prototypeName);
         $formDefinition->setRenderingOption('previewMode', true);
         $form = $formDefinition->bind($this->request, $this->response);
         $form->overrideCurrentPage($pageIndex);
@@ -379,42 +376,6 @@ class FormEditorController extends AbstractBackendController
     }
 
     /**
-     * Some data which is build by the form editor needs a transformation before
-     * it can be used by the framework.
-     * Multivalue elements like select elements produce data like:
-     *
-     * [
-     *   _label => 'label'
-     *   _value => 'value'
-     * ]
-     *
-     * This method transform this into:
-     *
-     * [
-     *   'value' => 'label'
-     * ]
-     *
-     * @param array $input
-     * @return 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->convertJsonArrayToAssociativeArray($value);
-            } else {
-                $output[$key] = $value;
-            }
-        }
-        return $output;
-    }
-
-    /**
      * Render the "text/x-formeditor-template" templates.
      *
      * @param array $formEditorDefinitions
index 2b3b4f8..b5a2cc9 100644 (file)
@@ -170,7 +170,7 @@ abstract class AbstractFinisher implements FinisherInterface
             return null;
         }
 
-        if (is_array($optionValue)) {
+        if (is_array($optionValue) || is_bool($optionValue)) {
             return $optionValue;
         }
 
diff --git a/typo3/sysext/form/Classes/Property/TypeConverter/FormDefinitionArrayConverter.php b/typo3/sysext/form/Classes/Property/TypeConverter/FormDefinitionArrayConverter.php
new file mode 100644 (file)
index 0000000..4a8089e
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Form\Property\TypeConverter;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Utility\ArrayUtility;
+use TYPO3\CMS\Extbase\Property\Exception as PropertyException;
+use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface;
+use TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter;
+use TYPO3\CMS\Form\Type\FormDefinitionArray;
+
+/**
+ * Converter for form definition arrays
+ *
+ * @internal
+ */
+class FormDefinitionArrayConverter extends AbstractTypeConverter
+{
+    /**
+     * @var array<string>
+     */
+    protected $sourceTypes = ['string'];
+
+    /**
+     * @var string
+     */
+    protected $targetType = FormDefinitionArray::class;
+
+    /**
+     * @var int
+     */
+    protected $priority = 10;
+
+    /**
+     * Convert from $source to $targetType, a noop if the source is an array.
+     * If it is an empty string it will be converted to an empty array.
+     *
+     * @param string $source
+     * @param string $targetType
+     * @param array $convertedChildProperties
+     * @param PropertyMappingConfigurationInterface $configuration
+     * @return FormDefinitionArray
+     * @throws PropertyException
+     */
+    public function convertFrom($source, $targetType, array $convertedChildProperties = [], PropertyMappingConfigurationInterface $configuration = null)
+    {
+        $rawFormDefinitionArray = json_decode($source, true);
+
+        if (json_last_error() !== JSON_ERROR_NONE) {
+            throw new PropertyException('Unable to decode JSON source: ' . json_last_error_msg(), 1512578002);
+        }
+
+        $rawFormDefinitionArray = ArrayUtility::stripTagsFromValuesRecursive($rawFormDefinitionArray);
+        $rawFormDefinitionArray = $this->convertJsonArrayToAssociativeArray($rawFormDefinitionArray);
+        $formDefinitionArray = new FormDefinitionArray($rawFormDefinitionArray);
+
+        return $formDefinitionArray;
+    }
+
+    /**
+     * Some data which is build by the form editor needs a transformation before
+     * it can be used by the framework.
+     * Multivalue elements like select elements produce data like:
+     *
+     * [
+     *   _label => 'label'
+     *   _value => 'value'
+     * ]
+     *
+     * This method transform this into:
+     *
+     * [
+     *   'value' => 'label'
+     * ]
+     *
+     * @param array $input
+     * @return 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->convertJsonArrayToAssociativeArray($value);
+            } else {
+                $output[$key] = $value;
+            }
+        }
+
+        return $output;
+    }
+}
diff --git a/typo3/sysext/form/Classes/Type/FormDefinitionArray.php b/typo3/sysext/form/Classes/Type/FormDefinitionArray.php
new file mode 100644 (file)
index 0000000..cdc99e3
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Form\Type;
+
+/*
+ * 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!
+ */
+
+/**
+ * Wrapper for basic form definition arrays
+ */
+class FormDefinitionArray extends \ArrayObject
+{
+}
index aecdd39..1ec7ee5 100644 (file)
@@ -1992,7 +1992,7 @@ define(['jquery'], function($) {
         _runningAjaxRequests['saveForm'] = $.post(_dataBackendEndpoints['saveForm'], {
           tx_form_web_formformbuilder: {
             formPersistenceIdentifier: _dataBackendPersistenceIdentifier,
-            formDefinition: utility().convertToSimpleObject(getApplicationStateStack().getCurrentState('formDefinition'))
+            formDefinition: JSON.stringify(utility().convertToSimpleObject(getApplicationStateStack().getCurrentState('formDefinition')))
           }
         }, function(data, textStatus, jqXHR) {
           if (_runningAjaxRequests['saveForm'] !== jqXHR) {
index 9a80dda..a40315f 100644 (file)
@@ -283,39 +283,6 @@ class FormEditorControllerTest extends \TYPO3\TestingFramework\Core\Unit\UnitTes
     /**
      * @test
      */
-    public function convertJsonArrayToAssociativeArrayReturnTransformedArray()
-    {
-        $mockController = $this->getAccessibleMock(FormEditorController::class, [
-            'dummy'
-        ], [], '', false);
-
-        $input = [
-            'francine' => 'stan',
-            'properties' => [
-                'options' => [
-                    0 => [
-                        '_label' => 'label',
-                        '_value' => 'value',
-                    ],
-                ],
-            ],
-        ];
-
-        $expected = [
-            'francine' => 'stan',
-            'properties' => [
-                'options' => [
-                    'value' => 'label',
-                ],
-            ],
-        ];
-
-        $this->assertSame($expected, $mockController->_call('convertJsonArrayToAssociativeArray', $input));
-    }
-
-    /**
-     * @test
-     */
     public function renderFormEditorTemplatesThrowsExceptionIfLayoutRootPathsNotSet()
     {
         $this->expectException(RenderingException::class);
index aa33f23..7fdbea9 100644 (file)
@@ -122,6 +122,27 @@ class AbstractFinisherTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCas
     /**
      * @test
      */
+    public function parseOptionReturnsBoolOptionValuesAsBool()
+    {
+        $mockAbstractFinisher = $this->getAccessibleMockForAbstractClass(
+            AbstractFinisher::class,
+            [],
+            '',
+            false
+        );
+
+        $mockAbstractFinisher->_set('options', [
+            'foo1' => false,
+        ]);
+
+        $expected = false;
+
+        $this->assertSame($expected, $mockAbstractFinisher->_call('parseOption', 'foo1'));
+    }
+
+    /**
+     * @test
+     */
     public function parseOptionReturnsValueFromFormRuntimeIfOptionNameReferenceAFormElementIdentifierWhoseValueIsAString()
     {
         $objectMangerProphecy = $this->prophesize(ObjectManager::class);
diff --git a/typo3/sysext/form/Tests/Unit/Property/TypeConverter/FormDefinitionArrayConverterTest.php b/typo3/sysext/form/Tests/Unit/Property/TypeConverter/FormDefinitionArrayConverterTest.php
new file mode 100644 (file)
index 0000000..39670c5
--- /dev/null
@@ -0,0 +1,47 @@
+<?php
+namespace TYPO3\CMS\Form\Tests\Unit\Property\TypeConverter;
+
+/*
+ * 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\Property\TypeConverter\FormDefinitionArrayConverter;
+use TYPO3\CMS\Form\Type\FormDefinitionArray;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+/**
+ * Test case for TYPO3\CMS\Form\Property\TypeConverter\FormDefinitionArrayConverter
+ */
+class FormDefinitionArrayConverterTest extends UnitTestCase
+{
+    /**
+     * @test
+     */
+    public function convertsJsonStringToFormDefinitionArray()
+    {
+        $typeConverter = new FormDefinitionArrayConverter();
+        $source = '{"francine":"stan","enabled":false,"properties":{"options":[{"_label":"label","_value":"value"}]}}';
+        $expected = [
+            'francine' => 'stan',
+            'enabled' => false,
+            'properties' => [
+                'options' => [
+                    'value' => 'label',
+                ],
+            ],
+        ];
+        $result = $typeConverter->convertFrom($source, FormDefinitionArray::class);
+
+        $this->assertInstanceOf(FormDefinitionArray::class, $result);
+        $this->assertSame($expected, $result->getArrayCopy());
+    }
+}
index 9ddd5f1..a23ef83 100644 (file)
@@ -73,6 +73,10 @@ call_user_func(function () {
         );
     }
 
+    \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter(
+        \TYPO3\CMS\Form\Property\TypeConverter\FormDefinitionArrayConverter::class
+    );
+
     // Register "formvh:" namespace
     $GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['formvh'][] = 'TYPO3\\CMS\\Form\\ViewHelpers';