266ce6ed13791caff00c3a5826c47dcb2408fffa
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Utility / ExtensionUtility.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Utility;
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 /**
18 * Utilities to manage plugins and modules of an extension. Also useful to auto-generate the autoloader registry
19 * file ext_autoload.php.
20 */
21 class ExtensionUtility
22 {
23 const PLUGIN_TYPE_PLUGIN = 'list_type';
24 const PLUGIN_TYPE_CONTENT_ELEMENT = 'CType';
25
26 /**
27 * Add auto-generated TypoScript to configure the Extbase Dispatcher.
28 *
29 * When adding a frontend plugin you will have to add both an entry to the TCA definition
30 * of tt_content table AND to the TypoScript template which must initiate the rendering.
31 * Including the plugin code after "defaultContentRendering" adds the necessary TypoScript
32 * for calling the appropriate controller and action of your plugin.
33 * This means, it will also work for the extension "css_styled_content"
34 * FOR USE IN ext_localconf.php FILES
35 * Usage: 2
36 *
37 * @param string $extensionName The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
38 * @param string $pluginName must be a unique id for your plugin in UpperCamelCase (the string length of the extension key added to the length of the plugin name should be less than 32!)
39 * @param array $controllerActions is an array of allowed combinations of controller and action stored in an array (controller name as key and a comma separated list of action names as value, the first controller and its first action is chosen as default)
40 * @param array $nonCacheableControllerActions is an optional array of controller name and action names which should not be cached (array as defined in $controllerActions)
41 * @param string $pluginType either \TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_PLUGIN (default) or \TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT
42 * @throws \InvalidArgumentException
43 */
44 public static function configurePlugin($extensionName, $pluginName, array $controllerActions, array $nonCacheableControllerActions = [], $pluginType = self::PLUGIN_TYPE_PLUGIN)
45 {
46 self::checkPluginNameFormat($pluginName);
47 self::checkExtensionNameFormat($extensionName);
48
49 // Check if vendor name is prepended to extensionName in the format {vendorName}.{extensionName}
50 $vendorName = null;
51 $delimiterPosition = strrpos($extensionName, '.');
52 if ($delimiterPosition !== false) {
53 $vendorName = str_replace('.', '\\', substr($extensionName, 0, $delimiterPosition));
54 $extensionName = substr($extensionName, $delimiterPosition + 1);
55
56 if (!empty($vendorName)) {
57 self::checkVendorNameFormat($vendorName, $extensionName);
58 }
59 }
60 $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
61
62 $pluginSignature = strtolower($extensionName . '_' . $pluginName);
63 if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName])) {
64 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName] = [];
65 }
66 foreach ($controllerActions as $controllerName => $actionsList) {
67 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerName] = ['actions' => \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $actionsList)];
68 if (!empty($nonCacheableControllerActions[$controllerName])) {
69 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerName]['nonCacheableActions'] = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $nonCacheableControllerActions[$controllerName]);
70 }
71 }
72
73 switch ($pluginType) {
74 case self::PLUGIN_TYPE_PLUGIN:
75 $pluginContent = trim('
76 tt_content.list.20.' . $pluginSignature . ' = USER
77 tt_content.list.20.' . $pluginSignature . ' {
78 userFunc = TYPO3\\CMS\\Extbase\\Core\\Bootstrap->run
79 extensionName = ' . $extensionName . '
80 pluginName = ' . $pluginName . (null !== $vendorName ? ("\n\t" . 'vendorName = ' . $vendorName) : '') . '
81 }');
82 break;
83 case self::PLUGIN_TYPE_CONTENT_ELEMENT:
84 $pluginContent = trim('
85 tt_content.' . $pluginSignature . ' =< lib.contentElement
86 tt_content.' . $pluginSignature . ' {
87 templateName = Generic
88 20 = USER
89 20 {
90 userFunc = TYPO3\\CMS\\Extbase\\Core\\Bootstrap->run
91 extensionName = ' . $extensionName . '
92 pluginName = ' . $pluginName . (null !== $vendorName ? ("\n\t\t" . 'vendorName = ' . $vendorName) : '') . '
93 }
94 }');
95 break;
96 default:
97 throw new \InvalidArgumentException('The pluginType "' . $pluginType . '" is not suported', 1289858856);
98 }
99 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['pluginType'] = $pluginType;
100 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScript($extensionName, 'setup', '
101 # Setting ' . $extensionName . ' plugin TypoScript
102 ' . $pluginContent, 'defaultContentRendering');
103 }
104
105 /**
106 * Register an Extbase PlugIn into backend's list of plugins
107 * FOR USE IN ext_tables.php FILES
108 *
109 * @param string $extensionName The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
110 * @param string $pluginName must be a unique id for your plugin in UpperCamelCase (the string length of the extension key added to the length of the plugin name should be less than 32!)
111 * @param string $pluginTitle is a speaking title of the plugin that will be displayed in the drop down menu in the backend
112 * @param string $pluginIcon is an icon identifier or file path prepended with "EXT:", that will be displayed in the drop down menu in the backend (optional)
113 * @throws \InvalidArgumentException
114 */
115 public static function registerPlugin($extensionName, $pluginName, $pluginTitle, $pluginIcon = null)
116 {
117 self::checkPluginNameFormat($pluginName);
118 self::checkExtensionNameFormat($extensionName);
119
120 $delimiterPosition = strrpos($extensionName, '.');
121 if ($delimiterPosition !== false) {
122 $extensionName = substr($extensionName, $delimiterPosition + 1);
123 }
124 $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
125 $pluginSignature = strtolower($extensionName) . '_' . strtolower($pluginName);
126
127 // At this point $extensionName is normalized, no matter which format the method was feeded with.
128 // Calculate the original extensionKey from this again.
129 $extensionKey = \TYPO3\CMS\Core\Utility\GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName);
130
131 // pluginType is usually defined by configurePlugin() in the global array. Use this or fall back to default "list_type".
132 $pluginType = isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['pluginType'])
133 ? $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['pluginType']
134 : 'list_type';
135
136 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin(
137 [$pluginTitle, $pluginSignature, $pluginIcon],
138 $pluginType,
139 $extensionKey
140 );
141 }
142
143 /**
144 * Registers an Extbase module (main or sub) to the backend interface.
145 * FOR USE IN ext_tables.php FILES
146 *
147 * @param string $extensionName The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
148 * @param string $mainModuleName The main module key. So $main would be an index in the $TBE_MODULES array and $sub could be an element in the lists there. If $subModuleName is not set a blank $extensionName module is created
149 * @param string $subModuleName The submodule key.
150 * @param string $position This can be used to set the position of the $sub module within the list of existing submodules for the main module. $position has this syntax: [cmd]:[submodule-key]. cmd can be "after", "before" or "top" (or blank which is default). If "after"/"before" then submodule will be inserted after/before the existing submodule with [submodule-key] if found. If not found, the bottom of list. If "top" the module is inserted in the top of the submodule list.
151 * @param array $controllerActions is an array of allowed combinations of controller and action stored in an array (controller name as key and a comma separated list of action names as value, the first controller and its first action is chosen as default)
152 * @param array $moduleConfiguration The configuration options of the module (icon, locallang.xlf file)
153 * @throws \InvalidArgumentException
154 */
155 public static function registerModule($extensionName, $mainModuleName = '', $subModuleName = '', $position = '', array $controllerActions = [], array $moduleConfiguration = [])
156 {
157 self::checkExtensionNameFormat($extensionName);
158
159 // Check if vendor name is prepended to extensionName in the format {vendorName}.{extensionName}
160 $vendorName = null;
161 if (false !== $delimiterPosition = strrpos($extensionName, '.')) {
162 $vendorName = str_replace('.', '\\', substr($extensionName, 0, $delimiterPosition));
163 $extensionName = substr($extensionName, $delimiterPosition + 1);
164
165 if (!empty($vendorName)) {
166 self::checkVendorNameFormat($vendorName, $extensionName);
167 }
168 }
169
170 $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
171 $defaultModuleConfiguration = [
172 'access' => 'admin',
173 'icon' => 'EXT:extbase/Resources/Public/Icons/Extension.png',
174 'labels' => ''
175 ];
176 if ($mainModuleName !== '' && !array_key_exists($mainModuleName, $GLOBALS['TBE_MODULES'])) {
177 $mainModuleName = $extensionName . \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToUpperCamelCase($mainModuleName);
178 } else {
179 $mainModuleName = $mainModuleName !== '' ? $mainModuleName : 'web';
180 }
181 // add mandatory parameter to use new pagetree
182 if ($mainModuleName === 'web') {
183 $defaultModuleConfiguration['navigationComponentId'] = 'typo3-pagetree';
184 }
185 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($defaultModuleConfiguration, $moduleConfiguration);
186 $moduleConfiguration = $defaultModuleConfiguration;
187 $moduleSignature = $mainModuleName;
188 if ($subModuleName !== '') {
189 $subModuleName = $extensionName . \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToUpperCamelCase($subModuleName);
190 $moduleSignature .= '_' . $subModuleName;
191 }
192 $moduleConfiguration['name'] = $moduleSignature;
193 if (null !== $vendorName) {
194 $moduleConfiguration['vendorName'] = $vendorName;
195 }
196 $moduleConfiguration['extensionName'] = $extensionName;
197 $moduleConfiguration['configureModuleFunction'] = [\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::class, 'configureModule'];
198 $GLOBALS['TBE_MODULES']['_configuration'][$moduleSignature] = $moduleConfiguration;
199 if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature])) {
200 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature] = [];
201 }
202 foreach ($controllerActions as $controllerName => $actions) {
203 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature]['controllers'][$controllerName] = [
204 'actions' => \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $actions)
205 ];
206 }
207 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addModule($mainModuleName, $subModuleName, $position);
208 }
209
210 /**
211 * Register a type converter by class name.
212 *
213 * @param string $typeConverterClassName
214 * @api
215 */
216 public static function registerTypeConverter($typeConverterClassName)
217 {
218 if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'])) {
219 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'] = [];
220 }
221 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'][] = $typeConverterClassName;
222 }
223
224 /**
225 * Check a given vendor name for CGL compliance.
226 * Log a deprecation message if it is not.
227 *
228 * @param string $vendorName The vendor name to check
229 * @param string $extensionName The extension name that is affected
230 */
231 protected static function checkVendorNameFormat($vendorName, $extensionName)
232 {
233 if (preg_match('/^[A-Z]/', $vendorName) !== 1) {
234 \TYPO3\CMS\Core\Utility\GeneralUtility::deprecationLog('The vendor name from tx_' . $extensionName . ' must begin with a capital letter.');
235 }
236 }
237
238 /**
239 * Check a given extension name for validity.
240 *
241 * @param string $extensionName The name of the extension
242 * @throws \InvalidArgumentException
243 */
244 protected static function checkExtensionNameFormat($extensionName)
245 {
246 if (empty($extensionName)) {
247 throw new \InvalidArgumentException('The extension name must not be empty', 1239891990);
248 }
249 }
250
251 /**
252 * Check a given plugin name for validity.
253 *
254 * @param string $pluginName The name of the plugin
255 * @throws \InvalidArgumentException
256 */
257 protected static function checkPluginNameFormat($pluginName)
258 {
259 if (empty($pluginName)) {
260 throw new \InvalidArgumentException('The plugin name must not be empty', 1239891988);
261 }
262 }
263 }