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