Commit 931f50c0 authored by Ralf Zimmermann's avatar Ralf Zimmermann Committed by Frank Nägler
Browse files

[BUGFIX] Preserve multivalue property sorting in form fields

This ensures e.g. the order of select options is preserved between
form edits.

Background: If you put "{"5":"5","4":"4"}" into JavaScript the order is
arbitrary. Internally this issue is already covered by the form editor
by transforming such values into some meta structure like
"[{'_label': '5', '_value': '5'}, ]{'_label': '4', '_value': '4'}".

This fails if some multivalue properties are already set, and such a
formDefinition is opened by the form editor: "{"5":"5","4":"4"}" becomes
"{"4":"4","5":"5"}".

This fix converts such multivalue properties right before this data is
interpreted by JavaScript.

Resolves: #83911
Releases: master, 8.7
Change-Id: Ife5f6be959958fcf9a2cf5c3942b2549f33287a1
Reviewed-on: https://review.typo3.org/55865


Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: default avatarTobi Kretschmann <tobi@tobishome.de>
Reviewed-by: default avatarMathias Schreiber <mathias.schreiber@typo3.com>
Tested-by: default avatarMathias Schreiber <mathias.schreiber@typo3.com>
Reviewed-by: Kay Strobach's avatarKay Strobach <typo3@kay-strobach.de>
Tested-by: Kay Strobach's avatarKay Strobach <typo3@kay-strobach.de>
Reviewed-by: Frank Nägler's avatarFrank Naegler <frank.naegler@typo3.org>
Tested-by: Frank Nägler's avatarFrank Naegler <frank.naegler@typo3.org>
parent 819a118f
......@@ -81,6 +81,7 @@ class FormEditorController extends AbstractBackendController
$configurationService = $this->objectManager->get(ConfigurationService::class);
$this->prototypeConfiguration = $configurationService->getPrototypeConfiguration($prototypeName);
$formDefinition = $this->transformFormDefinitionForFormEditor($formDefinition);
$formEditorDefinitions = $this->getFormEditorDefinitions();
$formEditorAppInitialData = [
......@@ -422,6 +423,97 @@ class FormEditorController extends AbstractBackendController
return $view->render();
}
/**
* @param array $formDefinition
* @return array
*/
protected function transformFormDefinitionForFormEditor(array $formDefinition): array
{
$multiValueProperties = [];
foreach ($this->prototypeConfiguration['formElementsDefinition'] as $type => $configuration) {
if (!isset($configuration['formEditor']['editors'])) {
continue;
}
foreach ($configuration['formEditor']['editors'] as $editorConfiguration) {
if ($editorConfiguration['templateName'] === 'Inspector-PropertyGridEditor') {
$multiValueProperties[$type][] = $editorConfiguration['propertyPath'];
}
}
}
return $this->transformMultiValueElementsForFormEditor($formDefinition, $multiValueProperties);
}
/**
* Some data needs a transformation before it can be used by the
* form editor. This rules for multivalue elements like select
* elements. To ensure the right sorting if the data goes into
* javascript, we need to do transformations:
*
* [
* '5' => '5',
* '4' => '4',
* '3' => '3'
* ]
*
*
* This method transform this into:
*
* [
* [
* _label => '5'
* _value => 5
* ],
* [
* _label => '4'
* _value => 4
* ],
* [
* _label => '3'
* _value => 3
* ],
* ]
*
* @param array $formDefinition
* @param array $multiValueProperties
* @return array
*/
protected function transformMultiValueElementsForFormEditor(
array $formDefinition,
array $multiValueProperties
): array {
$output = $formDefinition;
foreach ($formDefinition as $key => $value) {
if (isset($value['type']) && array_key_exists($value['type'], $multiValueProperties)) {
$multiValuePropertiesForType = $multiValueProperties[$value['type']];
foreach ($multiValuePropertiesForType as $multiValueProperty) {
if (!ArrayUtility::isValidPath($value, $multiValueProperty, '.')) {
continue;
}
$multiValuePropertyData = ArrayUtility::getValueByPath($value, $multiValueProperty, '.');
if (!is_array($multiValuePropertyData)) {
continue;
}
$newMultiValuePropertyData = [];
foreach ($multiValuePropertyData as $k => $v) {
$newMultiValuePropertyData[] = [
'_label' => $v,
'_value' => $k
];
}
$value = ArrayUtility::setValueByPath($value, $multiValueProperty, $newMultiValuePropertyData, '.');
}
}
$output[$key] = $value;
if (is_array($value)) {
$output[$key] = $this->transformMultiValueElementsForFormEditor($value, $multiValueProperties);
}
}
return $output;
}
/**
* Returns the current BE user.
*
......
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\Property\TypeConverter;
namespace TYPO3\CMS\Form\Mvc\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
......@@ -63,7 +63,7 @@ class FormDefinitionArrayConverter extends AbstractTypeConverter
}
$rawFormDefinitionArray = ArrayUtility::stripTagsFromValuesRecursive($rawFormDefinitionArray);
$rawFormDefinitionArray = $this->convertJsonArrayToAssociativeArray($rawFormDefinitionArray);
$rawFormDefinitionArray = $this->transformMultiValueElementsForFormFramework($rawFormDefinitionArray);
$formDefinitionArray = new FormDefinitionArray($rawFormDefinitionArray);
return $formDefinitionArray;
......@@ -88,7 +88,7 @@ class FormDefinitionArrayConverter extends AbstractTypeConverter
* @param array $input
* @return array
*/
protected function convertJsonArrayToAssociativeArray(array $input): array
protected function transformMultiValueElementsForFormFramework(array $input): array
{
$output = [];
......@@ -99,7 +99,7 @@ class FormDefinitionArrayConverter extends AbstractTypeConverter
}
if (is_array($value)) {
$output[$key] = $this->convertJsonArrayToAssociativeArray($value);
$output[$key] = $this->transformMultiValueElementsForFormFramework($value);
} else {
$output[$key] = $value;
}
......
......@@ -396,4 +396,86 @@ class FormEditorControllerTest extends UnitTestCase
$mockController->_call('renderFormEditorTemplates', []);
}
/**
* @test
*/
public function transformMultiValueElementsForFormEditorConvertMultiValueDataIntoMetaData()
{
$mockController = $this->getAccessibleMock(FormEditorController::class, [
'dummy'
], [], '', false);
$input = [
0 => [
'bar' => 'baz',
],
1 => [
'type' => 'SOMEELEMENT',
'properties' => [
'options' => [
5 => '5',
4 => '4',
3 => '3',
2 => '2',
1 => '1',
],
],
],
2 => [
0 => [
'type' => 'TEST',
'properties' => [
'options' => [
5 => '5',
4 => '4',
3 => '3',
2 => '2',
1 => '1',
],
],
],
],
];
$multiValueProperties = [
'TEST' => [
0 => 'properties.options',
],
];
$expected = [
0 => [
'bar' => 'baz',
],
1 => [
'type' => 'SOMEELEMENT',
'properties' => [
'options' => [
5 => '5',
4 => '4',
3 => '3',
2 => '2',
1 => '1',
],
],
],
2 => [
0 => [
'type' => 'TEST',
'properties' => [
'options' => [
['_label' => '5', '_value' => 5],
['_label' => '4', '_value' => 4],
['_label' => '3', '_value' => 3],
['_label' => '2', '_value' => 2],
['_label' => '1', '_value' => 1],
],
],
],
],
];
$this->assertSame($expected, $mockController->_call('transformMultiValueElementsForFormEditor', $input, $multiValueProperties));
}
}
<?php
namespace TYPO3\CMS\Form\Tests\Unit\Property\TypeConverter;
namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Property\TypeConverter;
/*
* This file is part of the TYPO3 CMS project.
......@@ -14,12 +14,12 @@ namespace TYPO3\CMS\Form\Tests\Unit\Property\TypeConverter;
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Form\Property\TypeConverter\FormDefinitionArrayConverter;
use TYPO3\CMS\Form\Mvc\Property\TypeConverter\FormDefinitionArrayConverter;
use TYPO3\CMS\Form\Type\FormDefinitionArray;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
/**
* Test case for TYPO3\CMS\Form\Property\TypeConverter\FormDefinitionArrayConverter
* Test case for TYPO3\CMS\Form\Mvc\Property\TypeConverter\FormDefinitionArrayConverter
*/
class FormDefinitionArrayConverterTest extends UnitTestCase
{
......
......@@ -33,7 +33,7 @@ call_user_func(function () {
= \TYPO3\CMS\Form\Mvc\Property\PropertyMappingConfiguration::class;
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter(
\TYPO3\CMS\Form\Property\TypeConverter\FormDefinitionArrayConverter::class
\TYPO3\CMS\Form\Mvc\Property\TypeConverter\FormDefinitionArrayConverter::class
);
\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter(
\TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter::class
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment