[+FEATURE] Extbase (MVC): Automatic target page determination
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Utility / Extension.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2009 Jochen Rau <jochen.rau@typoplanet.de>
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 *
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
24
25 /**
26 * Utilities to manage plugins and modules of an extension. Also useful to auto-generate the autoloader registry
27 * file ext_autoload.php.
28 *
29 * @package Extbase
30 * @subpackage Utility
31 * @version $ID:$
32 */
33 class Tx_Extbase_Utility_Extension {
34
35 /**
36 * Add auto-generated TypoScript to configure the Extbase Dispatcher.
37 *
38 * When adding a frontend plugin you will have to add both an entry to the TCA definition
39 * of tt_content table AND to the TypoScript template which must initiate the rendering.
40 * Since the static template with uid 43 is the "content.default" and practically always
41 * used for rendering the content elements it's very useful to have this function automatically
42 * adding the necessary TypoScript for calling the appropriate controller and action of your plugin.
43 * It will also work for the extension "css_styled_content"
44 * FOR USE IN ext_localconf.php FILES
45 * Usage: 2
46 *
47 * @param string $extensionName The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
48 * @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!)
49 * @param string $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)
50 * @param string $nonCacheableControllerActions is an optional array of controller name and action names which should not be cached (array as defined in $controllerActions)
51 * @param string $defaultControllerAction is an optional array controller name (as array key) and action name (as array value) that should be called as default
52 * @return void
53 */
54 public static function configurePlugin($extensionName, $pluginName, array $controllerActions, array $nonCacheableControllerActions = array()) {
55 if (empty($pluginName)) {
56 throw new InvalidArgumentException('The plugin name must not be empty', 1239891987);
57 }
58 if (empty($extensionName)) {
59 throw new InvalidArgumentException('The extension name was invalid (must not be empty and must match /[A-Za-z][_A-Za-z0-9]/)', 1239891989);
60 }
61 $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
62 $pluginSignature = strtolower($extensionName) . '_' . strtolower($pluginName);
63
64 $controllers = '';
65 foreach ($controllerActions as $controller => $actionsList) {
66 $controllers .= '
67 ' . $controller . '.actions = ' . $actionsList;
68 if (!empty($nonCacheableControllerActions[$controller])) {
69 $controllers .= '
70 ' . $controller . '.nonCacheableActions = ' . $nonCacheableControllerActions[$controller];
71 }
72 }
73
74 $switchableControllerActions = '
75 switchableControllerActions {' . $controllers . '
76 }';
77
78 reset($controllerActions);
79 $defaultController = key($controllerActions);
80 $controller = '
81 controller = ' . $defaultController;
82 $defaultAction = array_shift(t3lib_div::trimExplode(',', current($controllerActions)));
83 $action = '
84 action = ' . $defaultAction;
85
86 $pluginTemplate = 'plugin.tx_' . strtolower($extensionName) . ' {
87 settings {
88 }
89 persistence {
90 storagePid =
91 classes {
92 }
93 }
94 view {
95 templateRootPath =
96 layoutRootPath =
97 partialRootPath =
98 # the defaultPid can be an integer but defaults to the string "auto" which means that the target pid is determined automatically
99 defaultPid = auto
100 }
101 }';
102 t3lib_extMgm::addTypoScript($extensionName, 'setup', '
103 # Setting ' . $extensionName . ' plugin TypoScript
104 ' . $pluginTemplate);
105
106 $pluginContent = trim('
107 tt_content.list.20.' . $pluginSignature . ' = USER
108 tt_content.list.20.' . $pluginSignature . ' {
109 userFunc = tx_extbase_dispatcher->dispatch
110 pluginName = ' . $pluginName . '
111 extensionName = ' . $extensionName . '
112 ' . $controller .
113 $action .
114 $switchableControllerActions . '
115
116 settings =< plugin.tx_' . strtolower($extensionName) . '.settings
117 persistence =< plugin.tx_' . strtolower($extensionName) . '.persistence
118 view =< plugin.tx_' . strtolower($extensionName) . '.view
119 _LOCAL_LANG =< plugin.tx_' . strtolower($extensionName) . '._LOCAL_LANG
120 }');
121
122 t3lib_extMgm::addTypoScript($extensionName, 'setup', '
123 # Setting ' . $extensionName . ' plugin TypoScript
124 ' . $pluginContent, 43);
125 }
126
127 /**
128 * Register an Extbase PlugIn into backend's list of plugins
129 * FOR USE IN ext_tables.php FILES
130 *
131 * @param string $extensionName The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
132 * @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!)
133 * @param string $pluginTitle is a speaking title of the plugin that will be displayed in the drop down menu in the backend
134 * @return void
135 */
136 public static function registerPlugin($extensionName, $pluginName, $pluginTitle) {
137 if (empty($pluginName)) {
138 throw new InvalidArgumentException('The plugin name must not be empty', 1239891987);
139 }
140 if (empty($extensionName)) {
141 throw new InvalidArgumentException('The extension name was invalid (must not be empty and must match /[A-Za-z][_A-Za-z0-9]/)', 1239891989);
142 }
143 $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
144 $pluginSignature = strtolower($extensionName) . '_' . strtolower($pluginName);
145
146 t3lib_extMgm::addPlugin(array($pluginTitle, $pluginSignature), 'list_type');
147 }
148
149 /**
150 * This method is called from t3lib_loadModules::checkMod and it replaces old conf.php.
151 *
152 * @param string $key The module name
153 * @param string $fullpath Absolute path to module
154 * @param array $MCONF Reference to the array holding the configuration of the module
155 * @param array $MLANG Reference to the array holding the localized module labels
156 * @return array Configuration of the module
157 */
158 public function configureModule($key, $fullpath, array $MCONF = array(), array $MLANG = array()) {
159 $path = preg_replace('/\/[^\/.]+\/\.\.\//', '/', $fullpath); // because 'path/../path' does not work
160 $config = $GLOBALS['TBE_MODULES']['_configuration'][$key]['config'];
161 define('TYPO3_MOD_PATH', $config['extRelPath']);
162
163 // Fill $MCONF
164 $MCONF['name'] = $key;
165 $MCONF['access'] = $config['access'];
166 $MCONF['script'] = '_DISPATCH';
167
168 if (substr($config['icon'], 0, 4) === 'EXT:') {
169 list($extKey, $local) = explode('/', substr($config['icon'], 4), 2);
170 $config['icon'] = t3lib_extMgm::extRelPath($extKey) . $local;
171 }
172
173 // Initialize search for alternative icon:
174 $altIconKey = 'MOD:' . $key . '/' . $config['icon']; // Alternative icon key (might have an alternative set in $TBE_STYLES['skinImg']
175 $altIconAbsPath = is_array($GLOBALS['TBE_STYLES']['skinImg'][$altIconKey]) ? t3lib_div::resolveBackPath(PATH_typo3.$GLOBALS['TBE_STYLES']['skinImg'][$altIconKey][0]) : '';
176
177 // Set icon, either default or alternative:
178 if ($altIconAbsPath && @is_file($altIconAbsPath)) {
179 $tabImage = $altIconAbsPath;
180 } else {
181 // Setting default icon:
182 $tabImage = $config['icon'];
183 }
184
185 // Fill $MLANG
186 $MLANG['default']['ll_ref'] = $config['labels'];
187
188 // Finally, set the icon with correct path:
189 if (substr($tabImage, 0 ,3) === '../') {
190 $MLANG['default']['tabs_images']['tab'] = PATH_site . substr($tabImage, 3);
191 } else {
192 $MLANG['default']['tabs_images']['tab'] = PATH_typo3 . $tabImage;
193 }
194
195 // If LOCAL_LANG references are used for labels of the module:
196 if ($MLANG['default']['ll_ref']) {
197 // Now the 'default' key is loaded with the CURRENT language - not the english translation...
198 $MLANG['default']['labels']['tablabel'] = $GLOBALS['LANG']->sL($MLANG['default']['ll_ref'] . ':mlang_labels_tablabel');
199 $MLANG['default']['labels']['tabdescr'] = $GLOBALS['LANG']->sL($MLANG['default']['ll_ref'] . ':mlang_labels_tabdescr');
200 $MLANG['default']['tabs']['tab'] = $GLOBALS['LANG']->sL($MLANG['default']['ll_ref'] . ':mlang_tabs_tab');
201 $GLOBALS['LANG']->addModuleLabels($MLANG['default'], $key . '_');
202 } else { // ... otherwise use the old way:
203 $GLOBALS['LANG']->addModuleLabels($MLANG['default'], $key . '_');
204 $GLOBALS['LANG']->addModuleLabels($MLANG[$GLOBALS['LANG']->lang], $key . '_');
205 }
206
207 // Fill $modconf
208 $modconf['script'] = 'mod.php?M=' . rawurlencode($key);
209 $modconf['name'] = $key;
210
211 // Default tab setting
212 if ($MCONF['defaultMod']) {
213 $modconf['defaultMod'] = $MCONF['defaultMod'];
214 }
215 // Navigation Frame Script (GET params could be added)
216 if ($MCONF['navFrameScript']) {
217 $navFrameScript = explode('?', $MCONF['navFrameScript']);
218 $navFrameScript = $navFrameScript[0];
219 if (file_exists($path . '/' . $navFrameScript)) {
220 $modconf['navFrameScript'] = $this->getRelativePath(PATH_typo3, $fullpath . '/' . $MCONF['navFrameScript']);
221 }
222 }
223
224 // Additional params for Navigation Frame Script: "&anyParam=value&moreParam=1"
225 if ($MCONF['navFrameScriptParam']) {
226 $modconf['navFrameScriptParam'] = $MCONF['navFrameScriptParam'];
227 }
228
229 return $modconf;
230 }
231
232 /**
233 * Registers an Extbase module (main or sub) to the backend interface.
234 * FOR USE IN ext_tables.php FILES
235 *
236 * @param string $extensionName The extension name (in UpperCamelCase) or the extension key (in lower_underscore)
237 * @param string $main The main module key, $sub is the submodule key. So $main would be an index in the $TBE_MODULES array and $sub could be an element in the lists there. If $main is not set a blank $extensionName module is created
238 * @param string $sub The submodule key. If $sub is not set a blank $main module is created
239 * @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.
240 * @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)
241 * @param array $config The configuration options of the module (icon, locallang.xml file)
242 * @return void
243 */
244 public static function registerModule($extensionName, $main = '', $sub = '', $position = '', array $controllerActions, $config = array()) {
245 if (empty($extensionName)) {
246 throw new InvalidArgumentException('The extension name was invalid (must not be empty and must match /[A-Za-z][_A-Za-z0-9]/)', 1239891989);
247 }
248 $extensionKey = $extensionName; // FIXME This will break if the $extensionName is given as BlogExample
249 $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionName)));
250
251 $path = t3lib_extMgm::extPath($extensionKey, 'Classes/');
252 $relPath = t3lib_extMgm::extRelPath($extensionKey) . 'Classes/';
253
254 if (!is_array($config) || count($config) == 0) {
255 $config['access'] = 'admin';
256 $config['icon'] = '';
257 $config['labels'] = '';
258 $config['extRelPath'] = $relPath;
259 }
260
261 if ((strlen($main) > 0) && !array_key_exists($main, $GLOBALS['TBE_MODULES'])) {
262 $main = $extensionName . self::convertLowerUnderscoreToUpperCamelCase($main);
263 } else {
264 $main = (strlen($main) > 0) ? $main : 'web'; // TODO By now, $main must default to 'web'
265 }
266
267 if ((strlen($sub) > 0)) {
268 $sub = $extensionName . self::convertLowerUnderscoreToUpperCamelCase($sub);
269 $key = $main . '_' . $sub;
270 } else {
271 $key = $main;
272 }
273
274 $moduleConfig = array(
275 'name' => $key,
276 'extensionKey' => $extensionKey,
277 'extensionName' => $extensionName,
278 'controllerActions' => $controllerActions,
279 'config' => $config,
280 );
281 $GLOBALS['TBE_MODULES']['_configuration'][$key] = $moduleConfig;
282 $GLOBALS['TBE_MODULES']['_configuration'][$key]['configureModuleFunction'] = array('Tx_Extbase_Utility_Extension', 'configureModule');
283
284 t3lib_extMgm::addModule($main, $sub, $position);
285 }
286
287 // TODO PHPdoc
288 public static function convertCamelCaseToLowerCaseUnderscored($string) {
289 // FIXME The cache doesn't work IMO as it is static (did I really implemented this? ;-))
290 static $conversionMap = array();
291 if (!isset($conversionMap[$string])) {
292 $conversionMap[$string] = strtolower(preg_replace('/(?<=\w)([A-Z])/', '_\\1', $string));
293 }
294 return $conversionMap[$string];
295 }
296
297 public static function convertUnderscoredToLowerCamelCase($string) {
298 $string = str_replace(' ', '', ucwords(str_replace('_', ' ', strtolower($string))));
299 $string[0] = strtolower($string[0]);
300 return $string;
301 }
302
303 public static function convertLowerUnderscoreToUpperCamelCase($camelCasedString) {
304 return t3lib_div::underscoredToUpperCamelCase($camelCasedString);
305 }
306
307 /**
308 * Build the autoload registry for a given extension and place it ext_autoload.php.
309 *
310 * @param string $extensionKey Key of the extension
311 * @param string $extensionPath full path of the extension
312 * @return string HTML string which should be outputted
313 */
314 public function createAutoloadRegistryForExtension($extensionKey, $extensionPath) {
315 $classNameToFileMapping = array();
316 $extensionName = str_replace(' ', '', ucwords(str_replace('_', ' ', $extensionKey)));
317 $errors = $this->buildAutoloadRegistryForSinglePath($classNameToFileMapping, $extensionPath . 'Classes/', '.*tslib.*', '$extensionClassesPath . \'|\'');
318 if ($errors) {
319 return $errors;
320 }
321 $globalPrefix = '$extensionClassesPath = t3lib_extMgm::extPath(\'' . $extensionKey . '\') . \'Classes/\';';
322
323 $errors = array();
324 foreach ($classNameToFileMapping as $className => $fileName) {
325 if (!(strpos($className, 'tx_' . strtolower($extensionName)) === 0)) {
326 $errors[] = $className . ' does not start with Tx_' . $extensionName . ' and was not added to the autoloader registry.';
327 unset($classNameToFileMapping[$className]);
328 }
329 }
330 $autoloadFileString = $this->generateAutoloadPHPFileData($classNameToFileMapping, $globalPrefix);
331 if (!@file_put_contents($extensionPath . 'ext_autoload.php', $autoloadFileString)) {
332 $errors[] = '<b>' . $extensionPath . 'ext_autoload.php could not be written!</b>';
333 }
334 $errors[] = 'Wrote the following data: <pre>' . htmlspecialchars($autoloadFileString) . '</pre>';
335 return implode('<br />', $errors);
336 }
337
338 /**
339 * Generate autoload PHP file data. Takes an associative array with class name to file mapping, and outputs it as PHP.
340 * Does NOT escape the values in the associative array. Includes the <?php ... ?> syntax and an optional global prefix.
341 *
342 * @param array $classNameToFileMapping class name to file mapping
343 * @param string $globalPrefix Global prefix which is prepended to all code.
344 * @return string The full PHP string
345 */
346 protected function generateAutoloadPHPFileData($classNameToFileMapping, $globalPrefix = '') {
347 $output = '<?php' . PHP_EOL;
348 $output .= '// DO NOT CHANGE THIS FILE! It is automatically generated by Tx_Extbase_Utility_Extension::createAutoloadRegistryForExtension.' . PHP_EOL;
349 $output .= '// This file was generated on ' . date('Y-m-d H:i') . PHP_EOL;
350 $output .= PHP_EOL;
351 $output .= $globalPrefix . PHP_EOL;
352 $output .= 'return array(' . PHP_EOL;
353 foreach ($classNameToFileMapping as $className => $quotedFileName) {
354 $output .= ' \'' . $className . '\' => ' . $quotedFileName . ',' . PHP_EOL;
355 }
356 $output .= ');' . PHP_EOL;
357 $output .= '?>';
358 return $output;
359 }
360
361 /**
362 * Generate the $classNameToFileMapping for a given filePath.
363 *
364 * @param array $classNameToFileMapping (Reference to array) All values are appended to this array.
365 * @param string $path Path which should be crawled
366 * @param string $excludeRegularExpression Exclude regular expression, to exclude certain files from being processed
367 * @param string $valueWrap Wrap for the file name
368 * @return void
369 */
370 protected function buildAutoloadRegistryForSinglePath(&$classNameToFileMapping, $path, $excludeRegularExpression = '', $valueWrap = '\'|\'') {
371 // if (file_exists($path . 'Classes/')) {
372 // return "<b>This appears to be a new-style extension which has its PHP classes inside the Classes/ subdirectory. It is not needed to generate the autoload registry for these extensions.</b>";
373 // }
374 $extensionFileNames = t3lib_div::removePrefixPathFromList(t3lib_div::getAllFilesAndFoldersInPath(array(), $path, 'php', FALSE, 99, $excludeRegularExpression), $path);
375
376 foreach ($extensionFileNames as $extensionFileName) {
377 $classNamesInFile = $this->extractClassNames($path . $extensionFileName);
378 if (!count($classNamesInFile)) continue;
379 foreach ($classNamesInFile as $className) {
380 $classNameToFileMapping[strtolower($className)] = str_replace('|', $extensionFileName, $valueWrap);
381 }
382 }
383 }
384
385 /**
386 * Extracts class names from the given file.
387 *
388 * @param string $filePath File path (absolute)
389 * @return array Class names
390 */
391 protected function extractClassNames($filePath) {
392 $fileContent = php_strip_whitespace($filePath);
393 $classNames = array();
394 if (function_exists('token_get_all')) {
395 $tokens = token_get_all($fileContent);
396 while(1) {
397 // look for "class" or "interface"
398 $token = $this->findToken($tokens, array(T_ABSTRACT, T_CLASS, T_INTERFACE));
399 // fetch "class" token if "abstract" was found
400 if ($token === 'abstract') {
401 $token = $this->findToken($tokens, array(T_CLASS));
402 }
403 if ($token === false) {
404 // end of file
405 break;
406 }
407 // look for the name (a string) skipping only whitespace and comments
408 $token = $this->findToken($tokens, array(T_STRING), array(T_WHITESPACE, T_COMMENT, T_DOC_COMMENT));
409 if ($token === false) {
410 // unexpected end of file or token: remove found names because of parse error
411 t3lib_div::sysLog('Parse error in "' . $filePath. '".', 'Core', 2);
412 $classNames = array();
413 break;
414 }
415 $token = t3lib_div::strtolower($token);
416 // exclude XLASS classes
417 if (strncmp($token, 'ux_', 3)) {
418 $classNames[] = $token;
419 }
420 }
421 } else {
422 // TODO: parse PHP - skip coments and strings, apply regexp only on the remaining PHP code
423 $matches = array();
424 preg_match_all('/^[ \t]*(?:(?:abstract|final)?[ \t]*(?:class|interface))[ \t\n\r]+([a-zA-Z][a-zA-Z_0-9]*)/mS', $fileContent, $matches);
425 $classNames = array_map('t3lib_div::strtolower', $matches[1]);
426 }
427 return $classNames;
428 }
429
430 /**
431 * Find tokens in the tokenList
432 *
433 * @param array $tokenList list of tokens as returned by token_get_all()
434 * @param array $wantedToken the tokens to be found
435 * @param array $intermediateTokens optional: list of tokens that are allowed to skip when looking for the wanted token
436 * @return mixed
437 */
438 protected function findToken(array &$tokenList, array $wantedTokens, array $intermediateTokens = array()) {
439 $skipAllTokens = count($intermediateTokens) ? false : true;
440
441 $returnValue = false;
442 // Iterate with while since we need the current array position:
443 foreach ($tokenList as $token) {
444 // parse token (see http://www.php.net/manual/en/function.token-get-all.php for format of token list)
445 if (is_array($token)) {
446 list($id, $text) = $token;
447 } else {
448 $id = $text = $token;
449 }
450 if (in_array($id, $wantedTokens)) {
451 $returnValue = $text;
452 break;
453 }
454 // look for another token
455 if ($skipAllTokens || in_array($id, $intermediateTokens)) {
456 continue;
457 }
458 break;
459 }
460 return $returnValue;
461 }
462
463 /**
464 * Determines the plugin namespace of the specified plugin (defaults to "tx_[extensionName]_[pluginName]")
465 * If plugin.tx_$pluginSignature.view.pluginNamespace is set, this value is returned
466 * If pluginNamespace is not specified "tx_[extensionName]_[pluginName]" is returned.
467 *
468 * @param string $pluginSignature Plugin signature: strtolower($extensionName) . '_' . strtolower($pluginName)
469 * @return string plugin namespace
470 */
471 public static function getPluginNamespaceByPluginSignature($pluginSignature) {
472 $defaultPluginNamespace = 'tx_' . $pluginSignature;
473 $configurationManager = Tx_Extbase_Dispatcher::getConfigurationManager();
474 if (!isset($configurationManager) || !isset($GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.']) || !is_array($GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.'])) {
475 return $defaultPluginNamespace;
476 }
477 $pluginConfiguration = $GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.'][$pluginSignature . '.'];
478 $frameworkConfiguration = $configurationManager->getFrameworkConfiguration($pluginConfiguration);
479 if (!isset($frameworkConfiguration['view']['pluginNamespace']) || empty($frameworkConfiguration['view']['pluginNamespace'])) {
480 return $defaultPluginNamespace;
481 }
482 return $frameworkConfiguration['view']['pluginNamespace'];
483 }
484
485 /**
486 * Iterates through the global TypoScript configuration and returns the name of the plugin
487 * that matches specified extensionName, controllerName and actionName.
488 * If no matching plugin was found, NULL is returned.
489 * If more than one plugin matches, an Exception will be thrown
490 *
491 * @param string $extensionName name of the target extension (UpperCamelCase)
492 * @param string $controllerName name of the target controller (UpperCamelCase)
493 * @param string $actionName name of the target action (lowerCamelCase)
494 * @return string name of the target plugin (UpperCamelCase) or NULL if no matching plugin configuration was found
495 */
496 public static function getPluginNameByAction($extensionName, $controllerName, $actionName) {
497 if (!isset($GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.']) || !is_array($GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.'])) {
498 return NULL;
499 }
500 $pluginNames = array();
501 foreach($GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.'] as $pluginConfiguration) {
502 if (!is_array($pluginConfiguration) || !isset($pluginConfiguration['switchableControllerActions.']) || !isset($pluginConfiguration['extensionName'])) {
503 continue;
504 }
505 if (strtolower($extensionName) !== strtolower($pluginConfiguration['extensionName'])) {
506 continue;
507 }
508 foreach($pluginConfiguration['switchableControllerActions.'] as $controller => $switchableControllerActions) {
509 if (strtolower(rtrim($controller, '.')) !== strtolower($controllerName)) {
510 continue;
511 }
512 $actions = t3lib_div::trimExplode(',', $switchableControllerActions['actions']);
513 if (in_array($actionName, $actions)) {
514 $pluginNames[] = $pluginConfiguration['pluginName'];
515 }
516 }
517 }
518 if (count($pluginNames) > 1) {
519 throw new Tx_Extbase_Exception('There is more than one plugin that can handle this request (Extension: "' . $extensionName . '", Controller: "' . $controllerName . '", action: "' . $actionName . '"). Please specify "pluginName" argument' , 1280825466);
520 }
521 return count($pluginNames) > 0 ? $pluginNames[0] : NULL;
522 }
523
524 /**
525 * Determines the target page of the specified plugin.
526 * If plugin.tx_$pluginSignature.view.defaultPid is set, this value is used as target page id
527 * If defaultPid is set to "auto", a the target pid is determined by loading the tt_content record that contains this plugin
528 * If the page could not be determined, NULL is returned
529 * If defaultPid is "auto" and more than one page contains the specified plugin, an Exception is thrown
530 *
531 * @param string $pluginSignature Plugin signature: strtolower($extensionName) . '_' . strtolower($pluginName)
532 * @return integer uid of the target page or NULL if target page could not be determined
533 */
534 public static function getTargetPidByPluginSignature($pluginSignature) {
535 $configurationManager = Tx_Extbase_Dispatcher::getConfigurationManager();
536 if (!isset($configurationManager) || !isset($GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.']) || !is_array($GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.'])) {
537 return NULL;
538 }
539 $pluginConfiguration = $GLOBALS['TSFE']->tmpl->setup['tt_content.']['list.']['20.'][$pluginSignature . '.'];
540 $frameworkConfiguration = $configurationManager->getFrameworkConfiguration($pluginConfiguration);
541 if (!isset($frameworkConfiguration['view']['defaultPid']) || empty($frameworkConfiguration['view']['defaultPid'])) {
542 return NULL;
543 }
544 if ($frameworkConfiguration['view']['defaultPid'] === 'auto') {
545 $pages = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
546 'pid',
547 'tt_content',
548 'list_type=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($pluginSignature, 'tt_content') . $GLOBALS['TSFE']->sys_page->enableFields('tt_content'),
549 '',
550 '',
551 2
552 );
553 if (count($pages) > 1) {
554 throw new Tx_Extbase_Exception('There is more than one "' . $pluginSignature . '" plugin in the current page tree. Please remove one plugin or set the TypoScript configuration "plugin.' . $pluginSignature . '.view.defaultPid" to a fixed page id' , 1280773643);
555 }
556 return count($pages) > 0 ? $pages[0]['pid'] : NULL;
557 }
558 return (integer)$frameworkConfiguration['view']['defaultPid'];
559 }
560 }
561
562 ?>