‪TYPO3CMS  ‪main
ExtensionManagementUtility.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 
22 use TYPO3\CMS\Core\Package\PackageManager;
24 
32 {
33  protected static PackageManager ‪$packageManager;
34 
41  public static function ‪setPackageManager(PackageManager ‪$packageManager): void
42  {
43  static::$packageManager = ‪$packageManager;
44  }
45 
46  /**************************************
47  *
48  * PATHS and other evaluation
49  *
50  ***************************************/
51 
55  public static function ‪isLoaded(string $key): bool
56  {
57  return static::$packageManager->isPackageActive($key);
58  }
59 
70  public static function ‪resolvePackagePath(string $path): string
71  {
72  return static::$packageManager->resolvePackagePath($path);
73  }
74 
82  public static function ‪extPath(string $key, string $script = ''): string
83  {
84  if (!static::$packageManager->isPackageActive($key)) {
85  throw new \BadFunctionCallException('TYPO3 Fatal Error: Extension key "' . $key . '" is NOT loaded!', 1365429656);
86  }
87  return static::$packageManager->getPackage($key)->getPackagePath() . $script;
88  }
89 
96  public static function ‪getCN(string $key): string
97  {
98  return str_starts_with($key, 'user_')
99  ? 'user_' . str_replace('_', '', substr($key, 5))
100  : 'tx_' . str_replace('_', '', $key);
101  }
102 
113  public static function ‪getExtensionVersion(string $key): string
114  {
115  if (empty($key)) {
116  throw new \InvalidArgumentException('Extension key must be a non-empty string.', 1294586096);
117  }
118  if (!static::isLoaded($key)) {
119  return '';
120  }
121  $version = static::$packageManager->getPackage($key)->getPackageMetaData()->getVersion();
122  if (empty($version)) {
123  throw new ‪PackageException('Version number in composer manifest of package "' . $key . '" is missing or invalid', 1395614959);
124  }
125  return $version;
126  }
127 
128  /**************************************
129  *
130  * Adding BACKEND features
131  * (related to core features)
132  *
133  ***************************************/
134 
145  public static function ‪addTCAcolumns(string $table, array $columnArray): void
146  {
147  if (is_array(‪$GLOBALS['TCA'][$table]['columns'] ?? false)) {
148  // Candidate for array_merge() if integer-keys will some day make trouble...
149  ‪$GLOBALS['TCA'][$table]['columns'] = array_merge(‪$GLOBALS['TCA'][$table]['columns'], $columnArray);
150  }
151  }
152 
166  public static function ‪addToAllTCAtypes(string $table, string $newFieldsString, string $typeList = '', string $position = ''): void
167  {
168  $newFieldsString = trim($newFieldsString);
169  if ($newFieldsString === '' || !is_array(‪$GLOBALS['TCA'][$table]['types'] ?? false)) {
170  return;
171  }
172  if ($position !== '') {
173  [$positionIdentifier, $entityName] = ‪GeneralUtility::trimExplode(':', $position, false, 2);
174  } else {
175  $positionIdentifier = '';
176  $entityName = '';
177  }
178  $palettesChanged = [];
179 
180  foreach (‪$GLOBALS['TCA'][$table]['types'] as $type => &$typeDetails) {
181  // skip if we don't want to add the field for this type
182  if ($typeList !== '' && !‪GeneralUtility::inList($typeList, $type)) {
183  continue;
184  }
185  // skip if fields were already added
186  if (!isset($typeDetails['showitem'])) {
187  continue;
188  }
189 
190  $fieldArray = ‪GeneralUtility::trimExplode(',', $typeDetails['showitem'], true);
191  if (in_array($newFieldsString, $fieldArray, true)) {
192  continue;
193  }
194 
195  $fieldExists = false;
196  $newPosition = '';
197  if (is_array(‪$GLOBALS['TCA'][$table]['palettes'] ?? false)) {
198  // Get the palette names used in current showitem
199  $paletteCount = preg_match_all('/(?:^|,) # Line start or a comma
200  (?:
201  \\s*\\-\\-palette\\-\\-;[^;]*;([^,$]*)| # --palette--;label;paletteName
202  \\s*\\b[^;,]+\\b(?:;[^;]*;([^;,]+))?[^,]* # field;label;paletteName
203  )/x', $typeDetails['showitem'], $paletteMatches);
204  if ($paletteCount > 0) {
205  $paletteNames = array_filter(array_merge($paletteMatches[1], $paletteMatches[2]));
206  if (!empty($paletteNames)) {
207  foreach ($paletteNames as $paletteName) {
208  if (!isset(‪$GLOBALS['TCA'][$table]['palettes'][$paletteName])) {
209  continue;
210  }
211  $palette = ‪$GLOBALS['TCA'][$table]['palettes'][$paletteName];
212  switch ($positionIdentifier) {
213  case 'after':
214  case 'before':
215  if (preg_match('/\\b' . preg_quote($entityName, '/') . '\\b/', $palette['showitem']) > 0 || $entityName === 'palette:' . $paletteName) {
216  $newPosition = $positionIdentifier . ':--palette--;;' . $paletteName;
217  }
218  break;
219  case 'replace':
220  // check if fields have been added to palette before
221  if (isset($palettesChanged[$paletteName])) {
222  $fieldExists = true;
223  continue 2;
224  }
225  if (preg_match('/\\b' . preg_quote($entityName, '/') . '\\b/', $palette['showitem']) > 0) {
226  ‪self::addFieldsToPalette($table, $paletteName, $newFieldsString, $position);
227  // Memorize that we already changed this palette, in case other types also use it
228  $palettesChanged[$paletteName] = true;
229  $fieldExists = true;
230  continue 2;
231  }
232  break;
233  default:
234  // Intentionally left blank
235  }
236  }
237  }
238  }
239  }
240  if ($fieldExists === false) {
241  $typeDetails['showitem'] = ‪self::executePositionedStringInsertion(
242  $typeDetails['showitem'],
243  $newFieldsString,
244  $newPosition !== '' ? $newPosition : $position
245  );
246  }
247  }
248  unset($typeDetails);
249  }
250 
294  public static function ‪addFieldsToAllPalettesOfField(string $table, string $field, string $addFields, string $insertionPosition = ''): void
295  {
296  if (!isset(‪$GLOBALS['TCA'][$table]['columns'][$field])) {
297  return;
298  }
299  if (!is_array(‪$GLOBALS['TCA'][$table]['types'])) {
300  return;
301  }
302 
303  // Iterate through all types and search for the field that defines the palette to be extended
304  foreach (‪$GLOBALS['TCA'][$table]['types'] as $typeName => $typeArray) {
305  // Continue if types has no showitem at all or if requested field is not in it
306  if (!isset($typeArray['showitem']) || !str_contains($typeArray['showitem'], $field)) {
307  continue;
308  }
309  $fieldArrayWithOptions = ‪GeneralUtility::trimExplode(',', $typeArray['showitem']);
310  // Find the field we're handling
311  $newFieldStringArray = [];
312  foreach ($fieldArrayWithOptions as $fieldNumber => $fieldString) {
313  $newFieldStringArray[] = $fieldString;
314  $fieldArray = ‪GeneralUtility::trimExplode(';', $fieldString);
315  if ($fieldArray[0] !== $field) {
316  continue;
317  }
318  if (
319  isset($fieldArrayWithOptions[$fieldNumber + 1])
320  && str_starts_with($fieldArrayWithOptions[$fieldNumber + 1], '--palette--')
321  ) {
322  // Match for $field and next field is a palette - add fields to this one
323  $paletteName = ‪GeneralUtility::trimExplode(';', $fieldArrayWithOptions[$fieldNumber + 1]);
324  $paletteName = $paletteName[2];
325  ‪self::addFieldsToPalette($table, $paletteName, $addFields, $insertionPosition);
326  } else {
327  // Match for $field but next field is no palette - create a new one
328  $newPaletteName = 'generatedFor-' . $field;
329  ‪self::addFieldsToPalette($table, 'generatedFor-' . $field, $addFields, $insertionPosition);
330  $newFieldStringArray[] = '--palette--;;' . $newPaletteName;
331  }
332  }
333  ‪$GLOBALS['TCA'][$table]['types'][$typeName]['showitem'] = implode(', ', $newFieldStringArray);
334  }
335  }
336 
347  public static function ‪addFieldsToPalette(string $table, string $palette, string $addFields, string $insertionPosition = ''): void
348  {
349  if (isset(‪$GLOBALS['TCA'][$table])) {
350  $paletteData = &‪$GLOBALS['TCA'][$table]['palettes'][$palette];
351  // If palette already exists, merge the data:
352  if (is_array($paletteData)) {
353  $paletteData['showitem'] = ‪self::executePositionedStringInsertion($paletteData['showitem'], $addFields, $insertionPosition);
354  } else {
355  $paletteData['showitem'] = ‪self::removeDuplicatesForInsertion($addFields);
356  }
357  }
358  }
359 
391  public static function ‪addTcaSelectItem(string $table, string $field, array $item, string $relativeToField = '', string $relativePosition = ''): void
392  {
393  if ($relativePosition !== '' && $relativePosition !== 'before' && $relativePosition !== 'after' && $relativePosition !== 'replace') {
394  throw new \InvalidArgumentException('Relative position must be either empty or one of "before", "after", "replace".', 1303236967);
395  }
396  if (!isset(‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['items'])
397  || !is_array(‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['items'])
398  ) {
399  throw new \RuntimeException('Given select field item list was not found.', 1303237468);
400  }
401  // Make sure item keys are integers
402  ‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['items'] = array_values(‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['items']);
403  if ($relativePosition !== '') {
404  // Insert at specified position
405  $matchedPosition = ‪ArrayUtility::filterByValueRecursive($relativeToField, ‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['items']);
406  if (!empty($matchedPosition)) {
407  $relativeItemKey = key($matchedPosition);
408  if ($relativePosition === 'replace') {
409  ‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['items'][$relativeItemKey] = $item;
410  } else {
411  if ($relativePosition === 'before') {
412  $offset = $relativeItemKey;
413  } else {
414  $offset = $relativeItemKey + 1;
415  }
416  array_splice(‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['items'], $offset, 0, [0 => $item]);
417  }
418  } else {
419  // Insert at new item at the end of the array if relative position was not found
420  ‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['items'][] = $item;
421  }
422  } else {
423  // Insert at new item at the end of the array
424  ‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['items'][] = $item;
425  }
426  }
427 
438  public static function ‪addTcaSelectItemGroup(string $table, string $field, string $groupId, string $groupLabel, ?string $position = 'bottom'): void
439  {
440  if (!is_array(‪$GLOBALS['TCA'][$table]['columns'][$field]['config'] ?? null)) {
441  throw new \RuntimeException('Given select field item list was not found.', 1586728563);
442  }
443  $itemGroups = ‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['itemGroups'] ?? [];
444  // Group has been defined already, nothing to do
445  if (isset($itemGroups[$groupId])) {
446  return;
447  }
448  $position = (string)$position;
449  $positionGroupId = '';
450  if (str_contains($position, ':')) {
451  [$position, $positionGroupId] = explode(':', $position, 2);
452  }
453  // Referenced group was not not found, just append to the bottom
454  if (!isset($itemGroups[$positionGroupId])) {
455  $position = 'bottom';
456  }
457  switch ($position) {
458  case 'after':
459  $newItemGroups = [];
460  foreach ($itemGroups as $existingGroupId => $existingGroupLabel) {
461  $newItemGroups[$existingGroupId] = $existingGroupLabel;
462  if ($positionGroupId === $existingGroupId) {
463  $newItemGroups[$groupId] = $groupLabel;
464  }
465  }
466  $itemGroups = $newItemGroups;
467  break;
468  case 'before':
469  $newItemGroups = [];
470  foreach ($itemGroups as $existingGroupId => $existingGroupLabel) {
471  if ($positionGroupId === $existingGroupId) {
472  $newItemGroups[$groupId] = $groupLabel;
473  }
474  $newItemGroups[$existingGroupId] = $existingGroupLabel;
475  }
476  $itemGroups = $newItemGroups;
477  break;
478  case 'top':
479  $itemGroups = array_merge([$groupId => $groupLabel], $itemGroups);
480  break;
481  case 'bottom':
482  default:
483  $itemGroups[$groupId] = $groupLabel;
484  }
485  ‪$GLOBALS['TCA'][$table]['columns'][$field]['config']['itemGroups'] = $itemGroups;
486  }
487 
496  public static function ‪addFieldsToUserSettings(string $addFields, string $insertionPosition = ''): void
497  {
498  ‪$GLOBALS['TYPO3_USER_SETTINGS']['showitem'] = ‪self::executePositionedStringInsertion(‪$GLOBALS['TYPO3_USER_SETTINGS']['showitem'] ?? '', $addFields, $insertionPosition);
499  }
500 
518  protected static function ‪executePositionedStringInsertion(string $list, string $insertionList, string $insertionPosition = ''): string
519  {
520  $list = trim($list, ", \t\n\r\0\x0B");
521 
522  if ($insertionPosition !== '') {
523  [$location, $positionName] = ‪GeneralUtility::trimExplode(':', $insertionPosition, false, 2);
524  } else {
525  $location = '';
526  $positionName = '';
527  }
528 
529  if ($location !== 'replace') {
530  $insertionList = ‪self::removeDuplicatesForInsertion($insertionList, $list);
531  }
532 
533  if ($insertionList === '') {
534  return $list;
535  }
536  if ($list === '') {
537  return $insertionList;
538  }
539  if ($insertionPosition === '') {
540  return $list . ', ' . $insertionList;
541  }
542 
543  // The $insertPosition may be a palette: after:--palette--;;title
544  // In the $list the palette may contain a LLL string in between the ;;
545  // Adjust the regex to match that
546  $positionName = preg_quote($positionName, '/');
547  if (str_contains($positionName, ';;')) {
548  $positionName = str_replace(';;', ';[^;]*;', $positionName);
549  }
550 
551  $pattern = ('/(^|,\\s*)(' . $positionName . ')(;[^,$]+)?(,|$)/');
552  $newList = match ($location) {
553  'after' => preg_replace($pattern, '$1$2$3, ' . $insertionList . '$4', $list),
554  'before' => preg_replace($pattern, '$1' . $insertionList . ', $2$3$4', $list),
555  'replace' => preg_replace($pattern, '$1' . $insertionList . '$4', $list),
556  default => $list,
557  };
558 
559  // When preg_replace did not replace anything; append the $insertionList.
560  if ($newList === $list) {
561  return $list . ', ' . $insertionList;
562  }
563  return $newList;
564  }
565 
581  protected static function ‪removeDuplicatesForInsertion(string $insertionList, string $list = ''): string
582  {
583  $insertionListParts = preg_split('/\\s*,\\s*/', $insertionList);
584  $listMatches = [];
585  if ($list !== '') {
586  preg_match_all('/(?:^|,)\\s*\\b([^;,]+)\\b[^,]*/', $list, $listMatches);
587  $listMatches = $listMatches[1];
588  }
589 
590  $cleanInsertionListParts = [];
591  foreach ($insertionListParts as $fieldName) {
592  $fieldNameParts = explode(';', $fieldName, 2);
593  $cleanFieldName = $fieldNameParts[0];
594  if (
595  $cleanFieldName === '--linebreak--'
596  || (
597  !in_array($cleanFieldName, $cleanInsertionListParts, true)
598  && !in_array($cleanFieldName, $listMatches, true)
599  )
600  ) {
601  $cleanInsertionListParts[] = $fieldName;
602  }
603  }
604  return implode(', ', $cleanInsertionListParts);
605  }
606 
612  public static function ‪addPageTSConfig(string $content): void
613  {
614  trigger_error(
615  'ExtensionManagementUtility::addPageTSConfig() has been deprecated in TYPO3 v13.0 and will be removed in v14.0. Use Configuration/page.tsconfig files in extensions instead.',
616  E_USER_DEPRECATED
617  );
618  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig'] .= chr(10) . $content;
619  }
620 
626  public static function ‪addUserTSConfig(string $content): void
627  {
628  trigger_error(
629  'ExtensionManagementUtility::addUserTSConfig() has been deprecated in TYPO3 v13.0 and will be removed in v14.0. Use Configuration/user.tsconfig files in extensions instead.',
630  E_USER_DEPRECATED
631  );
632  ‪$GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUserTSconfig'] .= chr(10) . $content;
633  }
634 
635  /**************************************
636  *
637  * Adding SERVICES features
638  *
639  ***************************************/
648  public static function ‪addService(string $extKey, string $serviceType, string $serviceKey, array $info): void
649  {
650  if (!$serviceType) {
651  throw new \InvalidArgumentException('No serviceType given.', 1507321535);
652  }
653  $info['priority'] = max(0, min(100, $info['priority']));
654  ‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey] = $info;
655  ‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['extKey'] = $extKey;
656  ‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['serviceKey'] = $serviceKey;
657  ‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['serviceType'] = $serviceType;
658  // Change the priority (and other values) from $GLOBALS['TYPO3_CONF_VARS']
659  // $GLOBALS['TYPO3_CONF_VARS']['T3_SERVICES'][$serviceType][$serviceKey]['priority']
660  // even the activation is possible (a unix service might be possible on windows for some reasons)
661  if (is_array(‪$GLOBALS['TYPO3_CONF_VARS']['T3_SERVICES'][$serviceType][$serviceKey] ?? false)) {
662  // No check is done here - there might be configuration values only the service type knows about, so
663  // we pass everything
664  ‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey] = array_merge(‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey], ‪$GLOBALS['TYPO3_CONF_VARS']['T3_SERVICES'][$serviceType][$serviceKey]);
665  }
666  // OS check
667  // Empty $os means 'not limited to one OS', therefore a check is not needed
668  if (!empty(‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['available']) && (‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['os'] ?? '') != '') {
669  $os_type = ‪Environment::isWindows() ? 'WIN' : 'UNIX';
670  $os = ‪GeneralUtility::trimExplode(',', strtoupper(‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['os']));
671  if (!in_array($os_type, $os, true)) {
672  ‪self::deactivateService($serviceType, $serviceKey);
673  }
674  }
675  // Convert subtype list to array for quicker access
676  ‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['serviceSubTypes'] = [];
677  $serviceSubTypes = ‪GeneralUtility::trimExplode(',', $info['subtype']);
678  foreach ($serviceSubTypes as $subtype) {
679  ‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['serviceSubTypes'][$subtype] = $subtype;
680  }
681  }
682 
691  public static function ‪findService(string $serviceType, string $serviceSubType = '', array $excludeServiceKeys = []): array|false
692  {
693  $serviceKey = false;
694  $serviceInfo = false;
695  $priority = 0;
696  $quality = 0;
697  if (is_array(‪$GLOBALS['T3_SERVICES'][$serviceType])) {
698  foreach (‪$GLOBALS['T3_SERVICES'][$serviceType] as $key => $info) {
699  if (in_array($key, $excludeServiceKeys)) {
700  continue;
701  }
702  // Select a subtype randomly
703  // Useful to start a service by service key without knowing his subtypes - for testing purposes
704  if ($serviceSubType === '*') {
705  $serviceSubType = key($info['serviceSubTypes']);
706  }
707  // This matches empty subtype too
708  if (($info['available'] ?? false)
709  && (($info['subtype'] ?? null) == $serviceSubType || ($info['serviceSubTypes'][$serviceSubType] ?? false))
710  && ($info['priority'] ?? 0) >= $priority
711  ) {
712  // Has a lower quality than the already found, therefore we skip this service
713  if ($info['priority'] == $priority && $info['quality'] < $quality) {
714  continue;
715  }
716  // Check if the service is available
717  $info['available'] = ‪self::isServiceAvailable($serviceType, $key, $info);
718  // Still available after exec check?
719  if ($info['available']) {
720  $serviceKey = $key;
721  $priority = $info['priority'];
722  $quality = $info['quality'];
723  }
724  }
725  }
726  }
727  if ($serviceKey) {
728  $serviceInfo = ‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey];
729  }
730  return $serviceInfo;
731  }
732 
741  public static function ‪findServiceByKey(string $serviceKey): array
742  {
743  if (is_array(‪$GLOBALS['T3_SERVICES'])) {
744  // Loop on all service types
745  // NOTE: we don't care about the actual type, we are looking for a specific key
746  foreach (‪$GLOBALS['T3_SERVICES'] as $serviceType => $servicesPerType) {
747  if (isset($servicesPerType[$serviceKey])) {
748  $serviceDetails = $servicesPerType[$serviceKey];
749  // Test if service is available
750  if (self::isServiceAvailable($serviceType, $serviceKey, $serviceDetails)) {
751  // We have found the right service, return its information
752  return $serviceDetails;
753  }
754  }
755  }
756  }
757  throw new \TYPO3\CMS\Core\Exception('Service not found for key: ' . $serviceKey, 1319217244);
758  }
759 
768  public static function ‪isServiceAvailable(string $serviceType, string $serviceKey, array $serviceDetails): bool
769  {
770  // If the service depends on external programs - check if they exists
771  if (trim($serviceDetails['exec'] ?? '')) {
772  $executables = ‪GeneralUtility::trimExplode(',', $serviceDetails['exec'], true);
773  foreach ($executables as $executable) {
774  // If at least one executable file is not available, exit early returning FALSE
775  if (!‪CommandUtility::checkCommand($executable)) {
776  ‪self::deactivateService($serviceType, $serviceKey);
777  return false;
778  }
779  }
780  }
781  // The service is available
782  return true;
783  }
784 
791  public static function ‪deactivateService(string $serviceType, string $serviceKey): void
792  {
793  // ... maybe it's better to move non-available services to a different array??
794  ‪$GLOBALS['T3_SERVICES'][$serviceType][$serviceKey]['available'] = false;
795  }
796 
797  /**************************************
798  *
799  * Adding FRONTEND features
800  *
801  ***************************************/
814  public static function ‪addPlugin(array|‪SelectItem $itemArray, string $type = 'list_type', ?string $extensionKey = null): void
815  {
816  // $extensionKey is required, but presumably for BC reasons it still lives after $type in the
817  // parameter list, and $type is nominally optional.
818  if (!isset($extensionKey)) {
819  throw new \InvalidArgumentException(
820  'No extension key could be determined when calling addPlugin()!'
821  . LF
822  . 'This method is meant to be called from Configuration/TCA/Overrides files. '
823  . 'The extension key needs to be specified as third parameter. '
824  . 'Calling it from any other place e.g. ext_localconf.php does not work and is not supported.',
825  1404068038
826  );
827  }
828  $selectItem = is_array($itemArray) ? ‪SelectItem::fromTcaItemArray($itemArray) : $itemArray;
829  if (!$selectItem->hasIcon()) {
830  $iconPath = static::$packageManager->getPackage($extensionKey)->getPackageIcon();
831  if ($iconPath) {
832  $selectItem = $selectItem->‪withIcon('EXT:' . $extensionKey . '/' . $iconPath);
833  }
834  } elseif ($type === 'CType' && !isset(‪$GLOBALS['TCA']['tt_content']['ctrl']['typeicon_classes'][$selectItem->getValue()])) {
835  // Set the type icon as well
836  ‪$GLOBALS['TCA']['tt_content']['ctrl']['typeicon_classes'][$selectItem->getValue()] = $selectItem->getIcon();
837  }
838  if (!$selectItem->hasGroup()) {
839  $selectItem = $selectItem->withGroup('default');
840  }
841  // Override possible existing entries.
842  foreach (‪$GLOBALS['TCA']['tt_content']['columns'][$type]['config']['items'] ?? [] as $index => $item) {
843  if ((string)($item['value'] ?? '') === (string)$selectItem->getValue()) {
844  ‪$GLOBALS['TCA']['tt_content']['columns'][$type]['config']['items'][$index] = $selectItem->toArray();
845  return;
846  }
847  }
848  ‪$GLOBALS['TCA']['tt_content']['columns'][$type]['config']['items'][] = $selectItem->toArray();
849 
850  // Populate plugin subtype groups with CType group if missing.
851  $groupIdentifier = $selectItem->getGroup();
852  if ($type === 'list_type'
853  && !isset(‪$GLOBALS['TCA']['tt_content']['columns'][$type]['config']['itemGroups'][$groupIdentifier])
854  && is_string(‪$GLOBALS['TCA']['tt_content']['columns']['CType']['config']['itemGroups'][$groupIdentifier] ?? false)
855  ) {
856  ‪$GLOBALS['TCA']['tt_content']['columns'][$type]['config']['itemGroups'][$groupIdentifier] =
857  ‪$GLOBALS['TCA']['tt_content']['columns']['CType']['config']['itemGroups'][$groupIdentifier];
858  }
859 
860  // Ensure to have at least some basic information available when editing the new type in FormEngine
861  if (
862  $type === 'CType'
863  && !isset(‪$GLOBALS['TCA']['tt_content']['types'][$selectItem->getValue()])
864  && isset(‪$GLOBALS['TCA']['tt_content']['types']['header'])
865  ) {
866  ‪$GLOBALS['TCA']['tt_content']['types'][$selectItem->getValue()] = ‪$GLOBALS['TCA']['tt_content']['types']['header'];
867  }
868  }
869 
880  public static function ‪addPiFlexFormValue(string $piKeyToMatch, string $value, string $CTypeToMatch = 'list'): void
881  {
882  if (is_array(‪$GLOBALS['TCA']['tt_content']['columns']) && is_array(‪$GLOBALS['TCA']['tt_content']['columns']['pi_flexform']['config']['ds'])) {
883  ‪$GLOBALS['TCA']['tt_content']['columns']['pi_flexform']['config']['ds'][$piKeyToMatch . ',' . $CTypeToMatch] = $value;
884  }
885  }
886 
896  public static function ‪addToInsertRecords(string $table, string $content_table = 'tt_content', string $content_field = 'records'): void
897  {
898  if (is_array(‪$GLOBALS['TCA'][$content_table]['columns']) && isset(‪$GLOBALS['TCA'][$content_table]['columns'][$content_field]['config']['allowed'])) {
899  ‪$GLOBALS['TCA'][$content_table]['columns'][$content_field]['config']['allowed'] .= ',' . $table;
900  }
901  }
902 
927  public static function ‪addPItoST43(string $key, string $_ = '', string $suffix = '', string $type = 'list_type', bool $cacheable = false): void
928  {
929  $cN = ‪self::getCN($key);
930  // General plugin
931  $pluginContent = trim('
932 plugin.' . $cN . $suffix . ' = USER' . ($cacheable ? '' : '_INT') . '
933 plugin.' . $cN . $suffix . '.userFunc = ' . $cN . $suffix . '->main
934 ');
935  ‪self::addTypoScript($key, 'setup', '
936 # Setting ' . $key . ' plugin TypoScript
937 ' . $pluginContent);
938  // Add after defaultContentRendering
939  switch ($type) {
940  case 'list_type':
941  $addLine = 'tt_content.list.20.' . $key . $suffix . ' = < plugin.' . $cN . $suffix;
942  break;
943  case 'CType':
944  $addLine = trim('
945 tt_content.' . $key . $suffix . ' =< lib.contentElement
946 tt_content.' . $key . $suffix . ' {
947  templateName = Generic
948  20 =< plugin.' . $cN . $suffix . '
949 }
950 ');
951  break;
952  case 'includeLib':
953  $addLine = 'page.1000 = < plugin.' . $cN . $suffix;
954  break;
955  default:
956  $addLine = '';
957  }
958  if ($addLine) {
959  ‪self::addTypoScript($key, 'setup', '
960 # Setting ' . $key . ' plugin TypoScript
961 ' . $addLine . '
962 ', 'defaultContentRendering');
963  }
964  }
965 
981  public static function ‪addStaticFile(string $extKey, string $path, string $title): void
982  {
983  if (!$extKey) {
984  throw new \InvalidArgumentException('No extension key given.', 1507321291);
985  }
986  if (!$path) {
987  throw new \InvalidArgumentException('No file path given.', 1507321297);
988  }
989  if (is_array(‪$GLOBALS['TCA']['sys_template']['columns'])) {
990  $value = str_replace(',', '', 'EXT:' . $extKey . '/' . $path);
991  $itemArray = ['label' => trim($title . ' (' . $extKey . ')'), 'value' => $value];
992  ‪$GLOBALS['TCA']['sys_template']['columns']['include_static_file']['config']['items'][] = $itemArray;
993  }
994  }
995 
1005  public static function ‪registerPageTSConfigFile(string $extKey, string $filePath, string $title): void
1006  {
1007  if (!$extKey) {
1008  throw new \InvalidArgumentException('No extension key given.', 1447789490);
1009  }
1010  if (!$filePath) {
1011  throw new \InvalidArgumentException('No file path given.', 1447789491);
1012  }
1013  if (!is_array(‪$GLOBALS['TCA']['pages']['columns'] ?? null)) {
1014  throw new \InvalidArgumentException('No TCA definition for table "pages".', 1447789492);
1015  }
1016 
1017  $value = str_replace(',', '', 'EXT:' . $extKey . '/' . $filePath);
1018  $itemArray = ['label' => trim($title . ' (' . $extKey . ')'), 'value' => $value];
1019  ‪$GLOBALS['TCA']['pages']['columns']['tsconfig_includes']['config']['items'][] = $itemArray;
1020  }
1021 
1029  public static function ‪addTypoScriptSetup(string $content): void
1030  {
1031  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup'] ??= '';
1032  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup'])) {
1033  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup'] .= LF;
1034  }
1035  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_setup'] .= $content;
1036  }
1037 
1045  public static function ‪addTypoScriptConstants(string $content): void
1046  {
1047  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_constants'] ??= '';
1048  if (!empty(‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_constants'])) {
1049  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_constants'] .= LF;
1050  }
1051  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_constants'] .= $content;
1052  }
1053 
1071  public static function ‪addTypoScript(string $key, string $type, string $content, int|string $afterStaticUid = 0): void
1072  {
1073  if ($type !== 'setup' && $type !== 'constants') {
1074  throw new \InvalidArgumentException('Argument $type must be set to either "setup" or "constants" when calling addTypoScript from extension "' . $key . '"', 1507321200);
1075  }
1076  $content = '
1077 
1078 [GLOBAL]
1079 #############################################
1080 ## TypoScript added by extension "' . $key . '"
1081 #############################################
1082 
1083 ' . $content;
1084  if ($afterStaticUid) {
1085  // If 'defaultContentRendering' is targeted (formerly static uid 43),
1086  // the content is added after TypoScript of type contentRendering, e.g. fluid_styled_content, see
1087  // EXT:core/Classes/TypoScript/IncludeTree/SysTemplateTreeBuilder.php for more information on how the code is parsed.
1088  if ($afterStaticUid === 'defaultContentRendering' || $afterStaticUid == 43) {
1089  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_' . $type . '.']['defaultContentRendering'] ??= '';
1090  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_' . $type . '.']['defaultContentRendering'] .= $content;
1091  } else {
1092  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_' . $type . '.'][$afterStaticUid] ??= '';
1093  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_' . $type . '.'][$afterStaticUid] .= $content;
1094  }
1095  } else {
1096  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_' . $type] ??= '';
1097  ‪$GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_' . $type] .= $content;
1098  }
1099  }
1100 
1101  /***************************************
1102  *
1103  * Internal extension management methods
1104  *
1105  ***************************************/
1113  public static function ‪getExtensionIcon(string $extensionPath, bool $returnFullPath = false): string
1114  {
1115  trigger_error('ExtensionManagementUtility::getExtensionIcon() will be removed in v14.0. Use Package->getPackageIcon() instead.', E_USER_DEPRECATED);
1116  $icon = '';
1117  $resourcePath = 'Resources/Public/Icons/Extension.';
1118  foreach (['svg', 'png', 'gif'] as $fileExtension) {
1119  if (file_exists($extensionPath . $resourcePath . $fileExtension)) {
1120  $icon = $resourcePath . $fileExtension;
1121  break;
1122  }
1123  }
1124  return $returnFullPath ? $extensionPath . $icon : $icon;
1125  }
1126 
1130  public static function ‪getLoadedExtensionListArray(): array
1131  {
1132  return array_keys(static::$packageManager->getActivePackages());
1133  }
1134 
1141  public static function ‪loadExtension(string $extensionKey): void
1142  {
1143  if (static::$packageManager->isPackageActive($extensionKey)) {
1144  throw new \RuntimeException('Extension already loaded', 1342345486);
1145  }
1146  static::$packageManager->activatePackage($extensionKey);
1147  }
1148 
1154  public static function ‪unloadExtension(string $extensionKey): void
1155  {
1156  if (!static::$packageManager->isPackageActive($extensionKey)) {
1157  throw new \RuntimeException('Extension not loaded', 1342345487);
1158  }
1159  static::$packageManager->deactivatePackage($extensionKey);
1160  }
1161 }
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addService
‪static addService(string $extKey, string $serviceType, string $serviceKey, array $info)
Definition: ExtensionManagementUtility.php:648
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addPiFlexFormValue
‪static addPiFlexFormValue(string $piKeyToMatch, string $value, string $CTypeToMatch='list')
Definition: ExtensionManagementUtility.php:880
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addToInsertRecords
‪static addToInsertRecords(string $table, string $content_table='tt_content', string $content_field='records')
Definition: ExtensionManagementUtility.php:896
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\getLoadedExtensionListArray
‪static getLoadedExtensionListArray()
Definition: ExtensionManagementUtility.php:1130
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\findServiceByKey
‪static array findServiceByKey(string $serviceKey)
Definition: ExtensionManagementUtility.php:741
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\getExtensionIcon
‪static getExtensionIcon(string $extensionPath, bool $returnFullPath=false)
Definition: ExtensionManagementUtility.php:1113
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\getExtensionVersion
‪static string getExtensionVersion(string $key)
Definition: ExtensionManagementUtility.php:113
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addToAllTCAtypes
‪static addToAllTCAtypes(string $table, string $newFieldsString, string $typeList='', string $position='')
Definition: ExtensionManagementUtility.php:166
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isServiceAvailable
‪static bool isServiceAvailable(string $serviceType, string $serviceKey, array $serviceDetails)
Definition: ExtensionManagementUtility.php:768
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addTypoScriptSetup
‪static addTypoScriptSetup(string $content)
Definition: ExtensionManagementUtility.php:1029
‪TYPO3\CMS\Core\Utility
Definition: ArrayUtility.php:18
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\deactivateService
‪static deactivateService(string $serviceType, string $serviceKey)
Definition: ExtensionManagementUtility.php:791
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addStaticFile
‪static addStaticFile(string $extKey, string $path, string $title)
Definition: ExtensionManagementUtility.php:981
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addTcaSelectItemGroup
‪static addTcaSelectItemGroup(string $table, string $field, string $groupId, string $groupLabel, ?string $position='bottom')
Definition: ExtensionManagementUtility.php:438
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addPItoST43
‪static addPItoST43(string $key, string $_='', string $suffix='', string $type='list_type', bool $cacheable=false)
Definition: ExtensionManagementUtility.php:927
‪TYPO3\CMS\Core\Utility\CommandUtility\checkCommand
‪static bool int checkCommand(string $cmd, string $handler='')
Definition: CommandUtility.php:168
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\isLoaded
‪static isLoaded(string $key)
Definition: ExtensionManagementUtility.php:55
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:32
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addTCAcolumns
‪static addTCAcolumns(string $table, array $columnArray)
Definition: ExtensionManagementUtility.php:145
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\removeDuplicatesForInsertion
‪static string removeDuplicatesForInsertion(string $insertionList, string $list='')
Definition: ExtensionManagementUtility.php:581
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addFieldsToAllPalettesOfField
‪static addFieldsToAllPalettesOfField(string $table, string $field, string $addFields, string $insertionPosition='')
Definition: ExtensionManagementUtility.php:294
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addFieldsToPalette
‪static addFieldsToPalette(string $table, string $palette, string $addFields, string $insertionPosition='')
Definition: ExtensionManagementUtility.php:347
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\loadExtension
‪static loadExtension(string $extensionKey)
Definition: ExtensionManagementUtility.php:1141
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\extPath
‪static extPath(string $key, string $script='')
Definition: ExtensionManagementUtility.php:82
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\setPackageManager
‪static setPackageManager(PackageManager $packageManager)
Definition: ExtensionManagementUtility.php:41
‪TYPO3\CMS\Core\Schema\Struct\SelectItem
Definition: SelectItem.php:21
‪TYPO3\CMS\Core\Schema\Struct\SelectItem\fromTcaItemArray
‪static fromTcaItemArray(array $item, string $type='select')
Definition: SelectItem.php:45
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\findService
‪static array false findService(string $serviceType, string $serviceSubType='', array $excludeServiceKeys=[])
Definition: ExtensionManagementUtility.php:691
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addTypoScriptConstants
‪static addTypoScriptConstants(string $content)
Definition: ExtensionManagementUtility.php:1045
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addUserTSConfig
‪static addUserTSConfig(string $content)
Definition: ExtensionManagementUtility.php:626
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\$packageManager
‪static PackageManager $packageManager
Definition: ExtensionManagementUtility.php:33
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\registerPageTSConfigFile
‪static registerPageTSConfigFile(string $extKey, string $filePath, string $title)
Definition: ExtensionManagementUtility.php:1005
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addPlugin
‪static addPlugin(array|SelectItem $itemArray, string $type='list_type', ?string $extensionKey=null)
Definition: ExtensionManagementUtility.php:814
‪TYPO3\CMS\Core\Utility\ArrayUtility\filterByValueRecursive
‪static array filterByValueRecursive(mixed $needle='', array $haystack=[])
Definition: ArrayUtility.php:101
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addFieldsToUserSettings
‪static addFieldsToUserSettings(string $addFields, string $insertionPosition='')
Definition: ExtensionManagementUtility.php:496
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addTcaSelectItem
‪static addTcaSelectItem(string $table, string $field, array $item, string $relativeToField='', string $relativePosition='')
Definition: ExtensionManagementUtility.php:391
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\unloadExtension
‪static unloadExtension(string $extensionKey)
Definition: ExtensionManagementUtility.php:1154
‪TYPO3\CMS\Core\Utility\GeneralUtility\inList
‪static bool inList($list, $item)
Definition: GeneralUtility.php:422
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\resolvePackagePath
‪static resolvePackagePath(string $path)
Definition: ExtensionManagementUtility.php:70
‪TYPO3\CMS\Core\Package\Exception
Definition: ImportRequirementsException.php:18
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\executePositionedStringInsertion
‪static string executePositionedStringInsertion(string $list, string $insertionList, string $insertionPosition='')
Definition: ExtensionManagementUtility.php:518
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addPageTSConfig
‪static addPageTSConfig(string $content)
Definition: ExtensionManagementUtility.php:612
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:817
‪TYPO3\CMS\Core\Schema\Struct\SelectItem\withIcon
‪withIcon(?string $icon)
Definition: SelectItem.php:126
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addTypoScript
‪static addTypoScript(string $key, string $type, string $content, int|string $afterStaticUid=0)
Definition: ExtensionManagementUtility.php:1071
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\getCN
‪static getCN(string $key)
Definition: ExtensionManagementUtility.php:96
‪TYPO3\CMS\Core\Core\Environment\isWindows
‪static isWindows()
Definition: Environment.php:276