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