[FEATURE] Introduce unified Storage API for JS
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Controller / BackendController.php
1 <?php
2 namespace TYPO3\CMS\Backend\Controller;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * Class for rendering the TYPO3 backend version 4.2+
23 *
24 * @author Ingo Renner <ingo@typo3.org>
25 */
26 class BackendController {
27
28 /**
29 * @var string
30 */
31 protected $content;
32
33 /**
34 * @var string
35 */
36 protected $css;
37
38 /**
39 * @var array
40 */
41 protected $cssFiles = array();
42
43 /**
44 * @var string
45 */
46 protected $js;
47
48 /**
49 * @var array
50 */
51 protected $jsFiles;
52
53 /**
54 * @var array
55 */
56 protected $toolbarItems = array();
57
58 /**
59 * @var int Intentionally private as nobody should modify defaults
60 */
61 private $menuWidthDefault = 190;
62
63 /**
64 * @var int
65 */
66 protected $menuWidth;
67
68 /**
69 * @var bool
70 */
71 protected $debug;
72
73 /**
74 * @var \TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository
75 */
76 protected $backendModuleRepository;
77
78 /**
79 * @var \TYPO3\CMS\Backend\Module\ModuleLoader Object for loading backend modules
80 */
81 protected $moduleLoader;
82
83 /**
84 * @var \TYPO3\CMS\Core\Page\PageRenderer
85 */
86 protected $pageRenderer;
87
88 /**
89 * @return \TYPO3\CMS\Core\Page\PageRenderer
90 */
91 public function getPageRenderer() {
92 return $this->pageRenderer;
93 }
94
95 /**
96 * Constructor
97 */
98 public function __construct() {
99 $this->backendModuleRepository = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository::class);
100
101 // Set debug flag for BE development only
102 $this->debug = (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] === 1;
103 // Initializes the backend modules structure for use later.
104 $this->moduleLoader = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Module\ModuleLoader::class);
105 $this->moduleLoader->load($GLOBALS['TBE_MODULES']);
106 $this->pageRenderer = $GLOBALS['TBE_TEMPLATE']->getPageRenderer();
107 $this->pageRenderer->loadExtJS();
108 // included for the module menu JavaScript, please note that this is subject to change
109 $this->pageRenderer->loadJquery();
110 $this->pageRenderer->enableExtJSQuickTips();
111 $this->pageRenderer->addJsInlineCode('consoleOverrideWithDebugPanel', '//already done', FALSE);
112 $this->pageRenderer->addExtDirectCode();
113 // Add default BE javascript
114 $this->js = '';
115 $this->jsFiles = array(
116 'locallang' => $this->getLocalLangFileName(),
117 'modernizr' => 'contrib/modernizr/modernizr.min.js',
118 'md5' => 'sysext/backend/Resources/Public/JavaScript/md5.js',
119 'modulemenu' => 'sysext/backend/Resources/Public/JavaScript/modulemenu.js',
120 'evalfield' => 'sysext/backend/Resources/Public/JavaScript/jsfunc.evalfield.js',
121 'tabclosemenu' => 'js/extjs/ux/ext.ux.tabclosemenu.js',
122 'notifications' => 'sysext/backend/Resources/Public/JavaScript/notifications.js',
123 'backend' => 'sysext/backend/Resources/Public/JavaScript/backend.js',
124 'debugPanel' => 'js/extjs/debugPanel.js',
125 'viewport' => 'js/extjs/viewport.js',
126 'iframepanel' => 'sysext/backend/Resources/Public/JavaScript/iframepanel.js',
127 'backendcontentiframe' => 'js/extjs/backendcontentiframe.js',
128 'viewportConfiguration' => 'js/extjs/viewportConfiguration.js',
129 'util' => 'sysext/backend/Resources/Public/JavaScript/util.js'
130 );
131 if (!$this->debug) {
132 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LoginRefresh');
133 }
134 // Add default BE css
135 $this->pageRenderer->addCssLibrary('contrib/normalize/normalize.css', 'stylesheet', 'all', '', TRUE, TRUE);
136
137 // load FlashMessages functionality
138 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/FlashMessages');
139
140 // load Modals
141 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
142
143 // load the storage API and fill the UC into the PersistentStorage, so no additional AJAX call is needed
144 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Storage', 'function(Storage) {
145 Storage.Persistent.load(' . json_encode($GLOBALS['BE_USER']->uc) . ');
146 }');
147 $this->css = '';
148 $this->initializeToolbarItems();
149 $this->menuWidth = isset($GLOBALS['TBE_STYLES']['dims']['leftMenuFrameW']) ? (int)$GLOBALS['TBE_STYLES']['dims']['leftMenuFrameW'] : $this->menuWidthDefault;
150 $this->executeHook('constructPostProcess');
151 }
152
153 /**
154 * Initialize toolbar item objects
155 *
156 * @throws \RuntimeException
157 * @return void
158 */
159 protected function initializeToolbarItems() {
160 $toolbarItemInstances = array();
161 $classNameRegistry = $GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'];
162 foreach ($classNameRegistry as $className) {
163 $toolbarItemInstance = GeneralUtility::makeInstance($className);
164 if (!$toolbarItemInstance instanceof \TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface) {
165 throw new \RuntimeException(
166 'class ' . $className . ' is registered as toolbar item but does not implement'
167 . \TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface::class,
168 1415958218
169 );
170 }
171 $index = (int)$toolbarItemInstance->getIndex();
172 if ($index < 0 || $index > 100) {
173 throw new \RuntimeException(
174 'getIndex() must return an integer between 0 and 100',
175 1415968498
176 );
177 }
178 // Find next free position in array
179 while(array_key_exists($index, $toolbarItemInstances)) {
180 $index++;
181 }
182 $toolbarItemInstances[$index] = $toolbarItemInstance;
183 }
184 ksort($toolbarItemInstances);
185 $this->toolbarItems = $toolbarItemInstances;
186 }
187
188 /**
189 * Main function generating the BE scaffolding
190 *
191 * @return void
192 */
193 public function render() {
194 $this->executeHook('renderPreProcess');
195
196 // Prepare the scaffolding, at this point extension may still add javascript and css
197 $view = $this->getFluidTemplateObject('EXT:backend/Resources/Private/Templates/Backend/Main.html');
198 // @todo: kick logo view class and move all logic to Fluid
199 $view->assign('logo', GeneralUtility::makeInstance(\TYPO3\CMS\Backend\View\LogoView::class)->render());
200 $view->assign('moduleMenu', $this->generateModuleMenu());
201 $view->assign('toolbar', $this->renderToolbar());
202
203 /******************************************************
204 * Now put the complete backend document together
205 ******************************************************/
206 foreach ($this->cssFiles as $cssFileName => $cssFile) {
207 $this->pageRenderer->addCssFile($cssFile);
208 // Load additional css files to overwrite existing core styles
209 if (!empty($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName])) {
210 $this->pageRenderer->addCssFile($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName]);
211 }
212 }
213 if (!empty($this->css)) {
214 $this->pageRenderer->addCssInlineBlock('BackendInlineCSS', $this->css);
215 }
216 foreach ($this->jsFiles as $jsFile) {
217 $this->pageRenderer->addJsFile($jsFile);
218 }
219 $this->generateJavascript();
220 $this->pageRenderer->addJsInlineCode('BackendInlineJavascript', $this->js, FALSE);
221 $this->loadResourcesForRegisteredNavigationComponents();
222
223 // Add state provider
224 $GLOBALS['TBE_TEMPLATE']->setExtDirectStateProvider();
225 $states = $GLOBALS['BE_USER']->uc['BackendComponents']['States'];
226 // Save states in BE_USER->uc
227 $extOnReadyCode = '
228 Ext.state.Manager.setProvider(new TYPO3.state.ExtDirectProvider({
229 key: "BackendComponents.States",
230 autoRead: false
231 }));
232 ';
233
234 if ($states) {
235 $extOnReadyCode .= 'Ext.state.Manager.getProvider().initState(' . json_encode($states) . ');';
236 }
237
238 $extOnReadyCode .= '
239 TYPO3.Backend = new TYPO3.Viewport(TYPO3.Viewport.configuration);
240 if (typeof console === "undefined") {
241 console = TYPO3.Backend.DebugConsole;
242 }
243 TYPO3.ContextHelpWindow.init(' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('help_cshmanual')) . ');';
244 $this->pageRenderer->addExtOnReadyCode($extOnReadyCode);
245 // Set document title:
246 $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] ? $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ' [TYPO3 CMS ' . TYPO3_version . ']' : 'TYPO3 CMS ' . TYPO3_version;
247 // Renders the module page
248 $this->content = $GLOBALS['TBE_TEMPLATE']->render($title, $view->render());
249 $hookConfiguration = array('content' => &$this->content);
250 $this->executeHook('renderPostProcess', $hookConfiguration);
251 echo $this->content;
252 }
253
254 /**
255 * Loads the css and javascript files of all registered navigation widgets
256 *
257 * @return void
258 */
259 protected function loadResourcesForRegisteredNavigationComponents() {
260 if (!is_array($GLOBALS['TBE_MODULES']['_navigationComponents'])) {
261 return;
262 }
263 $loadedComponents = array();
264 foreach ($GLOBALS['TBE_MODULES']['_navigationComponents'] as $module => $info) {
265 if (in_array($info['componentId'], $loadedComponents)) {
266 continue;
267 }
268 $loadedComponents[] = $info['componentId'];
269 $component = strtolower(substr($info['componentId'], strrpos($info['componentId'], '-') + 1));
270 $componentDirectory = 'components/' . $component . '/';
271 if ($info['isCoreComponent']) {
272 $absoluteComponentPath = PATH_site . 'typo3/js/extjs/' . $componentDirectory;
273 $relativeComponentPath = '../' . str_replace(PATH_site, '', $absoluteComponentPath);
274 } else {
275 $absoluteComponentPath = ExtensionManagementUtility::extPath($info['extKey']) . $componentDirectory;
276 $relativeComponentPath = ExtensionManagementUtility::extRelPath($info['extKey']) . $componentDirectory;
277 }
278 $cssFiles = GeneralUtility::getFilesInDir($absoluteComponentPath . 'css/', 'css');
279 if (file_exists($absoluteComponentPath . 'css/loadorder.txt')) {
280 // Don't allow inclusion outside directory
281 $loadOrder = str_replace('../', '', GeneralUtility::getUrl($absoluteComponentPath . 'css/loadorder.txt'));
282 $cssFilesOrdered = GeneralUtility::trimExplode(LF, $loadOrder, TRUE);
283 $cssFiles = array_merge($cssFilesOrdered, $cssFiles);
284 }
285 foreach ($cssFiles as $cssFile) {
286 $this->pageRenderer->addCssFile($relativeComponentPath . 'css/' . $cssFile);
287 }
288 $jsFiles = GeneralUtility::getFilesInDir($absoluteComponentPath . 'javascript/', 'js');
289 if (file_exists($absoluteComponentPath . 'javascript/loadorder.txt')) {
290 // Don't allow inclusion outside directory
291 $loadOrder = str_replace('../', '', GeneralUtility::getUrl($absoluteComponentPath . 'javascript/loadorder.txt'));
292 $jsFilesOrdered = GeneralUtility::trimExplode(LF, $loadOrder, TRUE);
293 $jsFiles = array_merge($jsFilesOrdered, $jsFiles);
294 }
295 foreach ($jsFiles as $jsFile) {
296 $this->pageRenderer->addJsFile($relativeComponentPath . 'javascript/' . $jsFile);
297 }
298 $this->pageRenderer->addInlineSetting('RecordHistory', 'moduleUrl', BackendUtility::getModuleUrl('record_history'));
299 }
300 }
301
302 /**
303 * Renders the items in the top toolbar
304 *
305 * @return string top toolbar elements as HTML
306 */
307 protected function renderToolbar() {
308 $toolbar = array();
309 foreach ($this->toolbarItems as $toolbarItem) {
310 /** @var \TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface $toolbarItem */
311 if ($toolbarItem->checkAccess()) {
312 $hasDropDown = (bool)$toolbarItem->hasDropDown();
313 $additionalAttributes = (array)$toolbarItem->getAdditionalAttributes();
314
315 $liAttributes = array();
316
317 // Merge class: Add dropdown class if hasDropDown, add classes from additonal attributes
318 $classes = array();
319 if ($hasDropDown) {
320 $classes[] = 'dropdown';
321 }
322 if (isset($additionalAttributes['class'])) {
323 $classes[] = $additionalAttributes['class'];
324 unset($additionalAttributes['class']);
325 }
326 $liAttributes[] = 'class="' . implode(' ', $classes) . '"';
327
328 // Add further attributes
329 foreach($additionalAttributes as $name => $value) {
330 $liAttributes[] = $name . '="' . $value . '"';
331 }
332
333 // Create a unique id from class name
334 $className = get_class($toolbarItem);
335 $className = GeneralUtility::underscoredToLowerCamelCase($className);
336 $className = GeneralUtility::camelCaseToLowerCaseUnderscored($className);
337 $className = str_replace(array('_', '\\'), '-', $className);
338 $liAttributes[] = 'id="' . $className . '"';
339
340 $toolbar[] = '<li ' . implode(' ', $liAttributes) . '>';
341
342 if ($hasDropDown) {
343 $toolbar[] = '<a href="#" class="dropdown-toggle" data-toggle="dropdown">';
344 $toolbar[] = $toolbarItem->getItem();
345 $toolbar[] = '</a>';
346 $toolbar[] = '<div class="dropdown-menu" role="menu">';
347 $toolbar[] = $toolbarItem->getDropDown();
348 $toolbar[] = '</div>';
349 } else {
350 $toolbar[] = $toolbarItem->getItem();
351 }
352 $toolbar[] = '</li>';
353 }
354 }
355 return implode(LF, $toolbar);
356 }
357
358 /**
359 * Returns the file name to the LLL JavaScript, containing the localized labels,
360 * which can be used in JavaScript code.
361 *
362 * @return string File name of the JS file, relative to TYPO3_mainDir
363 */
364 protected function getLocalLangFileName() {
365 $code = $this->generateLocalLang();
366 $filePath = 'typo3temp/locallang-BE-' . sha1($code) . '.js';
367 if (!file_exists((PATH_site . $filePath))) {
368 // writeFileToTypo3tempDir() returns NULL on success (please double-read!)
369 if (GeneralUtility::writeFileToTypo3tempDir(PATH_site . $filePath, $code) !== NULL) {
370 throw new \RuntimeException('LocalLangFile could not be written to ' . $filePath, 1295193026);
371 }
372 }
373 return '../' . $filePath;
374 }
375
376 /**
377 * Reads labels required in JavaScript code from the localization system and returns them as JSON
378 * array in TYPO3.LLL.
379 *
380 * @return string JavaScript code containing the LLL labels in TYPO3.LLL
381 */
382 protected function generateLocalLang() {
383 $coreLabels = array(
384 'waitTitle' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_logging_in'),
385 'refresh_login_failed' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed'),
386 'refresh_login_failed_message' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed_message'),
387 'refresh_login_title' => sprintf($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_title'), htmlspecialchars($GLOBALS['BE_USER']->user['username'])),
388 'login_expired' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_expired'),
389 'refresh_login_username' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_username'),
390 'refresh_login_password' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_password'),
391 'refresh_login_emptyPassword' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_emptyPassword'),
392 'refresh_login_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_button'),
393 'refresh_logout_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_logout_button'),
394 'please_wait' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.please_wait'),
395 'loadingIndicator' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:loadingIndicator'),
396 'be_locked' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.be_locked'),
397 'refresh_login_countdown_singular' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown_singular'),
398 'refresh_login_countdown' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown'),
399 'login_about_to_expire' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_about_to_expire'),
400 'login_about_to_expire_title' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_about_to_expire_title'),
401 'refresh_login_refresh_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_refresh_button'),
402 'refresh_direct_logout_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_direct_logout_button'),
403 'tabs_closeAll' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeAll'),
404 'tabs_closeOther' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeOther'),
405 'tabs_close' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.close'),
406 'tabs_openInBrowserWindow' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:tabs.openInBrowserWindow'),
407 'csh_tooltip_loading' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:csh_tooltip_loading')
408 );
409 $labels = array(
410 'fileUpload' => array(
411 'windowTitle',
412 'buttonSelectFiles',
413 'buttonCancelAll',
414 'infoComponentMaxFileSize',
415 'infoComponentFileUploadLimit',
416 'infoComponentFileTypeLimit',
417 'infoComponentOverrideFiles',
418 'processRunning',
419 'uploadWait',
420 'uploadStarting',
421 'uploadProgress',
422 'uploadSuccess',
423 'errorQueueLimitExceeded',
424 'errorQueueFileSizeLimit',
425 'errorQueueZeroByteFile',
426 'errorQueueInvalidFiletype',
427 'errorUploadHttp',
428 'errorUploadMissingUrl',
429 'errorUploadIO',
430 'errorUploadSecurityError',
431 'errorUploadLimit',
432 'errorUploadFailed',
433 'errorUploadFileIDNotFound',
434 'errorUploadFileValidation',
435 'errorUploadFileCancelled',
436 'errorUploadStopped',
437 'allErrorMessageTitle',
438 'allErrorMessageText',
439 'allError401',
440 'allError2038'
441 ),
442 'liveSearch' => array(
443 'title',
444 'helpTitle',
445 'emptyText',
446 'loadingText',
447 'listEmptyText',
448 'showAllResults',
449 'helpDescription',
450 'helpDescriptionPages',
451 'helpDescriptionContent'
452 ),
453 'viewPort' => array(
454 'tooltipModuleMenuSplit',
455 'tooltipNavigationContainerSplitDrag',
456 'tooltipDebugPanelSplitDrag'
457 )
458 );
459 $generatedLabels = array();
460 $generatedLabels['core'] = $coreLabels;
461 // First loop over all categories (fileUpload, liveSearch, ..)
462 foreach ($labels as $categoryName => $categoryLabels) {
463 // Then loop over every single label
464 foreach ($categoryLabels as $label) {
465 // LLL identifier must be called $categoryName_$label, e.g. liveSearch_loadingText
466 $generatedLabels[$categoryName][$label] = $GLOBALS['LANG']->getLL($categoryName . '_' . $label);
467 }
468 }
469 return 'TYPO3.LLL = ' . json_encode($generatedLabels) . ';';
470 }
471
472 /**
473 * Generates the JavaScript code for the backend.
474 *
475 * @return void
476 */
477 protected function generateJavascript() {
478
479 // Needed for tceform manipulation (date picker)
480 $dateFormat = ($GLOBALS['TYPO3_CONF_VARS']['SYS']['USdateFormat'] ? array('MM-DD-YYYY', 'HH:mm MM-DD-YYYY') : array('DD-MM-YYYY', 'HH:mm DD-MM-YYYY'));
481 $this->pageRenderer->addInlineSetting('DateTimePicker', 'DateFormat', $dateFormat);
482 // define the window size of the element browser etc.
483 $popupWindowSize = trim($GLOBALS['BE_USER']->getTSConfigVal('options.popupWindowSize'));
484 if (!empty($popupWindowSize)) {
485 list($popupWindowWidth, $popupWindowHeight) = GeneralUtility::trimExplode('x', $popupWindowSize);
486 }
487 $popupWindowWidth = !empty($popupWindowWidth) ? (int)$popupWindowWidth : 700;
488 $popupWindowHeight = !empty($popupWindowHeight) ? (int)$popupWindowHeight : 750;
489
490 // define the window size of the popups within the RTE
491 $rtePopupWindowSize = trim($GLOBALS['BE_USER']->getTSConfigVal('options.rte.popupWindowSize'));
492 if (!empty($rtePopupWindowSize)) {
493 list($rtePopupWindowWidth, $rtePopupWindowHeight) = GeneralUtility::trimExplode('x', $rtePopupWindowSize);
494 }
495 $rtePopupWindowWidth = !empty($rtePopupWindowWidth) ? (int)$rtePopupWindowWidth : ($popupWindowWidth-200);
496 $rtePopupWindowHeight = !empty($rtePopupWindowHeight) ? (int)$rtePopupWindowHeight : ($popupWindowHeight-250);
497
498 $pathTYPO3 = GeneralUtility::dirname(GeneralUtility::getIndpEnv('SCRIPT_NAME')) . '/';
499 // If another page module was specified, replace the default Page module with the new one
500 $newPageModule = trim($GLOBALS['BE_USER']->getTSConfigVal('options.overridePageModule'));
501 $pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
502 if (!$GLOBALS['BE_USER']->check('modules', $pageModule)) {
503 $pageModule = '';
504 }
505 // Determine security level from conf vars and default to super challenged
506 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel']) {
507 $this->loginSecurityLevel = $GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel'];
508 } else {
509 $this->loginSecurityLevel = 'superchallenged';
510 }
511 $t3Configuration = array(
512 'siteUrl' => GeneralUtility::getIndpEnv('TYPO3_SITE_URL'),
513 'PATH_typo3' => $pathTYPO3,
514 'PATH_typo3_enc' => rawurlencode($pathTYPO3),
515 'username' => htmlspecialchars($GLOBALS['BE_USER']->user['username']),
516 'uniqueID' => GeneralUtility::shortMD5(uniqid('', TRUE)),
517 'securityLevel' => $this->loginSecurityLevel,
518 'TYPO3_mainDir' => TYPO3_mainDir,
519 'pageModule' => $pageModule,
520 'inWorkspace' => $GLOBALS['BE_USER']->workspace !== 0,
521 'workspaceFrontendPreviewEnabled' => $GLOBALS['BE_USER']->user['workspace_preview'] ? 1 : 0,
522 'veriCode' => $GLOBALS['BE_USER']->veriCode(),
523 'denyFileTypes' => PHP_EXTENSIONS_DEFAULT,
524 'moduleMenuWidth' => $this->menuWidth - 1,
525 'topBarHeight' => isset($GLOBALS['TBE_STYLES']['dims']['topFrameH']) ? (int)$GLOBALS['TBE_STYLES']['dims']['topFrameH'] : 30,
526 'showRefreshLoginPopup' => isset($GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup']) ? (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup'] : FALSE,
527 'listModulePath' => ExtensionManagementUtility::extRelPath('recordlist') . 'mod1/',
528 'debugInWindow' => $GLOBALS['BE_USER']->uc['debugInWindow'] ? 1 : 0,
529 'ContextHelpWindows' => array(
530 'width' => 600,
531 'height' => 400
532 ),
533 'PopupWindow' => array(
534 'width' => $popupWindowWidth,
535 'height' => $popupWindowHeight
536 ),
537 'RTEPopupWindow' => array(
538 'width' => $rtePopupWindowWidth,
539 'height' => $rtePopupWindowHeight
540 )
541 );
542 $this->js .= '
543 TYPO3.configuration = ' . json_encode($t3Configuration) . ';
544
545 /**
546 * TypoSetup object.
547 */
548 function typoSetup() { //
549 this.PATH_typo3 = TYPO3.configuration.PATH_typo3;
550 this.PATH_typo3_enc = TYPO3.configuration.PATH_typo3_enc;
551 this.username = TYPO3.configuration.username;
552 this.uniqueID = TYPO3.configuration.uniqueID;
553 this.navFrameWidth = 0;
554 this.securityLevel = TYPO3.configuration.securityLevel;
555 this.veriCode = TYPO3.configuration.veriCode;
556 this.denyFileTypes = TYPO3.configuration.denyFileTypes;
557 }
558 var TS = new typoSetup();
559 //backwards compatibility
560 /**
561 * Frameset Module object
562 *
563 * Used in main modules with a frameset for submodules to keep the ID between modules
564 * Typically that is set by something like this in a Web>* sub module:
565 * if (top.fsMod) top.fsMod.recentIds["web"] = "\'.(int)$this->id.\'";
566 * if (top.fsMod) top.fsMod.recentIds["file"] = "...(file reference/string)...";
567 */
568 function fsModules() { //
569 this.recentIds=new Array(); // used by frameset modules to track the most recent used id for list frame.
570 this.navFrameHighlightedID=new Array(); // used by navigation frames to track which row id was highlighted last time
571 this.currentMainLoaded="";
572 this.currentBank="0";
573 }
574 var fsMod = new fsModules();
575
576 top.goToModule = function(modName, cMR_flag, addGetVars) {
577 TYPO3.ModuleMenu.App.showModule(modName, addGetVars);
578 }
579 ' . $this->setStartupModule();
580 // Check editing of page:
581 $this->handlePageEditing();
582 }
583
584 /**
585 * Checking if the "&edit" variable was sent so we can open it for editing the page.
586 *
587 * @return void
588 */
589 protected function handlePageEditing() {
590 // EDIT page:
591 $editId = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('edit'));
592 $editRecord = '';
593 if ($editId) {
594 // Looking up the page to edit, checking permissions:
595 $where = ' AND (' . $GLOBALS['BE_USER']->getPagePermsClause(2) . ' OR ' . $GLOBALS['BE_USER']->getPagePermsClause(16) . ')';
596 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($editId)) {
597 $editRecord = BackendUtility::getRecordWSOL('pages', $editId, '*', $where);
598 } else {
599 $records = BackendUtility::getRecordsByField('pages', 'alias', $editId, $where);
600 if (is_array($records)) {
601 $editRecord = reset($records);
602 BackendUtility::workspaceOL('pages', $editRecord);
603 }
604 }
605 // If the page was accessible, then let the user edit it.
606 if (is_array($editRecord) && $GLOBALS['BE_USER']->isInWebMount($editRecord['uid'])) {
607 // Setting JS code to open editing:
608 $this->js .= '
609 // Load page to edit:
610 window.setTimeout("top.loadEditId(' . (int)$editRecord['uid'] . ');", 500);
611 ';
612 // Checking page edit parameter:
613 if (!$GLOBALS['BE_USER']->getTSConfigVal('options.bookmark_onEditId_dontSetPageTree')) {
614 $bookmarkKeepExpanded = $GLOBALS['BE_USER']->getTSConfigVal('options.bookmark_onEditId_keepExistingExpanded');
615 // Expanding page tree:
616 BackendUtility::openPageTree((int)$editRecord['pid'], !$bookmarkKeepExpanded);
617 }
618 } else {
619 $this->js .= '
620 // Warning about page editing:
621 alert(' . GeneralUtility::quoteJSvalue(sprintf($GLOBALS['LANG']->getLL('noEditPage'), $editId)) . ');
622 ';
623 }
624 }
625 }
626
627 /**
628 * Sets the startup module from either GETvars module and modParams or user configuration.
629 *
630 * @return string the JavaScript code for the startup module
631 */
632 protected function setStartupModule() {
633 $startModule = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('module'));
634 if (!$startModule) {
635 // start module on first login, will be removed once used the first time
636 if (isset($GLOBALS['BE_USER']->uc['startModuleOnFirstLogin'])) {
637 $startModule = $GLOBALS['BE_USER']->uc['startModuleOnFirstLogin'];
638 unset($GLOBALS['BE_USER']->uc['startModuleOnFirstLogin']);
639 $GLOBALS['BE_USER']->writeUC();
640 } elseif ($GLOBALS['BE_USER']->uc['startModule']) {
641 $startModule = $GLOBALS['BE_USER']->uc['startModule'];
642 } elseif ($GLOBALS['BE_USER']->uc['startInTaskCenter']) {
643 $startModule = 'user_task';
644 }
645
646 // check if the start module has additional parameters, so a redirect to a specific
647 // action is possible
648 if (strpos($startModule, '->') !== FALSE) {
649 list($startModule, $startModuleParameters) = explode('->', $startModule, 2);
650 }
651 }
652
653 $moduleParameters = GeneralUtility::_GET('modParams');
654 // if no GET parameters are set, check if there are parameters given from the UC
655 if (!$moduleParameters && $startModuleParameters) {
656 $moduleParameters = $startModuleParameters;
657 }
658
659 if ($startModule) {
660 return '
661 // start in module:
662 top.startInModule = [\'' . $startModule . '\', ' . GeneralUtility::quoteJSvalue($moduleParameters) . '];
663 ';
664 } else {
665 return '';
666 }
667 }
668
669 /**
670 * Adds a javascript snippet to the backend
671 *
672 * @param string $javascript Javascript snippet
673 * @return void
674 * @throws \InvalidArgumentException
675 */
676 public function addJavascript($javascript) {
677 // @todo do we need more checks?
678 if (!is_string($javascript)) {
679 throw new \InvalidArgumentException('parameter $javascript must be of type string', 1195129553);
680 }
681 $this->js .= $javascript;
682 }
683
684 /**
685 * Adds a javscript file to the backend after it has been checked that it exists
686 *
687 * @param string $javascriptFile Javascript file reference
688 * @return bool TRUE if the javascript file was successfully added, FALSE otherwise
689 */
690 public function addJavascriptFile($javascriptFile) {
691 $jsFileAdded = FALSE;
692 // @todo add more checks if necessary
693 if (file_exists(GeneralUtility::resolveBackPath(PATH_typo3 . $javascriptFile))) {
694 $this->jsFiles[] = $javascriptFile;
695 $jsFileAdded = TRUE;
696 }
697 return $jsFileAdded;
698 }
699
700 /**
701 * Adds a css snippet to the backend
702 *
703 * @param string $css Css snippet
704 * @return void
705 */
706 public function addCss($css) {
707 if (!is_string($css)) {
708 throw new \InvalidArgumentException('parameter $css must be of type string', 1195129642);
709 }
710 $this->css .= $css;
711 }
712
713 /**
714 * Adds a css file to the backend after it has been checked that it exists
715 *
716 * @param string $cssFileName The css file's name with out the .css ending
717 * @param string $cssFile Css file reference
718 * @return bool TRUE if the css file was added, FALSE otherwise
719 */
720 public function addCssFile($cssFileName, $cssFile) {
721 $cssFileAdded = FALSE;
722 if (empty($this->cssFiles[$cssFileName])) {
723 $this->cssFiles[$cssFileName] = $cssFile;
724 $cssFileAdded = TRUE;
725 }
726 return $cssFileAdded;
727 }
728
729 /**
730 * Adds an item to the toolbar, the class file for the toolbar item must be loaded at this point
731 *
732 * @param string $toolbarItemName Toolbar item name, f.e. tx_toolbarExtension_coolItem
733 * @param string $toolbarItemClassName Toolbar item class name, f.e. tx_toolbarExtension_coolItem
734 * @return void
735 * @throws \UnexpectedValueException
736 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Toolbar items are registered in $GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'] now.
737 */
738 public function addToolbarItem($toolbarItemName, $toolbarItemClassName) {
739 GeneralUtility::logDeprecatedFunction();
740 }
741
742 /**
743 * Executes defined hooks functions for the given identifier.
744 *
745 * These hook identifiers are valid:
746 * + constructPostProcess
747 * + renderPreProcess
748 * + renderPostProcess
749 *
750 * @param string $identifier Specific hook identifier
751 * @param array $hookConfiguration Additional configuration passed to hook functions
752 * @return void
753 */
754 protected function executeHook($identifier, array $hookConfiguration = array()) {
755 $options = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/backend.php'];
756 if (isset($options[$identifier]) && is_array($options[$identifier])) {
757 foreach ($options[$identifier] as $hookFunction) {
758 GeneralUtility::callUserFunction($hookFunction, $hookConfiguration, $this);
759 }
760 }
761 }
762
763 /**
764 * loads all modules from the repository
765 * and renders it with a template
766 *
767 * @return string
768 */
769 protected function generateModuleMenu() {
770 // get all modules except the user modules for the side menu
771 $moduleStorage = $this->backendModuleRepository->loadAllowedModules(array('user', 'help'));
772
773 $view = $this->getFluidTemplateObject('EXT:backend/Resources/Private/Templates/ModuleMenu/Main.html');
774 $view->assign('modules', $moduleStorage);
775 return $view->render();
776 }
777
778 /**
779 * Returns the Module menu for the AJAX API
780 *
781 * @param array $params
782 * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler
783 * @return void
784 */
785 public function getModuleMenuForReload($params, $ajaxRequestHandler) {
786 $content = $this->generateModuleMenu();
787 $ajaxRequestHandler->addContent('menu', $content);
788 $ajaxRequestHandler->setContentFormat('json');
789 }
790
791 /**
792 * returns a new standalone view, shorthand function
793 *
794 * @param string $templatePathAndFileName optional the path to set the template path and filename
795 * @return \TYPO3\CMS\Fluid\View\StandaloneView
796 */
797 protected function getFluidTemplateObject($templatePathAndFileName = NULL) {
798 $view = GeneralUtility::makeInstance(\TYPO3\CMS\Fluid\View\StandaloneView::class);
799 if ($templatePathAndFileName) {
800 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templatePathAndFileName));
801 }
802 return $view;
803 }
804
805 }