‪TYPO3CMS  11.5
ExtensionUtility.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
23 
29 {
30  public const ‪PLUGIN_TYPE_PLUGIN = 'list_type';
31  public const ‪PLUGIN_TYPE_CONTENT_ELEMENT = 'CType';
32 
50  public static function ‪configurePlugin($extensionName, $pluginName, array $controllerActions, array $nonCacheableControllerActions = [], $pluginType = self::PLUGIN_TYPE_PLUGIN)
51  {
52  ‪self::checkPluginNameFormat($pluginName);
53  ‪self::checkExtensionNameFormat($extensionName);
54 
55  // Check if vendor name is prepended to extensionName in the format {vendorName}.{extensionName}
56  $delimiterPosition = strrpos($extensionName, '.');
57  if ($delimiterPosition !== false) {
58  $vendorName = str_replace('.', '\\', substr($extensionName, 0, $delimiterPosition));
59  trigger_error(
60  'Calling method ' . __METHOD__ . ' with argument $extensionName ("' . $extensionName . '") containing the vendor name ("' . $vendorName . '") is deprecated and will stop working in TYPO3 11.0.',
61  E_USER_DEPRECATED
62  );
63  $extensionName = substr($extensionName, $delimiterPosition + 1);
64 
65  if (!empty($vendorName)) {
66  ‪self::checkVendorNameFormat($vendorName, $extensionName);
67  }
68  }
69  $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
70 
71  $pluginSignature = strtolower($extensionName . '_' . $pluginName);
72  if (!is_array(‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName] ?? false)) {
73  ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName] = [];
74  }
75  foreach ($controllerActions as $controllerClassName => $actionsList) {
76  $controllerAlias = ‪self::resolveControllerAliasFromControllerClassName($controllerClassName);
77  $vendorName = ‪self::resolveVendorFromExtensionAndControllerClassName($extensionName, $controllerClassName);
78  if (!empty($vendorName)) {
79  ‪self::checkVendorNameFormat($vendorName, $extensionName);
80  }
81 
82  ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerClassName] = [
83  'className' => $controllerClassName,
84  'alias' => $controllerAlias,
85  'actions' => ‪GeneralUtility::trimExplode(',', $actionsList),
86  ];
87 
88  if (isset($nonCacheableControllerActions[$controllerClassName]) && !empty($nonCacheableControllerActions[$controllerClassName])) {
89  ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerClassName]['nonCacheableActions'] = ‪GeneralUtility::trimExplode(
90  ',',
91  $nonCacheableControllerActions[$controllerClassName]
92  );
93  }
94  }
95 
96  switch ($pluginType) {
98  $pluginContent = trim('
99 tt_content.list.20.' . $pluginSignature . ' = USER
100 tt_content.list.20.' . $pluginSignature . ' {
101  userFunc = TYPO3\\CMS\\Extbase\\Core\\Bootstrap->run
102  extensionName = ' . $extensionName . '
103  pluginName = ' . $pluginName . '
104 }');
105  break;
107  $pluginContent = trim('
108 tt_content.' . $pluginSignature . ' =< lib.contentElement
109 tt_content.' . $pluginSignature . ' {
110  templateName = Generic
111  20 = USER
112  20 {
113  userFunc = TYPO3\\CMS\\Extbase\\Core\\Bootstrap->run
114  extensionName = ' . $extensionName . '
115  pluginName = ' . $pluginName . '
116  }
117 }');
118  break;
119  default:
120  throw new \InvalidArgumentException('The pluginType "' . $pluginType . '" is not supported', 1289858856);
121  }
122  ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['pluginType'] = $pluginType;
123  ‪ExtensionManagementUtility::addTypoScript($extensionName, 'setup', '
124 # Setting ' . $extensionName . ' plugin TypoScript
125 ' . $pluginContent, 'defaultContentRendering');
126  }
127 
139  public static function ‪registerPlugin($extensionName, $pluginName, $pluginTitle, $pluginIcon = null, $group = 'default')
140  {
141  ‪self::checkPluginNameFormat($pluginName);
142  ‪self::checkExtensionNameFormat($extensionName);
143 
144  $delimiterPosition = strrpos($extensionName, '.');
145  if ($delimiterPosition !== false) {
146  $vendorName = str_replace('.', '\\', substr($extensionName, 0, $delimiterPosition));
147  trigger_error(
148  'Calling method ' . __METHOD__ . ' with argument $extensionName ("' . $extensionName . '") containing the vendor name ("' . $vendorName . '") is deprecated and will stop working in TYPO3 11.0.',
149  E_USER_DEPRECATED
150  );
151  $extensionName = substr($extensionName, $delimiterPosition + 1);
152  }
153  $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
154  $pluginSignature = strtolower($extensionName) . '_' . strtolower($pluginName);
155 
156  // At this point $extensionName is normalized, no matter which format the method was fed with.
157  // Calculate the original extensionKey from this again.
158  $extensionKey = ‪GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName);
159 
160  // pluginType is usually defined by configurePlugin() in the global array. Use this or fall back to default "list_type".
161  $pluginType = ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['pluginType'] ?? 'list_type';
162 
163  $itemArray = [$pluginTitle, $pluginSignature, $pluginIcon];
164  if ($group) {
165  $itemArray[3] = $group;
166  }
168  $itemArray,
169  $pluginType,
170  $extensionKey
171  );
172  }
173 
186  public static function ‪registerModule($extensionName, $mainModuleName = '', $subModuleName = '', $position = '', array $controllerActions = [], array $moduleConfiguration = [])
187  {
188  ‪self::checkExtensionNameFormat($extensionName);
189 
190  // Check if vendor name is prepended to extensionName in the format {vendorName}.{extensionName}
191  if (false !== $delimiterPosition = strrpos($extensionName, '.')) {
192  trigger_error(
193  'Calling method ' . __METHOD__ . ' with argument $extensionName containing the vendor name is deprecated and will stop working in TYPO3 11.0.',
194  E_USER_DEPRECATED
195  );
196  $vendorName = str_replace('.', '\\', substr($extensionName, 0, $delimiterPosition));
197  $extensionName = substr($extensionName, $delimiterPosition + 1);
198 
199  if (!empty($vendorName)) {
200  ‪self::checkVendorNameFormat($vendorName, $extensionName);
201  }
202  }
203 
204  $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
205  $defaultModuleConfiguration = [
206  'access' => 'admin',
207  'icon' => 'EXT:extbase/Resources/Public/Icons/Extension.svg',
208  'labels' => '',
209  ];
210  if ($mainModuleName !== '' && !array_key_exists($mainModuleName, ‪$GLOBALS['TBE_MODULES'])) {
211  $mainModuleName = $extensionName . ‪GeneralUtility::underscoredToUpperCamelCase($mainModuleName);
212  } else {
213  $mainModuleName = $mainModuleName !== '' ? $mainModuleName : 'web';
214  }
215  // add mandatory parameter to use new pagetree
216  if ($mainModuleName === 'web') {
217  $defaultModuleConfiguration['navigationComponentId'] = 'TYPO3/CMS/Backend/PageTree/PageTreeElement';
218  }
219  ‪ArrayUtility::mergeRecursiveWithOverrule($defaultModuleConfiguration, $moduleConfiguration);
220  $moduleConfiguration = $defaultModuleConfiguration;
221  $moduleSignature = $mainModuleName;
222  if ($subModuleName !== '') {
223  $subModuleName = $extensionName . ‪GeneralUtility::underscoredToUpperCamelCase($subModuleName);
224  $moduleSignature .= '_' . $subModuleName;
225  }
226  $moduleConfiguration['name'] = $moduleSignature;
227  $moduleConfiguration['extensionName'] = $extensionName;
228  $moduleConfiguration['routeTarget'] = Bootstrap::class . '::handleBackendRequest';
229  if (!is_array(‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature] ?? false)) {
230  ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature] = [];
231  }
232  foreach ($controllerActions as $controllerClassName => $actionsList) {
233  $controllerAlias = ‪self::resolveControllerAliasFromControllerClassName($controllerClassName);
234  $vendorName = ‪self::resolveVendorFromExtensionAndControllerClassName($extensionName, $controllerClassName);
235  if (!empty($vendorName)) {
236  ‪self::checkVendorNameFormat($vendorName, $extensionName);
237  }
238 
239  ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature]['controllers'][$controllerClassName] = [
240  'className' => $controllerClassName,
241  'alias' => $controllerAlias,
242  'actions' => ‪GeneralUtility::trimExplode(',', $actionsList),
243  ];
244  }
245  ‪ExtensionManagementUtility::addModule($mainModuleName, $subModuleName, $position, null, $moduleConfiguration);
246  }
247 
259  public static function ‪getControllerClassName(
260  string $vendor,
261  string $extensionKey,
262  string $subPackageKey,
263  string $controllerAlias
264  ): string {
265  $objectName = str_replace(
266  [
267  '@extension',
268  '@subpackage',
269  '@controller',
270  '@vendor',
271  '\\\\',
272  ],
273  [
274  $extensionKey,
275  $subPackageKey,
276  $controllerAlias,
277  $vendor,
278  '\\',
279  ],
280  '@vendor\@extension\@subpackage\Controller\@controllerController'
281  );
282 
283  if ($objectName === false) {
284  throw new ‪NoSuchControllerException('The controller object "' . $objectName . '" does not exist.', 1220884009);
285  }
286  return trim($objectName, '\\');
287  }
288 
293  public static function ‪resolveControllerAliasFromControllerClassName(string $controllerClassName): string
294  {
295  // This method has been adjusted for TYPO3 10.3 to mitigate the issue that controller aliases
296  // could not longer be calculated from controller classes when calling
297  // \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin().
298  //
299  // The idea for version 11 is to let the user choose a controller alias and to check for its
300  // uniqueness per plugin. That way, the core does no longer rely on the namespace of
301  // controller classes to be in a specific format.
302  //
303  // todo: Change the way plugins are registered and enforce a controller alias to be set by
304  // the user to also free the core from guessing a simple alias by looking at the
305  // class name. This makes it possible to choose controller class names without a
306  // controller suffix.
307 
308  $strLen = strlen('Controller');
309 
310  if (!str_ends_with($controllerClassName, 'Controller')) {
311  return '';
312  }
313 
314  $controllerClassNameWithoutControllerSuffix = substr($controllerClassName, 0, -$strLen);
315 
316  if (strrpos($controllerClassNameWithoutControllerSuffix, 'Controller\\') === false) {
317  $positionOfLastSlash = (int)strrpos($controllerClassNameWithoutControllerSuffix, '\\');
318  $positionOfLastSlash += $positionOfLastSlash === 0 ? 0 : 1;
319 
320  return substr($controllerClassNameWithoutControllerSuffix, $positionOfLastSlash);
321  }
322 
323  $positionOfControllerNamespacePart = (int)strrpos(
324  $controllerClassNameWithoutControllerSuffix,
325  'Controller\\'
326  );
327 
328  return substr(
329  $controllerClassNameWithoutControllerSuffix,
330  $positionOfControllerNamespacePart + $strLen + 1
331  );
332  }
333 
339  public static function ‪resolveVendorFromExtensionAndControllerClassName(string $extensionName, string $controllerClassName): string
340  {
341  if (!str_contains($controllerClassName, '\\')) {
342  // Does not work with non namespaced classes
343  return '';
344  }
345 
346  if (false === $extensionNamePosition = strpos($controllerClassName, $extensionName)) {
347  // Does not work for classes that do not include the extension name as namespace part
348  return '';
349  }
350 
351  if (--$extensionNamePosition < 0) {
352  return '';
353  }
354 
355  return substr(
356  $controllerClassName,
357  0,
358  $extensionNamePosition
359  );
360  }
361 
367  public static function ‪registerTypeConverter($typeConverterClassName)
368  {
369  if (!isset(‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters']) ||
370  !is_array(‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'])
371  ) {
372  ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'] = [];
373  }
374  if (!in_array($typeConverterClassName, ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'])) {
375  ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'][] = $typeConverterClassName;
376  }
377  }
378 
386  protected static function ‪checkVendorNameFormat($vendorName, $extensionName)
387  {
388  if (preg_match('/^[A-Z]/', $vendorName) !== 1) {
389  trigger_error('The vendor name from tx_' . $extensionName . ' must begin with a capital letter.', E_USER_DEPRECATED);
390  }
391  }
392 
399  protected static function ‪checkExtensionNameFormat($extensionName)
400  {
401  if (empty($extensionName)) {
402  throw new \InvalidArgumentException('The extension name must not be empty', 1239891990);
403  }
404  }
405 
412  protected static function ‪checkPluginNameFormat($pluginName)
413  {
414  if (empty($pluginName)) {
415  throw new \InvalidArgumentException('The plugin name must not be empty', 1239891988);
416  }
417  }
418 }
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addModule
‪static addModule($main, $sub='', $position='', $path=null, $moduleConfiguration=[])
Definition: ExtensionManagementUtility.php:771
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\resolveVendorFromExtensionAndControllerClassName
‪static string resolveVendorFromExtensionAndControllerClassName(string $extensionName, string $controllerClassName)
Definition: ExtensionUtility.php:339
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\registerPlugin
‪static registerPlugin($extensionName, $pluginName, $pluginTitle, $pluginIcon=null, $group='default')
Definition: ExtensionUtility.php:139
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\resolveControllerAliasFromControllerClassName
‪static string resolveControllerAliasFromControllerClassName(string $controllerClassName)
Definition: ExtensionUtility.php:293
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility
Definition: ExtensionUtility.php:29
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\checkVendorNameFormat
‪static checkVendorNameFormat($vendorName, $extensionName)
Definition: ExtensionUtility.php:386
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\PLUGIN_TYPE_CONTENT_ELEMENT
‪const PLUGIN_TYPE_CONTENT_ELEMENT
Definition: ExtensionUtility.php:31
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\registerModule
‪static registerModule($extensionName, $mainModuleName='', $subModuleName='', $position='', array $controllerActions=[], array $moduleConfiguration=[])
Definition: ExtensionUtility.php:186
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addPlugin
‪static addPlugin($itemArray, $type='list_type', $extensionKey=null)
Definition: ExtensionManagementUtility.php:1201
‪TYPO3\CMS\Core\Utility\ArrayUtility\mergeRecursiveWithOverrule
‪static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
Definition: ArrayUtility.php:654
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\registerTypeConverter
‪static registerTypeConverter($typeConverterClassName)
Definition: ExtensionUtility.php:367
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\PLUGIN_TYPE_PLUGIN
‪const PLUGIN_TYPE_PLUGIN
Definition: ExtensionUtility.php:30
‪TYPO3\CMS\Core\Utility\GeneralUtility\camelCaseToLowerCaseUnderscored
‪static string camelCaseToLowerCaseUnderscored($string)
Definition: GeneralUtility.php:853
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\configurePlugin
‪static configurePlugin($extensionName, $pluginName, array $controllerActions, array $nonCacheableControllerActions=[], $pluginType=self::PLUGIN_TYPE_PLUGIN)
Definition: ExtensionUtility.php:50
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility
Definition: ExtensionManagementUtility.php:43
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\checkPluginNameFormat
‪static checkPluginNameFormat($pluginName)
Definition: ExtensionUtility.php:412
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\getControllerClassName
‪static string getControllerClassName(string $vendor, string $extensionKey, string $subPackageKey, string $controllerAlias)
Definition: ExtensionUtility.php:259
‪TYPO3\CMS\Extbase\Mvc\Exception\NoSuchControllerException
Definition: NoSuchControllerException.php:25
‪TYPO3\CMS\Extbase\Utility\ExtensionUtility\checkExtensionNameFormat
‪static checkExtensionNameFormat($extensionName)
Definition: ExtensionUtility.php:399
‪TYPO3\CMS\Core\Utility\ExtensionManagementUtility\addTypoScript
‪static addTypoScript(string $key, string $type, string $content, $afterStaticUid=0)
Definition: ExtensionManagementUtility.php:1434
‪TYPO3\CMS\Core\Utility\ArrayUtility
Definition: ArrayUtility.php:24
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Extbase\Core\Bootstrap
Definition: Bootstrap.php:42
‪TYPO3\CMS\Extbase\Utility
Definition: DebuggerUtility.php:18
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Utility\GeneralUtility\underscoredToUpperCamelCase
‪static string underscoredToUpperCamelCase($string)
Definition: GeneralUtility.php:829