* Merging all changes from TYPO3_4-0 branch back into HEAD
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_loadmodules.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2006 Kasper Skaarhoj (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 * $Id$
31 * Modifications by Rene Fritz, 2001
32 * Revised for TYPO3 3.6 July/2003 by Kasper Skaarhoj
33 *
34 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
35 * @internal
36 */
37 /**
38 * [CLASS/FUNCTION INDEX of SCRIPT]
39 *
40 *
41 *
42 * 79: class t3lib_loadModules
43 * 99: function load($modulesArray,$BE_USER='')
44 * 370: function checkExtensionModule($name)
45 * 389: function checkMod($name, $fullpath)
46 * 471: function checkModAccess($name,$MCONF)
47 * 495: function checkModWorkspace($name,$MCONF)
48 * 519: function parseModulesArray($arr)
49 * 548: function cleanName ($str)
50 * 559: function getRelativePath($baseDir,$destDir)
51 *
52 * TOTAL FUNCTIONS: 8
53 * (This index is automatically created/updated by the extension "extdeveval")
54 *
55 */
56
57
58
59
60
61
62
63
64
65
66
67
68 /**
69 * Load Backend Interface modules
70 *
71 * Typically instantiated like this:
72 * $this->loadModules = t3lib_div::makeInstance('t3lib_loadModules');
73 * $this->loadModules->load($TBE_MODULES);
74 *
75 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
76 * @package TYPO3
77 * @subpackage t3lib
78 */
79 class t3lib_loadModules {
80 var $modules = Array(); // After the init() function this array will contain the structure of available modules for the backend user.
81 var $absPathArray = array(); // Array with paths pointing to the location of modules from extensions
82
83 var $modListGroup = Array(); // this array will hold the elements that should go into the select-list of modules for groups...
84 var $modListUser = Array(); // this array will hold the elements that should go into the select-list of modules for users...
85
86 var $BE_USER = ''; // The backend user for use internally
87 var $observeWorkspaces = FALSE; // If set true, workspace "permissions" will be observed so non-allowed modules will not be included in the array of modules.
88
89
90 /**
91 * Init.
92 * The outcome of the load() function will be a $this->modules array populated with the backend module structure available to the BE_USER
93 * Further the global var $LANG will have labels and images for the modules loaded in an internal array.
94 *
95 * @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)
96 * @param object Optional backend user object to use. If not set, the global BE_USER object is used.
97 * @return void
98 */
99 function load($modulesArray,$BE_USER='') {
100 // Setting the backend user for use internally
101 if (is_object($BE_USER)) {
102 $this->BE_USER = $BE_USER;
103 } else {
104 $this->BE_USER = $GLOBALS['BE_USER'];
105 }
106
107 /*
108
109 $modulesArray might look like this when entering this function.
110 Notice the two modules added by extensions - they have a path attached
111
112 Array
113 (
114 [web] => list,info,perm,func
115 [file] => list
116 [doc] =>
117 [user] =>
118 [tools] => em,install,txphpmyadmin
119 [help] => about
120 [_PATHS] => Array
121 (
122 [tools_install] => /www/htdocs/typo3/32/coreinstall/typo3/ext/install/mod/
123 [tools_txphpmyadmin] => /www/htdocs/typo3/32/coreinstall/typo3/ext/phpmyadmin/modsub/
124 )
125
126 )
127
128 */
129 //
130 $this->absPathArray = $modulesArray['_PATHS'];
131 unset($modulesArray['_PATHS']);
132
133 /*
134 With the above data for modules the result of this function call will be:
135
136 Array
137 (
138 [web] => Array
139 (
140 [0] => list
141 [1] => info
142 [2] => perm
143 [3] => func
144 )
145
146 [file] => Array
147 (
148 [0] => list
149 )
150
151 [doc] => 1
152 [user] => 1
153 [tools] => Array
154 (
155 [0] => em
156 [1] => install
157 [2] => txphpmyadmin
158 )
159
160 [help] => Array
161 (
162 [0] => about
163 )
164
165 )
166 */
167 $theMods = $this->parseModulesArray($modulesArray);
168
169 /*
170 Originally modules were found in typo3/mod/
171 User defined modules were found in ../typo3conf/
172
173 Today almost all modules reside in extensions and they are found by the _PATHS array of the incoming $TBE_MODULES array
174 */
175 // Setting paths for 1) core modules (old concept from mod/) and 2) user-defined modules (from ../typo3conf)
176 $paths = array();
177 $paths['defMods'] = PATH_typo3.'mod/'; // Path of static modules
178 $paths['userMods'] = PATH_typo3.'../typo3conf/'; // local modules (maybe frontend specific)
179
180 // Traverses the module setup and creates the internal array $this->modules
181 foreach($theMods as $mods => $subMod) {
182 $path = NULL;
183
184 $extModRelPath = $this->checkExtensionModule($mods);
185 if ($extModRelPath) { // EXTENSION module:
186 $theMainMod = $this->checkMod($mods,PATH_site.$extModRelPath);
187 if (is_array($theMainMod) || $theMainMod!='notFound') {
188 $path = 1; // ... just so it goes on... submodules cannot be within this path!
189 }
190 } else { // 'CLASSIC' module
191 // Checking for typo3/mod/ module existence...
192 $theMainMod = $this->checkMod($mods,$paths['defMods'].$mods);
193 if (is_array($theMainMod) || $theMainMod!='notFound') {
194 $path = $paths['defMods'];
195 } else {
196 // If not typo3/mod/ then it could be user-defined in typo3conf/ ...?
197 $theMainMod = $this->checkMod($mods,$paths['userMods'].$mods);
198 if (is_array($theMainMod) || $theMainMod!='notFound') {
199 $path = $paths['userMods'];
200 }
201 }
202 }
203
204 // if $theMainMod is not set (false) there is no access to the module !(?)
205 if ($theMainMod && !is_null($path)) {
206 $this->modules[$mods] = $theMainMod;
207
208 // SUBMODULES - if any - are loaded (The 'doc' module cannot have submodules...)
209 if ($mods!='doc' && is_array($subMod)) {
210 foreach($subMod as $valsub) {
211 $extModRelPath = $this->checkExtensionModule($mods.'_'.$valsub);
212 if ($extModRelPath) { // EXTENSION submodule:
213 $theTempSubMod = $this->checkMod($mods.'_'.$valsub,PATH_site.$extModRelPath);
214 if (is_array($theTempSubMod)) { // default sub-module in either main-module-path, be it the default or the userdefined.
215 $this->modules[$mods]['sub'][$valsub] = $theTempSubMod;
216 }
217 } else { // 'CLASSIC' submodule
218 // Checking for typo3/mod/xxx/ module existence...
219 // FIXME what about $path = 1; from above and using $path as string here?
220 $theTempSubMod = $this->checkMod($mods.'_'.$valsub,$path.$mods.'/'.$valsub);
221 if (is_array($theTempSubMod)) { // default sub-module in either main-module-path, be it the default or the userdefined.
222 $this->modules[$mods]['sub'][$valsub] = $theTempSubMod;
223 } 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!
224 $theTempSubMod = $this->checkMod($mods.'_'.$valsub,$paths['userMods'].$mods.'/'.$valsub);
225 if (is_array($theTempSubMod)) {
226 $this->modules[$mods]['sub'][$valsub] = $theTempSubMod;
227 }
228 }
229 }
230 }
231 }
232 } else { // This must be done in order to fill out the select-lists for modules correctly!!
233 if ($mods!='doc' && is_array($subMod)) {
234 foreach($subMod as $valsub) {
235 // FIXME path can only be NULL here, or not?
236 $this->checkMod($mods.'_'.$valsub,$path.$mods.'/'.$valsub);
237 }
238 }
239 }
240 }
241 /*
242 At this point $this->modules should look like this:
243 Only modules which were accessible to the $BE_USER is listed in this array.
244
245 Array
246 (
247 [web] => Array
248 (
249 [name] => web
250 [script] => dummy.php
251 [defaultMod] => list
252 [sub] => Array
253 (
254 [list] => Array
255 (
256 [name] => web_list
257 [script] => mod/web/list/../../../db_list.php
258 )
259
260 [info] => Array
261 (
262 [name] => web_info
263 [script] => mod/web/info/index.php
264 )
265
266 [perm] => Array
267 (
268 [name] => web_perm
269 [script] => mod/web/perm/index.php
270 )
271
272 [func] => Array
273 (
274 [name] => web_func
275 [script] => mod/web/func/index.php
276 )
277
278 )
279
280 )
281
282 [file] => Array
283 (
284 [name] => file
285 [script] => dummy.php
286 [sub] => Array
287 (
288 [list] => Array
289 (
290 [name] => file_list
291 [script] => mod/file/list/../../../file_list.php
292 )
293
294 )
295
296 )
297
298 [doc] => Array
299 (
300 [name] => doc
301 [script] => mod/doc/../../alt_doc.php
302 )
303
304 [user] => Array
305 (
306 [name] => user
307 [script] => dummy.php
308 [defaultMod] => task
309 )
310
311 [tools] => Array
312 (
313 [name] => tools
314 [script] => dummy.php
315 [sub] => Array
316 (
317 [em] => Array
318 (
319 [name] => tools_em
320 [script] => mod/tools/em/index.php
321 )
322
323 [install] => Array
324 (
325 [name] => tools_install
326 [script] => ext/install/mod/../../../install/index.php
327 )
328
329 [txphpmyadmin] => Array
330 (
331 [name] => tools_txphpmyadmin
332 [script] => ext/phpmyadmin/modsub/index.php
333 )
334
335 )
336
337 )
338
339 [help] => Array
340 (
341 [name] => help
342 [script] => dummy.php
343 [defaultMod] => welcome
344 [sub] => Array
345 (
346 [about] => Array
347 (
348 [name] => help_about
349 [script] => mod/help/about/index.php
350 )
351
352 )
353
354 )
355
356 )
357
358 */
359
360 #debug($this->modules);
361 #debug($GLOBALS['LANG']->moduleLabels);
362 }
363
364 /**
365 * 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
366 *
367 * @param string Module name
368 * @return string If found, the relative path from PATH_site
369 */
370 function checkExtensionModule($name) {
371 global $TYPO3_LOADED_EXT;
372
373 if (isset($this->absPathArray[$name])) {
374 return ereg_replace ('\/$', '', substr($this->absPathArray[$name],strlen(PATH_site)));
375 }
376 }
377
378 /**
379 * Here we check for the module.
380 * Return values:
381 * 'notFound': If the module was not found in the path (no "conf.php" file)
382 * false: If no access to the module (access check failed)
383 * array(): Configuration array, in case a valid module where access IS granted exists.
384 *
385 * @param string Module name
386 * @param string Absolute path to module
387 * @return mixed See description of function
388 */
389 function checkMod($name, $fullpath) {
390 $modconf=Array();
391 $path = ereg_replace ('/[^/.]+/\.\./', '/', $fullpath); // because 'path/../path' does not work
392 if (@is_dir($path) && @file_exists($path.'/conf.php')) {
393 $MCONF = array();
394 $MLANG = array();
395 include($path.'/conf.php'); // The conf-file is included. This must be valid PHP.
396 if (!$MCONF['shy'] && $this->checkModAccess($name,$MCONF) && $this->checkModWorkspace($name,$MCONF)) {
397 $modconf['name']=$name;
398 // language processing. This will add module labels and image reference to the internal ->moduleLabels array of the LANG object.
399 if (is_object($GLOBALS['LANG'])) {
400 // $MLANG['default']['tabs_images']['tab'] is for modules the reference to the module icon.
401 // Here the path is transformed to an absolute reference.
402 if ($MLANG['default']['tabs_images']['tab']) {
403
404 // Initializing search for alternative icon:
405 $altIconKey = 'MOD:'.$name.'/'.$MLANG['default']['tabs_images']['tab']; // Alternative icon key (might have an alternative set in $TBE_STYLES['skinImg']
406 $altIconAbsPath = is_array($GLOBALS['TBE_STYLES']['skinImg'][$altIconKey]) ? t3lib_div::resolveBackPath(PATH_typo3.$GLOBALS['TBE_STYLES']['skinImg'][$altIconKey][0]) : '';
407
408 // Setting icon, either default or alternative:
409 if ($altIconAbsPath && @is_file($altIconAbsPath)) {
410 $MLANG['default']['tabs_images']['tab']=$this->getRelativePath(PATH_typo3,$altIconAbsPath);
411 } else {
412 // Setting default icon:
413 $MLANG['default']['tabs_images']['tab']=$this->getRelativePath(PATH_typo3,$fullpath.'/'.$MLANG['default']['tabs_images']['tab']);
414 }
415
416 // Finally, setting the icon with correct path:
417 if (substr($MLANG['default']['tabs_images']['tab'],0,3)=='../') {
418 $MLANG['default']['tabs_images']['tab'] = PATH_site.substr($MLANG['default']['tabs_images']['tab'],3);
419 } else {
420 $MLANG['default']['tabs_images']['tab'] = PATH_typo3.$MLANG['default']['tabs_images']['tab'];
421 }
422 }
423
424 // If LOCAL_LANG references are used for labels of the module:
425 if ($MLANG['default']['ll_ref']) {
426 // Now the 'default' key is loaded with the CURRENT language - not the english translation...
427 $MLANG['default']['labels']['tablabel'] = $GLOBALS['LANG']->sL($MLANG['default']['ll_ref'].':mlang_labels_tablabel');
428 $MLANG['default']['labels']['tabdescr'] = $GLOBALS['LANG']->sL($MLANG['default']['ll_ref'].':mlang_labels_tabdescr');
429 $MLANG['default']['tabs']['tab'] = $GLOBALS['LANG']->sL($MLANG['default']['ll_ref'].':mlang_tabs_tab');
430 $GLOBALS['LANG']->addModuleLabels($MLANG['default'],$name.'_');
431 } else { // ... otherwise use the old way:
432 $GLOBALS['LANG']->addModuleLabels($MLANG['default'],$name.'_');
433 $GLOBALS['LANG']->addModuleLabels($MLANG[$GLOBALS['LANG']->lang],$name.'_');
434 }
435 }
436
437 // Default script setup
438 if ($MCONF['script'] && @file_exists($path.'/'.$MCONF['script'])) {
439 $modconf['script'] = $this->getRelativePath(PATH_typo3,$fullpath.'/'.$MCONF['script']);
440 } else {
441 $modconf['script'] = 'dummy.php';
442 }
443 // Default tab setting
444 if ($MCONF['defaultMod']) {
445 $modconf['defaultMod'] = $MCONF['defaultMod'];
446 }
447 // Navigation Frame Script (GET params could be added)
448 if ($MCONF['navFrameScript']) {
449 $navFrameScript = explode('?', $MCONF['navFrameScript']);
450 $navFrameScript = $navFrameScript[0];
451 if (@file_exists($path.'/'.$navFrameScript)) {
452 $modconf['navFrameScript'] = $this->getRelativePath(PATH_typo3,$fullpath.'/'.$MCONF['navFrameScript']);
453 }
454 }
455 // additional params for Navigation Frame Script: "&anyParam=value&moreParam=1"
456 if ($MCONF['navFrameScriptParam']) {
457 $modconf['navFrameScriptParam'] = $MCONF['navFrameScriptParam'];
458 }
459 } else return false;
460 } else $modconf = 'notFound';
461 return $modconf;
462 }
463
464 /**
465 * Returns true if the internal BE_USER has access to the module $name with $MCONF (based on security level set for that module)
466 *
467 * @param string Module name
468 * @param array MCONF array (module configuration array) from the modules conf.php file (contains settings about what access level the module has)
469 * @return boolean True if access is granted for $this->BE_USER
470 */
471 function checkModAccess($name,$MCONF) {
472 if ($MCONF['access']) {
473 $access=strtolower($MCONF['access']);
474 // Checking if admin-access is required
475 if (strstr($access,'admin')) { // If admin-permissions is required then return true if user is admin
476 if ($this->BE_USER->isAdmin()) {return true;}
477 }
478 // This will add modules to the select-lists of user and groups
479 if (strstr($access,'user')) { $this->modListUser[]=$name; }
480 if (strstr($access,'group')) { $this->modListGroup[]=$name; }
481 // This checks if a user is permitted to access the module
482 if ($this->BE_USER->isAdmin() || $this->BE_USER->check('modules',$name)) {return true;} // If admin you can always access a module
483
484 } else return true; // If conf[access] is not set, then permission IS granted!
485 }
486
487 /**
488 * Check if a module is allowed inside the current workspace for be user
489 * Processing happens only if $this->observeWorkspaces is TRUE
490 *
491 * @param string Module name
492 * @param array MCONF array (module configuration array) from the modules conf.php file (contains settings about workspace restrictions)
493 * @return boolean True if access is granted for $this->BE_USER
494 */
495 function checkModWorkspace($name,$MCONF) {
496 if ($this->observeWorkspaces) {
497 $status = TRUE;
498 if ($MCONF['workspaces']) {
499 $status = FALSE;
500 if (($this->BE_USER->workspace===0 && t3lib_div::inList($MCONF['workspaces'],'online')) ||
501 ($this->BE_USER->workspace===-1 && t3lib_div::inList($MCONF['workspaces'],'offline')) ||
502 ($this->BE_USER->workspace>0 && t3lib_div::inList($MCONF['workspaces'],'custom'))) {
503 $status = TRUE;
504 }
505 } elseif ($this->BE_USER->workspace===-99) {
506 $status = FALSE;
507 }
508 return $status;
509 } else return TRUE;
510 }
511
512 /**
513 * Parses the moduleArray ($TBE_MODULES) into a internally useful structure.
514 * Returns an array where the keys are names of the module and the values may be true (only module) or an array (of submodules)
515 *
516 * @param array moduleArray ($TBE_MODULES)
517 * @return array Output structure with available modules
518 */
519 function parseModulesArray($arr) {
520 $theMods = Array();
521 if (is_array($arr)) {
522 foreach($arr as $mod => $subs) {
523 $mod = $this->cleanName($mod); // clean module name to alphanum
524 if ($mod) {
525 if ($subs) {
526 $subsArr = t3lib_div::trimExplode(',', $subs);
527 foreach($subsArr as $subMod) {
528 $subMod = $this->cleanName($subMod);
529 if ($subMod) {
530 $theMods[$mod][] = $subMod;
531 }
532 }
533 } else {
534 $theMods[$mod] = 1;
535 }
536 }
537 }
538 }
539 return $theMods;
540 }
541
542 /**
543 * The $str is cleaned so that it contains alphanumerical characters only. Modules must only consist of these characters
544 *
545 * @param string String to clean up
546 * @return string
547 */
548 function cleanName ($str) {
549 return preg_replace('/[^a-z0-9]/i','',$str);
550 }
551
552 /**
553 * Get relative path for $destDir compared to $baseDir
554 *
555 * @param string Base directory
556 * @param string Destination directory
557 * @return string The relative path of destination compared to base.
558 */
559 function getRelativePath($baseDir,$destDir){
560 // By Rene Fritz
561 // a special case , the dirs are equals
562 if ($baseDir == $destDir){
563 return './';
564 }
565
566 $baseDir = ereg_replace ('^/', '', $baseDir); // remove beginning
567 $destDir = ereg_replace ('^/', '', $destDir);
568
569 $found = true;
570 $slash_pos=0;
571
572 do {
573 $slash_pos = strpos ($destDir, '/');
574 if (substr($destDir, 0, $slash_pos) == substr($baseDir, 0, $slash_pos)){
575 $baseDir = substr($baseDir, $slash_pos+1);
576 $destDir = substr($destDir, $slash_pos+1);
577 } else {
578 $found = false;
579 }
580 } while($found == true);
581
582 $slashes = strlen ($baseDir) - strlen (str_replace('/', '', $baseDir));
583 for($i=0;$i < $slashes;$i++) {
584 $destDir = '../'.$destDir;
585 }
586 return $destDir;
587 }
588 }
589
590
591 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_loadmodules.php']) {
592 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_loadmodules.php']);
593 }
594 ?>