[TASK] Drop "iconsInOptionTags" option
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Migrations / TcaMigration.php
1 <?php
2 namespace TYPO3\CMS\Core\Migrations;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18 use TYPO3\CMS\Core\Utility\StringUtility;
19
20 /**
21 * Migrate TCA from old to new syntax. Used in bootstrap and Flex Form Data Structures.
22 *
23 * @internal Class and API may change any time.
24 */
25 class TcaMigration {
26
27 /**
28 * Accumulate migration messages
29 *
30 * @var array
31 */
32 protected $messages = array();
33
34 /**
35 * Migrate old TCA to new TCA.
36 *
37 * See unit tests for details.
38 *
39 * @param array $tca
40 * @return array
41 */
42 public function migrate(array $tca) {
43 $tca = $this->migrateT3editorWizardToRenderTypeT3editorIfNotEnabledByTypeConfig($tca);
44 $tca = $this->migrateSpecialConfigurationAndRemoveShowItemStylePointerConfig($tca);
45 $tca = $this->migrateT3editorWizardWithEnabledByTypeConfigToColumnsOverrides($tca);
46 $tca = $this->migrateShowItemAdditionalPaletteToOwnPalette($tca);
47 $tca = $this->migrateIconsForFormFieldWizardsToNewLocation($tca);
48 $tca = $this->migrateExtAndSysextPathToEXTPath($tca);
49 $tca = $this->migrateIconsInOptionTags($tca);
50 // @todo: if showitem/defaultExtras wizards[xy] is migrated to columnsOverrides here, enableByTypeConfig could be dropped
51 return $tca;
52 }
53
54 /**
55 * Get messages of migrated fields. Can be used for deprecation messages after migrate() was called.
56 *
57 * @return array Migration messages
58 */
59 public function getMessages() {
60 return $this->messages;
61 }
62
63 /**
64 * Migrate type=text field with t3editor wizard to renderType=t3editor without this wizard
65 *
66 * @param array $tca Incoming TCA
67 * @return array Migrated TCA
68 */
69 protected function migrateT3editorWizardToRenderTypeT3editorIfNotEnabledByTypeConfig(array $tca) {
70 $newTca = $tca;
71 foreach ($tca as $table => $tableDefinition) {
72 if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
73 continue;
74 }
75 foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
76 if (
77 !empty($fieldConfig['config']['type']) // type is set
78 && trim($fieldConfig['config']['type']) === 'text' // to "text"
79 && isset($fieldConfig['config']['wizards'])
80 && is_array($fieldConfig['config']['wizards']) // and there are wizards
81 ) {
82 foreach ($fieldConfig['config']['wizards'] as $wizardName => $wizardConfig) {
83 if (
84 !empty($wizardConfig['userFunc']) // a userFunc is defined
85 && trim($wizardConfig['userFunc']) === 'TYPO3\\CMS\\T3editor\\FormWizard->main' // and set to FormWizard
86 && (
87 !isset($wizardConfig['enableByTypeConfig']) // and enableByTypeConfig is not set
88 || (isset($wizardConfig['enableByTypeConfig']) && !$wizardConfig['enableByTypeConfig']) // or set, but not enabled
89 )
90 ) {
91 // Set renderType from text to t3editor
92 $newTca[$table]['columns'][$fieldName]['config']['renderType'] = 't3editor';
93 // Unset this wizard definition
94 unset($newTca[$table]['columns'][$fieldName]['config']['wizards'][$wizardName]);
95 // Move format parameter
96 if (!empty($wizardConfig['params']['format'])) {
97 $newTca[$table]['columns'][$fieldName]['config']['format'] = $wizardConfig['params']['format'];
98 }
99 $this->messages[] = 'Migrated t3editor wizard in TCA of table "' . $table . '" field "' . $fieldName . '" to a renderType definition.';
100 }
101 }
102 // If no wizard is left after migration, unset the whole sub array
103 if (empty($newTca[$table]['columns'][$fieldName]['config']['wizards'])) {
104 unset($newTca[$table]['columns'][$fieldName]['config']['wizards']);
105 }
106 }
107 }
108 }
109 return $newTca;
110 }
111
112 /**
113 * Remove "style pointer", the 5th parameter from "types" "showitem" configuration.
114 * Move "specConf", 4th parameter from "tyes" "showitem" to "types" "columnsOverrides.
115 *
116 * @param array $tca Incoming TCA
117 * @return array Modified TCA
118 */
119 protected function migrateSpecialConfigurationAndRemoveShowItemStylePointerConfig(array $tca) {
120 $newTca = $tca;
121 foreach ($tca as $table => $tableDefinition) {
122 if (!isset($tableDefinition['types']) || !is_array($tableDefinition['types'])) {
123 continue;
124 }
125 foreach ($tableDefinition['types'] as $typeName => $typeArray) {
126 if (!is_string($typeArray['showitem']) || strpos($typeArray['showitem'], ';') === FALSE) {
127 // Continue directly if no semicolon is found
128 continue;
129 }
130 $itemList = GeneralUtility::trimExplode(',', $typeArray['showitem'], TRUE);
131 $newFieldStrings = array();
132 foreach ($itemList as $fieldString) {
133 $fieldString = rtrim($fieldString, ';');
134 // Unpack the field definition, migrate and remove as much as possible
135 // Keep empty parameters in trimExplode here (third parameter FALSE), so position is not changed
136 $fieldArray = GeneralUtility::trimExplode(';', $fieldString);
137 $fieldArray = array(
138 'fieldName' => isset($fieldArray[0]) ? $fieldArray[0] : '',
139 'fieldLabel' => isset($fieldArray[1]) ? $fieldArray[1] : NULL,
140 'paletteName' => isset($fieldArray[2]) ? $fieldArray[2] : NULL,
141 'fieldExtra' => isset($fieldArray[3]) ? $fieldArray[3] : NULL,
142 );
143 $fieldName = $fieldArray['fieldName'];
144 if (!empty($fieldArray['fieldExtra'])) {
145 // Move fieldExtra "specConf" to columnsOverrides "defaultExtras"
146 if (!isset($newTca[$table]['types'][$typeName]['columnsOverrides'])) {
147 $newTca[$table]['types'][$typeName]['columnsOverrides'] = array();
148 }
149 if (!isset($newTca[$table]['types'][$typeName]['columnsOverrides'][$fieldArray['fieldName']])) {
150 $newTca[$table]['types'][$typeName]['columnsOverrides'][$fieldArray['fieldName']] = array();
151 }
152 // Merge with given defaultExtras from columns.
153 // They will be the first part of the string, so if "specConf" from types changes the same settings,
154 // those will override settings from defaultExtras of columns
155 $newDefaultExtras = array();
156 if (!empty($tca[$table]['columns'][$fieldArray['fieldName']]['defaultExtras'])) {
157 $newDefaultExtras[] = $tca[$table]['columns'][$fieldArray['fieldName']]['defaultExtras'];
158 }
159 $newDefaultExtras[] = $fieldArray['fieldExtra'];
160 $newTca[$table]['types'][$typeName]['columnsOverrides'][$fieldArray['fieldName']]['defaultExtras'] = implode(':', $newDefaultExtras);
161 }
162 unset($fieldArray['fieldExtra']);
163 if (count($fieldArray) === 3 && empty($fieldArray['paletteName'])) {
164 unset($fieldArray['paletteName']);
165 }
166 if (count($fieldArray) === 2 && empty($fieldArray['fieldLabel'])) {
167 unset($fieldArray['fieldLabel']);
168 }
169 if (count($fieldArray) === 1 && empty($fieldArray['fieldName'])) {
170 // The field may vanish if nothing is left
171 unset($fieldArray['fieldName']);
172 }
173 $newFieldString = implode(';', $fieldArray);
174 if ($newFieldString !== $fieldString) {
175 $this->messages[] = 'Changed showitem string of TCA table "' . $table . '" type "' . $typeName . '" due to changed field "' . $fieldName . '".';
176 }
177 if (!empty($newFieldString)) {
178 $newFieldStrings[] = $newFieldString;
179 }
180 }
181 $newTca[$table]['types'][$typeName]['showitem'] = implode(',', $newFieldStrings);
182 }
183 }
184 return $newTca;
185 }
186
187 /**
188 * Migrate type=text field with t3editor wizard that is "enableByTypeConfig" to columnsOverrides
189 * with renderType=t3editor
190 *
191 * @param array $tca Incoming TCA
192 * @return array Migrated TCA
193 */
194 protected function migrateT3editorWizardWithEnabledByTypeConfigToColumnsOverrides(array $tca) {
195 $newTca = $tca;
196 foreach ($tca as $table => $tableDefinition) {
197 if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
198 continue;
199 }
200 foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
201 if (
202 !empty($fieldConfig['config']['type']) // type is set
203 && trim($fieldConfig['config']['type']) === 'text' // to "text"
204 && isset($fieldConfig['config']['wizards'])
205 && is_array($fieldConfig['config']['wizards']) // and there are wizards
206 ) {
207 foreach ($fieldConfig['config']['wizards'] as $wizardName => $wizardConfig) {
208 if (
209 !empty($wizardConfig['userFunc']) // a userFunc is defined
210 && trim($wizardConfig['userFunc']) === 'TYPO3\CMS\T3editor\FormWizard->main' // and set to FormWizard
211 && !empty($wizardConfig['enableByTypeConfig']) // and enableByTypeConfig is enabled
212 ) {
213 // Remove this wizard
214 unset($newTca[$table]['columns'][$fieldName]['config']['wizards'][$wizardName]);
215 // Find configured types that use this wizard
216 if (!isset($tableDefinition['types']) || !is_array($tableDefinition['types'])) {
217 // No type definition at all ... continue directly
218 continue;
219 }
220 foreach ($tableDefinition['types'] as $typeName => $typeArray) {
221 if (
222 empty($typeArray['columnsOverrides'][$fieldName]['defaultExtras'])
223 || strpos($typeArray['columnsOverrides'][$fieldName]['defaultExtras'], $wizardName) === FALSE
224 ) {
225 // Continue directly if this wizard is not enabled for given type
226 continue;
227 }
228 $defaultExtras = $typeArray['columnsOverrides'][$fieldName]['defaultExtras'];
229 $defaultExtrasArray = GeneralUtility::trimExplode(':', $defaultExtras, TRUE);
230 $newDefaultExtrasArray = array();
231 foreach ($defaultExtrasArray as $fieldExtraField) {
232 // There might be multiple enabled wizards separated by | ... split them
233 if (substr($fieldExtraField, 0, 8) === 'wizards[') {
234 $enabledWizards = substr($fieldExtraField, 8, strlen($fieldExtraField) - 8); // Cut off "wizards[
235 $enabledWizards = substr($enabledWizards, 0, strlen($enabledWizards) - 1);
236 $enabledWizardsArray = GeneralUtility::trimExplode('|', $enabledWizards, TRUE);
237 $newEnabledWizardsArray = array();
238 foreach ($enabledWizardsArray as $enabledWizardName) {
239 if ($enabledWizardName === $wizardName) {
240 // Found a columnsOverrides configuration that has this wizard enabled
241 // Force renderType = t3editor
242 $newTca[$table]['types'][$typeName]['columnsOverrides'][$fieldName]['config']['renderType'] = 't3editor';
243 // Transfer format option if given
244 if (!empty($wizardConfig['params']['format'])) {
245 $newTca[$table]['types'][$typeName]['columnsOverrides'][$fieldName]['config']['format'] = $wizardConfig['params']['format'];
246 }
247 $this->messages[] = 'Migrated t3editor wizard in TCA of table "' . $table . '" field "' . $fieldName
248 . '" to a renderType definition with columnsOverrides in type "' . $typeName . '".';
249 } else {
250 // Some other enabled wizard
251 $newEnabledWizardsArray[] = $enabledWizardName;
252 }
253 }
254 if (!empty($newEnabledWizardsArray)) {
255 $newDefaultExtrasArray[] = 'wizards[' . implode('|', $newEnabledWizardsArray) . ']';
256 }
257 } else {
258 $newDefaultExtrasArray[] = $fieldExtraField;
259 }
260 }
261 if (!empty($newDefaultExtrasArray)) {
262 $newTca[$table]['types'][$typeName]['columnsOverrides'][$fieldName]['defaultExtras'] = implode(':', $newDefaultExtrasArray);
263 } else {
264 unset($newTca[$table]['types'][$typeName]['columnsOverrides'][$fieldName]['defaultExtras']);
265 }
266 }
267 }
268 }
269 // If no wizard is left after migration, unset the whole sub array
270 if (empty($newTca[$table]['columns'][$fieldName]['config']['wizards'])) {
271 unset($newTca[$table]['columns'][$fieldName]['config']['wizards']);
272 }
273 }
274 }
275 }
276 return $newTca;
277 }
278
279 /**
280 * Migrate types showitem 'aField;aLabel;aPalette' to 'afield;aLabel, --palette--;;aPalette'
281 *
282 * Old showitem can have a syntax like:
283 * fieldName;aLabel;aPalette
284 * This way, the palette with name "aPalette" is rendered after fieldName.
285 * The migration parses this to a syntax like:
286 * fieldName;aLabel, --palette--;;paletteName
287 *
288 * @param array $tca Incoming TCA
289 * @return array Migrated TCA
290 */
291 protected function migrateShowItemAdditionalPaletteToOwnPalette(array $tca) {
292 $newTca = $tca;
293 foreach ($tca as $table => $tableDefinition) {
294 if (!isset($tableDefinition['types']) || !is_array($tableDefinition['types'])) {
295 continue;
296 }
297 foreach ($tableDefinition['types'] as $typeName => $typeArray) {
298 if (
299 !isset($typeArray['showitem'])
300 || !is_string($typeArray['showitem'])
301 || strpos($typeArray['showitem'], ';') === FALSE // no field parameters
302 ) {
303 continue;
304 }
305 $itemList = GeneralUtility::trimExplode(',', $typeArray['showitem'], TRUE);
306 $newFieldStrings = array();
307 foreach ($itemList as $fieldString) {
308 $fieldArray = GeneralUtility::trimExplode(';', $fieldString);
309 $fieldArray = array(
310 'fieldName' => isset($fieldArray[0]) ? $fieldArray[0] : '',
311 'fieldLabel' => isset($fieldArray[1]) ? $fieldArray[1] : NULL,
312 'paletteName' => isset($fieldArray[2]) ? $fieldArray[2] : NULL,
313 );
314 if ($fieldArray['fieldName'] !== '--palette--' && $fieldArray['paletteName'] !== NULL) {
315 if ($fieldArray['fieldLabel']) {
316 $fieldString = $fieldArray['fieldName'] . ';' . $fieldArray['fieldLabel'];
317 } else {
318 $fieldString = $fieldArray['fieldName'];
319 }
320 $paletteString = '--palette--;;' . $fieldArray['paletteName'];
321 $this->messages[] = 'Migrated TCA table "' . $table . '" showitem field of type "' . $typeName . '": Moved additional palette'
322 . ' with name "' . $fieldArray['paletteName'] . '" as 3rd argument of field "' . $fieldArray['fieldName']
323 . '" to an own palette. The result of this part is: "' . $fieldString . ', ' . $paletteString . '"';
324 $newFieldStrings[] = $fieldString;
325 $newFieldStrings[] = $paletteString;
326 } else {
327 $newFieldStrings[] = $fieldString;
328 }
329 }
330 $newTca[$table]['types'][$typeName]['showitem'] = implode(',', $newFieldStrings);
331 }
332 }
333 return $newTca;
334 }
335
336 /**
337 * Migrate core icons for form field wizard to new location
338 *
339 * add.gif => EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_add.gif
340 * link_popup.gif => EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_link.gif
341 * wizard_rte2.gif => EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_rte.gif
342 * wizard_table.gif => EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_table.gif
343 * edit2.gif => EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_edit.gif
344 * list.gif => EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_list.gif
345 * wizard_forms.gif => EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_forms.gif
346 *
347 * @param array $tca Incoming TCA
348 * @return array Migrated TCA
349 */
350 protected function migrateIconsForFormFieldWizardsToNewLocation($tca) {
351 $newTca = $tca;
352
353 $oldFileNames = array(
354 'add.gif',
355 'link_popup.gif',
356 'wizard_rte2.gif',
357 'wizard_table.gif',
358 'edit2.gif',
359 'list.gif',
360 'wizard_forms.gif',
361 );
362
363 $newFileLocations = array(
364 'add.gif' => 'EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_add.gif',
365 'link_popup.gif' => 'EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_link.gif',
366 'wizard_rte2.gif' => 'EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_rte.gif',
367 'wizard_table.gif' => 'EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_table.gif',
368 'edit2.gif' => 'EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_edit.gif',
369 'list.gif' => 'EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_list.gif',
370 'wizard_forms.gif' => 'EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_forms.gif',
371 );
372
373 foreach ($tca as $table => $tableDefinition) {
374 if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
375 continue;
376 }
377 foreach ($tableDefinition['columns'] as $fieldName => $fieldConfig) {
378 if (
379 isset($fieldConfig['config']['wizards'])
380 && is_array($fieldConfig['config']['wizards']) // and there are wizards
381 ) {
382 foreach ($fieldConfig['config']['wizards'] as $wizardName => $wizardConfig) {
383 if (!is_array($wizardConfig)) {
384 continue;
385 }
386
387 foreach ($wizardConfig as $option => $value) {
388 if ($option === 'icon' && in_array($value, $oldFileNames, TRUE)) {
389 $newTca[$table]['columns'][$fieldName]['config']['wizards'][$wizardName]['icon'] = $newFileLocations[$value];
390 $this->messages[] = 'Migrated icon path of wizard "' . $wizardName . '" in field "' . $fieldName . '" from TCA table "' . $table . '". New path is: ' . $newFileLocations[$value];
391 }
392 }
393 }
394 }
395 }
396 }
397
398 return $newTca;
399 }
400
401 /**
402 * Migrate file reference which starts with ext/ or sysext/ to EXT:
403 *
404 * @param array $tca Incoming TCA
405 * @return array Migrated TCA
406 */
407 protected function migrateExtAndSysextPathToEXTPath($tca) {
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 (
414 !empty($fieldConfig['config']['type']) // type is set
415 && trim($fieldConfig['config']['type']) === 'select' // to "select"
416 && isset($fieldConfig['config']['items'])
417 && is_array($fieldConfig['config']['items']) // and there are items
418 ) {
419 foreach ($fieldConfig['config']['items'] as &$itemConfig) {
420 // more then two values? then the third entry is the image path
421 if (!empty($itemConfig[2])) {
422 // If the path starts with ext/ or sysext/ migrate it
423 if (
424 StringUtility::beginsWith($itemConfig[2], 'ext/')
425 || StringUtility::beginsWith($itemConfig[2], 'sysext/')
426 ) {
427 $tcaPath = implode('.', [$table, 'columns', $fieldName, 'config', 'items']);
428 $pathParts = GeneralUtility::trimExplode('/', $itemConfig[2]);
429 // remove first element (ext or sysext)
430 array_shift($pathParts);
431 $path = implode('/', $pathParts);
432 $this->messages[] = '[' . $tcaPath . '] ext/ or sysext/ within the path (' . $path . ') in items array is deprecated, use EXT: reference';
433 $itemConfig[2] = 'EXT:' . $path;
434 } elseif (StringUtility::beginsWith($itemConfig[2], 'i/')) {
435 $this->messages[] = '[' . $tcaPath . '] i/ within the path (' . $path . ') in items array is deprecated, use EXT: reference';
436 $itemConfig[2] = 'EXT:t3skin/icons/gfx/' . $itemConfig[2];
437 }
438 }
439 }
440 }
441 }
442 }
443 return $tca;
444 }
445
446 /**
447 * Migrate "iconsInOptionTags" for "select" TCA fields
448 *
449 * @param array $tca Incoming TCA
450 * @return array Migrated TCA
451 */
452 protected function migrateIconsInOptionTags($tca) {
453 $newTca = $tca;
454
455 foreach ($newTca as $table => &$tableDefinition) {
456 if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
457 continue;
458 }
459 foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
460 if (isset($fieldConfig['config']['iconsInOptionTags'])) {
461 unset($fieldConfig['config']['iconsInOptionTags']);
462 $this->messages[] = 'Configuration option "iconsInOptionTags" was removed from field "' . $fieldName . '" in TCA table "' . $table . '"';
463 }
464 }
465 }
466
467 return $newTca;
468 }
469 }