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