[TASK] Delete unused iframepanel JavaScript code
[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->addExtDirectCode();
116 // Add default BE javascript
117 $this->jsFiles = [
118 'locallang' => $this->getLocalLangFileName(),
119 'md5' => 'EXT:backend/Resources/Public/JavaScript/md5.js',
120 'evalfield' => 'EXT:backend/Resources/Public/JavaScript/jsfunc.evalfield.js',
121 'backend' => 'EXT:backend/Resources/Public/JavaScript/backend.js',
122 ];
123 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LoginRefresh', 'function(LoginRefresh) {
124 LoginRefresh.setIntervalTime(' . MathUtility::forceIntegerInRange((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['sessionTimeout'] - 60, 60) . ');
125 LoginRefresh.setLoginFramesetUrl(' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('login_frameset')) . ');
126 LoginRefresh.setLogoutUrl(' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('logout')) . ');
127 LoginRefresh.initialize();
128 }');
129
130 // load module menu
131 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ModuleMenu');
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::getRelativePath(PATH_site . TYPO3_mainDir, $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 'ContextHelpWindows' => [
586 'width' => 600,
587 'height' => 400
588 ]
589 ];
590 $this->js .= '
591 TYPO3.configuration = ' . json_encode($t3Configuration) . ';
592
593 /**
594 * TypoSetup object.
595 */
596 function typoSetup() { //
597 this.username = TYPO3.configuration.username;
598 this.uniqueID = TYPO3.configuration.uniqueID;
599 }
600 var TS = new typoSetup();
601 //backwards compatibility
602 /**
603 * Frameset Module object
604 *
605 * Used in main modules with a frameset for submodules to keep the ID between modules
606 * Typically that is set by something like this in a Web>* sub module:
607 * if (top.fsMod) top.fsMod.recentIds["web"] = "\'.(int)$this->id.\'";
608 * if (top.fsMod) top.fsMod.recentIds["file"] = "...(file reference/string)...";
609 */
610 function fsModules() { //
611 this.recentIds=new Array(); // used by frameset modules to track the most recent used id for list frame.
612 this.navFrameHighlightedID=new Array(); // used by navigation frames to track which row id was highlighted last time
613 this.currentBank="0";
614 }
615 var fsMod = new fsModules();
616
617 top.goToModule = function(modName, cMR_flag, addGetVars) {
618 TYPO3.ModuleMenu.App.showModule(modName, addGetVars);
619 }
620 ' . $this->setStartupModule();
621 // Check editing of page:
622 $this->handlePageEditing();
623 }
624
625 /**
626 * Checking if the "&edit" variable was sent so we can open it for editing the page.
627 *
628 * @return void
629 */
630 protected function handlePageEditing()
631 {
632 $beUser = $this->getBackendUser();
633 // EDIT page:
634 $editId = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('edit'));
635 $editRecord = '';
636 if ($editId) {
637 // Looking up the page to edit, checking permissions:
638 $where = ' AND (' . $beUser->getPagePermsClause(2) . ' OR ' . $beUser->getPagePermsClause(16) . ')';
639 if (MathUtility::canBeInterpretedAsInteger($editId)) {
640 $editRecord = BackendUtility::getRecordWSOL('pages', $editId, '*', $where);
641 } else {
642 $records = BackendUtility::getRecordsByField('pages', 'alias', $editId, $where);
643 if (is_array($records)) {
644 $editRecord = reset($records);
645 BackendUtility::workspaceOL('pages', $editRecord);
646 }
647 }
648 // If the page was accessible, then let the user edit it.
649 if (is_array($editRecord) && $beUser->isInWebMount($editRecord['uid'])) {
650 // Setting JS code to open editing:
651 $this->js .= '
652 // Load page to edit:
653 window.setTimeout("top.loadEditId(' . (int)$editRecord['uid'] . ');", 500);
654 ';
655 // Checking page edit parameter:
656 if (!$beUser->getTSConfigVal('options.bookmark_onEditId_dontSetPageTree')) {
657 $bookmarkKeepExpanded = $beUser->getTSConfigVal('options.bookmark_onEditId_keepExistingExpanded');
658 // Expanding page tree:
659 BackendUtility::openPageTree((int)$editRecord['pid'], !$bookmarkKeepExpanded);
660 }
661 } else {
662 $this->js .= '
663 // Warning about page editing:
664 require(["TYPO3/CMS/Backend/Modal", "TYPO3/CMS/Backend/Severity"], function(Modal, Severity) {
665 Modal.show("", ' . GeneralUtility::quoteJSvalue(sprintf($this->getLanguageService()->getLL('noEditPage'), $editId)) . ', Severity.notice, [{
666 text: ' . GeneralUtility::quoteJSvalue($this->getLanguageService()->sL('LLL:EXT:lang/locallang_common.xlf:close')) . ',
667 active: true,
668 btnClass: "btn-info",
669 name: "cancel",
670 trigger: function () {
671 Modal.currentModal.trigger("modal-dismiss");
672 }
673 }])
674 });';
675 }
676 }
677 }
678
679 /**
680 * Sets the startup module from either GETvars module and modParams or user configuration.
681 *
682 * @return string the JavaScript code for the startup module
683 */
684 protected function setStartupModule()
685 {
686 $startModule = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('module'));
687 $startModuleParameters = '';
688 if (!$startModule) {
689 $beUser = $this->getBackendUser();
690 // start module on first login, will be removed once used the first time
691 if (isset($beUser->uc['startModuleOnFirstLogin'])) {
692 $startModule = $beUser->uc['startModuleOnFirstLogin'];
693 unset($beUser->uc['startModuleOnFirstLogin']);
694 $beUser->writeUC();
695 } elseif ($beUser->uc['startModule']) {
696 $startModule = $beUser->uc['startModule'];
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 }