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