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