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