c818326c134f1e9154d05a011dec44156210b1b1
[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 * @return void
44 */
45 public static function configurePlugin($extensionName, $pluginName, array $controllerActions, array $nonCacheableControllerActions = array(), $pluginType = self::PLUGIN_TYPE_PLUGIN)
46 {
47 self::checkPluginNameFormat($pluginName);
48 self::checkExtensionNameFormat($extensionName);
49
50 // Check if vendor name is prepended to extensionName in the format {vendorName}.{extensionName}
51 $vendorName = null;
52 $delimiterPosition = strrpos($extensionName, '.');
53 if ($delimiterPosition !== false) {
54 $vendorName = str_replace('.', '\\', substr($extensionName, 0, $delimiterPosition));
55 $extensionName = substr($extensionName, $delimiterPosition + 1);
56
57 if (!empty($vendorName)) {
58 self::checkVendorNameFormat($vendorName, $extensionName);
59 }
60 }
61 $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
62
63 $pluginSignature = strtolower($extensionName . '_' . $pluginName);
64 if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName])) {
65 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName] = array();
66 }
67 foreach ($controllerActions as $controllerName => $actionsList) {
68 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerName] = array('actions' => \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $actionsList));
69 if (!empty($nonCacheableControllerActions[$controllerName])) {
70 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['controllers'][$controllerName]['nonCacheableActions'] = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $nonCacheableControllerActions[$controllerName]);
71 }
72 }
73
74 switch ($pluginType) {
75 case self::PLUGIN_TYPE_PLUGIN:
76 $pluginContent = trim('
77 tt_content.list.20.' . $pluginSignature . ' = USER
78 tt_content.list.20.' . $pluginSignature . ' {
79 userFunc = TYPO3\\CMS\\Extbase\\Core\\Bootstrap->run
80 extensionName = ' . $extensionName . '
81 pluginName = ' . $pluginName . (null !== $vendorName ? ("\n\t" . 'vendorName = ' . $vendorName) : '') . '
82 }');
83 break;
84 case self::PLUGIN_TYPE_CONTENT_ELEMENT:
85 $pluginContent = trim('
86 tt_content.' . $pluginSignature . ' = COA
87 tt_content.' . $pluginSignature . ' {
88 10 = < lib.stdheader
89 20 = USER
90 20 {
91 userFunc = TYPO3\\CMS\\Extbase\\Core\\Bootstrap->run
92 extensionName = ' . $extensionName . '
93 pluginName = ' . $pluginName . (null !== $vendorName ? ("\n\t\t" . 'vendorName = ' . $vendorName) : '') . '
94 }
95 }');
96 break;
97 default:
98 throw new \InvalidArgumentException('The pluginType "' . $pluginType . '" is not suported', 1289858856);
99 }
100 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['pluginType'] = $pluginType;
101 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScript($extensionName, 'setup', '
102 # Setting ' . $extensionName . ' plugin TypoScript
103 ' . $pluginContent, 'defaultContentRendering');
104 }
105
106 /**
107 * Register an Extbase PlugIn into backend's list of plugins
108 * FOR USE IN ext_tables.php FILES
109 *
110 * @param string $extensionName The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
111 * @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!)
112 * @param string $pluginTitle is a speaking title of the plugin that will be displayed in the drop down menu in the backend
113 * @param string $pluginIconPathAndFilename is a path to an icon file (relative to TYPO3_mainDir), that will be displayed in the drop down menu in the backend (optional)
114 * @throws \InvalidArgumentException
115 * @return void
116 */
117 public static function registerPlugin($extensionName, $pluginName, $pluginTitle, $pluginIconPathAndFilename = null)
118 {
119 self::checkPluginNameFormat($pluginName);
120 self::checkExtensionNameFormat($extensionName);
121
122 $delimiterPosition = strrpos($extensionName, '.');
123 if ($delimiterPosition !== false) {
124 $extensionName = substr($extensionName, $delimiterPosition + 1);
125 }
126 $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
127 $pluginSignature = strtolower($extensionName) . '_' . strtolower($pluginName);
128
129 // At this point $extensionName is normalized, no matter which format the method was feeded with.
130 // Calculate the original extensionKey from this again.
131 $extensionKey = \TYPO3\CMS\Core\Utility\GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName);
132
133 // pluginType is usually defined by configurePlugin() in the global array. Use this or fall back to default "list_type".
134 $pluginType = isset($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['pluginType'])
135 ? $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['plugins'][$pluginName]['pluginType']
136 : 'list_type';
137
138 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin(
139 array($pluginTitle, $pluginSignature, $pluginIconPathAndFilename),
140 $pluginType,
141 $extensionKey
142 );
143 }
144
145 /**
146 * Registers an Extbase module (main or sub) to the backend interface.
147 * FOR USE IN ext_tables.php FILES
148 *
149 * @param string $extensionName The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
150 * @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
151 * @param string $subModuleName The submodule key.
152 * @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.
153 * @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)
154 * @param array $moduleConfiguration The configuration options of the module (icon, locallang.xlf file)
155 * @throws \InvalidArgumentException
156 * @return void
157 */
158 public static function registerModule($extensionName, $mainModuleName = '', $subModuleName = '', $position = '', array $controllerActions = array(), array $moduleConfiguration = array())
159 {
160 self::checkExtensionNameFormat($extensionName);
161
162 // Check if vendor name is prepended to extensionName in the format {vendorName}.{extensionName}
163 $vendorName = null;
164 if (false !== $delimiterPosition = strrpos($extensionName, '.')) {
165 $vendorName = str_replace('.', '\\', substr($extensionName, 0, $delimiterPosition));
166 $extensionName = substr($extensionName, $delimiterPosition + 1);
167
168 if (!empty($vendorName)) {
169 self::checkVendorNameFormat($vendorName, $extensionName);
170 }
171 }
172 $extensionKey = \TYPO3\CMS\Core\Utility\GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName);
173 $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
174 $defaultModuleConfiguration = array(
175 'access' => 'admin',
176 'icon' => 'EXT:extbase/ext_icon.png',
177 'labels' => '',
178 'extRelPath' => \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extRelPath($extensionKey) . 'Classes/'
179 );
180 if ($mainModuleName !== '' && !array_key_exists($mainModuleName, $GLOBALS['TBE_MODULES'])) {
181 $mainModuleName = $extensionName . \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToUpperCamelCase($mainModuleName);
182 } else {
183 $mainModuleName = $mainModuleName !== '' ? $mainModuleName : 'web';
184 }
185 // add mandatory parameter to use new pagetree
186 if ($mainModuleName === 'web') {
187 $defaultModuleConfiguration['navigationComponentId'] = 'typo3-pagetree';
188 }
189 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($defaultModuleConfiguration, $moduleConfiguration);
190 $moduleConfiguration = $defaultModuleConfiguration;
191 $moduleSignature = $mainModuleName;
192 if ($subModuleName !== '') {
193 $subModuleName = $extensionName . \TYPO3\CMS\Core\Utility\GeneralUtility::underscoredToUpperCamelCase($subModuleName);
194 $moduleSignature .= '_' . $subModuleName;
195 }
196 $moduleConfiguration['name'] = $moduleSignature;
197 if (null !== $vendorName) {
198 $moduleConfiguration['vendorName'] = $vendorName;
199 }
200 $moduleConfiguration['extensionName'] = $extensionName;
201 $moduleConfiguration['configureModuleFunction'] = array(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::class, 'configureModule');
202 $GLOBALS['TBE_MODULES']['_configuration'][$moduleSignature] = $moduleConfiguration;
203 if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature])) {
204 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature] = array();
205 }
206 foreach ($controllerActions as $controllerName => $actions) {
207 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName]['modules'][$moduleSignature]['controllers'][$controllerName] = array(
208 'actions' => \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(',', $actions)
209 );
210 }
211 \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addModule($mainModuleName, $subModuleName, $position);
212 }
213
214 /**
215 * Register a type converter by class name.
216 *
217 * @param string $typeConverterClassName
218 * @return void
219 * @api
220 */
221 public static function registerTypeConverter($typeConverterClassName)
222 {
223 if (!is_array($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'])) {
224 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'] = array();
225 }
226 $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['typeConverters'][] = $typeConverterClassName;
227 }
228
229 /**
230 * Check a given vendor name for CGL compliance.
231 * Log a deprecation message if it is not.
232 *
233 * @param string $vendorName The vendor name to check
234 * @param string $extensionName The extension name that is affected
235 * @return void
236 */
237 protected static function checkVendorNameFormat($vendorName, $extensionName)
238 {
239 if (preg_match('/^[A-Z]/', $vendorName) !== 1) {
240 \TYPO3\CMS\Core\Utility\GeneralUtility::deprecationLog('The vendor name from tx_' . $extensionName . ' must begin with a capital letter.');
241 }
242 }
243
244 /**
245 * Check a given extension name for validity.
246 *
247 * @param string $extensionName The name of the extension
248 * @throws \InvalidArgumentException
249 * @return void
250 */
251 protected static function checkExtensionNameFormat($extensionName)
252 {
253 if (empty($extensionName)) {
254 throw new \InvalidArgumentException('The extension name must not be empty', 1239891990);
255 }
256 }
257
258 /**
259 * Check a given plugin name for validity.
260 *
261 * @param string $pluginName The name of the plugin
262 * @throws \InvalidArgumentException
263 * @return void
264 */
265 protected static function checkPluginNameFormat($pluginName)
266 {
267 if (empty($pluginName)) {
268 throw new \InvalidArgumentException('The plugin name must not be empty', 1239891988);
269 }
270 }
271 }