[TASK] Do not store files directly into typo3temp
[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\ServerRequestInterface;
18 use TYPO3\CMS\Backend\Domain\Repository\Module\BackendModuleRepository;
19 use TYPO3\CMS\Backend\Module\ModuleLoader;
20 use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
21 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Core\Http\Response;
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 implements \TYPO3\CMS\Core\Http\ControllerInterface {
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 * @return PageRenderer
97 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
98 */
99 public function getPageRenderer() {
100 GeneralUtility::logDeprecatedFunction();
101 return $this->pageRenderer;
102 }
103
104 /**
105 * Constructor
106 */
107 public function __construct() {
108 $this->getLanguageService()->includeLLFile('EXT:lang/locallang_misc.xlf');
109 $this->backendModuleRepository = GeneralUtility::makeInstance(BackendModuleRepository::class);
110
111 // Set debug flag for BE development only
112 $this->debug = (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] === 1;
113 // Initializes the backend modules structure for use later.
114 $this->moduleLoader = GeneralUtility::makeInstance(ModuleLoader::class);
115 $this->moduleLoader->load($GLOBALS['TBE_MODULES']);
116 $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
117 $this->pageRenderer->loadExtJS();
118 // included for the module menu JavaScript, please note that this is subject to change
119 $this->pageRenderer->loadJquery();
120 $this->pageRenderer->addJsInlineCode('consoleOverrideWithDebugPanel', '//already done', FALSE);
121 $this->pageRenderer->addExtDirectCode();
122 // Add default BE javascript
123 $this->jsFiles = array(
124 'locallang' => $this->getLocalLangFileName(),
125 'md5' => 'sysext/backend/Resources/Public/JavaScript/md5.js',
126 'modulemenu' => 'sysext/backend/Resources/Public/JavaScript/modulemenu.js',
127 'evalfield' => 'sysext/backend/Resources/Public/JavaScript/jsfunc.evalfield.js',
128 'notifications' => 'sysext/backend/Resources/Public/JavaScript/notifications.js',
129 'backend' => 'sysext/backend/Resources/Public/JavaScript/backend.js',
130 'viewport' => 'sysext/backend/Resources/Public/JavaScript/extjs/viewport.js',
131 'iframepanel' => 'sysext/backend/Resources/Public/JavaScript/iframepanel.js',
132 'backendcontentiframe' => 'sysext/backend/Resources/Public/JavaScript/extjs/backendcontentiframe.js',
133 'viewportConfiguration' => 'sysext/backend/Resources/Public/JavaScript/extjs/viewportConfiguration.js',
134 );
135 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LoginRefresh', 'function(LoginRefresh) {
136 LoginRefresh.setLoginFramesetUrl(' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('login_frameset')) . ');
137 LoginRefresh.setLogoutUrl(' . GeneralUtility::quoteJSvalue(BackendUtility::getModuleUrl('logout')) . ');
138 }');
139
140 // load Utility class
141 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Utility');
142
143 // load Notification functionality
144 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Notification');
145
146 // load Modals
147 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Modal');
148
149 // load Legacy CSS Support
150 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LegacyCssClasses');
151
152 // load the storage API and fill the UC into the PersistentStorage, so no additional AJAX call is needed
153 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Storage', 'function(Storage) {
154 Storage.Persistent.load(' . json_encode($this->getBackendUser()->uc) . ');
155 }');
156
157 // load debug console
158 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/DebugConsole');
159
160 // Load RSA encryption
161 $rsaEncryptionEncoder = GeneralUtility::makeInstance(RsaEncryptionEncoder::class);
162 $rsaEncryptionEncoder->enableRsaEncryption(TRUE);
163
164 $this->pageRenderer->addInlineSetting('ShowItem', 'moduleUrl', BackendUtility::getModuleUrl('show_item'));
165
166 $this->css = '';
167
168 $this->initializeToolbarItems();
169 if (isset($GLOBALS['TBE_STYLES']['dims']['leftMenuFrameW'])) {
170 $this->menuWidth = (int)$GLOBALS['TBE_STYLES']['dims']['leftMenuFrameW'];
171 }
172 $this->executeHook('constructPostProcess');
173 $this->includeLegacyBackendItems();
174 }
175
176 /**
177 * Add hooks from the additional backend items to load certain things for the main backend.
178 * This was previously called from the global scope from backend.php.
179 */
180 protected function includeLegacyBackendItems() {
181 $TYPO3backend = $this;
182 // Include extensions which may add css, javascript or toolbar items
183 if (is_array($GLOBALS['TYPO3_CONF_VARS']['typo3/backend.php']['additionalBackendItems'])) {
184 foreach ($GLOBALS['TYPO3_CONF_VARS']['typo3/backend.php']['additionalBackendItems'] as $additionalBackendItem) {
185 include_once $additionalBackendItem;
186 }
187 }
188
189 // Process ExtJS module js and css
190 if (is_array($GLOBALS['TBE_MODULES']['_configuration'])) {
191 foreach ($GLOBALS['TBE_MODULES']['_configuration'] as $moduleConfig) {
192 if (is_array($moduleConfig['cssFiles'])) {
193 foreach ($moduleConfig['cssFiles'] as $cssFileName => $cssFile) {
194 $files = array(\TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($cssFile));
195 $files = \TYPO3\CMS\Core\Utility\GeneralUtility::removePrefixPathFromList($files, PATH_site);
196 $TYPO3backend->addCssFile($cssFileName, '../' . $files[0]);
197 }
198 }
199 if (is_array($moduleConfig['jsFiles'])) {
200 foreach ($moduleConfig['jsFiles'] as $jsFile) {
201 $files = array(\TYPO3\CMS\Core\Utility\GeneralUtility::getFileAbsFileName($jsFile));
202 $files = \TYPO3\CMS\Core\Utility\GeneralUtility::removePrefixPathFromList($files, PATH_site);
203 $TYPO3backend->addJavascriptFile('../' . $files[0]);
204 }
205 }
206 }
207 }
208 }
209
210 /**
211 * Initialize toolbar item objects
212 *
213 * @throws \RuntimeException
214 * @return void
215 */
216 protected function initializeToolbarItems() {
217 $toolbarItemInstances = array();
218 $classNameRegistry = $GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'];
219 foreach ($classNameRegistry as $className) {
220 $toolbarItemInstance = GeneralUtility::makeInstance($className);
221 if (!$toolbarItemInstance instanceof ToolbarItemInterface) {
222 throw new \RuntimeException(
223 'class ' . $className . ' is registered as toolbar item but does not implement'
224 . ToolbarItemInterface::class,
225 1415958218
226 );
227 }
228 $index = (int)$toolbarItemInstance->getIndex();
229 if ($index < 0 || $index > 100) {
230 throw new \RuntimeException(
231 'getIndex() must return an integer between 0 and 100',
232 1415968498
233 );
234 }
235 // Find next free position in array
236 while(array_key_exists($index, $toolbarItemInstances)) {
237 $index++;
238 }
239 $toolbarItemInstances[$index] = $toolbarItemInstance;
240 }
241 ksort($toolbarItemInstances);
242 $this->toolbarItems = $toolbarItemInstances;
243 }
244
245 /**
246 * Injects the request object for the current request or subrequest
247 * As this controller goes only through the render() method, it is rather simple for now
248 * This will be split up in an abstract controller once proper routing/dispatcher is in place.
249 *
250 * @param ServerRequestInterface $request
251 * @return \Psr\Http\Message\ResponseInterface $response
252 */
253 public function processRequest(ServerRequestInterface $request) {
254 $this->render();
255 /** @var Response $response */
256 $response = GeneralUtility::makeInstance(Response::class);
257 $response->getBody()->write($this->content);
258 return $response;
259 }
260
261 /**
262 * Main function generating the BE scaffolding
263 *
264 * @return void
265 */
266 public function render() {
267 $this->executeHook('renderPreProcess');
268
269 // Prepare the scaffolding, at this point extension may still add javascript and css
270 $view = $this->getFluidTemplateObject($this->templatePath . 'Backend/Main.html');
271
272 // Render the TYPO3 logo in the left corner
273 $logoUrl = $GLOBALS['TBE_STYLES']['logo'] ?: 'sysext/backend/Resources/Public/Images/typo3-topbar@2x.png';
274 $logoPath = GeneralUtility::resolveBackPath(PATH_typo3 . $logoUrl);
275 list($logoWidth, $logoHeight) = @getimagesize($logoPath);
276
277 // High-resolution?
278 if (strpos($logoUrl, '@2x.') !== FALSE) {
279 $logoWidth = $logoWidth/2;
280 $logoHeight = $logoHeight/2;
281 }
282
283 $view->assign('logoUrl', $logoUrl);
284 $view->assign('logoWidth', $logoWidth);
285 $view->assign('logoHeight', $logoHeight);
286 $view->assign('logoLink', TYPO3_URL_GENERAL);
287 $view->assign('applicationVersion', TYPO3_version);
288 $view->assign('siteName', $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']);
289 $view->assign('moduleMenu', $this->generateModuleMenu());
290 $view->assign('toolbar', $this->renderToolbar());
291
292 /******************************************************
293 * Now put the complete backend document together
294 ******************************************************/
295 foreach ($this->cssFiles as $cssFileName => $cssFile) {
296 $this->pageRenderer->addCssFile($cssFile);
297 // Load additional css files to overwrite existing core styles
298 if (!empty($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName])) {
299 $this->pageRenderer->addCssFile($GLOBALS['TBE_STYLES']['stylesheets'][$cssFileName]);
300 }
301 }
302 if (!empty($this->css)) {
303 $this->pageRenderer->addCssInlineBlock('BackendInlineCSS', $this->css);
304 }
305 foreach ($this->jsFiles as $jsFile) {
306 $this->pageRenderer->addJsFile($jsFile);
307 }
308 $this->generateJavascript();
309 $this->pageRenderer->addJsInlineCode('BackendInlineJavascript', $this->js, FALSE);
310 $this->loadResourcesForRegisteredNavigationComponents();
311
312 // Add state provider
313 $this->getDocumentTemplate()->setExtDirectStateProvider();
314 $states = $this->getBackendUser()->uc['BackendComponents']['States'];
315 // Save states in BE_USER->uc
316 $extOnReadyCode = '
317 Ext.state.Manager.setProvider(new TYPO3.state.ExtDirectProvider({
318 key: "BackendComponents.States",
319 autoRead: false
320 }));
321 ';
322
323 if ($states) {
324 $extOnReadyCode .= 'Ext.state.Manager.getProvider().initState(' . json_encode($states) . ');';
325 }
326
327 $extOnReadyCode .= '
328 TYPO3.Backend = new TYPO3.Viewport(TYPO3.Viewport.configuration);
329 if (typeof console === "undefined") {
330 console = TYPO3.Backend.DebugConsole;
331 }';
332 $this->pageRenderer->addExtOnReadyCode($extOnReadyCode);
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 = array('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 if (!is_array($GLOBALS['TBE_MODULES']['_navigationComponents'])) {
348 return;
349 }
350 $loadedComponents = array();
351 foreach ($GLOBALS['TBE_MODULES']['_navigationComponents'] as $module => $info) {
352 if (in_array($info['componentId'], $loadedComponents)) {
353 continue;
354 }
355 $loadedComponents[] = $info['componentId'];
356 $component = strtolower(substr($info['componentId'], strrpos($info['componentId'], '-') + 1));
357 $componentDirectory = 'components/' . $component . '/';
358 if ($info['isCoreComponent']) {
359 $absoluteComponentPath = PATH_site . 'typo3/sysext/backend/Resources/Public/JavaScript/extjs/' . $componentDirectory;
360 $relativeComponentPath = '../' . str_replace(PATH_site, '', $absoluteComponentPath);
361 } else {
362 $absoluteComponentPath = ExtensionManagementUtility::extPath($info['extKey']) . $componentDirectory;
363 $relativeComponentPath = ExtensionManagementUtility::extRelPath($info['extKey']) . $componentDirectory;
364 }
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('../', '', GeneralUtility::getUrl($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('../', '', GeneralUtility::getUrl($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 $toolbar = array();
398 foreach ($this->toolbarItems as $toolbarItem) {
399 /** @var \TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface $toolbarItem */
400 if ($toolbarItem->checkAccess()) {
401 $hasDropDown = (bool)$toolbarItem->hasDropDown();
402 $additionalAttributes = (array)$toolbarItem->getAdditionalAttributes();
403
404 $liAttributes = array();
405
406 // Merge class: Add dropdown class if hasDropDown, add classes from additonal attributes
407 $classes = array();
408 if ($hasDropDown) {
409 $classes[] = 'dropdown';
410 }
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(array('_', '\\'), '-', $className);
427 $liAttributes[] = 'id="' . $className . '"';
428
429 $toolbar[] = '<li ' . implode(' ', $liAttributes) . '>';
430
431 if ($hasDropDown) {
432 $toolbar[] = '<a href="#" class="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 $code = $this->generateLocalLang();
456 $filePath = 'typo3temp/Language/Backend-' . sha1($code) . '.js';
457 if (!file_exists(PATH_site . $filePath)) {
458 // writeFileToTypo3tempDir() returns NULL on success (please double-read!)
459 $error = GeneralUtility::writeFileToTypo3tempDir(PATH_site . $filePath, $code);
460 if ($error !== NULL) {
461 throw new \RuntimeException('Locallang JS file could not be written to ' . $filePath . '. Reason: ' . $error, 1295193026);
462 }
463 }
464 return '../' . $filePath;
465 }
466
467 /**
468 * Reads labels required in JavaScript code from the localization system and returns them as JSON
469 * array in TYPO3.LLL.
470 *
471 * @return string JavaScript code containing the LLL labels in TYPO3.LLL
472 */
473 protected function generateLocalLang() {
474 $lang = $this->getLanguageService();
475 $coreLabels = array(
476 'waitTitle' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_logging_in'),
477 'refresh_login_failed' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed'),
478 'refresh_login_failed_message' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_failed_message'),
479 'refresh_login_title' => sprintf($lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_title'), htmlspecialchars($this->getBackendUser()->user['username'])),
480 'login_expired' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_expired'),
481 'refresh_login_username' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_username'),
482 'refresh_login_password' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_password'),
483 'refresh_login_emptyPassword' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_emptyPassword'),
484 'refresh_login_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_button'),
485 'refresh_logout_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_logout_button'),
486 'please_wait' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.please_wait'),
487 'loadingIndicator' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:loadingIndicator'),
488 'be_locked' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.be_locked'),
489 'refresh_login_countdown_singular' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown_singular'),
490 'refresh_login_countdown' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_countdown'),
491 'login_about_to_expire' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_refresh_about_to_expire'),
492 'login_about_to_expire_title' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.login_about_to_expire_title'),
493 'refresh_login_abort_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_abort_button'),
494 'refresh_login_confirm_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_confirm_button'),
495 'refresh_login_refresh_button' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:mess.refresh_login_refresh_button'),
496 'tabs_closeAll' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeAll'),
497 'tabs_closeOther' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.closeOther'),
498 'tabs_close' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.close'),
499 'tabs_openInBrowserWindow' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:tabs.openInBrowserWindow'),
500 'csh_tooltip_loading' => $lang->sL('LLL:EXT:lang/locallang_core.xlf:csh_tooltip_loading')
501 );
502 $labels = array(
503 'fileUpload' => array(
504 'windowTitle',
505 'buttonSelectFiles',
506 'buttonCancelAll',
507 'infoComponentMaxFileSize',
508 'infoComponentFileUploadLimit',
509 'infoComponentFileTypeLimit',
510 'infoComponentOverrideFiles',
511 'processRunning',
512 'uploadWait',
513 'uploadStarting',
514 'uploadProgress',
515 'uploadSuccess',
516 'errorQueueLimitExceeded',
517 'errorQueueFileSizeLimit',
518 'errorQueueZeroByteFile',
519 'errorQueueInvalidFiletype',
520 'errorUploadHttp',
521 'errorUploadMissingUrl',
522 'errorUploadIO',
523 'errorUploadSecurityError',
524 'errorUploadLimit',
525 'errorUploadFailed',
526 'errorUploadFileIDNotFound',
527 'errorUploadFileValidation',
528 'errorUploadFileCancelled',
529 'errorUploadStopped',
530 'allErrorMessageTitle',
531 'allErrorMessageText',
532 'allError401',
533 'allError2038'
534 ),
535 'liveSearch' => array(
536 'title',
537 'helpTitle',
538 'emptyText',
539 'loadingText',
540 'listEmptyText',
541 'showAllResults',
542 'helpDescription',
543 'helpDescriptionPages',
544 'helpDescriptionContent'
545 ),
546 'viewPort' => array(
547 'tooltipModuleMenuSplit',
548 'tooltipNavigationContainerSplitDrag',
549 'tooltipNavigationContainerSplitClick',
550 'tooltipDebugPanelSplitDrag'
551 )
552 );
553 $generatedLabels = array();
554 $generatedLabels['core'] = $coreLabels;
555 // First loop over all categories (fileUpload, liveSearch, ..)
556 foreach ($labels as $categoryName => $categoryLabels) {
557 // Then loop over every single label
558 foreach ($categoryLabels as $label) {
559 // LLL identifier must be called $categoryName_$label, e.g. liveSearch_loadingText
560 $generatedLabels[$categoryName][$label] = $this->getLanguageService()->getLL($categoryName . '_' . $label);
561 }
562 }
563 return 'TYPO3.LLL = ' . json_encode($generatedLabels) . ';';
564 }
565
566 /**
567 * Generates the JavaScript code for the backend.
568 *
569 * @return void
570 */
571 protected function generateJavascript() {
572
573 $beUser = $this->getBackendUser();
574 // Needed for FormEngine manipulation (date picker)
575 $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'));
576 $this->pageRenderer->addInlineSetting('DateTimePicker', 'DateFormat', $dateFormat);
577 // define the window size of the element browser etc.
578 $popupWindowWidth = 700;
579 $popupWindowHeight = 750;
580 $popupWindowSize = trim($beUser->getTSConfigVal('options.popupWindowSize'));
581 if (!empty($popupWindowSize)) {
582 list($popupWindowWidth, $popupWindowHeight) = GeneralUtility::intExplode('x', $popupWindowSize);
583 }
584
585 // define the window size of the popups within the RTE
586 $rtePopupWindowSize = trim($beUser->getTSConfigVal('options.rte.popupWindowSize'));
587 if (!empty($rtePopupWindowSize)) {
588 list($rtePopupWindowWidth, $rtePopupWindowHeight) = GeneralUtility::trimExplode('x', $rtePopupWindowSize);
589 }
590 $rtePopupWindowWidth = !empty($rtePopupWindowWidth) ? (int)$rtePopupWindowWidth : ($popupWindowWidth-200);
591 $rtePopupWindowHeight = !empty($rtePopupWindowHeight) ? (int)$rtePopupWindowHeight : ($popupWindowHeight-250);
592
593 $pathTYPO3 = GeneralUtility::dirname(GeneralUtility::getIndpEnv('SCRIPT_NAME')) . '/';
594 // If another page module was specified, replace the default Page module with the new one
595 $newPageModule = trim($beUser->getTSConfigVal('options.overridePageModule'));
596 $pageModule = BackendUtility::isModuleSetInTBE_MODULES($newPageModule) ? $newPageModule : 'web_layout';
597 if (!$beUser->check('modules', $pageModule)) {
598 $pageModule = '';
599 }
600 $t3Configuration = array(
601 'siteUrl' => GeneralUtility::getIndpEnv('TYPO3_SITE_URL'),
602 'PATH_typo3' => $pathTYPO3,
603 'PATH_typo3_enc' => rawurlencode($pathTYPO3),
604 'username' => htmlspecialchars($beUser->user['username']),
605 'userUid' => htmlspecialchars($beUser->user['uid']),
606 'uniqueID' => GeneralUtility::shortMD5(uniqid('', TRUE)),
607 'securityLevel' => trim($GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel']) ?: 'normal',
608 'TYPO3_mainDir' => TYPO3_mainDir,
609 'pageModule' => $pageModule,
610 'inWorkspace' => $beUser->workspace !== 0,
611 'workspaceFrontendPreviewEnabled' => $beUser->user['workspace_preview'] ? 1 : 0,
612 'veriCode' => $beUser->veriCode(),
613 'denyFileTypes' => PHP_EXTENSIONS_DEFAULT,
614 'moduleMenuWidth' => $this->menuWidth - 1,
615 'topBarHeight' => isset($GLOBALS['TBE_STYLES']['dims']['topFrameH']) ? (int)$GLOBALS['TBE_STYLES']['dims']['topFrameH'] : 30,
616 'showRefreshLoginPopup' => isset($GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup']) ? (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['showRefreshLoginPopup'] : FALSE,
617 'listModulePath' => ExtensionManagementUtility::extRelPath('recordlist') . 'mod1/',
618 'debugInWindow' => $beUser->uc['debugInWindow'] ? 1 : 0,
619 'ContextHelpWindows' => array(
620 'width' => 600,
621 'height' => 400
622 ),
623 'PopupWindow' => array(
624 'width' => $popupWindowWidth,
625 'height' => $popupWindowHeight
626 ),
627 'RTEPopupWindow' => array(
628 'width' => $rtePopupWindowWidth,
629 'height' => $rtePopupWindowHeight
630 )
631 );
632 $this->js .= '
633 TYPO3.configuration = ' . json_encode($t3Configuration) . ';
634
635 /**
636 * TypoSetup object.
637 */
638 function typoSetup() { //
639 this.PATH_typo3 = TYPO3.configuration.PATH_typo3;
640 this.PATH_typo3_enc = TYPO3.configuration.PATH_typo3_enc;
641 this.username = TYPO3.configuration.username;
642 this.uniqueID = TYPO3.configuration.uniqueID;
643 this.securityLevel = TYPO3.configuration.securityLevel;
644 this.veriCode = TYPO3.configuration.veriCode;
645 this.denyFileTypes = TYPO3.configuration.denyFileTypes;
646 }
647 var TS = new typoSetup();
648 //backwards compatibility
649 /**
650 * Frameset Module object
651 *
652 * Used in main modules with a frameset for submodules to keep the ID between modules
653 * Typically that is set by something like this in a Web>* sub module:
654 * if (top.fsMod) top.fsMod.recentIds["web"] = "\'.(int)$this->id.\'";
655 * if (top.fsMod) top.fsMod.recentIds["file"] = "...(file reference/string)...";
656 */
657 function fsModules() { //
658 this.recentIds=new Array(); // used by frameset modules to track the most recent used id for list frame.
659 this.navFrameHighlightedID=new Array(); // used by navigation frames to track which row id was highlighted last time
660 this.currentMainLoaded="";
661 this.currentBank="0";
662 }
663 var fsMod = new fsModules();
664
665 top.goToModule = function(modName, cMR_flag, addGetVars) {
666 TYPO3.ModuleMenu.App.showModule(modName, addGetVars);
667 }
668 ' . $this->setStartupModule();
669 // Check editing of page:
670 $this->handlePageEditing();
671 }
672
673 /**
674 * Checking if the "&edit" variable was sent so we can open it for editing the page.
675 *
676 * @return void
677 */
678 protected function handlePageEditing() {
679 $beUser = $this->getBackendUser();
680 // EDIT page:
681 $editId = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('edit'));
682 $editRecord = '';
683 if ($editId) {
684 // Looking up the page to edit, checking permissions:
685 $where = ' AND (' . $beUser->getPagePermsClause(2) . ' OR ' . $beUser->getPagePermsClause(16) . ')';
686 if (MathUtility::canBeInterpretedAsInteger($editId)) {
687 $editRecord = BackendUtility::getRecordWSOL('pages', $editId, '*', $where);
688 } else {
689 $records = BackendUtility::getRecordsByField('pages', 'alias', $editId, $where);
690 if (is_array($records)) {
691 $editRecord = reset($records);
692 BackendUtility::workspaceOL('pages', $editRecord);
693 }
694 }
695 // If the page was accessible, then let the user edit it.
696 if (is_array($editRecord) && $beUser->isInWebMount($editRecord['uid'])) {
697 // Setting JS code to open editing:
698 $this->js .= '
699 // Load page to edit:
700 window.setTimeout("top.loadEditId(' . (int)$editRecord['uid'] . ');", 500);
701 ';
702 // Checking page edit parameter:
703 if (!$beUser->getTSConfigVal('options.bookmark_onEditId_dontSetPageTree')) {
704 $bookmarkKeepExpanded = $beUser->getTSConfigVal('options.bookmark_onEditId_keepExistingExpanded');
705 // Expanding page tree:
706 BackendUtility::openPageTree((int)$editRecord['pid'], !$bookmarkKeepExpanded);
707 }
708 } else {
709 $this->js .= '
710 // Warning about page editing:
711 alert(' . GeneralUtility::quoteJSvalue(sprintf($this->getLanguageService()->getLL('noEditPage'), $editId)) . ');
712 ';
713 }
714 }
715 }
716
717 /**
718 * Sets the startup module from either GETvars module and modParams or user configuration.
719 *
720 * @return string the JavaScript code for the startup module
721 */
722 protected function setStartupModule() {
723 $startModule = preg_replace('/[^[:alnum:]_]/', '', GeneralUtility::_GET('module'));
724 $startModuleParameters = '';
725 if (!$startModule) {
726 $beUser = $this->getBackendUser();
727 // start module on first login, will be removed once used the first time
728 if (isset($beUser->uc['startModuleOnFirstLogin'])) {
729 $startModule = $beUser->uc['startModuleOnFirstLogin'];
730 unset($beUser->uc['startModuleOnFirstLogin']);
731 $beUser->writeUC();
732 } elseif ($beUser->uc['startModule']) {
733 $startModule = $beUser->uc['startModule'];
734 } elseif ($beUser->uc['startInTaskCenter']) {
735 $startModule = 'user_task';
736 }
737
738 // check if the start module has additional parameters, so a redirect to a specific
739 // action is possible
740 if (strpos($startModule, '->') !== FALSE) {
741 list($startModule, $startModuleParameters) = explode('->', $startModule, 2);
742 }
743 }
744
745 $moduleParameters = GeneralUtility::_GET('modParams');
746 // if no GET parameters are set, check if there are parameters given from the UC
747 if (!$moduleParameters && $startModuleParameters) {
748 $moduleParameters = $startModuleParameters;
749 }
750
751 if ($startModule) {
752 return '
753 // start in module:
754 top.startInModule = [' . GeneralUtility::quoteJSvalue($startModule) . ', ' . GeneralUtility::quoteJSvalue($moduleParameters) . '];
755 ';
756 } else {
757 return '';
758 }
759 }
760
761 /**
762 * Adds a javascript snippet to the backend
763 *
764 * @param string $javascript Javascript snippet
765 * @return void
766 * @throws \InvalidArgumentException
767 */
768 public function addJavascript($javascript) {
769 // @todo do we need more checks?
770 if (!is_string($javascript)) {
771 throw new \InvalidArgumentException('parameter $javascript must be of type string', 1195129553);
772 }
773 $this->js .= $javascript;
774 }
775
776 /**
777 * Adds a javscript file to the backend after it has been checked that it exists
778 *
779 * @param string $javascriptFile Javascript file reference
780 * @return bool TRUE if the javascript file was successfully added, FALSE otherwise
781 */
782 public function addJavascriptFile($javascriptFile) {
783 $jsFileAdded = FALSE;
784 // @todo add more checks if necessary
785 if (file_exists(GeneralUtility::resolveBackPath(PATH_typo3 . $javascriptFile))) {
786 $this->jsFiles[] = $javascriptFile;
787 $jsFileAdded = TRUE;
788 }
789 return $jsFileAdded;
790 }
791
792 /**
793 * Adds a css snippet to the backend
794 *
795 * @param string $css Css snippet
796 * @return void
797 * @throws \InvalidArgumentException
798 */
799 public function addCss($css) {
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 $cssFileAdded = FALSE;
815 if (empty($this->cssFiles[$cssFileName])) {
816 $this->cssFiles[$cssFileName] = $cssFile;
817 $cssFileAdded = TRUE;
818 }
819 return $cssFileAdded;
820 }
821
822 /**
823 * Adds an item to the toolbar, the class file for the toolbar item must be loaded at this point
824 *
825 * @param string $toolbarItemName Toolbar item name, f.e. tx_toolbarExtension_coolItem
826 * @param string $toolbarItemClassName Toolbar item class name, f.e. tx_toolbarExtension_coolItem
827 * @return void
828 * @throws \UnexpectedValueException
829 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Toolbar items are registered in $GLOBALS['TYPO3_CONF_VARS']['BE']['toolbarItems'] now.
830 */
831 public function addToolbarItem($toolbarItemName, $toolbarItemClassName) {
832 GeneralUtility::logDeprecatedFunction();
833 }
834
835 /**
836 * Executes defined hooks functions for the given identifier.
837 *
838 * These hook identifiers are valid:
839 * + constructPostProcess
840 * + renderPreProcess
841 * + renderPostProcess
842 *
843 * @param string $identifier Specific hook identifier
844 * @param array $hookConfiguration Additional configuration passed to hook functions
845 * @return void
846 */
847 protected function executeHook($identifier, array $hookConfiguration = array()) {
848 $options = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/backend.php'];
849 if (isset($options[$identifier]) && is_array($options[$identifier])) {
850 foreach ($options[$identifier] as $hookFunction) {
851 GeneralUtility::callUserFunction($hookFunction, $hookConfiguration, $this);
852 }
853 }
854 }
855
856 /**
857 * loads all modules from the repository
858 * and renders it with a template
859 *
860 * @return string
861 */
862 protected function generateModuleMenu() {
863 // get all modules except the user modules for the side menu
864 $moduleStorage = $this->backendModuleRepository->loadAllowedModules(array('user', 'help'));
865
866 $view = $this->getFluidTemplateObject($this->templatePath . 'ModuleMenu/Main.html');
867 $view->assign('modules', $moduleStorage);
868 return $view->render();
869 }
870
871 /**
872 * Returns the Module menu for the AJAX API
873 *
874 * @param array $params
875 * @param \TYPO3\CMS\Core\Http\AjaxRequestHandler $ajaxRequestHandler
876 * @return void
877 */
878 public function getModuleMenuForReload($params, $ajaxRequestHandler) {
879 $content = $this->generateModuleMenu();
880 $ajaxRequestHandler->addContent('menu', $content);
881 $ajaxRequestHandler->setContentFormat('json');
882 }
883
884 /**
885 * returns a new standalone view, shorthand function
886 *
887 * @param string $templatePathAndFileName optional the path to set the template path and filename
888 * @return \TYPO3\CMS\Fluid\View\StandaloneView
889 */
890 protected function getFluidTemplateObject($templatePathAndFileName = NULL) {
891 $view = GeneralUtility::makeInstance(StandaloneView::class);
892 if ($templatePathAndFileName) {
893 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templatePathAndFileName));
894 }
895 return $view;
896 }
897
898 /**
899 * Returns LanguageService
900 *
901 * @return \TYPO3\CMS\Lang\LanguageService
902 */
903 protected function getLanguageService() {
904 return $GLOBALS['LANG'];
905 }
906
907 /**
908 * Returns the current BE user.
909 *
910 * @return \TYPO3\CMS\Core\Authentication\BackendUserAuthentication
911 */
912 protected function getBackendUser() {
913 return $GLOBALS['BE_USER'];
914 }
915
916 /**
917 * Returns an instance of DocumentTemplate
918 *
919 * @return \TYPO3\CMS\Backend\Template\DocumentTemplate
920 */
921 protected function getDocumentTemplate() {
922 return $GLOBALS['TBE_TEMPLATE'];
923 }
924
925 }