‪TYPO3CMS  ‪main
TcaMigration.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
24 
31 {
33  protected array ‪$messages = [];
34 
45  public function ‪migrate(array ‪$tca): array
46  {
47  $this->‪validateTcaType($tca);
48 
49  ‪$tca = $this->‪migrateColumnsConfig($tca);
51  ‪$tca = $this->‪removeSelIconFieldPath($tca);
52  ‪$tca = $this->‪removeSetToDefaultOnCopy($tca);
65  ‪$tca = $this->‪migrateRequiredFlag($tca);
66  ‪$tca = $this->‪migrateNullFlag($tca);
72  ‪$tca = $this->‪removeAuthModeEnforce($tca);
74  ‪$tca = $this->‪migrateAuthMode($tca);
77  ‪$tca = $this->‪removeAlwaysDescription($tca);
79  ‪$tca = $this->‪removeCtrlCruserId($tca);
84  ‪$tca = $this->‪removeMmInsertFields($tca);
85  ‪$tca = $this->‪removeMmHasUidField($tca);
87 
88  return ‪$tca;
89  }
90 
96  public function ‪getMessages(): array
97  {
98  return ‪$this->messages;
99  }
100 
106  protected function ‪validateTcaType(array ‪$tca)
107  {
108  foreach (‪$tca as $table => $tableDefinition) {
109  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
110  continue;
111  }
112  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
113  if (isset($fieldConfig['config']) && is_array($fieldConfig['config']) && empty($fieldConfig['config']['type'])) {
114  throw new \UnexpectedValueException(
115  'Missing "type" in TCA of field "[\'' . $table . '\'][\'' . $fieldName . '\'][\'config\']".',
116  1482394401
117  );
118  }
119  }
120  }
121  }
122 
129  protected function ‪migrateColumnsConfig(array ‪$tca): array
130  {
131  foreach (‪$tca as $table => &$tableDefinition) {
132  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
133  continue;
134  }
135  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
136  if ((!isset($fieldConfig['config']) || !is_array($fieldConfig['config'])) && !isset($fieldConfig['type'])) {
137  $fieldConfig['config'] = [
138  'type' => 'none',
139  ];
140  $this->messages[] = 'TCA table "' . $table . '" columns field "' . $fieldName . '"'
141  . ' had no mandatory "config" section. This has been added with default type "none":'
142  . ' TCA "' . $table . '[\'columns\'][\'' . $fieldName . '\'][\'config\'][\'type\'] = \'none\'"';
143  }
144  }
145  }
146  return ‪$tca;
147  }
148 
155  {
156  if (isset(‪$tca['pages_language_overlay'])) {
157  $this->messages[] = 'The TCA table \'pages_language_overlay\' is'
158  . ' not used anymore and has been removed automatically in'
159  . ' order to avoid negative side-effects.';
160  unset(‪$tca['pages_language_overlay']);
161  }
162  return ‪$tca;
163  }
164 
171  {
172  foreach (‪$tca as $table => &$tableDefinition) {
173  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
174  continue;
175  }
176  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
177  if (!isset($fieldConfig['config']['enableMultiSelectFilterTextfield'])) {
178  continue;
179  }
180 
181  $this->messages[] = 'The TCA setting \'enableMultiSelectFilterTextfield\' is deprecated '
182  . ' and should be removed from TCA for ' . $table . '[\'columns\']'
183  . '[\'' . $fieldName . '\'][\'config\'][\'enableMultiSelectFilterTextfield\']';
184  unset($fieldConfig['config']['enableMultiSelectFilterTextfield']);
185  }
186  }
187  return ‪$tca;
188  }
189 
195  protected function ‪removeSelIconFieldPath(array ‪$tca): array
196  {
197  foreach (‪$tca as $table => &$configuration) {
198  if (isset($configuration['ctrl']['selicon_field_path'])) {
199  $this->messages[] = 'The TCA table \'' . $table . '\' defines '
200  . '[ctrl][selicon_field_path] which should be removed from TCA, '
201  . 'as it is not in use anymore.';
202  unset($configuration['ctrl']['selicon_field_path']);
203  }
204  }
205  return $tca;
206  }
207 
213  protected function removeSetToDefaultOnCopy(array $tca): array
214  {
215  foreach ($tca as $table => &$configuration) {
216  if (isset($configuration['ctrl']['setToDefaultOnCopy'])) {
217  $this->messages[] = 'The TCA table \'' . $table . '\' defines '
218  . '[ctrl][setToDefaultOnCopy] which should be removed from TCA, '
219  . 'as it is not in use anymore.';
220  unset($configuration['ctrl']['setToDefaultOnCopy']);
221  }
222  }
223  return $tca;
224  }
225 
235  protected function sanitizeControlSectionIntegrity(array $tca): array
236  {
237  $defaultControlSectionColumnConfig = [
238  'type' => 'passthrough',
239  'default' => 0,
240  ];
241  $controlSectionNames = [
242  'origUid' => $defaultControlSectionColumnConfig,
243  'languageField' => [
244  'type' => 'language',
245  ],
246  'transOrigPointerField' => $defaultControlSectionColumnConfig,
247  'translationSource' => $defaultControlSectionColumnConfig,
248  ];
249 
250  foreach ($tca as $tableName => &$configuration) {
251  foreach ($controlSectionNames as $controlSectionName => $controlSectionColumnConfig) {
252  $columnName = $configuration['ctrl'][$controlSectionName] ?? null;
253  if (empty($columnName) || !empty($configuration['columns'][$columnName])) {
254  continue;
255  }
256  $configuration['columns'][$columnName] = [
257  'config' => $controlSectionColumnConfig,
258  ];
259  }
260  }
261  return $tca;
262  }
263 
267  protected function removeExcludeFieldForTransOrigPointerField(array $tca): array
268  {
269  foreach ($tca as $table => &$configuration) {
270  if (isset($configuration['ctrl']['transOrigPointerField'],
271  $configuration['columns'][$configuration['ctrl']['transOrigPointerField']]['exclude'])
272  ) {
273  $this->messages[] = 'The \'' . $table . '\' TCA tables transOrigPointerField '
274  . '\'' . $configuration['ctrl']['transOrigPointerField'] . '\' is defined '
275  . ' as excluded field which is no longer needed and should therefore be removed. ';
276  unset($configuration['columns'][$configuration['ctrl']['transOrigPointerField']]['exclude']);
277  }
278  }
279 
280  return $tca;
281  }
282 
287  protected function removeShowRecordFieldListField(array $tca): array
288  {
289  foreach ($tca as $table => &$configuration) {
290  if (!isset($configuration['interface']['showRecordFieldList'])) {
291  continue;
292  }
293  $this->messages[] = 'The \'' . $table . '\' TCA configuration \'showRecordFieldList\''
294  . ' inside the section \'interface\' is not evaluated anymore and should therefore be removed.';
295  unset($configuration['interface']['showRecordFieldList']);
296  if ($configuration['interface'] === []) {
297  unset($configuration['interface']);
298  }
299  }
300 
301  return ‪$tca;
302  }
303 
311  {
312  foreach (‪$tca as $table => &$configuration) {
313  if (isset($configuration['ctrl']['shadowColumnsForNewPlaceholders'])) {
314  $this->messages[] = 'The TCA table \'' . $table . '\' defines '
315  . '[ctrl][shadowColumnsForNewPlaceholders] which should be removed from TCA, '
316  . 'as it is not in use anymore.';
317  unset($configuration['ctrl']['shadowColumnsForNewPlaceholders']);
318  }
319  if (isset($configuration['ctrl']['shadowColumnsForMovePlaceholders'])) {
320  $this->messages[] = 'The TCA table \'' . $table . '\' defines '
321  . '[ctrl][shadowColumnsForMovePlaceholders] which should be removed from TCA, '
322  . 'as it is not in use anymore.';
323  unset($configuration['ctrl']['shadowColumnsForMovePlaceholders']);
324  }
325  }
326  return $tca;
327  }
328 
333  protected function migrateLanguageFieldToTcaTypeLanguage(array $tca): array
334  {
335  foreach ($tca as $table => &$configuration) {
336  if (isset($configuration['ctrl']['languageField'], $configuration['columns'][$configuration['ctrl']['languageField']])
337  && ($configuration['columns'][$configuration['ctrl']['languageField']]['config']['type'] ?? '') !== 'language'
338  ) {
339  $this->messages[] = 'The TCA field \'' . $configuration['ctrl']['languageField'] . '\' '
340  . 'of table \'' . $table . '\' is defined as the \'languageField\' and should '
341  . 'therefore use the TCA type \'language\' instead of TCA type \'select\' with '
342  . '\'foreign_table=sys_language\' or \'special=languages\'.';
343  $configuration['columns'][$configuration['ctrl']['languageField']]['config'] = [
344  'type' => 'language',
345  ];
346  }
347  }
348 
349  return ‪$tca;
350  }
351 
356  protected function ‪migrateSpecialLanguagesToTcaTypeLanguage(array ‪$tca): array
357  {
358  foreach (‪$tca as $table => &$tableDefinition) {
359  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
360  continue;
361  }
362  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
363  if ((string)($fieldConfig['config']['type'] ?? '') !== 'select'
364  || (string)($fieldConfig['config']['special'] ?? '') !== 'languages'
365  ) {
366  continue;
367  }
368  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' is '
369  . 'defined as type \'select\' with the \'special=languages\' option. This is not '
370  . 'evaluated anymore and should be replaced by the TCA type \'language\'.';
371  $fieldConfig['config'] = [
372  'type' => 'language',
373  ];
374  }
375  }
376 
377  return ‪$tca;
378  }
379 
380  protected function ‪removeShowRemovedLocalizationRecords(array ‪$tca): array
381  {
382  foreach (‪$tca as $table => &$tableDefinition) {
383  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
384  continue;
385  }
386  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
387  if ((string)($fieldConfig['config']['type'] ?? '') !== 'inline'
388  || !isset($fieldConfig['config']['appearance']['showRemovedLocalizationRecords'])
389  ) {
390  continue;
391  }
392  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' is '
393  . 'defined as type \'inline\' with the \'appearance.showRemovedLocalizationRecords\' option set. '
394  . 'As this option is not evaluated anymore and no replacement exists, it should be removed from TCA.';
395  unset($fieldConfig['config']['appearance']['showRemovedLocalizationRecords']);
396  }
397  }
398 
399  return ‪$tca;
400  }
401 
406  protected function ‪migrateFileFolderConfiguration(array ‪$tca): array
407  {
408  foreach (‪$tca as $table => &$tableDefinition) {
409  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
410  continue;
411  }
412  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
413  if ((string)($fieldConfig['config']['type'] ?? '') !== 'select'
414  || !isset($fieldConfig['config']['fileFolder'])
415  ) {
416  continue;
417  }
418  $fieldConfig['config']['fileFolderConfig'] = [
419  'folder' => $fieldConfig['config']['fileFolder'],
420  ];
421  unset($fieldConfig['config']['fileFolder']);
422  if (isset($fieldConfig['config']['fileFolder_extList'])) {
423  $fieldConfig['config']['fileFolderConfig']['allowedExtensions'] = $fieldConfig['config']['fileFolder_extList'];
424  unset($fieldConfig['config']['fileFolder_extList']);
425  }
426  if (isset($fieldConfig['config']['fileFolder_recursions'])) {
427  $fieldConfig['config']['fileFolderConfig']['depth'] = $fieldConfig['config']['fileFolder_recursions'];
428  unset($fieldConfig['config']['fileFolder_recursions']);
429  }
430  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' is '
431  . 'defined as type \'select\' with the \'fileFolder\' configuration option set. To streamline '
432  . 'the configuration, all \'fileFolder\' related configuration options were moved into a '
433  . 'dedicated sub array \'fileFolderConfig\', while \'fileFolder\' is now just \'folder\' and '
434  . 'the other options have been renamed to \'allowedExtensions\' and \'depth\'. '
435  . 'The TCA configuration should be adjusted accordingly.';
436  }
437  }
438 
439  return ‪$tca;
440  }
441 
451  protected function ‪migrateLevelLinksPosition(array ‪$tca): array
452  {
453  foreach (‪$tca as $table => &$tableDefinition) {
454  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
455  continue;
456  }
457  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
458  if ((string)($fieldConfig['config']['type'] ?? '') !== 'inline'
459  || (string)($fieldConfig['config']['appearance']['levelLinksPosition'] ?? '') !== 'none'
460  ) {
461  continue;
462  }
463  // Unset levelLinksPosition and disable all level link buttons
464  unset($fieldConfig['config']['appearance']['levelLinksPosition']);
465  $fieldConfig['config']['appearance']['showAllLocalizationLink'] = false;
466  $fieldConfig['config']['appearance']['showSynchronizationLink'] = false;
467  $fieldConfig['config']['appearance']['showNewRecordLink'] = false;
468 
469  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' sets '
470  . '[appearance][levelLinksPosition] to "none", while only "top", "bottom" and "both" are supported. '
471  . 'The TCA configuration should be adjusted accordingly. In case you want to disable all level links, '
472  . 'use the corresponding level link specific options, e.g. [appearance][showNewRecordLink], instead.';
473  }
474  }
475 
476  return $tca;
477  }
478 
482  protected function migrateRootUidToStartingPoints(array $tca): array
483  {
484  foreach ($tca as $table => &$tableDefinition) {
485  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
486  continue;
487  }
488 
489  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
490  if ((int)($fieldConfig['config']['treeConfig']['rootUid'] ?? 0) === 0
491  || !in_array((string)($fieldConfig['config']['type'] ?? ''), ['select', 'category'], true)
492  ) {
493  continue;
494  }
495 
496  $fieldConfig['config']['treeConfig']['startingPoints'] = (string)(int)$fieldConfig['config']['treeConfig']['rootUid'];
497  unset($fieldConfig['config']['treeConfig']['rootUid']);
498 
499  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' sets '
500  . '[treeConfig][rootUid], which is superseded by [treeConfig][startingPoints].'
501  . 'The TCA configuration should be adjusted accordingly.';
502  }
503  }
504 
505  return $tca;
506  }
507 
512  protected function migrateInternalTypeFolderToTypeFolder(array $tca): array
513  {
514  foreach ($tca as $table => $tableDefinition) {
515  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
516  continue;
517  }
518 
519  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
520  if (($fieldConfig['config']['type'] ?? '') !== 'group' || !isset($fieldConfig['config']['internal_type'])) {
521  continue;
522  }
523  unset($tca[$table]['columns'][$fieldName]['config']['internal_type']);
524 
525  if ($fieldConfig['config']['internal_type'] === 'folder') {
526  $tca[$table]['columns'][$fieldName]['config']['type'] = 'folder';
527  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' has been migrated to '
528  . 'the TCA type \'folder\'. Please adjust your TCA accordingly.';
529  } else {
530  $this->messages[] = 'The property \'internal_type\' of the TCA field \'' . $fieldName . '\' of table \''
531  . $table . '\' is obsolete and has been removed. You can remove it from your TCA as it is not evaluated anymore.';
532  }
533  }
534  }
535 
536  return $tca;
537  }
538 
543  protected function migrateRequiredFlag(array $tca): array
544  {
545  foreach ($tca as $table => $tableDefinition) {
546  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
547  continue;
548  }
549 
550  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
551  if (!GeneralUtility::inList($fieldConfig['config']['eval'] ?? '', 'required')) {
552  continue;
553  }
554 
555  $evalList = GeneralUtility::trimExplode(',', $fieldConfig['config']['eval'], true);
556  // Remove "required" from $evalList
557  $evalList = array_filter($evalList, static function (string $eval) {
558  return $eval !== 'required';
559  });
560  if ($evalList !== []) {
561  // Write back filtered 'eval'
562  $tca[$table]['columns'][$fieldName]['config']['eval'] = implode(',', $evalList);
563  } else {
564  // 'eval' is empty, remove whole configuration
565  unset($tca[$table]['columns'][$fieldName]['config']['eval']);
566  }
567 
568  $tca[$table]['columns'][$fieldName]['config']['required'] = true;
569  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' defines '
570  . '"required" in its "eval" list. This is not evaluated anymore and should be replaced '
571  . ' by `\'required\' => true`.';
572  }
573  }
574 
575  return ‪$tca;
576  }
577 
582  protected function ‪migrateNullFlag(array ‪$tca): array
583  {
584  foreach (‪$tca as $table => $tableDefinition) {
585  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
586  continue;
587  }
588 
589  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
590  if (!‪GeneralUtility::inList($fieldConfig['config']['eval'] ?? '', 'null')) {
591  continue;
592  }
593 
594  $evalList = ‪GeneralUtility::trimExplode(',', $fieldConfig['config']['eval'], true);
595  // Remove "null" from $evalList
596  $evalList = array_filter($evalList, static function (string $eval) {
597  return $eval !== 'null';
598  });
599  if ($evalList !== []) {
600  // Write back filtered 'eval'
601  ‪$tca[$table]['columns'][$fieldName]['config']['eval'] = implode(',', $evalList);
602  } else {
603  // 'eval' is empty, remove whole configuration
604  unset(‪$tca[$table]['columns'][$fieldName]['config']['eval']);
605  }
606 
607  ‪$tca[$table]['columns'][$fieldName]['config']['nullable'] = true;
608  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' defines '
609  . '"null" in its "eval" list. This is not evaluated anymore and should be replaced '
610  . ' by `\'nullable\' => true`.';
611  }
612  }
613 
614  return ‪$tca;
615  }
616 
622  protected function ‪migrateEmailFlagToEmailType(array ‪$tca): array
623  {
624  foreach (‪$tca as $table => $tableDefinition) {
625  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
626  continue;
627  }
628 
629  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
630  if (($fieldConfig['config']['type'] ?? '') !== 'input'
631  || !‪GeneralUtility::inList($fieldConfig['config']['eval'] ?? '', 'email')
632  ) {
633  // Early return in case column is not of type=input or does not define eval=email
634  continue;
635  }
636 
637  // Set the TCA type to "email"
638  ‪$tca[$table]['columns'][$fieldName]['config']['type'] = 'email';
639 
640  // Unset "max"
641  unset(‪$tca[$table]['columns'][$fieldName]['config']['max']);
642 
643  $evalList = ‪GeneralUtility::trimExplode(',', $fieldConfig['config']['eval'], true);
644  $evalList = array_filter($evalList, static function (string $eval) {
645  // Remove anything except "unique" and "uniqueInPid" from eval
646  return in_array($eval, ['unique', 'uniqueInPid'], true);
647  });
648 
649  if ($evalList !== []) {
650  // Write back filtered 'eval'
651  ‪$tca[$table]['columns'][$fieldName]['config']['eval'] = implode(',', $evalList);
652  } else {
653  // 'eval' is empty, remove whole configuration
654  unset(‪$tca[$table]['columns'][$fieldName]['config']['eval']);
655  }
656 
657  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' defines '
658  . '"email" in its "eval" list. The field has therefore been migrated to the TCA type \'email\'. '
659  . 'Please adjust your TCA accordingly.';
660  }
661  }
662 
663  return ‪$tca;
664  }
665 
669  protected function ‪migrateTypeNoneColsToSize(array ‪$tca): array
670  {
671  foreach (‪$tca as $table => $tableDefinition) {
672  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
673  continue;
674  }
675 
676  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
677  if (($fieldConfig['config']['type'] ?? '') !== 'none' || !array_key_exists('cols', $fieldConfig['config'])) {
678  continue;
679  }
680 
681  ‪$tca[$table]['columns'][$fieldName]['config']['size'] = $fieldConfig['config']['cols'];
682  unset(‪$tca[$table]['columns'][$fieldName]['config']['cols']);
683 
684  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' defines '
685  . '"cols" in its config. This value has been migrated to the option "size". Please adjust your TCA accordingly.';
686  }
687  }
688 
689  return $tca;
690  }
691 
699  protected function migrateRenderTypeInputLinkToTypeLink(array $tca): array
700  {
701  foreach ($tca as $table => $tableDefinition) {
702  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
703  continue;
704  }
705 
706  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
707  if (($fieldConfig['config']['type'] ?? '') !== 'input'
708  || ($fieldConfig['config']['renderType'] ?? '') !== 'inputLink'
709  ) {
710  // Early return in case column is not of type=input with renderType=inputLink
711  continue;
712  }
713 
714  // Set the TCA type to "link"
715  $tca[$table]['columns'][$fieldName]['config']['type'] = 'link';
716 
717  // Unset "renderType", "max" and "eval"
718  unset(
719  $tca[$table]['columns'][$fieldName]['config']['max'],
720  $tca[$table]['columns'][$fieldName]['config']['renderType'],
721  $tca[$table]['columns'][$fieldName]['config']['eval'],
722  );
723 
724  // Unset "softref" if set to "typolink"
725  if (($fieldConfig['config']['softref'] ?? '') === 'typolink') {
726  unset($tca[$table]['columns'][$fieldName]['config']['softref']);
727  }
728 
729  // Migrate the linkPopup configuration
730  if (is_array($fieldConfig['config']['fieldControl']['linkPopup'] ?? false)) {
731  $linkPopupConfig = $fieldConfig['config']['fieldControl']['linkPopup'];
732  if ($linkPopupConfig['options']['blindLinkOptions'] ?? false) {
733  $availableTypes = $GLOBALS['TYPO3_CONF_VARS']['SYS']['linkHandler'] ?? [];
734  if ($availableTypes !== []) {
735  $availableTypes = array_keys($availableTypes);
736  } else {
737  // Fallback to a static list, in case linkHandler configuration is not available at this point
738  $availableTypes = ['page', 'file', 'folder', 'url', 'email', 'record', 'telephone'];
739  }
740  $tca[$table]['columns'][$fieldName]['config']['allowedTypes'] = array_values(array_diff(
741  $availableTypes,
742  GeneralUtility::trimExplode(',', str_replace('mail', 'email', (string)$linkPopupConfig['options']['blindLinkOptions']), true)
743  ));
744  }
745  if ($linkPopupConfig['disabled'] ?? false) {
746  $tca[$table]['columns'][$fieldName]['config']['appearance']['enableBrowser'] = false;
747  }
748  if ($linkPopupConfig['options']['title'] ?? false) {
749  $tca[$table]['columns'][$fieldName]['config']['appearance']['browserTitle'] = (string)$linkPopupConfig['options']['title'];
750  }
751  if ($linkPopupConfig['options']['blindLinkFields'] ?? false) {
752  $tca[$table]['columns'][$fieldName]['config']['appearance']['allowedOptions'] = array_values(array_diff(
753  ['target', 'title', 'class', 'params', 'rel'],
754  GeneralUtility::trimExplode(',', (string)$linkPopupConfig['options']['blindLinkFields'], true)
755  ));
756  }
757  if ($linkPopupConfig['options']['allowedExtensions'] ?? false) {
758  $tca[$table]['columns'][$fieldName]['config']['appearance']['allowedFileExtensions'] = GeneralUtility::trimExplode(
759  ',',
760  (string)$linkPopupConfig['options']['allowedExtensions'],
761  true
762  );
763  }
764  }
765 
766  // Unset ['fieldControl']['linkPopup'] - Note: We do this here to ensure
767  // also an invalid (e.g. not an array) field control configuration is removed.
768  unset($tca[$table]['columns'][$fieldName]['config']['fieldControl']['linkPopup']);
769 
770  // In case "linkPopup" has been the only configured fieldControl, unset ['fieldControl'], too.
771  if (empty($tca[$table]['columns'][$fieldName]['config']['fieldControl'])) {
772  unset($tca[$table]['columns'][$fieldName]['config']['fieldControl']);
773  }
774 
775  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' defines '
776  . 'renderType="inputLink". The field has therefore been migrated to the TCA type \'link\'. '
777  . 'This includes corresponding configuration of the "linkPopup", as well as obsolete field '
778  . 'configurations, such as "max" and "softref". Please adjust your TCA accordingly.';
779  }
780  }
781 
782  return ‪$tca;
783  }
784 
793  {
794  foreach (‪$tca as $table => $tableDefinition) {
795  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
796  continue;
797  }
798 
799  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
800  if (($fieldConfig['config']['type'] ?? '') !== 'input'
801  || (!‪GeneralUtility::inList($fieldConfig['config']['eval'] ?? '', 'password')
802  && !‪GeneralUtility::inList($fieldConfig['config']['eval'] ?? '', 'saltedPassword'))
803  ) {
804  // Early return in case column is not of type=input or does not define eval=passowrd
805  continue;
806  }
807 
808  // Set the TCA type to "password"
809  ‪$tca[$table]['columns'][$fieldName]['config']['type'] = 'password';
810 
811  // Unset "max", "search" and "eval"
812  unset(
813  ‪$tca[$table]['columns'][$fieldName]['config']['max'],
814  ‪$tca[$table]['columns'][$fieldName]['config']['search'],
815  ‪$tca[$table]['columns'][$fieldName]['config']['eval'],
816  );
817 
818  $evalList = ‪GeneralUtility::trimExplode(',', $fieldConfig['config']['eval'], true);
819 
820  // Disable password hashing, if eval=password is used standalone
821  if (in_array('password', $evalList, true) && !in_array('saltedPassword', $evalList, true)) {
822  ‪$tca[$table]['columns'][$fieldName]['config']['hashed'] = false;
823  }
824 
825  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' defines '
826  . '"password" or "saltedPassword" in its "eval" list. The field has therefore been migrated to '
827  . 'the TCA type \'password\'. This also includes the removal of obsolete field configurations,'
828  . 'such as "max" and "search". Please adjust your TCA accordingly.';
829  }
830  }
831 
832  return ‪$tca;
833  }
834 
844  {
845  foreach (‪$tca as $table => $tableDefinition) {
846  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
847  continue;
848  }
849 
850  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
851  if (($fieldConfig['config']['type'] ?? '') !== 'input'
852  || ($fieldConfig['config']['renderType'] ?? '') !== 'inputDateTime'
853  ) {
854  // Early return in case column is not of type=input with renderType=inputDateTime
855  continue;
856  }
857 
858  // Set the TCA type to "datetime"
859  ‪$tca[$table]['columns'][$fieldName]['config']['type'] = 'datetime';
860 
861  // Unset "renderType", "max" and "eval"
862  // Note: Also unset "format". This option had been documented but was actually
863  // never used in the FormEngine element. This migration will set it according
864  // to the corresponding "eval" value.
865  unset(
866  ‪$tca[$table]['columns'][$fieldName]['config']['max'],
867  ‪$tca[$table]['columns'][$fieldName]['config']['renderType'],
868  ‪$tca[$table]['columns'][$fieldName]['config']['format'],
869  ‪$tca[$table]['columns'][$fieldName]['config']['eval'],
870  );
871 
872  $evalList = ‪GeneralUtility::trimExplode(',', $fieldConfig['config']['eval'] ?? '', true);
873 
874  // Set the "format" based on "eval". If set to "datetime",
875  // no migration is done since this is the default format.
876  if (in_array('date', $evalList, true)) {
877  ‪$tca[$table]['columns'][$fieldName]['config']['format'] = 'date';
878  } elseif (in_array('time', $evalList, true)) {
879  ‪$tca[$table]['columns'][$fieldName]['config']['format'] = 'time';
880  } elseif (in_array('timesec', $evalList, true)) {
881  ‪$tca[$table]['columns'][$fieldName]['config']['format'] = 'timesec';
882  }
883 
884  if (isset($fieldConfig['config']['default'])) {
885  if (in_array($fieldConfig['config']['dbType'] ?? '', ‪QueryHelper::getDateTimeTypes(), true)) {
886  if ($fieldConfig['config']['default'] === ‪QueryHelper::getDateTimeFormats()[$fieldConfig['config']['dbType']]['empty']) {
887  // Unset default for native datetime fields if the default is the native "empty" value
888  unset(‪$tca[$table]['columns'][$fieldName]['config']['default']);
889  }
890  } elseif (!is_int($fieldConfig['config']['default'])) {
891  if ($fieldConfig['config']['default'] === '') {
892  // Always use int as default (string values are no longer supported for "datetime")
893  ‪$tca[$table]['columns'][$fieldName]['config']['default'] = 0;
894  } elseif (‪MathUtility::canBeInterpretedAsInteger($fieldConfig['config']['default'])) {
895  // Cast default to int, in case it can be interpreted as integer
896  ‪$tca[$table]['columns'][$fieldName]['config']['default'] = (int)$fieldConfig['config']['default'];
897  } else {
898  // Unset default in case it's a no longer supported string
899  unset(‪$tca[$table]['columns'][$fieldName]['config']['default']);
900  }
901  }
902  }
903 
904  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' defines '
905  . 'renderType="inputDateTime". The field has therefore been migrated to the TCA type \'datetime\'. '
906  . 'This includes corresponding migration of the "eval" list, as well as obsolete field '
907  . 'configurations, such as "max". Please adjust your TCA accordingly.';
908  }
909  }
910 
911  return ‪$tca;
912  }
913 
919  protected function ‪migrateRenderTypeColorpickerToTypeColor(array ‪$tca): array
920  {
921  foreach (‪$tca as $table => $tableDefinition) {
922  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
923  continue;
924  }
925 
926  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
927  if (($fieldConfig['config']['type'] ?? '') !== 'input'
928  || ($fieldConfig['config']['renderType'] ?? '') !== 'colorpicker'
929  ) {
930  // Early return in case column is not of type=input with renderType=colorpicker
931  continue;
932  }
933 
934  // Set the TCA type to "color"
935  ‪$tca[$table]['columns'][$fieldName]['config']['type'] = 'color';
936 
937  // Unset "renderType", "max" and "eval"
938  unset(
939  ‪$tca[$table]['columns'][$fieldName]['config']['max'],
940  ‪$tca[$table]['columns'][$fieldName]['config']['renderType'],
941  ‪$tca[$table]['columns'][$fieldName]['config']['eval'],
942  );
943 
944  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' defines '
945  . 'renderType="colorpicker". The field has therefore been migrated to the TCA type \'color\'. '
946  . 'This includes corresponding migration of the "eval" list, as well as obsolete field '
947  . 'configurations, such as "max". Please adjust your TCA accordingly.';
948  }
949  }
950 
951  return ‪$tca;
952  }
953 
957  protected function ‪removeAuthModeEnforce(array ‪$tca): array
958  {
959  foreach (‪$tca as $table => $tableDefinition) {
960  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
961  continue;
962  }
963  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
964  if (array_key_exists('authMode_enforce', $fieldConfig['config'] ?? [])) {
965  unset(‪$tca[$table]['columns'][$fieldName]['config']['authMode_enforce']);
966  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' uses '
967  . '\'authMode_enforce\'. This config key is obsolete and has been removed.'
968  . ' Please adjust your TCA accordingly.';
969  }
970  }
971  }
972  return ‪$tca;
973  }
974 
980  {
981  foreach (‪$tca as $table => $tableDefinition) {
982  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
983  continue;
984  }
985  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
986  if (($fieldConfig['config']['type'] ?? '') !== 'select' || ($fieldConfig['config']['authMode'] ?? '') !== 'individual') {
987  continue;
988  }
989  foreach ($fieldConfig['config']['items'] ?? [] as $index => $item) {
990  if (in_array($item[4] ?? '', ['EXPL_ALLOW', 'EXPL_DENY'], true)) {
991  ‪$tca[$table]['columns'][$fieldName]['config']['items'][$index][4] = '';
992  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' sets ' . $item[4]
993  . ' at position 5 of the items array. This was used in combination with \'authMode=individual\' and'
994  . ' is obsolete since \'individual\' is no longer supported.';
995  }
996  if (isset($item[5])) {
997  unset(‪$tca[$table]['columns'][$fieldName]['config']['items'][$index][5]);
998  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' sets ' . $item[5]
999  . ' at position 6 of the items array. This was used in combination with \'authMode=individual\' and'
1000  . ' is obsolete since \'individual\' is no longer supported.';
1001  }
1002  }
1003  }
1004  }
1005  return ‪$tca;
1006  }
1007 
1012  protected function ‪migrateAuthMode(array ‪$tca): array
1013  {
1014  foreach (‪$tca as $table => $tableDefinition) {
1015  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
1016  continue;
1017  }
1018  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
1019  if (array_key_exists('authMode', $fieldConfig['config'] ?? [])
1020  && $fieldConfig['config']['authMode'] !== 'explicitAllow'
1021  ) {
1022  ‪$tca[$table]['columns'][$fieldName]['config']['authMode'] = 'explicitAllow';
1023  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' sets '
1024  . '\'authMode\' to \'' . $fieldConfig['config']['authMode'] . '\'. The only allowed value is \'explicitAllow\','
1025  . ' and that value has been set now. Please adjust your TCA accordingly. Note this has impact on'
1026  . ' backend group access rights, these should be reviewed and new access right for this field should'
1027  . ' be set. An upgrade wizard partially migrates this and reports be_groups rows that need manual attention.';
1028  }
1029  }
1030  }
1031  return ‪$tca;
1032  }
1033 
1041  protected function ‪migrateEvalIntAndDouble2ToTypeNumber(array ‪$tca): array
1042  {
1043  foreach (‪$tca as $table => $tableDefinition) {
1044  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
1045  continue;
1046  }
1047 
1048  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
1049  // Return early, if not TCA type "input" or a renderType is set
1050  // or neither eval=int nor eval=double2 are set.
1051  if (
1052  ($fieldConfig['config']['type'] ?? '') !== 'input'
1053  || ($fieldConfig['config']['renderType'] ?? '') !== ''
1054  || (
1055  !‪GeneralUtility::inList($fieldConfig['config']['eval'] ?? '', 'int')
1056  && !‪GeneralUtility::inList($fieldConfig['config']['eval'] ?? '', 'double2')
1057  )
1058  ) {
1059  continue;
1060  }
1061 
1062  // Set the TCA type to "number"
1063  ‪$tca[$table]['columns'][$fieldName]['config']['type'] = 'number';
1064 
1065  // Unset "max" and "eval"
1066  unset(
1067  ‪$tca[$table]['columns'][$fieldName]['config']['max'],
1068  ‪$tca[$table]['columns'][$fieldName]['config']['eval'],
1069  );
1070 
1071  $numberType = '';
1072  $evalList = ‪GeneralUtility::trimExplode(',', $fieldConfig['config']['eval'], true);
1073 
1074  // Convert eval "double2" to format = "decimal" and store the "number type" for the deprecation log
1075  if (in_array('double2', $evalList, true)) {
1076  $numberType = 'double2';
1077  ‪$tca[$table]['columns'][$fieldName]['config']['format'] = 'decimal';
1078  } elseif (in_array('int', $evalList, true)) {
1079  $numberType = 'int';
1080  }
1081 
1082  $this->messages[] = 'The TCA field \'' . $fieldName . '\' in table \'' . $table . '\'" defines '
1083  . 'eval="' . $numberType . '". The field has therefore been migrated to the TCA type \'number\'. '
1084  . 'This includes corresponding migration of the "eval" list, as well as obsolete field '
1085  . 'configurations, such as "max". Please adjust your TCA accordingly.';
1086  }
1087  }
1088  return $tca;
1089  }
1090 
1095  protected function removeAlwaysDescription(array $tca): array
1096  {
1097  foreach ($tca as $table => &$tableDefinition) {
1098  if (!isset($tableDefinition['interface']['always_description'])) {
1099  continue;
1100  }
1101  unset($tableDefinition['interface']['always_description']);
1102  if ($tableDefinition['interface'] === []) {
1103  unset($tableDefinition['interface']);
1104  }
1105  $this->messages[] = 'The TCA property [\'interface\'][\'always_description\'] of table \'' . $table
1106  . '\' is not evaluated anymore and has therefore been removed. Please adjust your TCA accordingly.';
1107  }
1108  return $tca;
1109  }
1110 
1114  protected function removeCtrlCruserId(array $tca): array
1115  {
1116  foreach ($tca as $table => &$tableDefinition) {
1117  if (!isset($tableDefinition['ctrl']['cruser_id'])) {
1118  continue;
1119  }
1120  unset($tableDefinition['ctrl']['cruser_id']);
1121  $this->messages[] = 'The TCA property [\'ctrl\'][\'cruser_id\'] of table \'' . $table
1122  . '\' is not evaluated anymore and has therefore been removed. Please adjust your TCA accordingly.';
1123  }
1124  return $tca;
1125  }
1126 
1135  protected function migrateFalHandlingInInlineToTypeFile(array $tca): array
1136  {
1137  foreach ($tca as $table => &$tableDefinition) {
1138  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
1139  continue;
1140  }
1141 
1142  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
1143  if (($fieldConfig['config']['type'] ?? '') !== 'inline'
1144  || ($fieldConfig['config']['foreign_table'] ?? '') !== 'sys_file_reference'
1145  ) {
1146  // Early return in case column is not of type=inline with foreign_table=sys_file_reference
1147  continue;
1148  }
1149 
1150  // Place to add additional information, which will later be appended to the deprecation message
1151  $additionalInformation = '';
1152 
1153  // Set the TCA type to "file"
1154  $fieldConfig['config']['type'] = 'file';
1155 
1156  // Remove table relation related options, since they are
1157  // either not needed anymore or set by TcaPreperation automatically.
1158  unset(
1159  $fieldConfig['config']['foreign_table'],
1160  $fieldConfig['config']['foreign_field'],
1161  $fieldConfig['config']['foreign_sortby'],
1162  $fieldConfig['config']['foreign_table_field'],
1163  $fieldConfig['config']['foreign_match_fields'],
1164  $fieldConfig['config']['foreign_label'],
1165  $fieldConfig['config']['foreign_selector'],
1166  $fieldConfig['config']['foreign_unique'],
1167  );
1168 
1169  // "new" control is not supported for this type so remove it altogether for cleaner TCA
1170  unset($fieldConfig['config']['appearance']['enabledControls']['new']);
1171 
1172  // [appearance][headerThumbnail][field] is not needed anymore
1173  unset($fieldConfig['config']['appearance']['headerThumbnail']['field']);
1174 
1175  // A couple of further appearance options are not supported by type "file", unset them as well
1176  unset(
1177  $fieldConfig['config']['appearance']['showNewRecordLink'],
1178  $fieldConfig['config']['appearance']['newRecordLinkAddTitle'],
1179  $fieldConfig['config']['appearance']['newRecordLinkTitle'],
1180  $fieldConfig['config']['appearance']['levelLinksPosition'],
1181  $fieldConfig['config']['appearance']['useCombination'],
1182  $fieldConfig['config']['appearance']['suppressCombinationWarning']
1183  );
1184 
1185  // Migrate [appearance][showPossibleRecordsSelector] to [appearance][showFileSelectors]
1186  if (isset($fieldConfig['config']['appearance']['showPossibleRecordsSelector'])) {
1187  $fieldConfig['config']['appearance']['showFileSelectors'] = $fieldConfig['config']['appearance']['showPossibleRecordsSelector'];
1188  unset($fieldConfig['config']['appearance']['showPossibleRecordsSelector']);
1189  }
1190 
1191  // "customControls" hook has been replaced by the CustomFileControlsEvent
1192  if (isset($fieldConfig['config']['customControls'])) {
1193  $additionalInformation .= ' The \'customControls\' option is not evaluated anymore and has '
1194  . 'to be replaced with the PSR-14 \'CustomFileControlsEvent\'.';
1195  unset($fieldConfig['config']['customControls']);
1196  }
1197 
1198  // Migrate element browser related settings
1199  if (!empty($fieldConfig['config']['overrideChildTca']['columns']['uid_local']['config']['appearance'])) {
1200  if (!empty($fieldConfig['config']['overrideChildTca']['columns']['uid_local']['config']['appearance']['elementBrowserAllowed'])) {
1201  // Migrate "allowed" file extensions from appearance
1202  $fieldConfig['config']['allowed'] = $fieldConfig['config']['overrideChildTca']['columns']['uid_local']['config']['appearance']['elementBrowserAllowed'];
1203  }
1204  unset(
1205  $fieldConfig['config']['overrideChildTca']['columns']['uid_local']['config']['appearance']['elementBrowserType'],
1206  $fieldConfig['config']['overrideChildTca']['columns']['uid_local']['config']['appearance']['elementBrowserAllowed']
1207  );
1208  if (empty($fieldConfig['config']['overrideChildTca']['columns']['uid_local']['config']['appearance'])) {
1209  unset($fieldConfig['config']['overrideChildTca']['columns']['uid_local']['config']['appearance']);
1210  if (empty($fieldConfig['config']['overrideChildTca']['columns']['uid_local']['config'])) {
1211  unset($fieldConfig['config']['overrideChildTca']['columns']['uid_local']['config']);
1212  if (empty($fieldConfig['config']['overrideChildTca']['columns']['uid_local'])) {
1213  unset($fieldConfig['config']['overrideChildTca']['columns']['uid_local']);
1214  if (empty($fieldConfig['config']['overrideChildTca']['columns'])) {
1215  unset($fieldConfig['config']['overrideChildTca']['columns']);
1216  if (empty($fieldConfig['config']['overrideChildTca'])) {
1217  unset($fieldConfig['config']['overrideChildTca']);
1218  }
1219  }
1220  }
1221  }
1222  }
1223  }
1224 
1225  // Migrate file extension filter
1226  if (!empty($fieldConfig['config']['filter'])) {
1227  foreach ($fieldConfig['config']['filter'] as $key => $filter) {
1228  if (($filter['userFunc'] ?? '') === (FileExtensionFilter::class . '->filterInlineChildren')) {
1229  $allowedFileExtensions = (string)($filter['parameters']['allowedFileExtensions'] ?? '');
1230  // Note: Allowed file extensions in the filter take precedence over possible
1231  // extensions defined for the element browser. This is due to filters are evaluated
1232  // by the DataHandler while element browser is only applied in FormEngine UI.
1233  if ($allowedFileExtensions !== '') {
1234  $fieldConfig['config']['allowed'] = $allowedFileExtensions;
1235  }
1236  $disallowedFileExtensions = (string)($filter['parameters']['disallowedFileExtensions'] ?? '');
1237  if ($disallowedFileExtensions !== '') {
1238  $fieldConfig['config']['disallowed'] = $disallowedFileExtensions;
1239  }
1240  unset($fieldConfig['config']['filter'][$key]);
1241  }
1242  }
1243  // Remove filter if it got empty
1244  if (empty($fieldConfig['config']['filter'])) {
1245  unset($fieldConfig['config']['filter']);
1246  }
1247  }
1248 
1249  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' defines '
1250  . 'type="inline" with foreign_table=sys_file_reference. The field has therefore been '
1251  . 'migrated to the dedicated TCA type \'file\'' . $additionalInformation . ' '
1252  . 'Please adjust your TCA accordingly.';
1253  }
1254  }
1255 
1256  return $tca;
1257  }
1258 
1263  protected function removeFalRelatedElementBrowserOptions(array $tca): array
1264  {
1265  foreach ($tca as $table => &$tableDefinition) {
1266  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
1267  continue;
1268  }
1269 
1270  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
1271  if (($fieldConfig['config']['type'] ?? '') !== 'group'
1272  || (
1273  !isset($fieldConfig['config']['appearance']['elementBrowserType'])
1274  && !isset($fieldConfig['config']['appearance']['elementBrowserAllowed'])
1275  )
1276  ) {
1277  // Early return in case column is not of type=group or does not define the options in question
1278  continue;
1279  }
1280 
1281  unset(
1282  $fieldConfig['config']['appearance']['elementBrowserType'],
1283  $fieldConfig['config']['appearance']['elementBrowserAllowed']
1284  );
1285 
1286  // Also unset "appearance" if empty
1287  if (empty($fieldConfig['config']['appearance'])) {
1288  unset($fieldConfig['config']['appearance']);
1289  }
1290 
1291  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' defines '
1292  . 'fal related element browser options, which are no longer needed and therefore removed. '
1293  . 'Please adjust your TCA accordingly.';
1294  }
1295  }
1296 
1297  return $tca;
1298  }
1299 
1306  protected function removeFalRelatedOptionsFromTypeInline(array $tca): array
1307  {
1308  foreach ($tca as $table => &$tableDefinition) {
1309  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
1310  continue;
1311  }
1312 
1313  foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
1314  if (($fieldConfig['config']['type'] ?? '') !== 'inline'
1315  || (
1316  !isset($fieldConfig['config']['appearance']['headerThumbnail'])
1317  && !isset($fieldConfig['config']['appearance']['fileUploadAllowed'])
1318  && !isset($fieldConfig['config']['appearance']['fileByUrlAllowed'])
1319  )
1320  ) {
1321  // Early return in case column is not of type=inline or does not define the options in question
1322  continue;
1323  }
1324 
1325  unset(
1326  $fieldConfig['config']['appearance']['headerThumbnail'],
1327  $fieldConfig['config']['appearance']['fileUploadAllowed'],
1328  $fieldConfig['config']['appearance']['fileByUrlAllowed']
1329  );
1330 
1331  // Also unset "appearance" if empty
1332  if (empty($fieldConfig['config']['appearance'])) {
1333  unset($fieldConfig['config']['appearance']);
1334  }
1335 
1336  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' defines '
1337  . 'fal related appearance options, which are no longer evaluated and therefore removed. '
1338  . 'Please adjust your TCA accordingly.';
1339  }
1340  }
1341 
1342  return $tca;
1343  }
1344 
1348  protected function removePassContentFromTypeNone(array $tca): array
1349  {
1350  foreach ($tca as $table => $tableDefinition) {
1351  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
1352  continue;
1353  }
1354  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
1355  if (($fieldConfig['config']['type'] ?? '') === 'none'
1356  && array_key_exists('pass_content', $fieldConfig['config'] ?? [])
1357  ) {
1358  unset($tca[$table]['columns'][$fieldName]['config']['pass_content']);
1359  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' uses '
1360  . '\'pass_content\'. This config key is obsolete and has been removed. '
1361  . 'Please adjust your TCA accordingly.';
1362  }
1363  }
1364  }
1365  return $tca;
1366  }
1367 
1389  protected function migrateItemsToAssociativeArray(array $tca): array
1390  {
1391  foreach ($tca as $table => $tableDefinition) {
1392  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
1393  continue;
1394  }
1395  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
1396  if (
1397  array_key_exists('items', $fieldConfig['config'] ?? [])
1398  && in_array(($fieldConfig['config']['type'] ?? ''), ['select', 'radio', 'check'], true)
1399  ) {
1400  $hasLegacyItemConfiguration = false;
1401  $items = $fieldConfig['config']['items'];
1402  foreach ($items as $key => $item) {
1403  if (!is_array($item)) {
1404  continue;
1405  }
1406  if (array_key_exists(0, $item)) {
1407  $hasLegacyItemConfiguration = true;
1408  $items[$key]['label'] = $item[0];
1409  unset($items[$key][0]);
1410  }
1411  if (($fieldConfig['config']['type'] !== 'check') && array_key_exists(1, $item)) {
1412  $hasLegacyItemConfiguration = true;
1413  $items[$key]['value'] = $item[1];
1414  unset($items[$key][1]);
1415  }
1416  if ($fieldConfig['config']['type'] === 'select') {
1417  if (array_key_exists(2, $item)) {
1418  $hasLegacyItemConfiguration = true;
1419  $items[$key]['icon'] = $item[2];
1420  unset($items[$key][2]);
1421  }
1422  if (array_key_exists(3, $item)) {
1423  $hasLegacyItemConfiguration = true;
1424  $items[$key]['group'] = $item[3];
1425  unset($items[$key][3]);
1426  }
1427  if (array_key_exists(4, $item)) {
1428  $hasLegacyItemConfiguration = true;
1429  $items[$key]['description'] = $item[4];
1430  unset($items[$key][4]);
1431  }
1432  }
1433  }
1434  if ($hasLegacyItemConfiguration) {
1435  $tca[$table]['columns'][$fieldName]['config']['items'] = $items;
1436  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' uses '
1437  . 'the legacy way of defining \'items\'. Please switch to associated array keys: '
1438  . 'label, value, icon, group, description.';
1439  }
1440  }
1441  }
1442  }
1443  return $tca;
1444  }
1445 
1446  protected function removeMmInsertFields(array $tca): array
1447  {
1448  foreach ($tca as $table => $tableDefinition) {
1449  if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'] ?? false)) {
1450  continue;
1451  }
1452  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
1453  if (isset($fieldConfig['config']['MM_insert_fields'])) {
1454  unset($tca[$table]['columns'][$fieldName]['config']['MM_insert_fields']);
1455  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' uses '
1456  . '\'MM_insert_fields\'. This config key is obsolete and should be removed. '
1457  . 'Please adjust your TCA accordingly.';
1458  }
1459  }
1460  }
1461  return $tca;
1462  }
1463 
1464  protected function removeMmHasUidField(array $tca): array
1465  {
1466  foreach ($tca as $table => $tableDefinition) {
1467  if (!is_array($tableDefinition['columns'] ?? false)) {
1468  continue;
1469  }
1470  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
1471  if (isset($fieldConfig['config']['MM_hasUidField'])) {
1472  unset($tca[$table]['columns'][$fieldName]['config']['MM_hasUidField']);
1473  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' uses '
1474  . '\'MM_hasUidField\'. This config key is obsolete and should be removed. '
1475  . 'Please adjust your TCA accordingly.';
1476  }
1477  }
1478  }
1479  return $tca;
1480  }
1481 
1482  protected function migrateT3EditorToCodeEditor(array $tca): array
1483  {
1484  foreach ($tca as $table => $tableDefinition) {
1485  if (!is_array($tableDefinition['columns'] ?? false)) {
1486  continue;
1487  }
1488  foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
1489  if (($fieldConfig['config']['renderType'] ?? '') === 't3editor') {
1490  $tca[$table]['columns'][$fieldName]['config']['renderType'] = 'codeEditor';
1491  $this->messages[] = 'The TCA field \'' . $fieldName . '\' of table \'' . $table . '\' uses '
1492  . '\'renderType\' with the value \'t3editor\', which has been migrated to \'codeEditor\'. '
1493  . 'Please adjust your TCA accordingly.';
1494  }
1495  }
1496 
1497  foreach ($tableDefinition['types'] ?? [] as $typeName => $typeConfig) {
1498  foreach ($typeConfig['columnsOverrides'] ?? [] as $columnOverride => $columnOverrideConfig) {
1499  if (($columnOverrideConfig['config']['renderType'] ?? '') === 't3editor') {
1500  $tca[$table]['types'][$typeName]['columnsOverrides'][$columnOverride]['config']['renderType'] = 'codeEditor';
1501  $this->messages[] = 'The TCA column override \'' . $columnOverride . '\' of table \'' . $table . '\' uses '
1502  . '\'renderType\' with the value \'t3editor\', which has been migrated to \'codeEditor\'. '
1503  . 'Please adjust your TCA accordingly.';
1504  }
1505  }
1506  }
1507  }
1508  return $tca;
1509  }
1510 }
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeShowRemovedLocalizationRecords
‪removeShowRemovedLocalizationRecords(array $tca)
Definition: TcaMigration.php:380
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeSelectAuthModeIndividualItemsKeyword
‪removeSelectAuthModeIndividualItemsKeyword(array $tca)
Definition: TcaMigration.php:979
‪TYPO3\CMS\Core\Database\Query\QueryHelper\getDateTimeFormats
‪static array getDateTimeFormats()
Definition: QueryHelper.php:183
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateNullFlag
‪migrateNullFlag(array $tca)
Definition: TcaMigration.php:582
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeMmInsertFields
‪removeMmInsertFields(array $tca)
Definition: TcaMigration.php:1446
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeFalRelatedElementBrowserOptions
‪removeFalRelatedElementBrowserOptions(array $tca)
Definition: TcaMigration.php:1263
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrate
‪migrate(array $tca)
Definition: TcaMigration.php:45
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateEmailFlagToEmailType
‪migrateEmailFlagToEmailType(array $tca)
Definition: TcaMigration.php:622
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateInternalTypeFolderToTypeFolder
‪migrateInternalTypeFolderToTypeFolder(array $tca)
Definition: TcaMigration.php:512
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\getMessages
‪array getMessages()
Definition: TcaMigration.php:96
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeWorkspacePlaceholderShadowColumnsConfiguration
‪array removeWorkspacePlaceholderShadowColumnsConfiguration(array $tca)
Definition: TcaMigration.php:310
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migratePasswordAndSaltedPasswordToPasswordType
‪migratePasswordAndSaltedPasswordToPasswordType(array $tca)
Definition: TcaMigration.php:792
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateRequiredFlag
‪migrateRequiredFlag(array $tca)
Definition: TcaMigration.php:543
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateRenderTypeInputLinkToTypeLink
‪migrateRenderTypeInputLinkToTypeLink(array $tca)
Definition: TcaMigration.php:699
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateSpecialLanguagesToTcaTypeLanguage
‪migrateSpecialLanguagesToTcaTypeLanguage(array $tca)
Definition: TcaMigration.php:356
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeMmHasUidField
‪removeMmHasUidField(array $tca)
Definition: TcaMigration.php:1464
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateItemsToAssociativeArray
‪migrateItemsToAssociativeArray(array $tca)
Definition: TcaMigration.php:1389
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeSelIconFieldPath
‪array removeSelIconFieldPath(array $tca)
Definition: TcaMigration.php:195
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateRenderTypeInputDateTimeToTypeDatetime
‪migrateRenderTypeInputDateTimeToTypeDatetime(array $tca)
Definition: TcaMigration.php:843
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeExcludeFieldForTransOrigPointerField
‪removeExcludeFieldForTransOrigPointerField(array $tca)
Definition: TcaMigration.php:267
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeShowRecordFieldListField
‪removeShowRecordFieldListField(array $tca)
Definition: TcaMigration.php:287
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateLanguageFieldToTcaTypeLanguage
‪migrateLanguageFieldToTcaTypeLanguage(array $tca)
Definition: TcaMigration.php:333
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateLevelLinksPosition
‪migrateLevelLinksPosition(array $tca)
Definition: TcaMigration.php:451
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeEnableMultiSelectFilterTextfieldConfiguration
‪array removeEnableMultiSelectFilterTextfieldConfiguration(array $tca)
Definition: TcaMigration.php:170
‪TYPO3\CMS\Core\Configuration\Tca
Definition: TcaFactory.php:18
‪TYPO3\CMS\Core\Database\Query\QueryHelper
Definition: QueryHelper.php:32
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateRootUidToStartingPoints
‪migrateRootUidToStartingPoints(array $tca)
Definition: TcaMigration.php:482
‪TYPO3\CMS\Core\Database\Query\QueryHelper\getDateTimeTypes
‪static array getDateTimeTypes()
Definition: QueryHelper.php:211
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migratePagesLanguageOverlayRemoval
‪array migratePagesLanguageOverlayRemoval(array $tca)
Definition: TcaMigration.php:154
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateT3EditorToCodeEditor
‪migrateT3EditorToCodeEditor(array $tca)
Definition: TcaMigration.php:1482
‪TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter
Definition: FileExtensionFilter.php:31
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateFileFolderConfiguration
‪migrateFileFolderConfiguration(array $tca)
Definition: TcaMigration.php:406
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateFalHandlingInInlineToTypeFile
‪migrateFalHandlingInInlineToTypeFile(array $tca)
Definition: TcaMigration.php:1135
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateTypeNoneColsToSize
‪migrateTypeNoneColsToSize(array $tca)
Definition: TcaMigration.php:669
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\validateTcaType
‪validateTcaType(array $tca)
Definition: TcaMigration.php:106
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\$messages
‪array $messages
Definition: TcaMigration.php:33
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateColumnsConfig
‪migrateColumnsConfig(array $tca)
Definition: TcaMigration.php:129
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateEvalIntAndDouble2ToTypeNumber
‪migrateEvalIntAndDouble2ToTypeNumber(array $tca)
Definition: TcaMigration.php:1041
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeAlwaysDescription
‪removeAlwaysDescription(array $tca)
Definition: TcaMigration.php:1095
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪$tca
‪$tca
Definition: sys_file_metadata.php:5
‪TYPO3\CMS\Core\Utility\GeneralUtility\inList
‪static bool inList($list, $item)
Definition: GeneralUtility.php:422
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removePassContentFromTypeNone
‪removePassContentFromTypeNone(array $tca)
Definition: TcaMigration.php:1348
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\sanitizeControlSectionIntegrity
‪sanitizeControlSectionIntegrity(array $tca)
Definition: TcaMigration.php:235
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeAuthModeEnforce
‪removeAuthModeEnforce(array $tca)
Definition: TcaMigration.php:957
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateRenderTypeColorpickerToTypeColor
‪migrateRenderTypeColorpickerToTypeColor(array $tca)
Definition: TcaMigration.php:919
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:822
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeFalRelatedOptionsFromTypeInline
‪removeFalRelatedOptionsFromTypeInline(array $tca)
Definition: TcaMigration.php:1306
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeCtrlCruserId
‪removeCtrlCruserId(array $tca)
Definition: TcaMigration.php:1114
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration
Definition: TcaMigration.php:31
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\migrateAuthMode
‪migrateAuthMode(array $tca)
Definition: TcaMigration.php:1012
‪TYPO3\CMS\Core\Configuration\Tca\TcaMigration\removeSetToDefaultOnCopy
‪array removeSetToDefaultOnCopy(array $tca)
Definition: TcaMigration.php:213