b40826741017ad49cd3451cb727bd391fda9fac1
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / FormDataProvider / TcaFlexProcess.php
1 <?php
2 namespace TYPO3\CMS\Backend\Form\FormDataProvider;
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 TYPO3\CMS\Backend\Form\FormDataCompiler;
18 use TYPO3\CMS\Backend\Form\FormDataGroup\FlexFormSegment;
19 use TYPO3\CMS\Backend\Form\FormDataProviderInterface;
20 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
21 use TYPO3\CMS\Core\Utility\GeneralUtility;
22
23 /**
24 * Process data structures and data values, calculate defaults.
25 *
26 * This is typically the last provider, executed after TcaFlexPrepare
27 */
28 class TcaFlexProcess implements FormDataProviderInterface
29 {
30 /**
31 * Determine possible pageTsConfig overrides and apply them to ds.
32 * Determine available languages and sanitize dv for further processing. Then kick
33 * and validate further details like excluded fields. Finally for each possible
34 * value and ds call FormDataCompiler with set FlexFormSegment group to resolve
35 * single field stuff like item processor functions.
36 *
37 * @param array $result
38 * @return array
39 */
40 public function addData(array $result)
41 {
42 foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) {
43 if (empty($fieldConfig['config']['type']) || $fieldConfig['config']['type'] !== 'flex') {
44 continue;
45 }
46
47 $flexIdentifier = $this->getFlexIdentifier($result, $fieldName);
48 $pageTsConfigOfFlex = $this->getPageTsOfFlex($result, $fieldName, $flexIdentifier);
49 $result = $this->modifyOuterDataStructure($result, $fieldName, $pageTsConfigOfFlex);
50 $result = $this->removeExcludeFieldsFromDataStructure($result, $fieldName, $flexIdentifier);
51 $result = $this->removeDisabledFieldsFromDataStructure($result, $fieldName, $pageTsConfigOfFlex);
52 $result = $this->modifyDataStructureAndDataValuesByFlexFormSegmentGroup($result, $fieldName, $pageTsConfigOfFlex);
53 $result = $this->addDataStructurePointersToMetaData($result, $fieldName);
54 }
55
56 return $result;
57 }
58
59 /**
60 * Take care of ds_pointerField and friends to determine the correct sub array within
61 * TCA config ds.
62 *
63 * Gets extension identifier. Use second pointer field if it's value is not empty, "list" or "*",
64 * else it must be a plugin and first one will be used.
65 * This code basically determines the sub key of ds field:
66 * config = array(
67 * ds => array(
68 * 'aFlexConfig' => '<flexXml ...
69 * ^^^^^^^^^^^
70 * $flexformIdentifier contains "aFlexConfig" after this operation.
71 *
72 * @todo: This method is only implemented half. It basically should do all the
73 * @todo: pointer handling that is done within BackendUtility::getFlexFormDS() to $srcPointer.
74 * @todo: This can be solved now by adding 'identifier' from TcaFlexPrepare to 'config' array
75 *
76 * @param array $result Result array
77 * @param string $fieldName Current handle field name
78 * @return string Pointer
79 */
80 protected function getFlexIdentifier(array $result, $fieldName)
81 {
82 // @todo: Current implementation with the "list_type, CType" fallback is rather limited and customized for
83 // @todo: tt_content, also it forces a ds_pointerField to be defined and a casual "default" sub array does not work
84 $pointerFields = !empty($result['processedTca']['columns'][$fieldName]['config']['ds_pointerField'])
85 ? $result['processedTca']['columns'][$fieldName]['config']['ds_pointerField']
86 : 'list_type,CType';
87 $pointerFields = GeneralUtility::trimExplode(',', $pointerFields);
88 $flexformIdentifier = !empty($result['databaseRow'][$pointerFields[0]]) ? $result['databaseRow'][$pointerFields[0]] : '';
89 if (!empty($result['databaseRow'][$pointerFields[1]])
90 && $result['databaseRow'][$pointerFields[1]] !== 'list'
91 && $result['databaseRow'][$pointerFields[1]] !== '*'
92 ) {
93 $flexformIdentifier = $result['databaseRow'][$pointerFields[1]];
94 }
95 if (empty($flexformIdentifier)) {
96 $flexformIdentifier = 'default';
97 }
98
99 return $flexformIdentifier;
100 }
101
102 /**
103 * Determine TCEFORM.aTable.aField.matchingIdentifier
104 *
105 * @param array $result Result array
106 * @param string $fieldName Handled field name
107 * @param string $flexIdentifier Determined identifier
108 * @return array PageTsConfig for this flex
109 */
110 protected function getPageTsOfFlex(array $result, $fieldName, $flexIdentifier)
111 {
112 $table = $result['tableName'];
113 $pageTs = [];
114 if (!empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'][$flexIdentifier . '.'])
115 && is_array($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'][$flexIdentifier . '.'])) {
116 $pageTs = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'][$flexIdentifier . '.'];
117 }
118 return $pageTs;
119 }
120
121 /**
122 * Handle "outer" flex data structure changes like language and sheet
123 * description. Does not change "TCA" or values of single elements
124 *
125 * @param array $result Result array
126 * @param string $fieldName Current handle field name
127 * @param array $pageTsConfig Given pageTsConfig of this flex form
128 * @return array Modified item array
129 */
130 protected function modifyOuterDataStructure(array $result, $fieldName, $pageTsConfig)
131 {
132 $modifiedDataStructure = $result['processedTca']['columns'][$fieldName]['config']['ds'];
133
134 if (isset($modifiedDataStructure['sheets']) && is_array($modifiedDataStructure['sheets'])) {
135 // Handling multiple sheets
136 foreach ($modifiedDataStructure['sheets'] as $sheetName => $sheetStructure) {
137 if (isset($pageTsConfig[$sheetName . '.']) && is_array($pageTsConfig[$sheetName . '.'])) {
138 $pageTsOfSheet = $pageTsConfig[$sheetName . '.'];
139
140 // Remove whole sheet if disabled
141 if (!empty($pageTsOfSheet['disabled'])) {
142 unset($modifiedDataStructure['sheets'][$sheetName]);
143 continue;
144 }
145
146 // sheetTitle, sheetDescription, sheetShortDescr
147 $modifiedDataStructure['sheets'][$sheetName] = $this->modifySingleSheetInformation($sheetStructure, $pageTsOfSheet);
148 }
149 }
150 }
151
152 $result['processedTca']['columns'][$fieldName]['config']['ds'] = $modifiedDataStructure;
153
154 return $result;
155 }
156
157 /**
158 * Removes fields from data structure the user has no access to
159 *
160 * @param array $result Result array
161 * @param string $fieldName Current handle field name
162 * @param string $flexIdentifier Determined identifier
163 * @return array Modified result
164 */
165 protected function removeExcludeFieldsFromDataStructure(array $result, $fieldName, $flexIdentifier)
166 {
167 $dataStructure = $result['processedTca']['columns'][$fieldName]['config']['ds'];
168 $backendUser = $this->getBackendUser();
169 if ($backendUser->isAdmin() || !isset($dataStructure['sheets']) || !is_array($dataStructure['sheets'])) {
170 return $result;
171 }
172
173 $userNonExcludeFields = GeneralUtility::trimExplode(',', $backendUser->groupData['non_exclude_fields']);
174 $excludeFieldsPrefix = $result['tableName'] . ':' . $fieldName . ';' . $flexIdentifier . ';';
175 $nonExcludeFields = [];
176 foreach ($userNonExcludeFields as $userNonExcludeField) {
177 if (strpos($userNonExcludeField, $excludeFieldsPrefix) !== false) {
178 $exploded = explode(';', $userNonExcludeField);
179 $sheetName = $exploded[2];
180 $allowedFlexFieldName = $exploded[3];
181 $nonExcludeFields[$sheetName][$allowedFlexFieldName] = true;
182 }
183 }
184 foreach ($dataStructure['sheets'] as $sheetName => $sheetDefinition) {
185 if (!isset($sheetDefinition['ROOT']['el']) || !is_array($sheetDefinition['ROOT']['el'])) {
186 continue;
187 }
188 foreach ($sheetDefinition['ROOT']['el'] as $flexFieldName => $fieldDefinition) {
189 if (!empty($fieldDefinition['exclude']) && !isset($nonExcludeFields[$sheetName][$flexFieldName])) {
190 unset($result['processedTca']['columns'][$fieldName]['config']['ds']['sheets'][$sheetName]['ROOT']['el'][$flexFieldName]);
191 }
192 }
193 }
194
195 return $result;
196 }
197
198 /**
199 * Remove fields from data structure that are disabled in pageTsConfig.
200 *
201 * @param array $result Result array
202 * @param string $fieldName Current handle field name
203 * @param array $pageTsConfig Given pageTsConfig of this flex form
204 * @return array Modified item array
205 */
206 protected function removeDisabledFieldsFromDataStructure(array $result, $fieldName, $pageTsConfig)
207 {
208 $dataStructure = $result['processedTca']['columns'][$fieldName]['config']['ds'];
209 if (!isset($dataStructure['sheets']) || !is_array($dataStructure['sheets'])) {
210 return $result;
211 }
212 foreach ($dataStructure['sheets'] as $sheetName => $sheetDefinition) {
213 if (!isset($sheetDefinition['ROOT']['el']) || !is_array($sheetDefinition['ROOT']['el'])
214 || !isset($pageTsConfig[$sheetName . '.'])) {
215 continue;
216 }
217 foreach ($sheetDefinition['ROOT']['el'] as $flexFieldName => $fieldDefinition) {
218 if (!empty($pageTsConfig[$sheetName . '.'][$flexFieldName . '.']['disabled'])) {
219 unset($result['processedTca']['columns'][$fieldName]['config']['ds']['sheets'][$sheetName]['ROOT']['el'][$flexFieldName]);
220 }
221 }
222 }
223 return $result;
224 }
225
226 /**
227 * Feed single flex field and data to FlexFormSegment FormData compiler and merge result.
228 * This one is nasty. Goal is to have processed TCA stuff in DS and also have validated / processed data values.
229 *
230 * Three main parts in this method:
231 * * Process values of existing section container for default values
232 * * Process values and TCA of possible section container and create a default value row for each
233 * * Process TCA of "normal" fields and have default values in data ['templateRows']['containerName'] parallel to section ['el']
234 *
235 * @param array $result Result array
236 * @param string $fieldName Current handle field name
237 * @param array $pageTsConfig Given pageTsConfig of this flex form
238 * @return array Modified item array
239 * @throws \UnexpectedValueException
240 */
241 protected function modifyDataStructureAndDataValuesByFlexFormSegmentGroup(array $result, $fieldName, $pageTsConfig)
242 {
243 $dataStructure = $result['processedTca']['columns'][$fieldName]['config']['ds'];
244 $dataValues = $result['databaseRow'][$fieldName];
245 $tableName = $result['tableName'];
246
247 if (!isset($dataStructure['sheets']) || !is_array($dataStructure['sheets'])) {
248 return $result;
249 }
250
251 /** @var FlexFormSegment $formDataGroup */
252 $formDataGroup = GeneralUtility::makeInstance(FlexFormSegment::class);
253 /** @var FormDataCompiler $formDataCompiler */
254 $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
255
256 foreach ($dataStructure['sheets'] as $dataStructureSheetName => $dataStructureSheetDefinition) {
257 if (!isset($dataStructureSheetDefinition['ROOT']['el']) || !is_array($dataStructureSheetDefinition['ROOT']['el'])) {
258 continue;
259 }
260 $dataStructureSheetElements = $dataStructureSheetDefinition['ROOT']['el'];
261
262 // Prepare pageTsConfig of this sheet
263 $pageTsConfig['TCEFORM.'][$tableName . '.'] = [];
264 if (isset($pageTsConfig[$dataStructureSheetName . '.']) && is_array($pageTsConfig[$dataStructureSheetName . '.'])) {
265 $pageTsConfig['TCEFORM.'][$tableName . '.'] = $pageTsConfig[$dataStructureSheetName . '.'];
266 }
267
268 // It is possible to have a flex field field with of foreign_table (eg. type=select) that has markers in
269 // a foreign_table_where like ###PAGE_TSCONFIG_ID###. It was possible to set this in page TSConfig for flex fields like this:
270 // TCEFORM.theTable.theFlexfield.PAGE_TSCONFIG_ID = 42
271 // This hands over this PAGE_TSCONFIG_ID to all flex fields that have this foreign_table_where marker.
272 // This is a contradiction to the "usual" page TSConfig flex configuration that should be done for single flex fields:
273 // TCEFORM.theTable.theFlexfield.theDataStructure.theSheet.theField.PAGE_TSCONFIG_ID = 42
274 // The below code is a hack to still simulate the old behavior that is now deprecated.
275 // @deprecated since TYPO3 v8, will be removed in TYPO3 v9
276 // When deleting this code and comment block, the according code within AbstractItemProvider can be removed, too.
277 if (isset($result['pageTsConfig']['TCEFORM.'][$tableName . '.'][$fieldName . '.']['PAGE_TSCONFIG_ID'])) {
278 GeneralUtility::deprecationLog(
279 'The page TSConfig setting TCEFORM.' . $tableName . '.' . $fieldName . '.PAGE_TSCONFIG_ID for flex forms'
280 . ' is deprecated. Use this setting for single flex fields instead, example: TCEFORM.' . $tableName . '.'
281 . $fieldName . '.theDataStructureName.theSheet.theFieldName.PAGE_TSCONFIG_ID. Be aware these settings are'
282 . ' no longer allowed for fields within flex form section container elements.'
283 );
284 $pageTsConfig['flexHack.']['PAGE_TSCONFIG_ID'] = $result['pageTsConfig']['TCEFORM.'][$tableName . '.'][$fieldName . '.']['PAGE_TSCONFIG_ID'];
285 }
286 if (isset($result['pageTsConfig']['TCEFORM.'][$tableName . '.'][$fieldName . '.']['PAGE_TSCONFIG_IDLIST'])) {
287 GeneralUtility::deprecationLog(
288 'The page TSConfig setting TCEFORM.' . $tableName . '.' . $fieldName . '.PAGE_TSCONFIG_IDLIST for flex forms'
289 . ' is deprecated. Use this setting for single flex fields instead, example: TCEFORM.' . $tableName . '.'
290 . $fieldName . '.theDataStructureName.theSheet.theFieldName.PAGE_TSCONFIG_IDLIST. Be aware these settings are'
291 . ' no longer allowed for fields within flex form section container elements.'
292 );
293 $pageTsConfig['flexHack.']['PAGE_TSCONFIG_IDLIST'] = $result['pageTsConfig']['TCEFORM.'][$tableName . '.'][$fieldName . '.']['PAGE_TSCONFIG_IDLIST'];
294 }
295 if (isset($result['pageTsConfig']['TCEFORM.'][$tableName . '.'][$fieldName . '.']['PAGE_TSCONFIG_STR'])) {
296 GeneralUtility::deprecationLog(
297 'The page TSConfig setting TCEFORM.' . $tableName . '.' . $fieldName . '.PAGE_TSCONFIG_STR for flex forms'
298 . ' is deprecated. Use this setting for single flex fields instead, example: TCEFORM.' . $tableName . '.'
299 . $fieldName . '.theDataStructureName.theSheet.theFieldName.PAGE_TSCONFIG_STR. Be aware these settings are'
300 . ' no longer allowed for fields within flex form section container elements.'
301 );
302 $pageTsConfig['flexHack.']['PAGE_TSCONFIG_STR'] = $result['pageTsConfig']['TCEFORM.'][$tableName . '.'][$fieldName . '.']['PAGE_TSCONFIG_STR'];
303 }
304
305 // List of "new" tca fields that have no value within the flexform, yet. Those will be compiled in one go later.
306 $tcaNewColumns = [];
307 // List of "edit" tca fields that have a value in flexform, already. Those will be compiled in one go later.
308 $tcaEditColumns = [];
309 // Contains the data values for the "edit" tca fields.
310 $tcaValueArray = [
311 'uid' => $result['databaseRow']['uid'],
312 ];
313 foreach ($dataStructureSheetElements as $dataStructureSheetElementName => $dataStructureSheetElementDefinition) {
314 if (isset($dataStructureSheetElementDefinition['type']) && $dataStructureSheetElementDefinition['type'] === 'array'
315 && isset($dataStructureSheetElementDefinition['section']) && (string)$dataStructureSheetElementDefinition['section'] === '1'
316 ) {
317 // A section
318
319 // Existing section container elements
320 if (isset($dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['el'])
321 && is_array($dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['el'])
322 ) {
323 $containerArray = $dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['el'];
324 foreach ($containerArray as $aContainerNumber => $aContainerArray) {
325 if (is_array($aContainerArray)) {
326 foreach ($aContainerArray as $aContainerName => $aContainerElementArray) {
327 if ($aContainerName === '_TOGGLE') {
328 // Don't handle internal toggle state field
329 continue;
330 }
331 if (!isset($dataStructureSheetElements[$dataStructureSheetElementName]['el'][$aContainerName])) {
332 // Container not defined in ds
333 continue;
334 }
335
336 $newColumns = [];
337 $editColumns = [];
338 $valueArray = [
339 'uid' => $result['databaseRow']['uid'],
340 ];
341 foreach ($dataStructureSheetElements[$dataStructureSheetElementName]['el'][$aContainerName]['el'] as $singleFieldName => $singleFieldConfiguration) {
342 // $singleFieldValueArray = ['data']['sSections']['lDEF']['section_1']['el']['1']['container_1']['el']['element_1']
343 $singleFieldValueArray = [];
344 if (isset($aContainerElementArray['el'][$singleFieldName])
345 && is_array($aContainerElementArray['el'][$singleFieldName])
346 ) {
347 $singleFieldValueArray = $aContainerElementArray['el'][$singleFieldName];
348 }
349
350 if (array_key_exists('vDEF', $singleFieldValueArray)) {
351 $editColumns[$singleFieldName] = $singleFieldConfiguration;
352 $valueArray[$singleFieldName] = $singleFieldValueArray['vDEF'];
353 } else {
354 $newColumns[$singleFieldName] = $singleFieldConfiguration;
355 }
356 }
357
358 $inputToFlexFormSegment = [
359 'tableName' => $result['tableName'],
360 'command' => '',
361 // It is currently not possible to have pageTsConfig for section container
362 'pageTsConfig' => [],
363 'databaseRow' => $valueArray,
364 'processedTca' => [
365 'ctrl' => [],
366 'columns' => [],
367 ],
368 'flexParentDatabaseRow' => $result['databaseRow'],
369 ];
370
371 if (!empty($newColumns)) {
372 $inputToFlexFormSegment['command'] = 'new';
373 $inputToFlexFormSegment['processedTca']['columns'] = $newColumns;
374 $flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment);
375
376 foreach ($newColumns as $singleFieldName => $_) {
377 // Set data value result
378 if (array_key_exists($singleFieldName, $flexSegmentResult['databaseRow'])) {
379 $result['databaseRow'][$fieldName]
380 ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]
381 ['el'][$aContainerNumber][$aContainerName]['el'][$singleFieldName]['vDEF'] =
382 $flexSegmentResult['databaseRow'][$singleFieldName];
383 }
384 // Set TCA structure result, actually, this call *might* be obsolete since the "dummy"
385 // handling below will set it again.
386 $result['processedTca']['columns'][$fieldName]['config']['ds']
387 ['sheets'][$dataStructureSheetName]['ROOT']['el']
388 [$dataStructureSheetElementName]['el'][$aContainerName]['el'][$singleFieldName] =
389 $flexSegmentResult['processedTca']['columns'][$singleFieldName];
390 }
391 }
392
393 if (!empty($editColumns)) {
394 $inputToFlexFormSegment['command'] = 'edit';
395 $inputToFlexFormSegment['processedTca']['columns'] = $editColumns;
396 $flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment);
397
398 foreach ($editColumns as $singleFieldName => $_) {
399 // Set data value result
400 if (array_key_exists($singleFieldName, $flexSegmentResult['databaseRow'])) {
401 $result['databaseRow'][$fieldName]
402 ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]
403 ['el'][$aContainerNumber][$aContainerName]['el'][$singleFieldName]['vDEF'] =
404 $flexSegmentResult['databaseRow'][$singleFieldName];
405 }
406 // Set TCA structure result, actually, this call *might* be obsolete since the "dummy"
407 // handling below will set it again.
408 $result['processedTca']['columns'][$fieldName]['config']['ds']
409 ['sheets'][$dataStructureSheetName]['ROOT']['el']
410 [$dataStructureSheetElementName]['el'][$aContainerName]['el'][$singleFieldName] =
411 $flexSegmentResult['processedTca']['columns'][$singleFieldName];
412 }
413 }
414 }
415 }
416 }
417 // End of existing data value handling
418 } else {
419 // Force the section to be an empty array if there are no existing containers
420 $result['databaseRow'][$fieldName]
421 ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['el'] = [];
422 }
423
424 // Prepare "fresh" row for every possible container
425 if (isset($dataStructureSheetElements[$dataStructureSheetElementName]['el']) && is_array($dataStructureSheetElements[$dataStructureSheetElementName]['el'])) {
426 foreach ($dataStructureSheetElements[$dataStructureSheetElementName]['el'] as $possibleContainerName => $possibleContainerConfiguration) {
427 if (isset($possibleContainerConfiguration['el']) && is_array($possibleContainerConfiguration['el'])) {
428 // Initialize result data array templateRows
429 $result['databaseRow'][$fieldName]
430 ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['templateRows']
431 [$possibleContainerName]['el']
432 = [];
433 foreach ($possibleContainerConfiguration['el'] as $singleFieldName => $singleFieldConfiguration) {
434
435 // Nesting type=inline in container sections is not supported. Throw an exception if configured.
436 if (isset($singleFieldConfiguration['config']['type']) && $singleFieldConfiguration['config']['type'] === 'inline') {
437 throw new \UnexpectedValueException(
438 'Invalid flex form data structure on field name "' . $fieldName . '" with element "' . $singleFieldName . '"'
439 . ' in section container "' . $possibleContainerName . '": Nesting inline elements in flex form'
440 . ' sections is not allowed.',
441 1458745468
442 );
443 }
444
445 // Nesting sections is not supported. Throw an exception if configured.
446 if (is_array($singleFieldConfiguration)
447 && isset($singleFieldConfiguration['type']) && $singleFieldConfiguration['type'] === 'array'
448 && isset($singleFieldConfiguration['section']) && (string)$singleFieldConfiguration['section'] === '1'
449 ) {
450 throw new \UnexpectedValueException(
451 'Invalid flex form data structure on field name "' . $fieldName . '" with element "' . $singleFieldName . '"'
452 . ' in section container "' . $possibleContainerName . '": Nesting sections in container elements'
453 . ' sections is not allowed.',
454 1458745712
455 );
456 }
457
458 $inputToFlexFormSegment = [
459 'tableName' => $result['tableName'],
460 'command' => 'new',
461 'pageTsConfig' => [],
462 'databaseRow' => [
463 'uid' => $result['databaseRow']['uid'],
464 ],
465 'processedTca' => [
466 'ctrl' => [],
467 'columns' => [
468 $singleFieldName => $singleFieldConfiguration,
469 ],
470 ],
471 'selectTreeCompileItems' => false,
472 'flexParentDatabaseRow' => $result['databaseRow'],
473 ];
474 $flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment);
475 if (array_key_exists($singleFieldName, $flexSegmentResult['databaseRow'])) {
476 $result['databaseRow'][$fieldName]
477 ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['templateRows']
478 [$possibleContainerName]['el'][$singleFieldName]['vDEF']
479 = $flexSegmentResult['databaseRow'][$singleFieldName];
480 }
481 $result['processedTca']['columns'][$fieldName]['config']['ds']
482 ['sheets'][$dataStructureSheetName]['ROOT']['el'][$dataStructureSheetElementName]['el']
483 [$possibleContainerName]['el'][$singleFieldName]
484 = $flexSegmentResult['processedTca']['columns'][$singleFieldName];
485 }
486 }
487 }
488 } // End of preparation for each possible container
489
490 // type without section is not ok
491 } elseif (isset($dataStructureSheetElementDefinition['type']) || isset($dataStructureSheetElementDefinition['section'])) {
492 throw new \UnexpectedValueException(
493 'Broken data structure on field name ' . $fieldName . '. section without type or vice versa is not allowed',
494 1440685208
495 );
496
497 // A "normal" TCA element
498 } else {
499 if (isset($dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName])
500 && array_key_exists('vDEF', $dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName])
501 ) {
502 $tcaEditColumns[$dataStructureSheetElementName] = $dataStructureSheetElementDefinition;
503 $tcaValueArray[$dataStructureSheetElementName] = $dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['vDEF'];
504 } else {
505 $tcaNewColumns[$dataStructureSheetElementName] = $dataStructureSheetElementDefinition;
506 }
507 } // End of single element handling
508 }
509
510 // process the tca columns for the current sheet
511 $inputToFlexFormSegment = [
512 // tablename of "parent" is given down for inline elements to resolve correctly
513 'tableName' => $result['tableName'],
514 'command' => '',
515 'pageTsConfig' => $pageTsConfig,
516 'databaseRow' => $tcaValueArray,
517 'processedTca' => [
518 'ctrl' => [],
519 'columns' => [],
520 ],
521 'flexParentDatabaseRow' => $result['databaseRow'],
522 // Whether to compile TCA tree items - inherit from parent
523 'selectTreeCompileItems' => $result['selectTreeCompileItems'],
524 ];
525
526 if (!empty($tcaNewColumns)) {
527 $inputToFlexFormSegment['command'] = 'new';
528 $inputToFlexFormSegment['processedTca']['columns'] = $tcaNewColumns;
529 $flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment);
530
531 foreach ($tcaNewColumns as $dataStructureSheetElementName => $_) {
532 // Set data value result
533 if (array_key_exists($dataStructureSheetElementName, $flexSegmentResult['databaseRow'])) {
534 $result['databaseRow'][$fieldName]
535 ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['vDEF']
536 = $flexSegmentResult['databaseRow'][$dataStructureSheetElementName];
537 }
538 // Set TCA structure result
539 $result['processedTca']['columns'][$fieldName]['config']['ds']
540 ['sheets'][$dataStructureSheetName]['ROOT']['el'][$dataStructureSheetElementName]
541 = $flexSegmentResult['processedTca']['columns'][$dataStructureSheetElementName];
542 }
543 }
544
545 if (!empty($tcaEditColumns)) {
546 $inputToFlexFormSegment['command'] = 'edit';
547 $inputToFlexFormSegment['processedTca']['columns'] = $tcaEditColumns;
548 $flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment);
549
550 foreach ($tcaEditColumns as $dataStructureSheetElementName => $_) {
551 // Set data value result
552 if (array_key_exists($dataStructureSheetElementName, $flexSegmentResult['databaseRow'])) {
553 $result['databaseRow'][$fieldName]
554 ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['vDEF']
555 = $flexSegmentResult['databaseRow'][$dataStructureSheetElementName];
556 }
557 // Set TCA structure result
558 $result['processedTca']['columns'][$fieldName]['config']['ds']
559 ['sheets'][$dataStructureSheetName]['ROOT']['el'][$dataStructureSheetElementName]
560 = $flexSegmentResult['processedTca']['columns'][$dataStructureSheetElementName];
561 }
562 }
563 }
564
565 return $result;
566 }
567
568 /**
569 * Modify data structure of a single "sheet"
570 * Sets "secondary" data like sheet names and so on, but does NOT modify single elements
571 *
572 * @param array $dataStructure Given data structure
573 * @param array $pageTsOfSheet Page Ts config of given field
574 * @return array Modified data structure
575 */
576 protected function modifySingleSheetInformation(array $dataStructure, array $pageTsOfSheet)
577 {
578 // Return if no elements defined
579 if (!isset($dataStructure['ROOT']['el']) || !is_array($dataStructure['ROOT']['el'])) {
580 return $dataStructure;
581 }
582
583 // Rename sheet (tab)
584 if (!empty($pageTsOfSheet['sheetTitle'])) {
585 $dataStructure['ROOT']['sheetTitle'] = $pageTsOfSheet['sheetTitle'];
586 }
587 // Set sheet description (tab)
588 if (!empty($pageTsOfSheet['sheetDescription'])) {
589 $dataStructure['ROOT']['sheetDescription'] = $pageTsOfSheet['sheetDescription'];
590 }
591 // Set sheet short description (tab)
592 if (!empty($pageTsOfSheet['sheetShortDescr'])) {
593 $dataStructure['ROOT']['sheetShortDescr'] = $pageTsOfSheet['sheetShortDescr'];
594 }
595
596 return $dataStructure;
597 }
598
599 /**
600 * Add fields and values used by ds_pointerField to the meta data array so they can be used in AJAX context during rendering.
601 *
602 * @todo: This method is a stopgap measure to get required information into the AJAX controller
603 *
604 * @param array $result Result array
605 * @param string $fieldName Current handle field name
606 * @return array
607 * @internal
608 */
609 protected function addDataStructurePointersToMetaData(array $result, $fieldName)
610 {
611 if (empty($result['processedTca']['columns'][$fieldName]['config']['ds_pointerField'])) {
612 return $result;
613 }
614
615 $pointerFields = GeneralUtility::trimExplode(
616 ',',
617 $result['processedTca']['columns'][$fieldName]['config']['ds_pointerField']
618 );
619 $dsPointers = [
620 $pointerFields[0] => !empty($result['databaseRow'][$pointerFields[0]]) ? $result['databaseRow'][$pointerFields[0]] : ''
621 ];
622
623 if (!empty($pointerFields[1])) {
624 $dsPointers[$pointerFields[1]] =
625 !empty($result['databaseRow'][$pointerFields[1]]) ? $result['databaseRow'][$pointerFields[1]] : '';
626 }
627 $result['processedTca']['columns'][$fieldName]['config']['ds']['meta']['dataStructurePointers'] = $dsPointers;
628 return $result;
629 }
630
631 /**
632 * @return BackendUserAuthentication
633 */
634 protected function getBackendUser()
635 {
636 return $GLOBALS['BE_USER'];
637 }
638 }