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