TYPO3  7.6
TcaFlexProcess.php
Go to the documentation of this file.
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 
22 
29 {
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  }
54 
55  return $result;
56  }
57 
78  protected function getFlexIdentifier(array $result, $fieldName)
79  {
80  // @todo: Current implementation with the "list_type, CType" fallback is rather limited and customized for
81  // @todo: tt_content, also it forces a ds_pointerField to be defined and a casual "default" sub array does not work
82  $pointerFields = !empty($result['processedTca']['columns'][$fieldName]['config']['ds_pointerField'])
83  ? $result['processedTca']['columns'][$fieldName]['config']['ds_pointerField']
84  : 'list_type,CType';
85  $pointerFields = GeneralUtility::trimExplode(',', $pointerFields);
86  $flexformIdentifier = !empty($result['databaseRow'][$pointerFields[0]]) ? $result['databaseRow'][$pointerFields[0]] : '';
87  if (!empty($result['databaseRow'][$pointerFields[1]])
88  && $result['databaseRow'][$pointerFields[1]] !== 'list'
89  && $result['databaseRow'][$pointerFields[1]] !== '*'
90  ) {
91  $flexformIdentifier = $result['databaseRow'][$pointerFields[1]];
92  }
93  if (empty($flexformIdentifier)) {
94  $flexformIdentifier = 'default';
95  }
96 
97  return $flexformIdentifier;
98  }
99 
108  protected function getPageTsOfFlex(array $result, $fieldName, $flexIdentifier)
109  {
110  $table = $result['tableName'];
111  $pageTs = [];
112  if (!empty($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'][$flexIdentifier . '.'])
113  && is_array($result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'][$flexIdentifier . '.'])) {
114  $pageTs = $result['pageTsConfig']['TCEFORM.'][$table . '.'][$fieldName . '.'][$flexIdentifier . '.'];
115  }
116  return $pageTs;
117  }
118 
128  protected function modifyOuterDataStructure(array $result, $fieldName, $pageTsConfig)
129  {
130  $modifiedDataStructure = $result['processedTca']['columns'][$fieldName]['config']['ds'];
131 
132  if (isset($modifiedDataStructure['sheets']) && is_array($modifiedDataStructure['sheets'])) {
133  // Handling multiple sheets
134  foreach ($modifiedDataStructure['sheets'] as $sheetName => $sheetStructure) {
135  if (isset($pageTsConfig[$sheetName . '.']) && is_array($pageTsConfig[$sheetName . '.'])) {
136  $pageTsOfSheet = $pageTsConfig[$sheetName . '.'];
137 
138  // Remove whole sheet if disabled
139  if (!empty($pageTsOfSheet['disabled'])) {
140  unset($modifiedDataStructure['sheets'][$sheetName]);
141  continue;
142  }
143 
144  // sheetTitle, sheetDescription, sheetShortDescr
145  $modifiedDataStructure['sheets'][$sheetName] = $this->modifySingleSheetInformation($sheetStructure, $pageTsOfSheet);
146  }
147  }
148  }
149 
150  $result['processedTca']['columns'][$fieldName]['config']['ds'] = $modifiedDataStructure;
151 
152  return $result;
153  }
154 
163  protected function removeExcludeFieldsFromDataStructure(array $result, $fieldName, $flexIdentifier)
164  {
165  $dataStructure = $result['processedTca']['columns'][$fieldName]['config']['ds'];
166  $backendUser = $this->getBackendUser();
167  if ($backendUser->isAdmin() || !isset($dataStructure['sheets']) || !is_array($dataStructure['sheets'])) {
168  return $result;
169  }
170 
171  $userNonExcludeFields = GeneralUtility::trimExplode(',', $backendUser->groupData['non_exclude_fields']);
172  $excludeFieldsPrefix = $result['tableName'] . ':' . $fieldName . ';' . $flexIdentifier . ';';
173  $nonExcludeFields = [];
174  foreach ($userNonExcludeFields as $userNonExcludeField) {
175  if (strpos($userNonExcludeField, $excludeFieldsPrefix) !== false) {
176  $exploded = explode(';', $userNonExcludeField);
177  $sheetName = $exploded[2];
178  $allowedFlexFieldName = $exploded[3];
179  $nonExcludeFields[$sheetName][$allowedFlexFieldName] = true;
180  }
181  }
182  foreach ($dataStructure['sheets'] as $sheetName => $sheetDefinition) {
183  if (!isset($sheetDefinition['ROOT']['el']) || !is_array($sheetDefinition['ROOT']['el'])) {
184  continue;
185  }
186  foreach ($sheetDefinition['ROOT']['el'] as $flexFieldName => $fieldDefinition) {
187  if (!empty($fieldDefinition['exclude']) && !isset($nonExcludeFields[$sheetName][$flexFieldName])) {
188  unset($result['processedTca']['columns'][$fieldName]['config']['ds']['sheets'][$sheetName]['ROOT']['el'][$flexFieldName]);
189  }
190  }
191  }
192 
193  return $result;
194  }
195 
204  protected function removeDisabledFieldsFromDataStructure(array $result, $fieldName, $pageTsConfig)
205  {
206  $dataStructure = $result['processedTca']['columns'][$fieldName]['config']['ds'];
207  if (!isset($dataStructure['sheets']) || !is_array($dataStructure['sheets'])) {
208  return $result;
209  }
210  foreach ($dataStructure['sheets'] as $sheetName => $sheetDefinition) {
211  if (!isset($sheetDefinition['ROOT']['el']) || !is_array($sheetDefinition['ROOT']['el'])
212  || !isset($pageTsConfig[$sheetName . '.'])) {
213  continue;
214  }
215  foreach ($sheetDefinition['ROOT']['el'] as $flexFieldName => $fieldDefinition) {
216  if (!empty($pageTsConfig[$sheetName . '.'][$flexFieldName . '.']['disabled'])) {
217  unset($result['processedTca']['columns'][$fieldName]['config']['ds']['sheets'][$sheetName]['ROOT']['el'][$flexFieldName]);
218  }
219  }
220  }
221  return $result;
222  }
223 
238  protected function modifyDataStructureAndDataValuesByFlexFormSegmentGroup(array $result, $fieldName, $pageTsConfig)
239  {
240  $dataStructure = $result['processedTca']['columns'][$fieldName]['config']['ds'];
241  $dataValues = $result['databaseRow'][$fieldName];
242  $tableName = $result['tableName'];
243 
244  if (!isset($dataStructure['sheets']) || !is_array($dataStructure['sheets'])) {
245  return $result;
246  }
247 
249  $formDataGroup = GeneralUtility::makeInstance(FlexFormSegment::class);
251  $formDataCompiler = GeneralUtility::makeInstance(FormDataCompiler::class, $formDataGroup);
252 
253  foreach ($dataStructure['sheets'] as $dataStructureSheetName => $dataStructureSheetDefinition) {
254  if (!isset($dataStructureSheetDefinition['ROOT']['el']) || !is_array($dataStructureSheetDefinition['ROOT']['el'])) {
255  continue;
256  }
257  $dataStructureSheetElements = $dataStructureSheetDefinition['ROOT']['el'];
258 
259  // Prepare pageTsConfig of this sheet
260  $pageTsConfig['TCEFORM.'][$tableName . '.'] = [];
261  if (isset($pageTsConfig[$dataStructureSheetName . '.']) && is_array($pageTsConfig[$dataStructureSheetName . '.'])) {
262  $pageTsConfig['TCEFORM.'][$tableName . '.'] = $pageTsConfig[$dataStructureSheetName . '.'];
263  }
264 
265  $tcaNewColumns = [];
266  $tcaEditColumns = [];
267  $tcaValueArray = [
268  'uid' => $result['databaseRow']['uid'],
269  ];
270  foreach ($dataStructureSheetElements as $dataStructureSheetElementName => $dataStructureSheetElementDefinition) {
271  if (isset($dataStructureSheetElementDefinition['type']) && $dataStructureSheetElementDefinition['type'] === 'array'
272  && isset($dataStructureSheetElementDefinition['section']) && (string)$dataStructureSheetElementDefinition['section'] === '1'
273  ) {
274  // A section
275 
276  // Existing section container elements
277  if (isset($dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['el'])
278  && is_array($dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['el'])
279  ) {
280  $containerArray = $dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['el'];
281  foreach ($containerArray as $aContainerNumber => $aContainerArray) {
282  if (is_array($aContainerArray)) {
283  foreach ($aContainerArray as $aContainerName => $aContainerElementArray) {
284  if ($aContainerName === '_TOGGLE') {
285  // Don't handle internal toggle state field
286  continue;
287  }
288  if (!isset($dataStructureSheetElements[$dataStructureSheetElementName]['el'][$aContainerName])) {
289  // Container not defined in ds
290  continue;
291  }
292 
293  $newColumns = [];
294  $editColumns = [];
295  $valueArray = [
296  'uid' => $result['databaseRow']['uid'],
297  ];
298  foreach ($dataStructureSheetElements[$dataStructureSheetElementName]['el'][$aContainerName]['el'] as $singleFieldName => $singleFieldConfiguration) {
299  // $singleFieldValueArray = ['data']['sSections']['lDEF']['section_1']['el']['1']['container_1']['el']['element_1']
300  $singleFieldValueArray = [];
301  if (isset($aContainerElementArray['el'][$singleFieldName])
302  && is_array($aContainerElementArray['el'][$singleFieldName])
303  ) {
304  $singleFieldValueArray = $aContainerElementArray['el'][$singleFieldName];
305  }
306 
307  if (array_key_exists('vDEF', $singleFieldValueArray)) {
308  $editColumns[$singleFieldName] = $singleFieldConfiguration;
309  $valueArray[$singleFieldName] = $singleFieldValueArray['vDEF'];
310  } else {
311  $newColumns[$singleFieldName] = $singleFieldConfiguration;
312  }
313  }
314 
315  $inputToFlexFormSegment = [
316  'tableName' => $result['tableName'],
317  'command' => '',
318  // It is currently not possible to have pageTsConfig for section container
319  'pageTsConfig' => [],
320  'databaseRow' => $valueArray,
321  'processedTca' => [
322  'ctrl' => [],
323  'columns' => [],
324  ],
325  'flexParentDatabaseRow' => $result['databaseRow'],
326  ];
327 
328  if (!empty($newColumns)) {
329  $inputToFlexFormSegment['command'] = 'new';
330  $inputToFlexFormSegment['processedTca']['columns'] = $newColumns;
331  $flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment);
332 
333  foreach ($newColumns as $singleFieldName => $_) {
334  // Set data value result
335  if (array_key_exists($singleFieldName, $flexSegmentResult['databaseRow'])) {
336  $result['databaseRow'][$fieldName]
337  ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]
338  ['el'][$aContainerNumber][$aContainerName]['el'][$singleFieldName]['vDEF'] =
339  $flexSegmentResult['databaseRow'][$singleFieldName];
340  }
341  // Set TCA structure result, actually, this call *might* be obsolete since the "dummy"
342  // handling below will set it again.
343  $result['processedTca']['columns'][$fieldName]['config']['ds']
344  ['sheets'][$dataStructureSheetName]['ROOT']['el']
345  [$dataStructureSheetElementName]['el'][$aContainerName]['el'][$singleFieldName] =
346  $flexSegmentResult['processedTca']['columns'][$singleFieldName];
347  }
348  }
349 
350  if (!empty($editColumns)) {
351  $inputToFlexFormSegment['command'] = 'edit';
352  $inputToFlexFormSegment['processedTca']['columns'] = $editColumns;
353  $flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment);
354 
355  foreach ($editColumns as $singleFieldName => $_) {
356  // Set data value result
357  if (array_key_exists($singleFieldName, $flexSegmentResult['databaseRow'])) {
358  $result['databaseRow'][$fieldName]
359  ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]
360  ['el'][$aContainerNumber][$aContainerName]['el'][$singleFieldName]['vDEF'] =
361  $flexSegmentResult['databaseRow'][$singleFieldName];
362  }
363  // Set TCA structure result, actually, this call *might* be obsolete since the "dummy"
364  // handling below will set it again.
365  $result['processedTca']['columns'][$fieldName]['config']['ds']
366  ['sheets'][$dataStructureSheetName]['ROOT']['el']
367  [$dataStructureSheetElementName]['el'][$aContainerName]['el'][$singleFieldName] =
368  $flexSegmentResult['processedTca']['columns'][$singleFieldName];
369  }
370  }
371  }
372  }
373  }
374  // End of existing data value handling
375  } else {
376  // Force the section to be an empty array if there are no existing containers
377  $result['databaseRow'][$fieldName]
378  ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['el'] = [];
379  }
380 
381  // Prepare "fresh" row for every possible container
382  if (isset($dataStructureSheetElements[$dataStructureSheetElementName]['el']) && is_array($dataStructureSheetElements[$dataStructureSheetElementName]['el'])) {
383  foreach ($dataStructureSheetElements[$dataStructureSheetElementName]['el'] as $possibleContainerName => $possibleContainerConfiguration) {
384  if (isset($possibleContainerConfiguration['el']) && is_array($possibleContainerConfiguration['el'])) {
385  // Initialize result data array templateRows
386  $result['databaseRow'][$fieldName]
387  ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['templateRows']
388  [$possibleContainerName]['el']
389  = [];
390  foreach ($possibleContainerConfiguration['el'] as $singleFieldName => $singleFieldConfiguration) {
391  $inputToFlexFormSegment = [
392  'tableName' => $result['tableName'],
393  'command' => 'new',
394  'pageTsConfig' => [],
395  'databaseRow' => [
396  'uid' => $result['databaseRow']['uid'],
397  ],
398  'processedTca' => [
399  'ctrl' => [],
400  'columns' => [
401  $singleFieldName => $singleFieldConfiguration,
402  ],
403  ],
404  'flexParentDatabaseRow' => $result['databaseRow'],
405  ];
406  $flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment);
407  if (array_key_exists($singleFieldName, $flexSegmentResult['databaseRow'])) {
408  $result['databaseRow'][$fieldName]
409  ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['templateRows']
410  [$possibleContainerName]['el'][$singleFieldName]['vDEF']
411  = $flexSegmentResult['databaseRow'][$singleFieldName];
412  }
413  $result['processedTca']['columns'][$fieldName]['config']['ds']
414  ['sheets'][$dataStructureSheetName]['ROOT']['el'][$dataStructureSheetElementName]['el']
415  [$possibleContainerName]['el'][$singleFieldName]
416  = $flexSegmentResult['processedTca']['columns'][$singleFieldName];
417  }
418  }
419  }
420  } // End of preparation for each possible container
421 
422  // type without section is not ok
423  } elseif (isset($dataStructureSheetElementDefinition['type']) || isset($dataStructureSheetElementDefinition['section'])) {
424  throw new \UnexpectedValueException(
425  'Broken data structure on field name ' . $fieldName . '. section without type or vice versa is not allowed',
426  1440685208
427  );
428 
429  // A "normal" TCA element
430  } else {
431  if (isset($dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName])
432  && array_key_exists('vDEF', $dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName])
433  ) {
434  $tcaEditColumns[$dataStructureSheetElementName] = $dataStructureSheetElementDefinition;
435  $tcaValueArray[$dataStructureSheetElementName] = $dataValues['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['vDEF'];
436  } else {
437  $tcaNewColumns[$dataStructureSheetElementName] = $dataStructureSheetElementDefinition;
438  }
439 
440  } // End of single element handling
441  }
442 
443  // process the tca columns for the current sheet
444  $inputToFlexFormSegment = [
445  // tablename of "parent" is given down for inline elements to resolve correctly
446  'tableName' => $result['tableName'],
447  'command' => '',
448  'pageTsConfig' => $pageTsConfig,
449  'databaseRow' => $tcaValueArray,
450  'processedTca' => [
451  'ctrl' => [],
452  'columns' => [],
453  ],
454  'flexParentDatabaseRow' => $result['databaseRow'],
455  ];
456 
457  if (!empty($tcaNewColumns)) {
458  $inputToFlexFormSegment['command'] = 'new';
459  $inputToFlexFormSegment['processedTca']['columns'] = $tcaNewColumns;
460  $flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment);
461 
462  foreach ($tcaNewColumns as $dataStructureSheetElementName => $_) {
463  // Set data value result
464  if (array_key_exists($dataStructureSheetElementName, $flexSegmentResult['databaseRow'])) {
465  $result['databaseRow'][$fieldName]
466  ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['vDEF']
467  = $flexSegmentResult['databaseRow'][$dataStructureSheetElementName];
468  }
469  // Set TCA structure result
470  $result['processedTca']['columns'][$fieldName]['config']['ds']
471  ['sheets'][$dataStructureSheetName]['ROOT']['el'][$dataStructureSheetElementName]
472  = $flexSegmentResult['processedTca']['columns'][$dataStructureSheetElementName];
473  }
474  }
475 
476  if (!empty($tcaEditColumns)) {
477  $inputToFlexFormSegment['command'] = 'edit';
478  $inputToFlexFormSegment['processedTca']['columns'] = $tcaEditColumns;
479  $flexSegmentResult = $formDataCompiler->compile($inputToFlexFormSegment);
480 
481  foreach ($tcaEditColumns as $dataStructureSheetElementName => $_) {
482  // Set data value result
483  if (array_key_exists($dataStructureSheetElementName, $flexSegmentResult['databaseRow'])) {
484  $result['databaseRow'][$fieldName]
485  ['data'][$dataStructureSheetName]['lDEF'][$dataStructureSheetElementName]['vDEF']
486  = $flexSegmentResult['databaseRow'][$dataStructureSheetElementName];
487  }
488  // Set TCA structure result
489  $result['processedTca']['columns'][$fieldName]['config']['ds']
490  ['sheets'][$dataStructureSheetName]['ROOT']['el'][$dataStructureSheetElementName]
491  = $flexSegmentResult['processedTca']['columns'][$dataStructureSheetElementName];
492  }
493  }
494  }
495 
496  return $result;
497  }
498 
507  protected function modifySingleSheetInformation(array $dataStructure, array $pageTsOfSheet)
508  {
509  // Return if no elements defined
510  if (!isset($dataStructure['ROOT']['el']) || !is_array($dataStructure['ROOT']['el'])) {
511  return $dataStructure;
512  }
513 
514  // Rename sheet (tab)
515  if (!empty($pageTsOfSheet['sheetTitle'])) {
516  $dataStructure['ROOT']['sheetTitle'] = $pageTsOfSheet['sheetTitle'];
517  }
518  // Set sheet description (tab)
519  if (!empty($pageTsOfSheet['sheetDescription'])) {
520  $dataStructure['ROOT']['sheetDescription'] = $pageTsOfSheet['sheetDescription'];
521  }
522  // Set sheet short description (tab)
523  if (!empty($pageTsOfSheet['sheetShortDescr'])) {
524  $dataStructure['ROOT']['sheetShortDescr'] = $pageTsOfSheet['sheetShortDescr'];
525  }
526 
527  return $dataStructure;
528  }
529 
533  protected function getBackendUser()
534  {
535  return $GLOBALS['BE_USER'];
536  }
537 }