[!!!][TASK] Improve flex and TCA handling in FormEngine
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / FormSelectTreeAjaxController.php
1 <?php
2 namespace TYPO3\CMS\Backend\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Backend\Form\FormDataCompiler;
20 use TYPO3\CMS\Backend\Form\FormDataGroup\TcaDatabaseRecord;
21 use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
22 use TYPO3\CMS\Core\Utility\GeneralUtility;
23
24 /**
25 * Backend controller for selectTree ajax operations
26 */
27 class FormSelectTreeAjaxController
28 {
29 /**
30 * Returns json representing category tree
31 *
32 * @param ServerRequestInterface $request
33 * @param ResponseInterface $response
34 * @throws \RuntimeException
35 * @return ResponseInterface
36 */
37 public function fetchDataAction(ServerRequestInterface $request, ResponseInterface $response)
38 {
39 $tableName = $request->getQueryParams()['tableName'];
40 $fieldName = $request->getQueryParams()['fieldName'];
41
42 // Prepare processedTca: Remove all column definitions except the one that contains
43 // our tree definition. This way only this field is calculated, everything else is ignored.
44 if (!isset($GLOBALS['TCA'][$tableName]) || !is_array($GLOBALS['TCA'][$tableName])) {
45 throw new \RuntimeException(
46 'TCA for table ' . $tableName . ' not found',
47 1479386729
48 );
49 }
50 $processedTca = $GLOBALS['TCA'][$tableName];
51 if (!isset($processedTca['columns'][$fieldName]) || !is_array($processedTca['columns'][$fieldName])) {
52 throw new \RuntimeException(
53 'TCA for table ' . $tableName . ' and field ' . $fieldName . ' not found',
54 1479386990
55 );
56 }
57
58 // Force given record type and set showitem to our field only
59 $recordTypeValue = $request->getQueryParams()['recordTypeValue'];
60 $processedTca['types'][$recordTypeValue]['showitem'] = $fieldName;
61 // Unset all columns except our field
62 $processedTca['columns'] = [
63 $fieldName => $processedTca['columns'][$fieldName],
64 ];
65
66 $dataStructureIdentifier = '';
67 $flexFormSheetName = '';
68 $flexFormFieldName = '';
69 $flexFormContainerIdentifier = '';
70 $flexFormContainerFieldName = '';
71 $flexSectionContainerPreparation = [];
72 if ($processedTca['columns'][$fieldName]['config']['type'] === 'flex') {
73 if (!empty($request->getQueryParams()['dataStructureIdentifier'])) {
74 $dataStructureIdentifier = json_encode($request->getQueryParams()['dataStructureIdentifier']);
75 }
76 $flexFormSheetName = $request->getQueryParams()['flexFormSheetName'];
77 $flexFormFieldName = $request->getQueryParams()['flexFormFieldName'];
78 $flexFormContainerName = $request->getQueryParams()['flexFormContainerName'];
79 $flexFormContainerIdentifier = $request->getQueryParams()['flexFormContainerIdentifier'];
80 $flexFormContainerFieldName = $request->getQueryParams()['flexFormContainerFieldName'];
81 $flexFormSectionContainerIsNew = (bool)$request->getQueryParams()['flexFormSectionContainerIsNew'];
82
83 $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
84 $dataStructure = $flexFormTools->parseDataStructureByIdentifier($dataStructureIdentifier);
85
86 // Reduce given data structure down to the relevant element only
87 if (empty($flexFormContainerFieldName)) {
88 if (isset($dataStructure['sheets'][$flexFormSheetName]['ROOT']
89 ['el'][$flexFormFieldName])
90 ) {
91 $dataStructure = [
92 'sheets' => [
93 $flexFormSheetName => [
94 'ROOT' => [
95 'type' => 'array',
96 'el' => [
97 $flexFormFieldName => $dataStructure['sheets'][$flexFormSheetName]['ROOT']
98 ['el'][$flexFormFieldName],
99 ],
100 ],
101 ],
102 ],
103 ];
104 }
105 } else {
106 if (isset($dataStructure['sheets'][$flexFormSheetName]['ROOT']
107 ['el'][$flexFormFieldName]
108 ['el'][$flexFormContainerName]
109 ['el'][$flexFormContainerFieldName])
110 ) {
111 // If this is a tree in a section container that has just been added by the FlexFormAjaxController
112 // "new container" action, then this container is not yet persisted, so we need to trigger the
113 // TcaFlexProcess data provider again to prepare the DS and databaseRow of that container.
114 if ($flexFormSectionContainerIsNew) {
115 $flexSectionContainerPreparation = [
116 'flexFormSheetName' => $flexFormSheetName,
117 'flexFormFieldName' => $flexFormFieldName,
118 'flexFormContainerName' => $flexFormContainerName,
119 'flexFormContainerIdentifier' => $flexFormContainerIdentifier,
120 ];
121 }
122 // Now restrict the data structure to our tree element only
123 $dataStructure = [
124 'sheets' => [
125 $flexFormSheetName => [
126 'ROOT' => [
127 'type' => 'array',
128 'el' => [
129 $flexFormFieldName => [
130 'section' => 1,
131 'type' => 'array',
132 'el' => [
133 $flexFormContainerName => [
134 'el' => [
135 $flexFormContainerFieldName => $dataStructure['sheets'][$flexFormSheetName]['ROOT']
136 ['el'][$flexFormFieldName]
137 ['el'][$flexFormContainerName]
138 ['el'][$flexFormContainerFieldName]
139 ],
140 ],
141 ],
142 ],
143 ],
144 ],
145 ],
146 ],
147 ];
148 }
149 }
150 $processedTca['columns'][$fieldName]['config']['ds'] = $dataStructure;
151 $processedTca['columns'][$fieldName]['config']['dataStructureIdentifier'] = $dataStructureIdentifier;
152 }
153
154 $formDataGroup = GeneralUtility::makeInstance(TcaDatabaseRecord::class);
155 $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
156 $formDataCompilerInput = [
157 'tableName' => $tableName,
158 'vanillaUid' => (int)$request->getQueryParams()['uid'],
159 'command' => $request->getQueryParams()['command'],
160 'processedTca' => $processedTca,
161 'recordTypeValue' => $recordTypeValue,
162 'selectTreeCompileItems' => true,
163 'flexSectionContainerPreparation' => $flexSectionContainerPreparation,
164 ];
165 $formData = $formDataCompiler->compile($formDataCompilerInput);
166
167 if ($formData['processedTca']['columns'][$fieldName]['config']['type'] === 'flex') {
168 if (empty($flexFormContainerFieldName)) {
169 $treeData = $formData['processedTca']['columns'][$fieldName]['config']['ds']
170 ['sheets'][$flexFormSheetName]['ROOT']
171 ['el'][$flexFormFieldName]['config']['items'];
172 } else {
173 $treeData = $formData['processedTca']['columns'][$fieldName]['config']['ds']
174 ['sheets'][$flexFormSheetName]['ROOT']
175 ['el'][$flexFormFieldName]
176 ['children'][$flexFormContainerIdentifier]
177 ['el'][$flexFormContainerFieldName]['config']['items'];
178 }
179 } else {
180 $treeData = $formData['processedTca']['columns'][$fieldName]['config']['items'];
181 }
182
183 $response->getBody()->write(json_encode($treeData));
184 return $response;
185 }
186 }