diff --git a/typo3/sysext/backend/Classes/Controller/SelectTreeController.php b/typo3/sysext/backend/Classes/Controller/SelectTreeController.php index 01a25f69ebd8019d0674a548e24b76a18f73383f..a0f6423a6b2b68ba22809a6a9768f5ebe4bfa5d5 100644 --- a/typo3/sysext/backend/Classes/Controller/SelectTreeController.php +++ b/typo3/sysext/backend/Classes/Controller/SelectTreeController.php @@ -18,6 +18,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Backend\Form\FormDataCompiler; use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord; +use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools; use TYPO3\CMS\Core\Utility\GeneralUtility; /** @@ -35,25 +36,76 @@ class SelectTreeController public function fetchDataAction(ServerRequestInterface $request, ResponseInterface $response) { $tableName = $request->getQueryParams()['table']; - if (!$this->getBackendUser()->check('tables_select', $tableName)) { - return $response; + $fieldName = $request->getQueryParams()['field']; + + // Prepare processedTca: Remove all column definitions except the one that contains + // our tree definition. This way only this field is calculated, everything else is ignored. + if (!isset($GLOBALS['TCA'][$tableName]) || !is_array($GLOBALS['TCA'][$tableName])) { + throw new \RuntimeException( + 'TCA for table ' . $tableName . ' not found', + 1479386729 + ); } + $processedTca = $GLOBALS['TCA'][$tableName]; + if (!isset($processedTca['columns'][$fieldName]) || !is_array($processedTca['columns'][$fieldName])) { + throw new \RuntimeException( + 'TCA for table ' . $tableName . ' and field ' . $fieldName . ' not found', + 1479386990 + ); + } + + // Force given record type and set showitem to our field only + $recordTypeValue = $request->getQueryParams()['record_type_value']; + $processedTca['types'][$recordTypeValue]['showitem'] = $fieldName; + // Unset all columns except our field + $processedTca['columns'] = [ + $fieldName => $processedTca['columns'][$fieldName], + ]; + + $flexFormPath = []; + if ($processedTca['columns'][$fieldName]['config']['type'] === 'flex') { + $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class); + $dataStructureIdentifier = json_encode($request->getQueryParams()['flex_form_datastructure_identifier']); + $dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier); + // Try to reduce given data structure down to the relevant element only + $flexFormPath = $request->getQueryParams()['flex_form_path']; + $fieldPattern = 'data[' . $tableName . ']['; + $flexFormPath = str_replace($fieldPattern, '', $flexFormPath); + $flexFormPath = substr($flexFormPath, 0, -1); + $flexFormPath = explode('][', $flexFormPath); + if (isset($dataStructure['sheets'][$flexFormPath[3]]['ROOT']['el'][$flexFormPath[5]])) { + $dataStructure = [ + 'sheets' => [ + $flexFormPath[3] => [ + 'ROOT' => [ + 'type' => 'array', + 'el' => [ + $flexFormPath[5] => $dataStructure['sheets'][$flexFormPath[3]]['ROOT']['el'][$flexFormPath[5]], + ], + ], + ], + ], + ]; + } + $processedTca['columns'][$fieldName]['config']['ds'] = $dataStructure; + $processedTca['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier; + } + $formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class); $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup); - $formDataCompilerInput = [ 'tableName' => $request->getQueryParams()['table'], 'vanillaUid' => (int)$request->getQueryParams()['uid'], 'command' => $request->getQueryParams()['command'], + 'processedTca' => $processedTca, + 'recordTypeValue' => $recordTypeValue, + 'selectTreeCompileItems' => true, ]; - - $fieldName = $request->getQueryParams()['field']; $formData = $formDataCompiler->compile($formDataCompilerInput); if ($formData['processedTca']['columns'][$fieldName]['config']['type'] === 'flex') { - $flexFormFieldName = $request->getQueryParams()['flex_form_field_name']; - $value = $this->searchForFieldInFlexStructure($formData['processedTca']['columns'][$fieldName]['config'], $flexFormFieldName); - $treeData = $value['config']['treeData']; + $treeData = $formData['processedTca']['columns'][$fieldName]['config']['ds'] + ['sheets'][$flexFormPath[3]]['ROOT']['el'][$flexFormPath[5]]['config']['treeData']; } else { $treeData = $formData['processedTca']['columns'][$fieldName]['config']['treeData']; } @@ -62,37 +114,4 @@ class SelectTreeController $response->getBody()->write($json); return $response; } - - /** - * A workaround for flexforms - there is no easy way to get flex field by key, so we need to search for it - * - * @todo remove me once flexforms are refactored - * - * @param array $array - * @param string $needle - * @return array - */ - protected function searchForFieldInFlexStructure(array $array, $needle) - { - $needle = trim($needle); - $iterator = new \RecursiveArrayIterator($array); - $recursive = new \RecursiveIteratorIterator( - $iterator, - \RecursiveIteratorIterator::SELF_FIRST - ); - foreach ($recursive as $key => $value) { - if ($key === $needle) { - return $value; - } - } - return []; - } - - /** - * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication - */ - protected function getBackendUser() - { - return $GLOBALS['BE_USER']; - } } diff --git a/typo3/sysext/backend/Classes/Form/Container/FlexFormElementContainer.php b/typo3/sysext/backend/Classes/Form/Container/FlexFormElementContainer.php index 5e8d4911732a7f2a97b2251c6d3be04099c3fde9..c8b7cc842fff2982d2ceabbe557e9e9c075167ea 100644 --- a/typo3/sysext/backend/Classes/Form/Container/FlexFormElementContainer.php +++ b/typo3/sysext/backend/Classes/Form/Container/FlexFormElementContainer.php @@ -79,8 +79,6 @@ class FlexFormElementContainer extends AbstractContainer // Set up options for single element $fakeParameterArray = [ 'fieldConf' => [ - // @todo review this field during flex refactoring - 'flexFormFieldName' => $flexFormFieldName, 'label' => $languageService->sL(trim($flexFormFieldArray['label'])), 'config' => $flexFormFieldArray['config'], 'children' => $flexFormFieldArray['children'], diff --git a/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php b/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php index d1ae3ff5711f3a6392f2163ccbf86da0c4aebfdd..4af7e775f0c6b75c8b13d414fd538783aadb5ff2 100644 --- a/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php +++ b/typo3/sysext/backend/Classes/Form/Element/SelectTreeElement.php @@ -76,7 +76,12 @@ class SelectTreeElement extends AbstractFormElement $heightInPx = $height * $this->itemHeight; $treeWrapperId = 'tree_' . $formElementId; - $flexFormFieldName = !empty($parameterArray['fieldConf']['flexFormFieldName']) ? htmlspecialchars($parameterArray['fieldConf']['flexFormFieldName']) : ''; + $fieldName = $this->data['fieldName']; + $flexDataStructureIdentifier = ''; + if ($this->data['processedTca']['columns'][$fieldName]['config']['type'] === 'flex') { + $flexDataStructureIdentifier = $this->data['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier']; + } + $html = []; $html[] = '<div class="typo3-tceforms-tree">'; $html[] = ' <input class="treeRecord" type="hidden"'; @@ -85,8 +90,9 @@ class SelectTreeElement extends AbstractFormElement $html[] = ' data-relatedfieldname="' . htmlspecialchars($parameterArray['itemFormElName']) . '"'; $html[] = ' data-table="' . htmlspecialchars($this->data['tableName']) . '"'; $html[] = ' data-field="' . htmlspecialchars($this->data['fieldName']) . '"'; - $html[] = ' data-flex-form-field-name="' . $flexFormFieldName . '"'; + $html[] = ' data-flex-form-datastructure-identifier="' . htmlspecialchars($flexDataStructureIdentifier) . '"'; $html[] = ' data-uid="' . (int)$this->data['vanillaUid'] . '"'; + $html[] = ' data-recordtypevalue="' . $this->data['recordTypeValue'] . '"'; $html[] = ' data-command="' . htmlspecialchars($this->data['command']) . '"'; $html[] = ' data-read-only="' . $readOnly . '"'; $html[] = ' data-tree-exclusive-keys="' . htmlspecialchars($exclusiveKeys) . '"'; diff --git a/typo3/sysext/backend/Classes/Form/FormDataCompiler.php b/typo3/sysext/backend/Classes/Form/FormDataCompiler.php index 24fff348aff55b7fe333d09a2520b48b6db25693..ff8729cf9475bd09ac9695bde60cf7e079acdd6e 100644 --- a/typo3/sysext/backend/Classes/Form/FormDataCompiler.php +++ b/typo3/sysext/backend/Classes/Form/FormDataCompiler.php @@ -190,7 +190,7 @@ class FormDataCompiler // can be shown. This array holds those additional language records, Array key is sys_language_uid. 'additionalLanguageRows' => [], // The tca record type value of the record. Forced to string, there can be "named" type values. - 'recordTypeValue' => '0', + 'recordTypeValue' => '', // TCA of table with processed fields. After processing, this array contains merged and resolved // array data, items were resolved, only used types are set, renderTypes are set. 'processedTca' => [], @@ -204,6 +204,10 @@ class FormDataCompiler // itemsProcFunc need to have this data at hand to do their job. 'flexParentDatabaseRow' => [], + // If true, TcaSelectTreeItems data provider will compile tree items. This is false by default since + // on opening a record items are not calculated but are fetch in an ajax request, see SelectTreeController. + 'selectTreeCompileItems' => false, + // BackendUser->uc['inlineView'] - This array holds status of expand / collapsed inline items // This array is "flat", an inline structure with parent uid 1 having firstChild uid 2 having secondChild uid 3 // firstChild and secondChild are not nested. If an uid is set it means "record is expanded", example: diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/DatabaseRecordTypeValue.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/DatabaseRecordTypeValue.php index e78cb7a115114aaf1f76fcdefa1601cae002dfd8..dc8a85dc128f37094d442e80d8092c13028fae3e 100644 --- a/typo3/sysext/backend/Classes/Form/FormDataProvider/DatabaseRecordTypeValue.php +++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/DatabaseRecordTypeValue.php @@ -46,6 +46,11 @@ class DatabaseRecordTypeValue implements FormDataProviderInterface ); } + // Guard clause to suppress any calculation if record type value has been set from outside already + if ($result['recordTypeValue'] !== '') { + return $result; + } + $recordTypeValue = '0'; if (!empty($result['processedTca']['ctrl']['type'])) { $tcaTypeField = $result['processedTca']['ctrl']['type']; diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/InitializeProcessedTca.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/InitializeProcessedTca.php index 2d807f4fd27c31a75dcc229e8d6ddfef9babd16e..4cfb47adf60d644b6315081be4bb3dba812cf0b2 100644 --- a/typo3/sysext/backend/Classes/Form/FormDataProvider/InitializeProcessedTca.php +++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/InitializeProcessedTca.php @@ -30,16 +30,19 @@ class InitializeProcessedTca implements FormDataProviderInterface */ public function addData(array $result) { - if ( - !isset($GLOBALS['TCA'][$result['tableName']]) - || !is_array($GLOBALS['TCA'][$result['tableName']]) - ) { - throw new \UnexpectedValueException( - 'TCA for table ' . $result['tableName'] . ' not found', - 1437914223 - ); + if (empty($result['processedTca'])) { + if ( + !isset($GLOBALS['TCA'][$result['tableName']]) + || !is_array($GLOBALS['TCA'][$result['tableName']]) + ) { + throw new \UnexpectedValueException( + 'TCA for table ' . $result['tableName'] . ' not found', + 1437914223 + ); + } + + $result['processedTca'] = $GLOBALS['TCA'][$result['tableName']]; } - $result['processedTca'] = $GLOBALS['TCA'][$result['tableName']]; if (!is_array($result['processedTca']['columns'])) { throw new \UnexpectedValueException( diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexPrepare.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexPrepare.php index 054fd306432fd29326c05cba3db61e37eab5b202..e0f2843b3714403890d3a597f14ed2c317817167 100644 --- a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexPrepare.php +++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexPrepare.php @@ -63,16 +63,21 @@ class TcaFlexPrepare implements FormDataProviderInterface */ protected function initializeDataStructure(array $result, $fieldName) { - $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class); - $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier( - $result['processedTca']['columns'][$fieldName], - $result['tableName'], - $fieldName, - $result['databaseRow'] - ); - // Add the identifier to TCA to use it later during rendering - $result['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier; - $dataStructureArray = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier); + if (!isset($result['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier'])) { + $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class); + $dataStructureIdentifier = $flexFormTools->getDataStructureIdentifier( + $result['processedTca']['columns'][$fieldName], + $result['tableName'], + $fieldName, + $result['databaseRow'] + ); + // Add the identifier to TCA to use it later during rendering + $result['processedTca']['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier; + $dataStructureArray = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier); + } else { + // Assume the data structure has been given from outside if the data structure identifier is already set. + $dataStructureArray = $result['processedTca']['columns'][$fieldName]['config']['ds']; + } if (!isset($dataStructureArray['meta']) || !is_array($dataStructureArray['meta'])) { $dataStructureArray['meta'] = []; } diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexProcess.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexProcess.php index ada1c46063fa87818932502e8c3b10b85f3a4bbc..b40826741017ad49cd3451cb727bd391fda9fac1 100644 --- a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexProcess.php +++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaFlexProcess.php @@ -468,6 +468,7 @@ class TcaFlexProcess implements FormDataProviderInterface $singleFieldName => $singleFieldConfiguration, ], ], + 'selectTreeCompileItems' => false, 'flexParentDatabaseRow' => $result['databaseRow'], ]; $flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment); @@ -518,6 +519,8 @@ class TcaFlexProcess implements FormDataProviderInterface 'columns' => [], ], 'flexParentDatabaseRow' => $result['databaseRow'], + // Whether to compile TCA tree items - inherit from parent + 'selectTreeCompileItems' => $result['selectTreeCompileItems'], ]; if (!empty($tcaNewColumns)) { diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaSelectTreeItems.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaSelectTreeItems.php index eede7112f17cc949d3ba54f9235bf7289fc29ee5..92fed04939be7fd3cee7e68f5a82ba10e153250a 100644 --- a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaSelectTreeItems.php +++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaSelectTreeItems.php @@ -46,42 +46,8 @@ class TcaSelectTreeItems extends AbstractItemProvider implements FormDataProvide continue; } - $fieldConfig['config']['items'] = $this->sanitizeItemArray($fieldConfig['config']['items'], $table, $fieldName); $fieldConfig['config']['maxitems'] = $this->sanitizeMaxItems($fieldConfig['config']['maxitems']); - $pageTsConfigAddItems = $this->addItemsFromPageTsConfig($result, $fieldName, []); - $fieldConfig['config']['items'] = $this->addItemsFromSpecial($result, $fieldName, $fieldConfig['config']['items']); - $fieldConfig['config']['items'] = $this->addItemsFromFolder($result, $fieldName, $fieldConfig['config']['items']); - $staticItems = $fieldConfig['config']['items'] + $pageTsConfigAddItems; - - $fieldConfig['config']['items'] = $this->addItemsFromForeignTable($result, $fieldName, $fieldConfig['config']['items']); - $dynamicItems = array_diff_key($fieldConfig['config']['items'], $staticItems); - - $fieldConfig['config']['items'] = $this->removeItemsByKeepItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']); - $fieldConfig['config']['items'] = $pageTsConfigAddItems + $fieldConfig['config']['items']; - $fieldConfig['config']['items'] = $this->removeItemsByRemoveItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']); - - $fieldConfig['config']['items'] = $this->removeItemsByUserLanguageFieldRestriction($result, $fieldName, $fieldConfig['config']['items']); - $fieldConfig['config']['items'] = $this->removeItemsByUserAuthMode($result, $fieldName, $fieldConfig['config']['items']); - $fieldConfig['config']['items'] = $this->removeItemsByDoktypeUserRestriction($result, $fieldName, $fieldConfig['config']['items']); - - // Resolve "itemsProcFunc" - if (!empty($fieldConfig['config']['itemsProcFunc'])) { - $fieldConfig['config']['items'] = $this->resolveItemProcessorFunction($result, $fieldName, $fieldConfig['config']['items']); - // itemsProcFunc must not be used anymore - unset($fieldConfig['config']['itemsProcFunc']); - } - - // Translate labels - $fieldConfig['config']['items'] = $this->translateLabels($result, $fieldConfig['config']['items'], $table, $fieldName); - - $staticValues = $this->getStaticValues($fieldConfig['config']['items'], $dynamicItems); - $result['databaseRow'][$fieldName] = $this->processDatabaseFieldValue($result['databaseRow'], $fieldName); - $result['databaseRow'][$fieldName] = $this->processSelectFieldValue($result, $fieldName, $staticValues); - - // Keys may contain table names, so a numeric array is created - $fieldConfig['config']['items'] = array_values($fieldConfig['config']['items']); - // A couple of tree specific config parameters can be overwritten via page TS. // Pick those that influence the data fetching and write them into the config // given to the tree data provider @@ -102,7 +68,44 @@ class TcaSelectTreeItems extends AbstractItemProvider implements FormDataProvide } } - $fieldConfig['config']['treeData'] = $this->renderTree($result, $fieldConfig, $fieldName, $staticItems); + if ($result['selectTreeCompileItems']) { + $fieldConfig['config']['items'] = $this->sanitizeItemArray($fieldConfig['config']['items'], $table, $fieldName); + + $pageTsConfigAddItems = $this->addItemsFromPageTsConfig($result, $fieldName, []); + $fieldConfig['config']['items'] = $this->addItemsFromSpecial($result, $fieldName, $fieldConfig['config']['items']); + $fieldConfig['config']['items'] = $this->addItemsFromFolder($result, $fieldName, $fieldConfig['config']['items']); + $staticItems = $fieldConfig['config']['items'] + $pageTsConfigAddItems; + + $fieldConfig['config']['items'] = $this->addItemsFromForeignTable($result, $fieldName, $fieldConfig['config']['items']); + $dynamicItems = array_diff_key($fieldConfig['config']['items'], $staticItems); + + $fieldConfig['config']['items'] = $this->removeItemsByKeepItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']); + $fieldConfig['config']['items'] = $pageTsConfigAddItems + $fieldConfig['config']['items']; + $fieldConfig['config']['items'] = $this->removeItemsByRemoveItemsPageTsConfig($result, $fieldName, $fieldConfig['config']['items']); + + $fieldConfig['config']['items'] = $this->removeItemsByUserLanguageFieldRestriction($result, $fieldName, $fieldConfig['config']['items']); + $fieldConfig['config']['items'] = $this->removeItemsByUserAuthMode($result, $fieldName, $fieldConfig['config']['items']); + $fieldConfig['config']['items'] = $this->removeItemsByDoktypeUserRestriction($result, $fieldName, $fieldConfig['config']['items']); + + // Resolve "itemsProcFunc" + if (!empty($fieldConfig['config']['itemsProcFunc'])) { + $fieldConfig['config']['items'] = $this->resolveItemProcessorFunction($result, $fieldName, $fieldConfig['config']['items']); + // itemsProcFunc must not be used anymore + unset($fieldConfig['config']['itemsProcFunc']); + } + + // Translate labels + $fieldConfig['config']['items'] = $this->translateLabels($result, $fieldConfig['config']['items'], $table, $fieldName); + + $staticValues = $this->getStaticValues($fieldConfig['config']['items'], $dynamicItems); + $result['databaseRow'][$fieldName] = $this->processDatabaseFieldValue($result['databaseRow'], $fieldName); + $result['databaseRow'][$fieldName] = $this->processSelectFieldValue($result, $fieldName, $staticValues); + + // Keys may contain table names, so a numeric array is created + $fieldConfig['config']['items'] = array_values($fieldConfig['config']['items']); + + $fieldConfig['config']['treeData'] = $this->renderTree($result, $fieldConfig, $fieldName, $staticItems); + } $result['processedTca']['columns'][$fieldName] = $fieldConfig; } @@ -151,7 +154,6 @@ class TcaSelectTreeItems extends AbstractItemProvider implements FormDataProvide $treeConfig = [ 'items' => $treeItems, - 'selectedNodes' => $this->prepareSelectedNodes($fieldConfig['config']['items'], $result['databaseRow'][$fieldName]) ]; return $treeConfig; @@ -186,31 +188,6 @@ class TcaSelectTreeItems extends AbstractItemProvider implements FormDataProvide return $additionalItems; } - /** - * Make sure to only keep the selected nodes that are really available in the database and for the user - * (e.g. after permissions etc) - * - * @param array $itemArray - * @param array $databaseValues - * @return array - * @todo: this is ugly - should be removed with the tree rewrite - */ - protected function prepareSelectedNodes(array $itemArray, array $databaseValues) - { - $selectedNodes = []; - if (!empty($databaseValues)) { - foreach ($databaseValues as $selectedNode) { - foreach ($itemArray as $possibleSelectBoxItem) { - if ((string)$possibleSelectBoxItem[1] === (string)$selectedNode) { - $selectedNodes[] = $selectedNode; - } - } - } - } - - return $selectedNodes; - } - /** * Determines whether the current field is a valid target for this DataProvider * diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTreeElement.js b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTreeElement.js index a596eae76b56f480f3a4d2e341af3d229a9dad40..c9f5c769b18650dd17f62b7d8c5e4409ba895892 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTreeElement.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/FormEngine/Element/SelectTreeElement.js @@ -33,7 +33,9 @@ define(['jquery', 'TYPO3/CMS/Backend/FormEngine/Element/SelectTree'], function ( table: treeInput.data('table'), field: treeInput.data('field'), uid: treeInput.data('uid'), - flex_form_field_name: treeInput.data('flex-form-field-name'), + record_type_value: treeInput.data('recordtypevalue'), + flex_form_datastructure_identifier: treeInput.data('flex-form-datastructure-identifier'), + flex_form_path: treeInput.data('formengine-input-name'), command: treeInput.data('command') }; var $wrapper = treeInput.parent().siblings('.svg-tree-wrapper'); diff --git a/typo3/sysext/backend/Tests/Unit/Controller/SelectTreeControllerTest.php b/typo3/sysext/backend/Tests/Unit/Controller/SelectTreeControllerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..5dddc632b5acb6063df2f061a9188f1729556fa2 --- /dev/null +++ b/typo3/sysext/backend/Tests/Unit/Controller/SelectTreeControllerTest.php @@ -0,0 +1,55 @@ +<?php +namespace TYPO3\CMS\Backend\Tests\Unit\Controller; + +/* + * 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 Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\ServerRequestInterface; +use TYPO3\CMS\Backend\Controller\SelectTreeController; +use TYPO3\CMS\Core\Tests\UnitTestCase; + +/** + * Test case + */ +class SelectTreeControllerTest extends UnitTestCase +{ + /** + * @test + */ + public function fetchDataActionThrowsExceptionIfTcaOfTableDoesNotExist() + { + $requestProphecy = $this->prophesize(ServerRequestInterface::class); + $responseProphecy = $this->prophesize(ResponseInterface::class); + $this->expectException(\RuntimeException::class); + $this->expectExceptionCode(1479386729); + (new SelectTreeController())->fetchDataAction($requestProphecy->reveal(), $responseProphecy->reveal()); + } + + /** + * @test + */ + public function fetchDataActionThrowsExceptionIfTcaOfTableFieldDoesNotExist() + { + $responseProphecy = $this->prophesize(ResponseInterface::class); + $requestProphecy = $this->prophesize(ServerRequestInterface::class); + $requestProphecy->getQueryParams()->shouldBeCalled()->willReturn([ + 'table' => 'aTable', + 'field' => 'aField', + ]); + $GLOBALS['TCA']['aTable']['columns'] = []; + $this->expectException(\RuntimeException::class); + $this->expectExceptionCode(1479386990); + (new SelectTreeController())->fetchDataAction($requestProphecy->reveal(), $responseProphecy->reveal()); + } +} diff --git a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRecordTypeValueTest.php b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRecordTypeValueTest.php index 2193388c8fa38d505decd6e6a3121f229976a7be..89ef3b0889fde2a28d4a146e44cab7210cda3907 100644 --- a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRecordTypeValueTest.php +++ b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/DatabaseRecordTypeValueTest.php @@ -58,12 +58,47 @@ class DatabaseRecordTypeValueTest extends UnitTestCase $this->subject->addData($input); } + /** + * @test + */ + public function addDataKeepsExistingTcaRecordTypeValue() + { + $input = [ + 'recordTypeValue' => 'egon', + 'processedTca' => [ + 'types' => [ + '1' => 'foo', + ], + ], + ]; + $expected = $input; + $this->assertSame($expected, $this->subject->addData($input)); + } + + /** + * @test + */ + public function addDataKeepsExistingTcaRecordTypeValueWithValueZero() + { + $input = [ + 'recordTypeValue' => 0, + 'processedTca' => [ + 'types' => [ + '1' => 'foo', + ], + ], + ]; + $expected = $input; + $this->assertSame($expected, $this->subject->addData($input)); + } + /** * @test */ public function addDataSetsRecordTypeValueToHistoricalOneIfTypeZeroIsNotDefined() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'types' => [ '1' => 'foo', @@ -81,6 +116,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataSetsRecordTypeValueToZero() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'types' => [ '0' => 'foo', @@ -100,6 +136,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataThrowsExceptionIfTypePointsToANotExistingField() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'type' => 'notExists', @@ -125,6 +162,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataSetsRecordTypeValueToValueOfDatabaseField() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'type' => 'aField', @@ -150,6 +188,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataSetsRecordTypeValueToZeroIfValueOfDatabaseFieldIsNotDefinedInTca() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'type' => 'aField', @@ -175,6 +214,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataSetsRecordTypeValueToZeroIfValueOfDatabaseFieldIsEmptyString() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'type' => 'aField', @@ -200,6 +240,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataThrowsExceptionIfValueTypesNotExistsAndNoFallbackExists() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'type' => 'aField', @@ -225,6 +266,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataSetsRecordTypeValueToValueOfDefaultLanguageRecordIfConfiguredAsExclude() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'languageField' => 'sys_language_uid', @@ -260,6 +302,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataSetsRecordTypeValueToValueOfDefaultLanguageRecordIfConfiguredAsMergeIfNotBlank() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'languageField' => 'sys_language_uid', @@ -295,6 +338,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataSetsRecordTypeValueToValueOfLocalizedRecordIfConfiguredAsMergeIfNotBlankButNotBlank() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'languageField' => 'sys_language_uid', @@ -330,6 +374,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataThrowsExceptionForForeignTypeConfigurationNotAsSelectOrGroup() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'type' => 'localField:foreignField', @@ -359,6 +404,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataThrowsExceptionForForeignTypeIfPointerConfigurationHasNoTable() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'type' => 'localField:foreignField', @@ -391,6 +437,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataSetsTypeValueFromForeignTableRecord() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'type' => 'localField:foreignField', @@ -434,6 +481,7 @@ class DatabaseRecordTypeValueTest extends UnitTestCase public function addDataSetsTypeValueFromNestedTcaGroupField() { $input = [ + 'recordTypeValue' => '', 'processedTca' => [ 'ctrl' => [ 'type' => 'uid_local:type', diff --git a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/InitializeProcessedTcaTest.php b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/InitializeProcessedTcaTest.php index cd8557cc92ba0be8af3ef4f11b00da3b7586cc4b..e904e76d554bad62fa59ba430a4cdcb0b05bb2ef 100644 --- a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/InitializeProcessedTcaTest.php +++ b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/InitializeProcessedTcaTest.php @@ -48,6 +48,23 @@ class InitializeProcessedTcaTest extends UnitTestCase $this->assertEquals($expected, $result['processedTca']); } + /** + * @test + */ + public function addDataKeepsGivenProcessedTca() + { + $input = [ + 'tableName' => 'aTable', + 'processedTca' => [ + 'columns' => [ + 'afield' => [], + ], + ], + ]; + $expected = $input; + $this->assertEquals($expected, $this->subject->addData($input)); + } + /** * @test */ diff --git a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaFlexPrepareTest.php b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaFlexPrepareTest.php index 0f48598c424af6a922f28ff2b44c7bd05341407d..bda0637ad86269369d4d3445d54546e03cc96618 100644 --- a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaFlexPrepareTest.php +++ b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaFlexPrepareTest.php @@ -64,6 +64,53 @@ class TcaFlexPrepareTest extends UnitTestCase parent::tearDown(); } + /** + * @test + */ + public function addDataKeepsExistingDataStructure() + { + $input = [ + 'systemLanguageRows' => [], + 'tableName' => 'aTableName', + 'databaseRow' => [ + 'aField' => [ + 'data' => [], + 'meta' => [], + ], + ], + 'processedTca' => [ + 'columns' => [ + 'aField' => [ + 'config' => [ + 'type' => 'flex', + 'dataStructureIdentifier' => '{"type":"tca","tableName":"aTableName","fieldName":"aField","dataStructureKey":"default"}', + 'ds' => [ + 'sheets' => [ + 'sDEF' => [ + 'ROOT' => [ + 'type' => 'array', + 'el' => [ + 'aFlexField' => [ + 'label' => 'aFlexFieldLabel', + 'config' => [ + 'type' => 'input', + ], + ], + ], + ], + ], + ], + 'meta' => [], + ], + ], + ], + ], + ], + ]; + $expected = $input; + $this->assertEquals($expected, $this->subject->addData($input)); + } + /** * @test */ diff --git a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSelectTreeItemsTest.php b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSelectTreeItemsTest.php index 35787a8f801b4a2e610cd9c60d6cd4e5d2f4bc96..f3d66dd20a878e288bdb86b30bab3514ad1399dc 100644 --- a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSelectTreeItemsTest.php +++ b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSelectTreeItemsTest.php @@ -175,13 +175,13 @@ class TcaSelectTreeItemsTest extends UnitTestCase ], ], ], + 'selectTreeCompileItems' => true, ]; $expected = $input; $expected['databaseRow']['aField'] = ['1']; $expected['processedTca']['columns']['aField']['config']['treeData'] = [ 'items' => [['fake', 'tree', 'data']], - 'selectedNodes' => [] ]; $this->assertEquals($expected, $this->subject->addData($input)); } @@ -247,6 +247,7 @@ class TcaSelectTreeItemsTest extends UnitTestCase ], ], ], + 'selectTreeCompileItems' => true, ]; $this->subject->addData($input); diff --git a/typo3/sysext/core/Tests/Acceptance/Backend/Page/AddPageInPageModuleCest.php b/typo3/sysext/core/Tests/Acceptance/Backend/Page/AddPageInPageModuleCest.php index 823d18b1b95e70c0e4b305df8629a3c6160f3f32..d77facf0619f6c079f4cd7d1ad2e1e624b91d366 100644 --- a/typo3/sysext/core/Tests/Acceptance/Backend/Page/AddPageInPageModuleCest.php +++ b/typo3/sysext/core/Tests/Acceptance/Backend/Page/AddPageInPageModuleCest.php @@ -65,7 +65,6 @@ class AddPageInPageModuleCest // Check empty $I->amGoingTo('check empty error'); - $I->click($saveButton); $I->wait(2); $editControllerDiv = '#EditDocumentController > div'; $generalTab = $editControllerDiv . ' > div:nth-child(1) > ul > li';