[BUGFIX] Some methods in t3lib_TSparser should be static
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_loadmodules.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
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 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * This document provides a class that loads the modules for the TYPO3 interface.
29 *
30 * Modifications by René Fritz, 2001
31 * Revised for TYPO3 3.6 July/2003 by Kasper Skårhøj
32 *
33 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
34 * @internal
35 */
36
37
38 /**
39 * Load Backend Interface modules
40 *
41 * Typically instantiated like this:
42 * $this->loadModules = t3lib_div::makeInstance('t3lib_loadModules');
43 * $this->loadModules->load($TBE_MODULES);
44 *
45 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
46 * @package TYPO3
47 * @subpackage t3lib
48 */
49 class t3lib_loadModules {
50 var $modules = array(); // After the init() function this array will contain the structure of available modules for the backend user.
51 var $absPathArray = array(); // Array with paths pointing to the location of modules from extensions
52
53 var $modListGroup = array(); // this array will hold the elements that should go into the select-list of modules for groups...
54 var $modListUser = array(); // this array will hold the elements that should go into the select-list of modules for users...
55
56 /**
57 * The backend user for use internally
58 *
59 * @var t3lib_beUserAuth
60 */
61 var $BE_USER;
62 var $observeWorkspaces = FALSE; // If set TRUE, workspace "permissions" will be observed so non-allowed modules will not be included in the array of modules.
63
64 /**
65 * Contains the registered navigation components
66 *
67 * @var array
68 */
69 protected $navigationComponents = array();
70
71 /**
72 * Init.
73 * The outcome of the load() function will be a $this->modules array populated with the backend module structure available to the BE_USER
74 * Further the global var $LANG will have labels and images for the modules loaded in an internal array.
75 *
76 * @param array $modulesArray should be the global var $TBE_MODULES, $BE_USER can optionally be set to an alternative Backend user object than the global var $BE_USER (which is the currently logged in user)
77 * @param object Optional backend user object to use. If not set, the global BE_USER object is used.
78 * @return void
79 */
80 function load($modulesArray, $BE_USER = '') {
81 // Setting the backend user for use internally
82 if (is_object($BE_USER)) {
83 $this->BE_USER = $BE_USER;
84 } else {
85 $this->BE_USER = $GLOBALS['BE_USER'];
86 }
87
88 /*
89 $modulesArray might look like this when entering this function.
90 Notice the two modules added by extensions - they have a path attached
91
92 Array
93 (
94 [web] => list,info,perm,func
95 [file] => list
96 [user] =>
97 [tools] => em,install,txphpmyadmin
98 [help] => about
99 [_PATHS] => Array
100 (
101 [tools_install] => /www/htdocs/typo3/32/coreinstall/typo3/ext/install/mod/
102 [tools_txphpmyadmin] => /www/htdocs/typo3/32/coreinstall/typo3/ext/phpmyadmin/modsub/
103 )
104
105 )
106 */
107
108 // Collect required module meta information
109 $this->absPathArray = $modulesArray['_PATHS'];
110 $this->navigationComponents = $modulesArray['_navigationComponents'];
111 $theMods = $this->parseModulesArray($modulesArray);
112
113 /*
114 Originally modules were found in typo3/mod/
115 User defined modules were found in ../typo3conf/
116
117 Today almost all modules reside in extensions and they are found by the _PATHS array of the incoming $TBE_MODULES array
118 */
119 // Setting paths for 1) core modules (old concept from mod/) and 2) user-defined modules (from ../typo3conf)
120 $paths = array();
121 $paths['defMods'] = PATH_typo3 . 'mod/'; // Path of static modules
122 $paths['userMods'] = PATH_typo3 . '../typo3conf/'; // local modules (maybe frontend specific)
123
124 // Traverses the module setup and creates the internal array $this->modules
125 foreach ($theMods as $mods => $subMod) {
126 $path = NULL;
127
128 $extModRelPath = $this->checkExtensionModule($mods);
129 if ($extModRelPath) { // EXTENSION module:
130 $theMainMod = $this->checkMod($mods, PATH_site . $extModRelPath);
131 if (is_array($theMainMod) || $theMainMod != 'notFound') {
132 $path = 1; // ... just so it goes on... submodules cannot be within this path!
133 }
134 } else { // 'CLASSIC' module
135 // Checking for typo3/mod/ module existence...
136 $theMainMod = $this->checkMod($mods, $paths['defMods'] . $mods);
137 if (is_array($theMainMod) || $theMainMod != 'notFound') {
138 $path = $paths['defMods'];
139 } else {
140 // If not typo3/mod/ then it could be user-defined in typo3conf/ ...?
141 $theMainMod = $this->checkMod($mods, $paths['userMods'] . $mods);
142 if (is_array($theMainMod) || $theMainMod != 'notFound') {
143 $path = $paths['userMods'];
144 }
145 }
146 }
147
148 // if $theMainMod is not set (FALSE) there is no access to the module !(?)
149 if ($theMainMod && !is_null($path)) {
150 $this->modules[$mods] = $theMainMod;
151
152 // SUBMODULES - if any - are loaded
153 if (is_array($subMod)) {
154 foreach ($subMod as $valsub) {
155 $extModRelPath = $this->checkExtensionModule($mods . '_' . $valsub);
156 if ($extModRelPath) { // EXTENSION submodule:
157 $theTempSubMod = $this->checkMod($mods . '_' . $valsub, PATH_site . $extModRelPath);
158 if (is_array($theTempSubMod)) { // default sub-module in either main-module-path, be it the default or the userdefined.
159 $this->modules[$mods]['sub'][$valsub] = $theTempSubMod;
160 }
161 } else { // 'CLASSIC' submodule
162 // Checking for typo3/mod/xxx/ module existence...
163 // FIXME what about $path = 1; from above and using $path as string here?
164 $theTempSubMod = $this->checkMod($mods . '_' . $valsub, $path . $mods . '/' . $valsub);
165 if (is_array($theTempSubMod)) { // default sub-module in either main-module-path, be it the default or the userdefined.
166 $this->modules[$mods]['sub'][$valsub] = $theTempSubMod;
167 } elseif ($path == $paths['defMods']) { // If the submodule did not exist in the default module path, then check if there is a submodule in the submodule path!
168 $theTempSubMod = $this->checkMod($mods . '_' . $valsub, $paths['userMods'] . $mods . '/' . $valsub);
169 if (is_array($theTempSubMod)) {
170 $this->modules[$mods]['sub'][$valsub] = $theTempSubMod;
171 }
172 }
173 }
174 }
175 }
176 } else { // This must be done in order to fill out the select-lists for modules correctly!!
177 if (is_array($subMod)) {
178 foreach ($subMod as $valsub) {
179 // FIXME path can only be NULL here, or not?
180 $this->checkMod($mods . '_' . $valsub, $path . $mods . '/' . $valsub);
181 }
182 }
183 }
184 }
185 }
186
187 /**
188 * If the module name ($name) is a module from an extension (has path in $this->absPathArray) then that path is returned relative to PATH_site
189 *
190 * @param string Module name
191 * @return string If found, the relative path from PATH_site
192 */
193 function checkExtensionModule($name) {
194 if (isset($this->absPathArray[$name])) {
195 return rtrim(substr($this->absPathArray[$name], strlen(PATH_site)), '/');
196 }
197 }
198
199 /**
200 * Here we check for the module.
201 * Return values:
202 * 'notFound': If the module was not found in the path (no "conf.php" file)
203 * FALSE: If no access to the module (access check failed)
204 * array(): Configuration array, in case a valid module where access IS granted exists.
205 *
206 * @param string Module name
207 * @param string Absolute path to module
208 * @return mixed See description of function
209 */
210 function checkMod($name, $fullpath) {
211 if ($name == 'user_ws' && !t3lib_extMgm::isLoaded('version')) {
212 return FALSE;
213 }
214
215 // Check for own way of configuring module
216 if (is_array($GLOBALS['TBE_MODULES']['_configuration'][$name]['configureModuleFunction'])) {
217 $obj = $GLOBALS['TBE_MODULES']['_configuration'][$name]['configureModuleFunction'];
218 if (is_callable($obj)) {
219 $MCONF = call_user_func($obj, $name, $fullpath);
220 if ($this->checkModAccess($name, $MCONF) !== TRUE) {
221 return FALSE;
222 }
223 return $MCONF;
224 }
225 }
226
227 // check if this is a submodule
228 if (strpos($name, '_') !== FALSE) {
229 list($mainModule, ) = explode('_', $name, 2);
230 }
231
232 $modconf = array();
233 $path = preg_replace('/\/[^\/.]+\/\.\.\//', '/', $fullpath); // because 'path/../path' does not work
234 if (@is_dir($path) && file_exists($path . '/conf.php')) {
235 $MCONF = array();
236 $MLANG = array();
237 include($path . '/conf.php'); // The conf-file is included. This must be valid PHP.
238 if (!$MCONF['shy'] && $this->checkModAccess($name, $MCONF) && $this->checkModWorkspace($name, $MCONF)) {
239 $modconf['name'] = $name;
240 // language processing. This will add module labels and image reference to the internal ->moduleLabels array of the LANG object.
241 if (is_object($GLOBALS['LANG'])) {
242 // $MLANG['default']['tabs_images']['tab'] is for modules the reference to the module icon.
243 // Here the path is transformed to an absolute reference.
244 if ($MLANG['default']['tabs_images']['tab']) {
245
246 // Initializing search for alternative icon:
247 $altIconKey = 'MOD:' . $name . '/' . $MLANG['default']['tabs_images']['tab']; // Alternative icon key (might have an alternative set in $TBE_STYLES['skinImg']
248 $altIconAbsPath = is_array($GLOBALS['TBE_STYLES']['skinImg'][$altIconKey]) ? t3lib_div::resolveBackPath(PATH_typo3 . $GLOBALS['TBE_STYLES']['skinImg'][$altIconKey][0]) : '';
249
250 // Setting icon, either default or alternative:
251 if ($altIconAbsPath && @is_file($altIconAbsPath)) {
252 $MLANG['default']['tabs_images']['tab'] = $this->getRelativePath(PATH_typo3, $altIconAbsPath);
253 } else {
254 // Setting default icon:
255 $MLANG['default']['tabs_images']['tab'] = $this->getRelativePath(PATH_typo3, $fullpath . '/' . $MLANG['default']['tabs_images']['tab']);
256 }
257
258 // Finally, setting the icon with correct path:
259 if (substr($MLANG['default']['tabs_images']['tab'], 0, 3) == '../') {
260 $MLANG['default']['tabs_images']['tab'] = PATH_site . substr($MLANG['default']['tabs_images']['tab'], 3);
261 } else {
262 $MLANG['default']['tabs_images']['tab'] = PATH_typo3 . $MLANG['default']['tabs_images']['tab'];
263 }
264 }
265
266 // If LOCAL_LANG references are used for labels of the module:
267 if ($MLANG['default']['ll_ref']) {
268 // Now the 'default' key is loaded with the CURRENT language - not the english translation...
269 $MLANG['default']['labels']['tablabel'] = $GLOBALS['LANG']->sL($MLANG['default']['ll_ref'] . ':mlang_labels_tablabel');
270 $MLANG['default']['labels']['tabdescr'] = $GLOBALS['LANG']->sL($MLANG['default']['ll_ref'] . ':mlang_labels_tabdescr');
271 $MLANG['default']['tabs']['tab'] = $GLOBALS['LANG']->sL($MLANG['default']['ll_ref'] . ':mlang_tabs_tab');
272 $GLOBALS['LANG']->addModuleLabels($MLANG['default'], $name . '_');
273 } else { // ... otherwise use the old way:
274 $GLOBALS['LANG']->addModuleLabels($MLANG['default'], $name . '_');
275 $GLOBALS['LANG']->addModuleLabels($MLANG[$GLOBALS['LANG']->lang], $name . '_');
276 }
277 }
278
279 // Default script setup
280 if ($MCONF['script'] === '_DISPATCH') {
281 if ($MCONF['extbase']) {
282 $modconf['script'] = 'mod.php?M=Tx_' . rawurlencode($name);
283 } else {
284 $modconf['script'] = 'mod.php?M=' . rawurlencode($name);
285 }
286 } elseif ($MCONF['script'] && file_exists($path . '/' . $MCONF['script'])) {
287 $modconf['script'] = $this->getRelativePath(PATH_typo3, $fullpath . '/' . $MCONF['script']);
288 } else {
289 $modconf['script'] = 'dummy.php';
290 }
291 // Default tab setting
292 if ($MCONF['defaultMod']) {
293 $modconf['defaultMod'] = $MCONF['defaultMod'];
294 }
295 // Navigation Frame Script (GET params could be added)
296 if ($MCONF['navFrameScript']) {
297 $navFrameScript = explode('?', $MCONF['navFrameScript']);
298 $navFrameScript = $navFrameScript[0];
299 if (file_exists($path . '/' . $navFrameScript)) {
300 $modconf['navFrameScript'] = $this->getRelativePath(PATH_typo3, $fullpath . '/' . $MCONF['navFrameScript']);
301 }
302 }
303 // additional params for Navigation Frame Script: "&anyParam=value&moreParam=1"
304 if ($MCONF['navFrameScriptParam']) {
305 $modconf['navFrameScriptParam'] = $MCONF['navFrameScriptParam'];
306 }
307
308 // check if there is a navigation component (like the pagetree)
309 if (is_array($this->navigationComponents[$name])) {
310 $modconf['navigationComponentId'] = $this->navigationComponents[$name]['componentId'];
311 // check if the parent has a navigation component that also
312 // goes down to the submodules (if they haven't overwritten it yet)
313 } elseif ($mainModule && is_array($this->navigationComponents[$mainModule])) {
314 $modconf['navigationComponentId'] = $this->navigationComponents[$mainModule]['componentId'];
315 }
316 } else {
317 return FALSE;
318 }
319 } else {
320 $modconf = 'notFound';
321 }
322 return $modconf;
323 }
324
325 /**
326 * Returns TRUE if the internal BE_USER has access to the module $name with $MCONF (based on security level set for that module)
327 *
328 * @param string Module name
329 * @param array MCONF array (module configuration array) from the modules conf.php file (contains settings about what access level the module has)
330 * @return boolean TRUE if access is granted for $this->BE_USER
331 */
332 function checkModAccess($name, $MCONF) {
333 if ($MCONF['access']) {
334 $access = strtolower($MCONF['access']);
335 // Checking if admin-access is required
336 if (strstr($access, 'admin')) { // If admin-permissions is required then return TRUE if user is admin
337 if ($this->BE_USER->isAdmin()) {
338 return TRUE;
339 }
340 }
341 // This will add modules to the select-lists of user and groups
342 if (strstr($access, 'user')) {
343 $this->modListUser[] = $name;
344 }
345 if (strstr($access, 'group')) {
346 $this->modListGroup[] = $name;
347 }
348 // This checks if a user is permitted to access the module
349 if ($this->BE_USER->isAdmin() || $this->BE_USER->check('modules', $name)) {
350 return TRUE;
351 } // If admin you can always access a module
352
353 } else {
354 return TRUE;
355 } // If conf[access] is not set, then permission IS granted!
356 }
357
358 /**
359 * Check if a module is allowed inside the current workspace for be user
360 * Processing happens only if $this->observeWorkspaces is TRUE
361 *
362 * @param string Module name
363 * @param array MCONF array (module configuration array) from the modules conf.php file (contains settings about workspace restrictions)
364 * @return boolean TRUE if access is granted for $this->BE_USER
365 */
366 function checkModWorkspace($name, $MCONF) {
367 if ($this->observeWorkspaces) {
368 $status = TRUE;
369 if ($MCONF['workspaces']) {
370 $status = FALSE;
371 if (($this->BE_USER->workspace === 0 && t3lib_div::inList($MCONF['workspaces'], 'online')) ||
372 ($this->BE_USER->workspace === -1 && t3lib_div::inList($MCONF['workspaces'], 'offline')) ||
373 ($this->BE_USER->workspace > 0 && t3lib_div::inList($MCONF['workspaces'], 'custom'))) {
374 $status = TRUE;
375 }
376 } elseif ($this->BE_USER->workspace === -99) {
377 $status = FALSE;
378 }
379 return $status;
380 } else {
381 return TRUE;
382 }
383 }
384
385 /**
386 * Parses the moduleArray ($TBE_MODULES) into a internally useful structure.
387 * Returns an array where the keys are names of the module and the values may be TRUE (only module) or an array (of submodules)
388 *
389 * @param array moduleArray ($TBE_MODULES)
390 * @return array Output structure with available modules
391 */
392 function parseModulesArray($arr) {
393 $theMods = array();
394 if (is_array($arr)) {
395 foreach ($arr as $mod => $subs) {
396 // Module names must not start with an underline character
397 if (strpos($mod, '_') === 0) {
398 continue;
399 }
400 // Module names must be alpha-numeric
401 $mod = $this->cleanName($mod);
402 if ($mod) {
403 if ($subs) {
404 $subsArr = t3lib_div::trimExplode(',', $subs);
405 foreach ($subsArr as $subMod) {
406 $subMod = $this->cleanName($subMod);
407 if ($subMod) {
408 $theMods[$mod][] = $subMod;
409 }
410 }
411 } else {
412 $theMods[$mod] = 1;
413 }
414 }
415 }
416 }
417 return $theMods;
418 }
419
420 /**
421 * The $str is cleaned so that it contains alphanumerical characters only. Modules must only consist of these characters
422 *
423 * @param string String to clean up
424 * @return string
425 */
426 function cleanName($str) {
427 return preg_replace('/[^a-z0-9]/i', '', $str);
428 }
429
430 /**
431 * Get relative path for $destDir compared to $baseDir
432 *
433 * @param string Base directory
434 * @param string Destination directory
435 * @return string The relative path of destination compared to base.
436 */
437 function getRelativePath($baseDir, $destDir) {
438 // By René Fritz
439 // a special case , the dirs are equals
440 if ($baseDir == $destDir) {
441 return './';
442 }
443
444 $baseDir = ltrim($baseDir, '/'); // remove beginning
445 $destDir = ltrim($destDir, '/');
446
447 $found = TRUE;
448 $slash_pos = 0;
449
450 do {
451 $slash_pos = strpos($destDir, '/');
452 if (substr($destDir, 0, $slash_pos) == substr($baseDir, 0, $slash_pos)) {
453 $baseDir = substr($baseDir, $slash_pos + 1);
454 $destDir = substr($destDir, $slash_pos + 1);
455 } else {
456 $found = FALSE;
457 }
458 } while ($found == TRUE);
459
460 $slashes = strlen($baseDir) - strlen(str_replace('/', '', $baseDir));
461 for ($i = 0; $i < $slashes; $i++) {
462 $destDir = '../' . $destDir;
463 }
464 return t3lib_div::resolveBackPath($destDir);
465 }
466 }
467
468
469 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_loadmodules.php'])) {
470 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_loadmodules.php']);
471 }
472
473 ?>