[BUGFIX] Remove non existing fields in FormEngine ListOfFieldsContainer 30/51630/4
authorChristian Kuhn <lolli@schwarzbu.ch>
Fri, 10 Feb 2017 17:52:36 +0000 (18:52 +0100)
committerSusanne Moog <susanne.moog@typo3.org>
Sun, 12 Feb 2017 08:17:38 +0000 (09:17 +0100)
ListOfFieldsContainer of FormEngine render construct is used
to reduce the field list of a full record down to a given list of
fields that should be rendered, only.
It is triggered for instance from the list module if only a single
field of multiple different records should be edited at once.
Until now, the container did not fully validate if a given field
actually exists within the showitem field or palette and basically
delegated the handling down to a different container which in the
end skipped that field if it was not configured in TCA for given
record type.
The patch fixes this by improving the lookup. Field rendering is
no longer delegated down to other containers if the field in
question does not exist within the 'types' section and is not
within a referenced 'palette' of given record type.

Change-Id: I750ebf7c3d87ecb381f7bbe21b63528765841277
Resolves: #79750
Releases: master
Reviewed-on: https://review.typo3.org/51630
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Andreas Fernandez <typo3@scripting-base.de>
Tested-by: Andreas Fernandez <typo3@scripting-base.de>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
typo3/sysext/backend/Classes/Form/Container/ListOfFieldsContainer.php
typo3/sysext/backend/Tests/Unit/Form/Container/ListOfFieldsContainerTest.php [new file with mode: 0644]

index 377cfdd..b051988 100644 (file)
@@ -50,18 +50,26 @@ class ListOfFieldsContainer extends AbstractContainer
 
         $finalFieldsList = [];
         foreach ($fieldListToRender as $fieldName) {
-            $found = false;
             foreach ($fieldsByShowitem as $fieldByShowitem) {
                 $fieldByShowitemArray = $this->explodeSingleFieldShowItemConfiguration($fieldByShowitem);
                 if ($fieldByShowitemArray['fieldName'] === $fieldName) {
-                    $found = true;
                     $finalFieldsList[] = implode(';', $fieldByShowitemArray);
                     break;
                 }
-            }
-            if (!$found) {
-                // @todo: Field is shown even if it is not in type showitem list? Will be shown if field is in a palette.
-                $finalFieldsList[] = $fieldName;
+                if ($fieldByShowitemArray['fieldName'] === '--palette--'
+                    && isset($this->data['processedTca']['palettes'][$fieldByShowitemArray['paletteName']]['showitem'])
+                    && is_string($this->data['processedTca']['palettes'][$fieldByShowitemArray['paletteName']]['showitem'])
+                ) {
+                    $paletteName = $fieldByShowitemArray['paletteName'];
+                    $paletteFields = GeneralUtility::trimExplode(',', $this->data['processedTca']['palettes'][$paletteName]['showitem'], true);
+                    foreach ($paletteFields as $paletteField) {
+                        $paletteFieldArray = $this->explodeSingleFieldShowItemConfiguration($paletteField);
+                        if ($paletteFieldArray['fieldName'] === $fieldName) {
+                            $finalFieldsList[] = implode(';', $paletteFieldArray);
+                            break;
+                        }
+                    }
+                }
             }
         }
 
diff --git a/typo3/sysext/backend/Tests/Unit/Form/Container/ListOfFieldsContainerTest.php b/typo3/sysext/backend/Tests/Unit/Form/Container/ListOfFieldsContainerTest.php
new file mode 100644 (file)
index 0000000..a22ab75
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+declare(strict_types=1);
+namespace TYPO3\CMS\Backend\Tests\Unit\Form\Container;
+
+/*
+ * 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\Backend\Form\Container\ListOfFieldsContainer;
+use TYPO3\CMS\Backend\Form\Container\PaletteAndSingleContainer;
+use TYPO3\CMS\Backend\Form\NodeFactory;
+use TYPO3\Components\TestingFramework\Core\Unit\UnitTestCase;
+
+/**
+ * Test case
+ */
+class ListOfFieldsContainerTest extends UnitTestCase
+{
+    /**
+     * @test
+     */
+    public function renderDelegatesShowitemField()
+    {
+        $nodeFactoryProphecy = $this->prophesize(NodeFactory::class);
+        $paletteAndSingleContainerProphecy = $this->prophesize(PaletteAndSingleContainer::class);
+        $paletteAndSingleContainerProphecy->render(Argument::cetera())->shouldBeCalled()->willReturn('');
+
+        $input = [
+            'recordTypeValue' => 'aType',
+            'processedTca' => [
+                'types' => [
+                    'aType' => [
+                        'showitem' => 'aField',
+                    ],
+                ],
+            ],
+            'fieldListToRender' => 'aField',
+        ];
+
+        $expected = $input;
+        $expected['renderType'] = 'paletteAndSingleContainer';
+
+        // Verify 'fieldArray' contains 'aField' since that is a showitem field of this type
+        $expected['fieldsArray'] = [
+            'aField;;',
+        ];
+
+        $nodeFactoryProphecy->create($expected)->willReturn($paletteAndSingleContainerProphecy->reveal());
+        (new ListOfFieldsContainer($nodeFactoryProphecy->reveal(), $input))->render();
+    }
+
+    /**
+     * @test
+     */
+    public function renderDelegatesShowitemFieldAndRemovesDuplicates()
+    {
+        $nodeFactoryProphecy = $this->prophesize(NodeFactory::class);
+        $paletteAndSingleContainerProphecy = $this->prophesize(PaletteAndSingleContainer::class);
+        $paletteAndSingleContainerProphecy->render(Argument::cetera())->shouldBeCalled()->willReturn('');
+
+        $input = [
+            'recordTypeValue' => 'aType',
+            'processedTca' => [
+                'types' => [
+                    'aType' => [
+                        'showitem' => 'aField, bField;bLabel, cField',
+                    ],
+                ],
+            ],
+            'fieldListToRender' => 'aField, bField, aField',
+        ];
+
+        $expected = $input;
+        $expected['renderType'] = 'paletteAndSingleContainer';
+        // Duplicates are suppressed but label is kept
+        $expected['fieldsArray'] = [
+            'aField;;',
+            'bField;bLabel;',
+        ];
+
+        $nodeFactoryProphecy->create($expected)->willReturn($paletteAndSingleContainerProphecy->reveal());
+        (new ListOfFieldsContainer($nodeFactoryProphecy->reveal(), $input))->render();
+    }
+
+    /**
+     * @test
+     */
+    public function renderDelegatesPaletteFields()
+    {
+        $nodeFactoryProphecy = $this->prophesize(NodeFactory::class);
+        $paletteAndSingleContainerProphecy = $this->prophesize(PaletteAndSingleContainer::class);
+        $paletteAndSingleContainerProphecy->render(Argument::cetera())->shouldBeCalled()->willReturn('');
+
+        $input = [
+            'recordTypeValue' => 'aType',
+            'processedTca' => [
+                'types' => [
+                    'aType' => [
+                        'showitem' => '--palette--;;aPalette, --palette--;;anotherPalette',
+                    ],
+                ],
+                'palettes' => [
+                    'aPalette' => [
+                        'showitem' => 'aField',
+                    ],
+                    'anotherPalette' => [
+                        'showitem' => 'bField;bLabel, cField',
+                    ]
+                ],
+            ],
+            'fieldListToRender' => 'aField, bField',
+        ];
+
+        $expected = $input;
+        $expected['renderType'] = 'paletteAndSingleContainer';
+        // Both palette fields are found
+        $expected['fieldsArray'] = [
+            'aField;;',
+            'bField;bLabel;',
+        ];
+
+        $nodeFactoryProphecy->create($expected)->willReturn($paletteAndSingleContainerProphecy->reveal());
+        (new ListOfFieldsContainer($nodeFactoryProphecy->reveal(), $input))->render();
+    }
+
+    /**
+     * @test
+     */
+    public function renderRemovesNotExistingTypesField()
+    {
+        $nodeFactoryProphecy = $this->prophesize(NodeFactory::class);
+        $paletteAndSingleContainerProphecy = $this->prophesize(PaletteAndSingleContainer::class);
+        $paletteAndSingleContainerProphecy->render(Argument::cetera())->shouldBeCalled()->willReturn('');
+
+        $input = [
+            'recordTypeValue' => 'aType',
+            'processedTca' => [
+                'types' => [
+                    'aType' => [
+                        'showitem' => '--palette--;;aPalette',
+                    ],
+                ],
+                'palettes' => [
+                    'aPalette' => [
+                        'showitem' => 'aField',
+                    ],
+                ],
+            ],
+            'fieldListToRender' => 'aField, iDontExist',
+        ];
+
+        $expected = $input;
+        $expected['renderType'] = 'paletteAndSingleContainer';
+        // Duplicates are suppressed but label is kept
+        $expected['fieldsArray'] = [
+            'aField;;',
+        ];
+
+        $nodeFactoryProphecy->create($expected)->willReturn($paletteAndSingleContainerProphecy->reveal());
+        (new ListOfFieldsContainer($nodeFactoryProphecy->reveal(), $input))->render();
+    }
+}