Made Clear Cache buttons of backend.php AJAX based, showing a nice spinner image...
[Packages/TYPO3.CMS.git] / typo3 / classes / class.modulemenu.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2007 Ingo Renner <ingo@typo3.org>
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
29 /**
30 * class to render the TYPO3 backend menu for the modules
31 *
32 * @author Ingo Renner <ingo@typo3.org>
33 * @package TYPO3
34 * @subpackage core
35 */
36 class ModuleMenu {
37
38 /**
39 * module loading object
40 *
41 * @var t3lib_loadModules
42 */
43 protected $moduleLoader;
44
45 private $backPath;
46 private $cacheActions;
47 private $linkModules;
48 private $loadedModules;
49 private $fsMod; //TODO find a more descriptive name, left over from alt_menu_functions
50
51 /**
52 * constructor, initializes several variables
53 *
54 * @return void
55 */
56 public function __construct() {
57
58 $this->backPath = '';
59 $this->fsMod = array();
60 $this->linkModules = true;
61
62 // Loads the backend modules available for the logged in user.
63 $this->moduleLoader = t3lib_div::makeInstance('t3lib_loadModules');
64 $this->moduleLoader->observeWorkspaces = true;
65 $this->moduleLoader->load($GLOBALS['TBE_MODULES']);
66 $this->loadedModules = $this->moduleLoader->modules;
67
68 // init cache actions
69 $this->cacheActions = array();
70 $this->initCacheActions();
71 }
72
73 /**
74 * sets the path back to /typo3/
75 *
76 * @param string path back to /typo3/
77 * @return void
78 */
79 public function setBackPath($backPath) {
80 if(!is_string($backPath)) {
81 throw new InvalidArgumentException('parameter $backPath must be of type string', 1193315266);
82 }
83
84 $this->backPath = $backPath;
85 }
86
87 /**
88 * loads the collapse states for the main modules from user's configuration (uc)
89 *
90 * @return array collapse states
91 */
92 private function getCollapsedStates() {
93
94 $collapsedStates = array();
95 if($GLOBALS['BE_USER']->uc['moduleData']['moduleMenu']) {
96 $collapsedStates = $GLOBALS['BE_USER']->uc['moduleData']['moduleMenu'];
97 }
98
99 return $collapsedStates;
100 }
101
102 /**
103 * returns the loaded modules
104 *
105 * @return array array of loaded modules
106 */
107 public function getLoadedModules() {
108 return $this->loadedModules;
109 }
110
111 /**
112 * renders the backend menu as unordered list
113 *
114 * @return string menu html code to use in the backend
115 */
116 public function render() {
117 $menu = '';
118 $onBlur = $GLOBALS['CLIENT']['FORMSTYLE'] ? 'this.blur();' : '';
119
120 $rawModuleData = $this->getRawModuleData();
121
122 foreach($rawModuleData as $moduleKey => $moduleData) {
123
124 $moduleLabel = $moduleData['title'];
125 if($moduleData['link'] && $this->linkModules) {
126 $moduleLabel = '<a href="#" onclick="top.goToModule(\''.$moduleData['name'].'\');'.$onBlur.'return false;">'.$moduleLabel.'</a>';
127 }
128
129 $menu .= '<li><div>'.$moduleData['icon']['html'].' '.$moduleLabel.'</div>';
130
131 // traverse submodules
132 if(is_array($moduleData['subitems'])) {
133 $menu .= $this->renderSubModules($moduleData['subitems']);
134 }
135
136 $menu .= '</li>'."\n";
137 }
138
139 return '<ul id="typo3-menu">'."\n".$menu.'</ul>'."\n";
140 }
141
142 /**
143 * renders submodules
144 *
145 * @param array array of (sub)module data
146 * @return string (sub)module html code
147 */
148 public function renderSubModules($modules) {
149 $moduleMenu = '';
150 $onBlur = $GLOBALS['CLIENT']['FORMSTYLE'] ? 'this.blur();' : '';
151
152 foreach($modules as $moduleKey => $moduleData) {
153 // Setting additional JavaScript
154 $additionalJavascript = '';
155 if($moduleData['parentNavigationFrameScript']) {
156 $parentModuleName = substr($moduleData['name'], 0, strpos($moduleData['name'], '_'));
157 $additionalJavascript = "+'&id='+top.rawurlencode(top.fsMod.recentIds['".$parentModuleName."'])";
158 }
159
160 if($moduleData['link'] && $this->linkModules) {
161
162 $onClickString = htmlspecialchars('top.goToModule(\''.$moduleData['name'].'\');'.$onBlur.'return false;');
163 $submoduleLink = '<a href="#" onclick="'.$onClickString.'" title="'.$moduleData['description'].'">'
164 .$moduleData['icon']['html'].' '
165 .'<span>'.htmlspecialchars($moduleData['title']).'</span>'
166 .'</a>';
167 }
168
169 $moduleMenu .= '<li>'.$submoduleLink.'</li>'."\n";
170 }
171
172 return '<ul>'."\n".$moduleMenu.'</ul>'."\n";
173 }
174
175 /**
176 * gets the raw module data
177 *
178 * @return array multi dimension array with module data
179 */
180 public function getRawModuleData() {
181 $modules = array();
182
183 // Remove the 'doc' module?
184 if($GLOBALS['BE_USER']->getTSConfigVal('options.disableDocModuleInAB')) {
185 unset($this->loadedModules['doc']);
186 }
187
188 foreach($this->loadedModules as $moduleName => $moduleData) {
189 $moduleNavigationFramePrefix = $this->getNavigationFramePrefix($moduleData);
190
191 if($moduleNavigationFramePrefix) {
192 $this->fsMod[$moduleName] = 'fsMod.recentIds["'.$moduleName.'"]="";';
193 }
194
195 $moduleLink = '';
196 if(!is_array($moduleData['sub'])) {
197 $moduleLink = $moduleData['script'];
198 }
199 $moduleLink = t3lib_div::resolveBackPath($moduleLink);
200
201 $moduleKey = $moduleName.'_tab';
202 $moduleCssId = 'ID_'.t3lib_div::md5int($moduleName);
203 $moduleIcon = $this->getModuleIcon($moduleKey);
204
205 if($moduleLink && $moduleNavigationFramePrefix) {
206 $moduleLink = $moduleNavigationFramePrefix.rawurlencode($moduleLink);
207 }
208
209 $modules[$moduleKey] = array(
210 'name' => $moduleName,
211 'title' => $GLOBALS['LANG']->moduleLabels['tabs'][$moduleKey],
212 'onclick' => 'top.goToModule(\''.$moduleName.'\');',
213 'cssId' => $moduleCssId,
214 'icon' => $moduleIcon,
215 'link' => $moduleLink,
216 'prefix' => $moduleNavigationFramePrefix
217 );
218
219 if(is_array($moduleData['sub'])) {
220
221 foreach($moduleData['sub'] as $submoduleName => $submoduleData) {
222 $submoduleLink = t3lib_div::resolveBackPath($submoduleData['script']);
223 $submoduleNavigationFramePrefix = $this->getNavigationFramePrefix($moduleData, $submoduleData);
224
225 $submoduleKey = $moduleName.'_'.$submoduleName.'_tab';
226 $submoduleCssId = 'ID_'.t3lib_div::md5int($moduleName.'_'.$submoduleName);
227 $submoduleIcon = $this->getModuleIcon($submoduleKey);
228 $submoduleDescription = $GLOBALS['LANG']->moduleLabels['labels'][$submoduleKey.'label'];
229
230 $originalLink = $submoduleLink;
231 if($submoduleLink && $submoduleNavigationFramePrefix) {
232 $submoduleLink = $submoduleNavigationFramePrefix.rawurlencode($submoduleLink);
233 }
234
235 $modules[$moduleKey]['subitems'][$submoduleKey] = array(
236 'name' => $moduleName.'_'.$submoduleName,
237 'title' => $GLOBALS['LANG']->moduleLabels['tabs'][$submoduleKey],
238 'onclick' => 'top.goToModule(\''.$moduleName.'_'.$submoduleName.'\');',
239 'cssId' => $submoduleCssId,
240 'icon' => $submoduleIcon,
241 'link' => $submoduleLink,
242 'originalLink' => $originalLink,
243 'prefix' => $submoduleNavigationFramePrefix,
244 'description' => $submoduleDescription
245 );
246
247 if($moduleData['navFrameScript']) {
248 $modules[$moduleKey]['subitems'][$submoduleKey]['parentNavigationFrameScript'] = $moduleData['navFrameScript'];
249 }
250 }
251 }
252 }
253
254 return $modules;
255 }
256
257 /**
258 * gets the module icon and its size
259 *
260 * @param string module key
261 * @return array icon data array with 'filename', 'size', and 'html'
262 */
263 private function getModuleIcon($moduleKey) {
264 $icon = array();
265 $iconFileRelative = $this->getModuleIconRelative($GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleKey]);
266 $iconFileAbsolute = $this->getModuleIconAbsolute($GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleKey]);
267 $iconSizes = @getimagesize($iconFileAbsolute);
268 $iconTitle = $GLOBALS['LANG']->moduleLabels['tabs'][$moduleKey];
269
270 $icon['filename'] = $iconFileRelative;
271 $icon['size'] = $iconSizes[3];
272 $icon['title'] = htmlspecialchars($iconTitle);
273 $icon['html'] = '<img src="'.$iconFileRelative.'" '.$iconSizes[3].' title="'.htmlspecialchars($iconTitle).'" alt="'.htmlspecialchars($iconTitle).'" />';
274
275 return $icon;
276 }
277
278 /**
279 * Returns the filename readable for the script from PATH_typo3.
280 * That means absolute names are just returned while relative names are
281 * prepended with the path pointing back to typo3/ dir
282 *
283 * @param string icon filename
284 * @return string icon filename with absolute path
285 * @see getModuleIconRelative()
286 */
287 private function getModuleIconAbsolute($iconFilename) {
288
289 if(!t3lib_div::isAbsPath($iconFilename)) {
290 $iconFilename = $this->backPath.$iconFilename;
291 }
292
293 return $iconFilename;
294 }
295
296 /**
297 * Returns relative path to the icon filename for use in img-tags
298 *
299 * @param string icon filename
300 * @return string icon filename with relative path
301 * @see getModuleIconAbsolute()
302 */
303 private function getModuleIconRelative($iconFilename) {
304 if(t3lib_div::isAbsPath($iconFilename)) {
305 $iconFilename = '../'.substr($iconFilename, strlen(PATH_site));
306 }
307
308 return $this->backPath.$iconFilename;
309 }
310
311 /**
312 * Returns a prefix used to call the navigation frame with parameters which then will call the scripts defined in the modules info array.
313 *
314 * @param array module data array
315 * @param array submodule data array
316 * @return string result URL string
317 */
318 private function getNavigationFramePrefix($moduleData, $subModuleData = array()) {
319 $prefix = '';
320
321 $navigationFrameScript = $moduleData['navFrameScript'];
322 if($subModuleData['navFrameScript']) {
323 $navigationFrameScript = $subModuleData['navFrameScript'];
324 }
325
326 $navigationFrameParameter = $moduleData['navFrameScriptParam'];
327 if($subModuleData['navFrameScriptParam']) {
328 $navigationFrameParameter = $subModuleData['navFrameScriptParam'];
329 }
330
331 if($navigationFrameScript) {
332 $navigationFrameScript = t3lib_div::resolveBackPath($navigationFrameScript);
333 $navigationFrameScript = $this->appendQuestionmarkToLink($navigationFrameScript);
334
335 if($GLOBALS['BE_USER']->uc['condensedMode']) {
336 $prefix = $navigationFrameScript.$navigationFrameParameter.'&currentSubScript=';
337 } else {
338 $prefix = 'alt_mod_frameset.php?'
339 .'fW="+top.TS.navFrameWidth+"'
340 .'&nav="+top.TS.PATH_typo3+"'
341 .rawurlencode($navigationFrameScript.$navigationFrameParameter)
342 .'&script=';
343 }
344 }
345
346 return $prefix;
347 }
348
349 /**
350 * generates javascript code to switch between modules
351 *
352 * @return string javascript code snippet to switch modules
353 */
354 public function getGotoModuleJavascript() {
355
356 $moduleJavascriptCommands = array();
357 $rawModuleData = $this->getRawModuleData();
358
359 foreach($rawModuleData as $mainModuleKey => $mainModuleData) {
360 if($mainModuleData['subitems']) {
361 foreach($mainModuleData['subitems'] as $subModuleKey => $subModuleData) {
362
363 $parentModuleName = substr($subModuleData['name'], 0, strpos($subModuleData['name'], '_'));
364 $javascriptCommand = '';
365
366 // Setting additional JavaScript if frameset script:
367 $additionalJavascript = '';
368 if($subModuleData['parentNavigationFrameScript']) {
369 $additionalJavascript = "+'&id='+top.rawurlencode(top.fsMod.recentIds['".$parentModuleName."'])";
370 }
371
372 if($subModuleData['link'] && $this->linkModules) {
373 // For condensed mode, send &cMR parameter to frameset script.
374 if($additionalJavascript && $GLOBALS['BE_USER']->uc['condensedMode']) {
375 $additionalJavascript .= "+(cMR?'&cMR=1':'')";
376 }
377
378 $javascriptCommand = '
379 top.content.location=top.getModuleUrl(top.TS.PATH_typo3+"'.$this->appendQuestionmarkToLink($subModuleData['link']).'"'.$additionalJavascript.'+additionalGetVariables);
380 top.fsMod.currentMainLoaded="'.$parentModuleName.'";
381 ';
382
383 if($subModuleData['navFrameScript']) {
384 $javascriptCommand .= '
385 top.currentSubScript="'.$subModuleData['originalLink'].'";';
386 }
387
388 if(!$GLOBALS['BE_USER']->uc['condensedMode'] && $subModuleData['parentNavigationFrameScript']) {
389 $additionalJavascript = "+'&id='+top.rawurlencode(top.fsMod.recentIds['".$parentModuleName."'])";
390
391 $submoduleNavigationFrameScript = $subModuleData['navigationFrameScript'] ? $subModuleData['navigationFrameScript'] : $subModuleData['parentNavigationFrameScript'];
392 $submoduleNavigationFrameScript = t3lib_div::resolveBackPath($submoduleNavigationFrameScript);
393
394 // add GET parameters for sub module to the navigation script
395 $submoduleNavigationFrameScript = $this->appendQuestionmarkToLink($submoduleNavigationFrameScript).$subModuleData['navigationFrameScript'];
396
397 $javascriptCommand = '
398 if (top.content.list_frame && top.fsMod.currentMainLoaded=="'.$parentModuleName.'") {
399 top.currentSubScript="'.$subModuleData['originalLink'].'";
400 top.content.list_frame.location=top.getModuleUrl(top.TS.PATH_typo3+"'.$this->appendQuestionmarkToLink($subModuleData['originalLink']).'"'.$additionalJavascript.'+additionalGetVariables);
401 if(top.currentSubNavScript!="'.$submoduleNavigationFrameScript.'") {
402 top.currentSubNavScript="'.$submoduleNavigationFrameScript.'";
403 top.content.nav_frame.location=top.getModuleUrl(top.TS.PATH_typo3+"'.$submoduleNavigationFrameScript.'");
404 }
405 } else {
406 top.content.location=top.TS.PATH_typo3+(
407 top.nextLoadModuleUrl?
408 "'.($subModuleData['prefix'] ? $this->appendQuestionmarkToLink($subModuleData['link']).'&exScript=' : '').'listframe_loader.php":
409 "'.$this->appendQuestionmarkToLink($subModuleData['link']).'"'.$additionalJavascript.'+additionalGetVariables
410 );
411 top.fsMod.currentMainLoaded="'.$parentModuleName.'";
412 top.currentSubScript="'.$subModuleData['originalLink'].'";
413 }
414 ';
415 }
416
417 $javascriptCommand .= '
418 top.highlightModuleMenuItem("'.$subModuleData['cssId'].'");
419 ';
420 $moduleJavascriptCommands[] = "case '".$subModuleData['name']."': \n ".$javascriptCommand." \n break;";
421 }
422 }
423 } elseif(!$mainModuleData['subitems'] && !empty($mainModuleData['link'])) {
424 // main module has no sub modules but instead is linked itself (doc module)
425 $javascriptCommand = '
426 top.content.location=top.getModuleUrl(top.TS.PATH_typo3+"'.$this->appendQuestionmarkToLink($mainModuleData['link']).'"+additionalGetVariables);
427 top.highlightModuleMenuItem("'.$mainModuleData['cssId'].'", 1);
428 ';
429 $moduleJavascriptCommands[] = "case '".$mainModuleData['name']."': \n ".$javascriptCommand." \n break;";
430 }
431 }
432
433 $javascriptCode = '
434 /**
435 * Function used to switch switch module.
436 */
437 var currentModuleLoaded = "";
438 function goToModule(modName,cMR_flag,addGetVars) { //
439 var additionalGetVariables = "";
440 if (addGetVars) additionalGetVariables = addGetVars;
441
442 var cMR = 0;
443 if (cMR_flag) cMR = 1;
444
445 currentModuleLoaded = modName;
446
447 switch(modName) {'
448 ."\n".implode("\n", $moduleJavascriptCommands)."\n".'
449 }
450 }';
451
452 return $javascriptCode;
453 }
454
455 /**
456 * Appends a '?' if there is none in the string already
457 *
458 * @param string Link URL
459 * @return string link URl appended with ? if there wasn't one
460 */
461 private function appendQuestionmarkToLink($link) {
462 if(!strstr($link, '?')) {
463 $link .= '?';
464 }
465
466 return $link;
467 }
468
469 /**
470 * renders the logout button form
471 *
472 * @return string html code snippet displaying the logout button
473 */
474 public function renderLogoutButton() {
475 $buttonLabel = $GLOBALS['BE_USER']->user['ses_backuserid'] ? 'LLL:EXT:lang/locallang_core.php:buttons.exit' : 'LLL:EXT:lang/locallang_core.php:buttons.logout';
476
477 $buttonForm = '
478 <form action="logout.php" target="_top">
479 <input type="submit" value="'.$GLOBALS['LANG']->sL($buttonLabel, 1).'" />
480 </form>';
481
482 return $buttonForm;
483 }
484
485 /**
486 * renders the actions to clear several caches
487 *
488 * @return string cache actions html code snippet
489 */
490 public function renderCacheActions() {
491 $renderedCacheActions = array('<ul id="cache-actions">');
492
493 foreach($this->cacheActions as $actionKey => $cacheAction) {
494 $js =
495 'top.origIcon = $$(\'#action-'.$actionKey.' img\')[0].src;'.
496 '$$(\'#action-'.$actionKey.' img\')[0].src = \'gfx/spinner.gif\';'.
497 'new Ajax.Request(\''.htmlspecialchars($cacheAction['href']).'\', { '.
498 'method: \'get\', '.
499 'onComplete: function() {$$(\'#action-'.$actionKey.' img\')[0].src = top.origIcon;}'.
500 '}); '.
501 'return false;';
502
503 $renderedCacheActions[] = '<li id="action-'.$actionKey.'"><a onclick="'.$js.'" href="#'.htmlspecialchars($cacheAction['href']).'">'.$cacheAction['icon'].' '.$cacheAction['title'].'</a></li>';
504 }
505
506 $renderedCacheActions[] = '</ul>';
507
508 return implode("\n", $renderedCacheActions);
509 }
510
511 /**
512 * initializes cache actions
513 *
514 * @return void
515 */
516 private function initCacheActions() {
517
518 // Clearing of cache-files in typo3conf/ + menu
519 if ($GLOBALS['TYPO3_CONF_VARS']['EXT']['extCache']) {
520 $title = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:rm.clearCache_allTypo3Conf');
521 $this->cacheActions[] = array(
522 'id' => 'temp_CACHED',
523 'title' => $title,
524 'href' => $this->backPath.'tce_db.php?vC='.$GLOBALS['BE_USER']->veriCode().'&cacheCmd=temp_CACHED',
525 'icon' => '<img'.t3lib_iconWorks::skinImg($this->backPath, 'gfx/clear_cache_files_in_typo3c.gif', 'width="21" height="18"').' title="'.htmlspecialchars($title).'" alt="" />'
526 );
527 }
528
529 // Clear all page cache
530 $title = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:rm.clearCache_all');
531 $this->cacheActions[] = array(
532 'id' => 'all',
533 'title' => $title,
534 'href' => $this->backPath.'tce_db.php?vC='.$GLOBALS['BE_USER']->veriCode().'&cacheCmd=all',
535 'icon' => '<img'.t3lib_iconWorks::skinImg($this->backPath, 'gfx/clear_all_cache.gif', 'width="21" height="18"').' title="'.htmlspecialchars($title).'" alt="" />'
536 );
537 }
538
539 /**
540 * turns linking of modules on or off
541 *
542 * @param boolean status for linking modules with a-tags, set to false to turn lining off
543 */
544 public function setLinkModules($linkModules) {
545 if(!is_bool($linkModules)) {
546 throw new InvalidArgumentException('parameter $linkModules must be of type bool', 1193326558);
547 }
548
549 $this->linkModules = $linkModules;
550 }
551
552 /**
553 * gets the frameset (leftover) helper
554 *
555 * @return array array of javascript snippets
556 */
557 public function getFsMod() {
558 return $this->fsMod;
559 }
560 }
561
562
563 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.modulemenu.php']) {
564 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['typo3/classes/class.modulemenu.php']);
565 }
566
567 ?>