[TASK] Add tool-function to strip PATH_site-part of paths
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / View / ModuleMenuView.php
1 <?php
2 namespace TYPO3\CMS\Backend\View;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2007-2013 Ingo Renner <ingo@typo3.org>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31
32 /**
33 * class to render the TYPO3 backend menu for the modules
34 *
35 * @author Ingo Renner <ingo@typo3.org>
36 */
37 class ModuleMenuView {
38
39 /**
40 * Module loading object
41 *
42 * @var \TYPO3\CMS\Backend\Module\ModuleLoader
43 */
44 protected $moduleLoader;
45
46 protected $backPath;
47
48 protected $linkModules;
49
50 protected $loadedModules;
51
52 /**
53 * Constructor, initializes several variables
54 */
55 public function __construct() {
56 if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
57 $GLOBALS['LANG']->includeLLFile('EXT:lang/locallang_misc.xlf');
58 }
59 $this->backPath = '';
60 $this->linkModules = TRUE;
61 // Loads the backend modules available for the logged in user.
62 $this->moduleLoader = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Module\\ModuleLoader');
63 $this->moduleLoader->observeWorkspaces = TRUE;
64 $this->moduleLoader->load($GLOBALS['TBE_MODULES']);
65 $this->loadedModules = $this->moduleLoader->modules;
66 }
67
68 /**
69 * sets the path back to /typo3/
70 *
71 * @param string $backPath Path back to /typo3/
72 * @throws \InvalidArgumentException
73 * @return void
74 */
75 public function setBackPath($backPath) {
76 if (!is_string($backPath)) {
77 throw new \InvalidArgumentException('parameter $backPath must be of type string', 1193315266);
78 }
79 $this->backPath = $backPath;
80 }
81
82 /**
83 * loads the collapse states for the main modules from user's configuration (uc)
84 *
85 * @return array Collapse states
86 */
87 protected function getCollapsedStates() {
88 $collapsedStates = array();
89 if ($GLOBALS['BE_USER']->uc['moduleData']['moduleMenu']) {
90 $collapsedStates = $GLOBALS['BE_USER']->uc['moduleData']['moduleMenu'];
91 }
92 return $collapsedStates;
93 }
94
95 /**
96 * ModuleMenu Store loading data
97 *
98 * @param array $params
99 * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj
100 * @return array
101 */
102 public function getModuleData($params, $ajaxObj) {
103 $data = array('success' => TRUE, 'root' => array());
104 $rawModuleData = $this->getRawModuleData();
105 $index = 0;
106 foreach ($rawModuleData as $moduleKey => $moduleData) {
107 $key = substr($moduleKey, 8);
108 $num = count($data['root']);
109 if ($moduleData['link'] != 'dummy.php' || $moduleData['link'] == 'dummy.php' && is_array($moduleData['subitems'])) {
110 $data['root'][$num]['key'] = $key;
111 $data['root'][$num]['menuState'] = $GLOBALS['BE_USER']->uc['moduleData']['menuState'][$moduleKey];
112 $data['root'][$num]['label'] = $moduleData['title'];
113 $data['root'][$num]['subitems'] = is_array($moduleData['subitems']) ? count($moduleData['subitems']) : 0;
114 if ($moduleData['link'] && $this->linkModules) {
115 $data['root'][$num]['link'] = 'top.goToModule(\'' . $moduleData['name'] . '\')';
116 }
117 // Traverse submodules
118 if (is_array($moduleData['subitems'])) {
119 foreach ($moduleData['subitems'] as $subKey => $subData) {
120 $data['root'][$num]['sub'][] = array(
121 'name' => $subData['name'],
122 'description' => $subData['description'],
123 'label' => $subData['title'],
124 'icon' => $subData['icon']['filename'],
125 'navframe' => $subData['parentNavigationFrameScript'],
126 'link' => $subData['link'],
127 'originalLink' => $subData['originalLink'],
128 'index' => $index++,
129 'navigationFrameScript' => $subData['navigationFrameScript'],
130 'navigationFrameScriptParam' => $subData['navigationFrameScriptParam'],
131 'navigationComponentId' => $subData['navigationComponentId']
132 );
133 }
134 }
135 }
136 }
137 if ($ajaxObj) {
138 $ajaxObj->setContent($data);
139 $ajaxObj->setContentFormat('jsonbody');
140 } else {
141 return $data;
142 }
143 }
144
145 /**
146 * Returns the loaded modules
147 *
148 * @return array Array of loaded modules
149 */
150 public function getLoadedModules() {
151 return $this->loadedModules;
152 }
153
154 /**
155 * saves the menu's toggle state in the backend user's uc
156 *
157 * @param array $params Array of parameters from the AJAX interface, currently unused
158 * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxObj Object of type AjaxRequestHandler
159 * @return void
160 */
161 public function saveMenuState($params, $ajaxObj) {
162 $menuItem = GeneralUtility::_POST('menuid');
163 $state = GeneralUtility::_POST('state') === 'true' ? 1 : 0;
164 $GLOBALS['BE_USER']->uc['moduleData']['menuState'][$menuItem] = $state;
165 $GLOBALS['BE_USER']->writeUC();
166 }
167
168 /**
169 * Reads User configuration from options.hideModules and removes
170 * modules from $this->loadedModules accordingly.
171 *
172 * @return void
173 */
174 protected function unsetHiddenModules() {
175 // Hide modules if set in userTS.
176 $hiddenModules = $GLOBALS['BE_USER']->getTSConfig('options.hideModules');
177 if (!empty($hiddenModules['value'])) {
178 $hiddenMainModules = GeneralUtility::trimExplode(',', $hiddenModules['value'], TRUE);
179 foreach ($hiddenMainModules as $hiddenMainModule) {
180 unset($this->loadedModules[$hiddenMainModule]);
181 }
182 }
183
184 // Hide sub-modules if set in userTS.
185 if (!empty($hiddenModules['properties']) && is_array($hiddenModules['properties'])) {
186 foreach ($hiddenModules['properties'] as $mainModuleName => $subModules) {
187 $hiddenSubModules = GeneralUtility::trimExplode(',', $subModules, TRUE);
188 foreach ($hiddenSubModules as $hiddenSubModule) {
189 unset($this->loadedModules[$mainModuleName]['sub'][$hiddenSubModule]);
190 }
191 }
192 }
193 }
194
195 /**
196 * gets the raw module data
197 *
198 * @return array Multi dimension array with module data
199 */
200 public function getRawModuleData() {
201 $modules = array();
202
203 // Unset modules that are meant to be hidden from the menu.
204 $this->unsetHiddenModules();
205
206 foreach ($this->loadedModules as $moduleName => $moduleData) {
207 $moduleLink = '';
208 if (!is_array($moduleData['sub'])) {
209 $moduleLink = $moduleData['script'];
210 }
211 $moduleLink = GeneralUtility::resolveBackPath($moduleLink);
212 $moduleKey = 'modmenu_' . $moduleName;
213 $moduleIcon = $this->getModuleIcon($moduleKey);
214 $modules[$moduleKey] = array(
215 'name' => $moduleName,
216 'title' => $GLOBALS['LANG']->moduleLabels['tabs'][$moduleName . '_tab'],
217 'onclick' => 'top.goToModule(\'' . $moduleName . '\');',
218 'icon' => $moduleIcon,
219 'link' => $moduleLink,
220 'description' => $GLOBALS['LANG']->moduleLabels['labels'][$moduleKey . 'label']
221 );
222 if (!is_array($moduleData['sub']) && $moduleData['script'] != 'dummy.php') {
223 // Work around for modules with own main entry, but being self the only submodule
224 $modules[$moduleKey]['subitems'][$moduleKey] = array(
225 'name' => $moduleName,
226 'title' => $GLOBALS['LANG']->moduleLabels['tabs'][$moduleName . '_tab'],
227 'onclick' => 'top.goToModule(\'' . $moduleName . '\');',
228 'icon' => $this->getModuleIcon($moduleName . '_tab'),
229 'link' => $moduleLink,
230 'originalLink' => $moduleLink,
231 'description' => $GLOBALS['LANG']->moduleLabels['labels'][$moduleKey . 'label'],
232 'navigationFrameScript' => NULL,
233 'navigationFrameScriptParam' => NULL,
234 'navigationComponentId' => NULL
235 );
236 } elseif (is_array($moduleData['sub'])) {
237 foreach ($moduleData['sub'] as $submoduleName => $submoduleData) {
238 $submoduleLink = GeneralUtility::resolveBackPath($submoduleData['script']);
239 $submoduleKey = $moduleName . '_' . $submoduleName . '_tab';
240 $submoduleIcon = $this->getModuleIcon($submoduleKey);
241 $submoduleDescription = $GLOBALS['LANG']->moduleLabels['labels'][$submoduleKey . 'label'];
242 $originalLink = $submoduleLink;
243 $modules[$moduleKey]['subitems'][$submoduleKey] = array(
244 'name' => $moduleName . '_' . $submoduleName,
245 'title' => $GLOBALS['LANG']->moduleLabels['tabs'][$submoduleKey],
246 'onclick' => 'top.goToModule(\'' . $moduleName . '_' . $submoduleName . '\');',
247 'icon' => $submoduleIcon,
248 'link' => $submoduleLink,
249 'originalLink' => $originalLink,
250 'description' => $submoduleDescription,
251 'navigationFrameScript' => $submoduleData['navFrameScript'],
252 'navigationFrameScriptParam' => $submoduleData['navFrameScriptParam'],
253 'navigationComponentId' => $submoduleData['navigationComponentId']
254 );
255 // if the main module has a navframe script, inherit to the submodule,
256 // but only if it is not disabled explicitly (option is set to FALSE)
257 if ($moduleData['navFrameScript'] && $submoduleData['inheritNavigationComponentFromMainModule'] !== FALSE) {
258 $modules[$moduleKey]['subitems'][$submoduleKey]['parentNavigationFrameScript'] = $moduleData['navFrameScript'];
259 }
260 }
261 }
262 }
263 return $modules;
264 }
265
266 /**
267 * gets the module icon and its size
268 *
269 * @param string $moduleKey Module key
270 * @return array Icon data array with 'filename', 'size', and 'html'
271 */
272 protected function getModuleIcon($moduleKey) {
273 $icon = array(
274 'filename' => '',
275 'size' => '',
276 'title' => '',
277 'html' => ''
278 );
279 $iconFileRelative = $this->getModuleIconRelative($GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleKey]);
280 $iconFileAbsolute = $this->getModuleIconAbsolute($GLOBALS['LANG']->moduleLabels['tabs_images'][$moduleKey]);
281 $iconSizes = @getimagesize($iconFileAbsolute);
282 $iconTitle = $GLOBALS['LANG']->moduleLabels['tabs'][$moduleKey];
283 if (!empty($iconFileRelative)) {
284 $icon['filename'] = $iconFileRelative;
285 $icon['size'] = $iconSizes[3];
286 $icon['title'] = htmlspecialchars($iconTitle);
287 $icon['html'] = '<img src="' . $iconFileRelative . '" ' . $iconSizes[3] . ' title="' . htmlspecialchars($iconTitle) . '" alt="' . htmlspecialchars($iconTitle) . '" />';
288 }
289 return $icon;
290 }
291
292 /**
293 * Returns the filename readable for the script from PATH_typo3.
294 * That means absolute names are just returned while relative names are
295 * prepended with the path pointing back to typo3/ dir
296 *
297 * @param string $iconFilename Icon filename
298 * @return string Icon filename with absolute path
299 * @see getModuleIconRelative()
300 */
301 protected function getModuleIconAbsolute($iconFilename) {
302 if (!GeneralUtility::isAbsPath($iconFilename)) {
303 $iconFilename = $this->backPath . $iconFilename;
304 }
305 return $iconFilename;
306 }
307
308 /**
309 * Returns relative path to the icon filename for use in img-tags
310 *
311 * @param string $iconFilename Icon filename
312 * @return string Icon filename with relative path
313 * @see getModuleIconAbsolute()
314 */
315 protected function getModuleIconRelative($iconFilename) {
316 if (GeneralUtility::isAbsPath($iconFilename)) {
317 $iconFilename = '../' . \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix($iconFilename);
318 }
319 return $this->backPath . $iconFilename;
320 }
321
322 /**
323 * Appends a '?' if there is none in the string already
324 *
325 * @param string $link Link URL
326 * @return string Link URl appended with ? if there wasn't one
327 */
328 protected function appendQuestionmarkToLink($link) {
329 if (!strstr($link, '?')) {
330 $link .= '?';
331 }
332 return $link;
333 }
334
335 /**
336 * renders the logout button form
337 *
338 * @return string Html code snippet displaying the logout button
339 */
340 public function renderLogoutButton() {
341 $buttonLabel = $GLOBALS['BE_USER']->user['ses_backuserid'] ? 'LLL:EXT:lang/locallang_core.xlf:buttons.exit' : 'LLL:EXT:lang/locallang_core.xlf:buttons.logout';
342 $buttonForm = '
343 <form action="logout.php" target="_top">
344 <input type="submit" id="logout-submit-button" value="' . $GLOBALS['LANG']->sL($buttonLabel, TRUE) . '" />
345 </form>';
346 return $buttonForm;
347 }
348
349 /**
350 * turns linking of modules on or off
351 *
352 * @param boolean $linkModules Status for linking modules with a-tags, set to FALSE to turn lining off
353 * @throws \InvalidArgumentException
354 * @return void
355 */
356 public function setLinkModules($linkModules) {
357 if (!is_bool($linkModules)) {
358 throw new \InvalidArgumentException('parameter $linkModules must be of type bool', 1193326558);
359 }
360 $this->linkModules = $linkModules;
361 }
362
363 }