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