[TASK] Deprecate BackendUtility::getListViewLink()
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Template / DocumentTemplate.php
1 <?php
2 namespace TYPO3\CMS\Backend\Template;
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\Utility\BackendUtility;
20 use TYPO3\CMS\Core\Imaging\Icon;
21 use TYPO3\CMS\Core\Imaging\IconFactory;
22 use TYPO3\CMS\Core\Page\PageRenderer;
23 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
24 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
25 use TYPO3\CMS\Core\Utility\GeneralUtility;
26
27 /**
28 * TYPO3 Backend Template Class
29 *
30 * This class contains functions for starting and ending the HTML of backend modules
31 * It also contains methods for outputting sections of content.
32 * Further there are functions for making icons, links, setting form-field widths etc.
33 * Color scheme and stylesheet definitions are also available here.
34 * Finally this file includes the language class for TYPO3's backend.
35 *
36 * After this file $LANG and $TBE_TEMPLATE are global variables / instances of their respective classes.
37 *
38 * Please refer to Inside TYPO3 for a discussion of how to use this API.
39 */
40 class DocumentTemplate
41 {
42 // Vars you typically might want to/should set from outside after making instance of this class:
43 /**
44 * This can be set to the HTML-code for a formtag.
45 * Useful when you need a form to span the whole page; Inserted exactly after the body-tag.
46 *
47 * @var string
48 */
49 public $form = '';
50
51 /**
52 * Additional header code (eg. a JavaScript section) could be accommulated in this var. It will be directly outputted in the header.
53 *
54 * @var string
55 */
56 public $JScode = '';
57
58 /**
59 * Additional header code for ExtJS. It will be included in document header and inserted in a Ext.onReady(function()
60 *
61 * @var string
62 */
63 public $extJScode = '';
64
65 /**
66 * Similar to $JScode but for use as array with associative keys to prevent double inclusion of JS code. a <script> tag is automatically wrapped around.
67 *
68 * @var array
69 */
70 public $JScodeArray = array('jumpToUrl' => '
71 function jumpToUrl(URL) {
72 window.location.href = URL;
73 return false;
74 }
75 ');
76
77 /**
78 * Additional 'page-end' code could be accumulated in this var. It will be outputted at the end of page before </body> and some other internal page-end code.
79 *
80 * @var string
81 */
82 public $postCode = '';
83
84 /**
85 * HTML template with markers for module
86 *
87 * @var string
88 */
89 public $moduleTemplate = '';
90
91 /**
92 * The base file (not overlaid by TBE_STYLES) for the current module, useful for hooks when finding out which modules is rendered currently
93 *
94 * @var string
95 */
96 protected $moduleTemplateFilename = '';
97
98 /**
99 * Script ID
100 *
101 * @var string
102 */
103 public $scriptID = '';
104
105 /**
106 * Id which can be set for the body tag. Default value is based on script ID
107 *
108 * @var string
109 */
110 public $bodyTagId = '';
111
112 /**
113 * You can add additional attributes to the body-tag through this variable.
114 *
115 * @var string
116 */
117 public $bodyTagAdditions = '';
118
119 /**
120 * Additional CSS styles which will be added to the <style> section in the header
121 * used as array with associative keys to prevent double inclusion of CSS code
122 *
123 * @var array
124 */
125 public $inDocStylesArray = array();
126
127 /**
128 * Compensation for large documents (used in \TYPO3\CMS\Backend\Form\FormEngine)
129 *
130 * @var float
131 */
132 public $form_largeComp = 1.33;
133
134 /**
135 * Filename of stylesheet (relative to PATH_typo3)
136 *
137 * @var string
138 */
139 public $styleSheetFile = '';
140
141 /**
142 * Filename of stylesheet #2 - linked to right after the $this->styleSheetFile script (relative to PATH_typo3)
143 *
144 * @var string
145 */
146 public $styleSheetFile2 = '';
147
148 /**
149 * Filename of a post-stylesheet - included right after all inline styles.
150 *
151 * @var string
152 */
153 public $styleSheetFile_post = '';
154
155 /**
156 * Whether to use the X-UA-Compatible meta tag
157 *
158 * @var bool
159 */
160 protected $useCompatibilityTag = true;
161
162 /**
163 * X-Ua-Compatible version output in meta tag
164 *
165 * @var string
166 */
167 protected $xUaCompatibilityVersion = 'IE=edge';
168
169 // Skinning
170 /**
171 * Include these CSS directories from skins by default
172 *
173 * @var array
174 */
175 protected $stylesheetsSkins = array(
176 'structure' => 'Resources/Public/Css/structure/',
177 'visual' => 'Resources/Public/Css/visual/'
178 );
179
180 /**
181 * JavaScript files loaded for every page in the Backend
182 *
183 * @var array
184 */
185 protected $jsFiles = array();
186
187 /**
188 * JavaScript files loaded for every page in the Backend, but explicitly excluded from concatenation (useful for libraries etc.)
189 *
190 * @var array
191 */
192 protected $jsFilesNoConcatenation = array();
193
194 /**
195 * Indicates if a <div>-output section is open
196 *
197 * @var int
198 * @internal
199 */
200 public $sectionFlag = 0;
201
202 /**
203 * (Default) Class for wrapping <DIV>-tag of page. Is set in class extensions.
204 *
205 * @var string
206 */
207 public $divClass = '';
208
209 /**
210 * @var string
211 */
212 public $pageHeaderBlock = '';
213
214 /**
215 * @var string
216 */
217 public $endOfPageJsBlock = '';
218
219 /**
220 * @var bool
221 */
222 public $hasDocheader = true;
223
224 /**
225 * @var PageRenderer
226 */
227 protected $pageRenderer = null;
228
229 /**
230 * Alternative template file
231 *
232 * @var string
233 */
234 protected $pageHeaderFooterTemplateFile = '';
235
236 /**
237 * @var bool
238 */
239 protected $extDirectStateProvider = false;
240
241 /**
242 * Whether flashmessages should be rendered or not
243 *
244 * @var bool $showFlashMessages
245 */
246 public $showFlashMessages = true;
247
248 /**
249 * @var IconFactory
250 */
251 protected $iconFactory;
252
253 /**
254 * @var MarkerBasedTemplateService
255 */
256 protected $templateService;
257
258 const STATUS_ICON_ERROR = 3;
259 const STATUS_ICON_WARNING = 2;
260 const STATUS_ICON_NOTIFICATION = 1;
261 const STATUS_ICON_OK = -1;
262
263 /**
264 * Constructor
265 */
266 public function __construct()
267 {
268 // Initializes the page rendering object:
269 $this->initPageRenderer();
270
271 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
272
273 // initialize Marker Support
274 $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
275
276 // Setting default scriptID:
277 $this->scriptID = GeneralUtility::_GET('M') !== null ? GeneralUtility::_GET('M') : GeneralUtility::_GET('route');
278 $this->bodyTagId = preg_replace('/[^A-Za-z0-9-]/', '-', $this->scriptID);
279 // Individual configuration per script? If so, make a recursive merge of the arrays:
280 if (is_array($GLOBALS['TBE_STYLES']['scriptIDindex'][$this->scriptID])) {
281 // Make copy
282 $ovr = $GLOBALS['TBE_STYLES']['scriptIDindex'][$this->scriptID];
283 // merge styles.
284 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['TBE_STYLES'], $ovr);
285 // Have to unset - otherwise the second instantiation will do it again!
286 unset($GLOBALS['TBE_STYLES']['scriptIDindex'][$this->scriptID]);
287 }
288 // Main Stylesheets:
289 if ($GLOBALS['TBE_STYLES']['stylesheet']) {
290 $this->styleSheetFile = $GLOBALS['TBE_STYLES']['stylesheet'];
291 }
292 if ($GLOBALS['TBE_STYLES']['stylesheet2']) {
293 $this->styleSheetFile2 = $GLOBALS['TBE_STYLES']['stylesheet2'];
294 }
295 if ($GLOBALS['TBE_STYLES']['styleSheetFile_post']) {
296 $this->styleSheetFile_post = $GLOBALS['TBE_STYLES']['styleSheetFile_post'];
297 }
298 if ($GLOBALS['TBE_STYLES']['inDocStyles_TBEstyle']) {
299 $this->inDocStylesArray['TBEstyle'] = $GLOBALS['TBE_STYLES']['inDocStyles_TBEstyle'];
300 }
301 // include all stylesheets
302 foreach ($this->getSkinStylesheetDirectories() as $stylesheetDirectory) {
303 $this->addStylesheetDirectory($stylesheetDirectory);
304 }
305 }
306
307 /**
308 * Initializes the page renderer object
309 */
310 protected function initPageRenderer()
311 {
312 if ($this->pageRenderer !== null) {
313 return;
314 }
315 $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
316 $this->pageRenderer->setLanguage($GLOBALS['LANG']->lang);
317 $this->pageRenderer->enableConcatenateFiles();
318 $this->pageRenderer->enableCompressCss();
319 $this->pageRenderer->enableCompressJavascript();
320 // Add all JavaScript files defined in $this->jsFiles to the PageRenderer
321 foreach ($this->jsFilesNoConcatenation as $file) {
322 $this->pageRenderer->addJsFile(
323 $file,
324 'text/javascript',
325 true,
326 false,
327 '',
328 true
329 );
330 }
331 // Add all JavaScript files defined in $this->jsFiles to the PageRenderer
332 foreach ($this->jsFiles as $file) {
333 $this->pageRenderer->addJsFile($file);
334 }
335 if ((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] === 1) {
336 $this->pageRenderer->enableDebugMode();
337 }
338 }
339
340 /**
341 * Sets inclusion of StateProvider
342 *
343 * @return void
344 */
345 public function setExtDirectStateProvider()
346 {
347 $this->extDirectStateProvider = true;
348 }
349
350 /*****************************************
351 *
352 * EVALUATION FUNCTIONS
353 * Various centralized processing
354 *
355 *****************************************/
356
357 /**
358 * Makes link to page $id in frontend (view page)
359 * Returns an icon which links to the frontend index.php document for viewing the page with id $id
360 * $id must be a page-uid
361 * If the BE_USER has access to Web>List then a link to that module is shown as well (with return-url)
362 *
363 * @param int $id The page id
364 * @return string HTML string with linked icon(s)
365 */
366 public function viewPageIcon($id)
367 {
368 // If access to Web>List for user, then link to that module.
369 $str = '<a href="' . htmlspecialchars(BackendUtility::getModuleUrl('web_list', [
370 'id' => $id,
371 'returnUrl' > GeneralUtility::getIndpEnv('REQUEST_URI')
372 ])) . '" title="' . htmlspecialchars($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.showList')) . '">' . $this->iconFactory->getIcon('actions-system-list-open', Icon::SIZE_SMALL)->render() . '</a>';
373
374 // Make link to view page
375 $str .= '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($id, '', BackendUtility::BEgetRootLine($id))) . '" title="' . htmlspecialchars($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage')) . '">' . $this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL)->render() . '</a>';
376 return $str;
377 }
378
379 /**
380 * Makes the header (icon+title) for a page (or other record). Used in most modules under Web>*
381 * $table and $row must be a tablename/record from that table
382 * $path will be shown as alt-text for the icon.
383 * The title will be truncated to 45 chars.
384 *
385 * @param string $table Table name
386 * @param array $row Record row
387 * @param string $path Alt text
388 * @param bool $noViewPageIcon Set $noViewPageIcon TRUE if you don't want a magnifier-icon for viewing the page in the frontend
389 * @param array $tWrap is an array with indexes 0 and 1 each representing HTML-tags (start/end) which will wrap the title
390 * @param bool $enableClickMenu If TRUE, render click menu code around icon image
391 * @return string HTML content
392 */
393 public function getHeader($table, $row, $path, $noViewPageIcon = false, $tWrap = array('', ''), $enableClickMenu = true)
394 {
395 $viewPage = '';
396 if (is_array($row) && $row['uid']) {
397 $iconImgTag = '<span title="' . htmlspecialchars($path) . '">' . $this->iconFactory->getIconForRecord($table, $row, Icon::SIZE_SMALL)->render() . '</span>';
398 $title = strip_tags(BackendUtility::getRecordTitle($table, $row));
399 $viewPage = $noViewPageIcon ? '' : $this->viewPageIcon($row['uid']);
400 } else {
401 $iconImgTag = '<span title="' . htmlspecialchars($path) . '">' . $this->iconFactory->getIcon('apps-pagetree-page-domain', Icon::SIZE_SMALL)->render() . '</span>';
402 $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
403 }
404
405 if ($enableClickMenu) {
406 $iconImgTag = BackendUtility::wrapClickMenuOnIcon($iconImgTag, $table, $row['uid']);
407 }
408
409 return '<span class="typo3-moduleHeader">' . $iconImgTag . $viewPage . $tWrap[0] . htmlspecialchars(GeneralUtility::fixed_lgd_cs($title, 45)) . $tWrap[1] . '</span>';
410 }
411
412 /**
413 * Like ->getHeader() but for files and folders
414 * Returns the icon with the path of the file/folder set in the alt/title attribute. Shows the name after the icon.
415 *
416 * @param \TYPO3\CMS\Core\Resource\ResourceInterface $resource
417 * @param array $tWrap is an array with indexes 0 and 1 each representing HTML-tags (start/end) which will wrap the title
418 * @param bool $enableClickMenu If TRUE, render click menu code around icon image
419 * @return string
420 */
421 public function getResourceHeader(\TYPO3\CMS\Core\Resource\ResourceInterface $resource, $tWrap = array('', ''), $enableClickMenu = true)
422 {
423 try {
424 $path = $resource->getStorage()->getName() . $resource->getParentFolder()->getIdentifier();
425 $iconImgTag = '<span title="' . htmlspecialchars($path) . '">' . $this->iconFactory->getIconForResource($resource, Icon::SIZE_SMALL)->render() . '</span>';
426 } catch (\TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException $e) {
427 $iconImgTag = '';
428 }
429
430 if ($enableClickMenu && ($resource instanceof \TYPO3\CMS\Core\Resource\File)) {
431 $metaData = $resource->_getMetaData();
432 $iconImgTag = BackendUtility::wrapClickMenuOnIcon($iconImgTag, 'sys_file_metadata', $metaData['uid']);
433 }
434
435 return '<span class="typo3-moduleHeader">' . $iconImgTag . $tWrap[0] . htmlspecialchars(GeneralUtility::fixed_lgd_cs($resource->getName(), 45)) . $tWrap[1] . '</span>';
436 }
437
438 /**
439 * Returns a linked shortcut-icon which will call the shortcut frame and set a shortcut there back to the calling page/module
440 *
441 * @param string $gvList Is the list of GET variables to store (if any)
442 * @param string $setList Is the list of SET[] variables to store (if any) - SET[] variables a stored in $GLOBALS["SOBE"]->MOD_SETTINGS for backend modules
443 * @param string $modName Module name string
444 * @param string|int $motherModName Is used to enter the "parent module name" if the module is a submodule under eg. Web>* or File>*. You can also set this value to 1 in which case the currentLoadedModule is sent to the shortcut script (so - not a fixed value!) - that is used in file_edit and wizard_rte modules where those are really running as a part of another module.
445 * @param string $classes
446 * @return string HTML content
447 */
448 public function makeShortcutIcon($gvList, $setList, $modName, $motherModName = '', $classes = '')
449 {
450 $gvList = 'route,' . $gvList;
451 $storeUrl = $this->makeShortcutUrl($gvList, $setList);
452 $pathInfo = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
453 // Fallback for alt_mod. We still pass in the old xMOD... stuff, but TBE_MODULES only knows about "record_edit".
454 // We still need to pass the xMOD name to createShortcut below, since this is used for icons.
455 $moduleName = $modName === 'xMOD_alt_doc.php' ? 'record_edit' : $modName;
456 // Add the module identifier automatically if typo3/index.php is used:
457 if (GeneralUtility::_GET('M') !== null) {
458 $storeUrl = '&M=' . $moduleName . $storeUrl;
459 }
460 if ((int)$motherModName === 1) {
461 $motherModule = 'top.currentModuleLoaded';
462 } elseif ($motherModName) {
463 $motherModule = GeneralUtility::quoteJSvalue($motherModName);
464 } else {
465 $motherModule = '\'\'';
466 }
467 $confirmationText = GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.makeBookmark'));
468
469 $shortcutUrl = $pathInfo['path'] . '?' . $storeUrl;
470 $shortcutExist = BackendUtility::shortcutExists($shortcutUrl);
471
472 if ($shortcutExist) {
473 return '<a class="active ' . htmlspecialchars($classes) . '" title="">' .
474 $this->iconFactory->getIcon('actions-system-shortcut-active', Icon::SIZE_SMALL)->render() . '</a>';
475 }
476 $url = GeneralUtility::quoteJSvalue(rawurlencode($shortcutUrl));
477 $onClick = 'top.TYPO3.ShortcutMenu.createShortcut(' . GeneralUtility::quoteJSvalue(rawurlencode($modName)) .
478 ', ' . $url . ', ' . $confirmationText . ', ' . $motherModule . ', this);return false;';
479
480 return '<a href="#" class="' . htmlspecialchars($classes) . '" onclick="' . htmlspecialchars($onClick) . '" title="' .
481 $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.makeBookmark', true) . '">' .
482 $this->iconFactory->getIcon('actions-system-shortcut-new', Icon::SIZE_SMALL)->render() . '</a>';
483 }
484
485 /**
486 * MAKE url for storing
487 * Internal func
488 *
489 * @param string $gvList Is the list of GET variables to store (if any)
490 * @param string $setList Is the list of SET[] variables to store (if any) - SET[] variables a stored in $GLOBALS["SOBE"]->MOD_SETTINGS for backend modules
491 * @return string
492 * @access private
493 * @see makeShortcutIcon()
494 */
495 public function makeShortcutUrl($gvList, $setList)
496 {
497 $GET = GeneralUtility::_GET();
498 $storeArray = array_merge(GeneralUtility::compileSelectedGetVarsFromArray($gvList, $GET), array('SET' => GeneralUtility::compileSelectedGetVarsFromArray($setList, (array)$GLOBALS['SOBE']->MOD_SETTINGS)));
499 $storeUrl = GeneralUtility::implodeArrayForUrl('', $storeArray);
500 return $storeUrl;
501 }
502
503 /**
504 * Returns <input> attributes to set the width of an text-type input field.
505 * For client browsers with no CSS support the cols/size attribute is returned.
506 * For CSS compliant browsers (recommended) a ' style="width: ...px;"' is returned.
507 *
508 * @param int $size A relative number which multiplied with approx. 10 will lead to the width in pixels
509 * @param bool $textarea A flag you can set for textareas - DEPRECATED as there is no difference any more between the two
510 * @param string $styleOverride A string which will be returned as attribute-value for style="" instead of the calculated width (if CSS is enabled)
511 * @return string Tag attributes for an <input> tag (regarding width)
512 */
513 public function formWidth($size = 48, $textarea = false, $styleOverride = '')
514 {
515 return ' style="' . ($styleOverride ?: 'width:' . ceil($size * 9.58) . 'px;') . '"';
516 }
517
518 /**
519 * Returns JavaScript variables setting the returnUrl and thisScript location for use by JavaScript on the page.
520 * Used in fx. db_list.php (Web>List)
521 *
522 * @param string $thisLocation URL to "this location" / current script
523 * @return string Urls are returned as JavaScript variables T3_RETURN_URL and T3_THIS_LOCATION
524 * @see typo3/db_list.php
525 */
526 public function redirectUrls($thisLocation = '')
527 {
528 $thisLocation = $thisLocation ? $thisLocation : GeneralUtility::linkThisScript(array(
529 'CB' => '',
530 'SET' => '',
531 'cmd' => '',
532 'popViewId' => ''
533 ));
534 $out = '
535 var T3_RETURN_URL = ' . GeneralUtility::quoteJSvalue(str_replace('%20', '', rawurlencode(GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'))))) . ';
536 var T3_THIS_LOCATION = ' . GeneralUtility::quoteJSvalue(str_replace('%20', '', rawurlencode($thisLocation))) . '
537 ';
538 return $out;
539 }
540
541 /**
542 * Defines whether to use the X-UA-Compatible meta tag.
543 *
544 * @param bool $useCompatibilityTag Whether to use the tag
545 * @return void
546 */
547 public function useCompatibilityTag($useCompatibilityTag = true)
548 {
549 $this->useCompatibilityTag = (bool)$useCompatibilityTag;
550 }
551
552 /*****************************************
553 *
554 * PAGE BUILDING FUNCTIONS.
555 * Use this to build the HTML of your backend modules
556 *
557 *****************************************/
558 /**
559 * Returns page start
560 * This includes the proper header with charset, title, meta tag and beginning body-tag.
561 *
562 * @param string $title HTML Page title for the header
563 * @return string Returns the whole header section of a HTML-document based on settings in internal variables (like styles, javascript code, charset, generator and docType)
564 * @see endPage()
565 */
566 public function startPage($title)
567 {
568 // hook pre start page
569 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['preStartPageHook'])) {
570 $preStartPageHook = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['preStartPageHook'];
571 if (is_array($preStartPageHook)) {
572 $hookParameters = array(
573 'title' => &$title
574 );
575 foreach ($preStartPageHook as $hookFunction) {
576 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
577 }
578 }
579 }
580 // alternative template for Header and Footer
581 if ($this->pageHeaderFooterTemplateFile) {
582 $file = GeneralUtility::getFileAbsFileName($this->pageHeaderFooterTemplateFile, true);
583 if ($file) {
584 $this->pageRenderer->setTemplateFile($file);
585 }
586 }
587
588 // Disable rendering of XHTML tags
589 $this->pageRenderer->setRenderXhtml(false);
590
591 $languageCode = $this->pageRenderer->getLanguage() === 'default' ? 'en' : $this->pageRenderer->getLanguage();
592 $this->pageRenderer->setHtmlTag('<html lang="' . $languageCode . '">');
593
594 $headerStart = '<!DOCTYPE html>';
595 $this->pageRenderer->setXmlPrologAndDocType($headerStart);
596 $this->pageRenderer->setHeadTag('<head>' . LF . '<!-- TYPO3 Script ID: ' . htmlspecialchars($this->scriptID) . ' -->');
597 header('Content-Type:text/html;charset=utf-8');
598 $this->pageRenderer->setCharSet('utf-8');
599 $this->pageRenderer->addMetaTag($this->generator());
600 $this->pageRenderer->addMetaTag('<meta name="robots" content="noindex,follow">');
601 $this->pageRenderer->addMetaTag('<meta charset="utf-8">');
602 $this->pageRenderer->addMetaTag('<meta name="viewport" content="width=device-width, initial-scale=1">');
603 $this->pageRenderer->setFavIcon($this->getBackendFavicon());
604 if ($this->useCompatibilityTag) {
605 $this->pageRenderer->addMetaTag($this->xUaCompatible($this->xUaCompatibilityVersion));
606 }
607 $this->pageRenderer->setTitle($title);
608 // add docstyles
609 $this->docStyle();
610 if ($this->extDirectStateProvider) {
611 $this->pageRenderer->addJsFile(ExtensionManagementUtility::extRelPath('backend') . 'Resources/Public/JavaScript/ExtDirect.StateProvider.js');
612 }
613 $this->pageRenderer->addHeaderData($this->JScode);
614 foreach ($this->JScodeArray as $name => $code) {
615 $this->pageRenderer->addJsInlineCode($name, $code, false);
616 }
617
618 if ($this->extJScode) {
619 $this->pageRenderer->addExtOnReadyCode($this->extJScode);
620 }
621
622 // Load jquery and twbs JS libraries on every backend request
623 $this->pageRenderer->loadJquery();
624 // Note: please do not reference "bootstrap" outside of the TYPO3 Core (not in your own extensions)
625 // as this is preliminary as long as Twitter bootstrap does not support AMD modules
626 // this logic will be changed once Twitter bootstrap 4 is included
627 $this->pageRenderer->addJsFile(ExtensionManagementUtility::extRelPath('core') . 'Resources/Public/JavaScript/Contrib/bootstrap/bootstrap.js');
628
629 // hook for additional headerData
630 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['preHeaderRenderHook'])) {
631 $preHeaderRenderHook = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['preHeaderRenderHook'];
632 if (is_array($preHeaderRenderHook)) {
633 $hookParameters = array(
634 'pageRenderer' => &$this->pageRenderer
635 );
636 foreach ($preHeaderRenderHook as $hookFunction) {
637 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
638 }
639 }
640 }
641 // Construct page header.
642 $str = $this->pageRenderer->render(PageRenderer::PART_HEADER);
643 $this->JScode = ($this->extJScode = '');
644 $this->JScodeArray = array();
645 $this->endOfPageJsBlock = $this->pageRenderer->render(PageRenderer::PART_FOOTER);
646 $str .= $this->docBodyTagBegin() . ($this->divClass ? '
647
648 <!-- Wrapping DIV-section for whole page BEGIN -->
649 <div class="' . $this->divClass . '">
650 ' : '') . trim($this->form);
651 return $str;
652 }
653
654 /**
655 * Returns page end; This includes finishing form, div, body and html tags.
656 *
657 * @return string The HTML end of a page
658 * @see startPage()
659 */
660 public function endPage()
661 {
662 $str = $this->sectionEnd() . $this->postCode . $this->wrapScriptTags(BackendUtility::getUpdateSignalCode()) . ($this->form ? '
663 </form>' : '');
664 // If something is in buffer like debug, put it to end of page
665 if (ob_get_contents()) {
666 $str .= ob_get_clean();
667 if (!headers_sent()) {
668 header('Content-Encoding: None');
669 }
670 }
671 $str .= ($this->divClass ? '
672
673 <!-- Wrapping DIV-section for whole page END -->
674 </div>' : '') . $this->endOfPageJsBlock;
675
676 // Logging: Can't find better place to put it:
677 if (TYPO3_DLOG) {
678 GeneralUtility::devLog('END of BACKEND session', \TYPO3\CMS\Backend\Template\DocumentTemplate::class, 0, array('_FLUSH' => true));
679 }
680 return $str;
681 }
682
683 /**
684 * Shortcut for render the complete page of a module
685 *
686 * @param string $title page title
687 * @param string $content page content
688 * @return string complete page
689 */
690 public function render($title, $content)
691 {
692 $pageContent = $this->startPage($title);
693 $pageContent .= $content;
694 $pageContent .= $this->endPage();
695 return $this->insertStylesAndJS($pageContent);
696 }
697
698 /**
699 * Returns the header-bar in the top of most backend modules
700 * Closes section if open.
701 *
702 * @param string $text The text string for the header
703 * @return string HTML content
704 */
705 public function header($text)
706 {
707 $str = '
708
709 <!-- MAIN Header in page top -->
710 <h1 class="t3js-title-inlineedit">' . htmlspecialchars($text) . '</h1>
711 ';
712 return $this->sectionEnd() . $str;
713 }
714
715 /**
716 * Begins an output section and sets header and content
717 *
718 * @param string $label The header
719 * @param string $text The HTML-content
720 * @param bool $nostrtoupper A flag that will prevent the header from being converted to uppercase
721 * @param bool $sH Defines the type of header (if set, "<h3>" rather than the default "h4")
722 * @param int $type The number of an icon to show with the header (see the icon-function). -1,1,2,3
723 * @param bool $allowHTMLinHeader If set, HTML tags are allowed in $label (otherwise this value is by default htmlspecialchars()'ed)
724 * @return string HTML content
725 * @see icons(), sectionHeader()
726 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9.
727 */
728 public function section($label, $text, $nostrtoupper = false, $sH = false, $type = 0, $allowHTMLinHeader = false)
729 {
730 GeneralUtility::logDeprecatedFunction();
731 $str = '';
732 // Setting header
733 if ($label) {
734 if (!$allowHTMLinHeader) {
735 $label = htmlspecialchars($label);
736 }
737 $str .= $this->sectionHeader($this->icons($type) . $label, $sH, $nostrtoupper ? '' : ' class="uppercase"');
738 }
739 // Setting content
740 $str .= '
741
742 <!-- Section content -->
743 ' . $text;
744 return $this->sectionBegin() . $str;
745 }
746
747 /**
748 * Inserts a divider image
749 * Ends a section (if open) before inserting the image
750 *
751 * @param int $dist The margin-top/-bottom of the <hr> ruler.
752 * @return string HTML content
753 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9.
754 */
755 public function divider($dist)
756 {
757 GeneralUtility::logDeprecatedFunction();
758 $dist = (int)$dist;
759 $str = '
760
761 <!-- DIVIDER -->
762 <hr style="margin-top: ' . $dist . 'px; margin-bottom: ' . $dist . 'px;" />
763 ';
764 return $this->sectionEnd() . $str;
765 }
766
767 /**
768 * Make a section header.
769 * Begins a section if not already open.
770 *
771 * @param string $label The label between the <h3> or <h4> tags. (Allows HTML)
772 * @param bool $sH If set, <h3> is used, otherwise <h4>
773 * @param string $addAttrib Additional attributes to h-tag, eg. ' class=""'
774 * @return string HTML content
775 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9.
776 */
777 public function sectionHeader($label, $sH = false, $addAttrib = '')
778 {
779 GeneralUtility::logDeprecatedFunction();
780 $tag = $sH ? 'h2' : 'h3';
781 if ($addAttrib && $addAttrib[0] !== ' ') {
782 $addAttrib = ' ' . $addAttrib;
783 }
784 $str = '
785
786 <!-- Section header -->
787 <' . $tag . $addAttrib . '>' . $label . '</' . $tag . '>
788 ';
789 return $this->sectionBegin() . $str;
790 }
791
792 /**
793 * Begins an output section.
794 * Returns the <div>-begin tag AND sets the ->sectionFlag TRUE (if the ->sectionFlag is not already set!)
795 * You can call this function even if a section is already begun since the function will only return something if the sectionFlag is not already set!
796 *
797 * @return string HTML content
798 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9.
799 */
800 public function sectionBegin()
801 {
802 GeneralUtility::logDeprecatedFunction();
803 if (!$this->sectionFlag) {
804 $this->sectionFlag = 1;
805 $str = '
806
807 <!-- ***********************
808 Begin output section.
809 *********************** -->
810 <div>
811 ';
812 return $str;
813 } else {
814 return '';
815 }
816 }
817
818 /**
819 * Ends and output section
820 * Returns the </div>-end tag AND clears the ->sectionFlag (but does so only IF the sectionFlag is set - that is a section is 'open')
821 * See sectionBegin() also.
822 *
823 * @return string HTML content
824 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9.
825 */
826 public function sectionEnd()
827 {
828 if ($this->sectionFlag) {
829 GeneralUtility::logDeprecatedFunction();
830 $this->sectionFlag = 0;
831 return '
832 </div>
833 <!-- *********************
834 End output section.
835 ********************* -->
836 ';
837 } else {
838 return '';
839 }
840 }
841
842 /**
843 * Creates the bodyTag.
844 * You can add to the bodyTag by $this->bodyTagAdditions
845 *
846 * @return string HTML body tag
847 */
848 public function docBodyTagBegin()
849 {
850 return '<body ' . trim($this->bodyTagAdditions . ($this->bodyTagId ? ' id="' . $this->bodyTagId . '"' : '')) . '>';
851 }
852
853 /**
854 * Outputting document style
855 *
856 * @return string HTML style section/link tags
857 */
858 public function docStyle()
859 {
860 // Implode it all:
861 $inDocStyles = implode(LF, $this->inDocStylesArray);
862
863 // Reset styles so they won't be added again in insertStylesAndJS()
864 $this->inDocStylesArray = array();
865
866 if ($this->styleSheetFile) {
867 $this->pageRenderer->addCssFile($this->styleSheetFile);
868 }
869 if ($this->styleSheetFile2) {
870 $this->pageRenderer->addCssFile($this->styleSheetFile2);
871 }
872
873 if ($inDocStyles !== '') {
874 $this->pageRenderer->addCssInlineBlock('inDocStyles', $inDocStyles . LF . '/*###POSTCSSMARKER###*/');
875 }
876
877 if ($this->styleSheetFile_post) {
878 $this->pageRenderer->addCssFile($this->styleSheetFile_post);
879 }
880 }
881
882 /**
883 * Insert additional style sheet link
884 *
885 * @param string $key some key identifying the style sheet
886 * @param string $href uri to the style sheet file
887 * @param string $title value for the title attribute of the link element
888 * @param string $relation value for the rel attribute of the link element
889 * @return void
890 */
891 public function addStyleSheet($key, $href, $title = '', $relation = 'stylesheet')
892 {
893 $this->pageRenderer->addCssFile($href, $relation, 'screen', $title);
894 }
895
896 /**
897 * Add all *.css files of the directory $path to the stylesheets
898 *
899 * @param string $path directory to add
900 * @return void
901 */
902 public function addStyleSheetDirectory($path)
903 {
904 // Calculation needed, when TYPO3 source is used via a symlink
905 // absolute path to the stylesheets
906 $filePath = GeneralUtility::getFileAbsFileName($path, false, true);
907 // Clean the path
908 $resolvedPath = GeneralUtility::resolveBackPath($filePath);
909 // Read all files in directory and sort them alphabetically
910 $files = GeneralUtility::getFilesInDir($resolvedPath, 'css', false, 1);
911 foreach ($files as $file) {
912 $this->pageRenderer->addCssFile($path . $file, 'stylesheet', 'all');
913 }
914 }
915
916 /**
917 * Insert post rendering document style into already rendered content
918 * This is needed for extobjbase
919 *
920 * @param string $content style-content to insert.
921 * @return string content with inserted styles
922 */
923 public function insertStylesAndJS($content)
924 {
925 $styles = LF . implode(LF, $this->inDocStylesArray);
926 $content = str_replace('/*###POSTCSSMARKER###*/', $styles, $content);
927
928 // Insert accumulated JS
929 $jscode = $this->JScode . LF . $this->wrapScriptTags(implode(LF, $this->JScodeArray));
930 $content = str_replace('<!--###POSTJSMARKER###-->', $jscode, $content);
931 return $content;
932 }
933
934 /**
935 * Returns an array of all stylesheet directories belonging to core and skins
936 *
937 * @return array Stylesheet directories
938 */
939 public function getSkinStylesheetDirectories()
940 {
941 $stylesheetDirectories = array();
942 // Stylesheets from skins
943 // merge default css directories ($this->stylesheetsSkin) with additional ones and include them
944 if (is_array($GLOBALS['TBE_STYLES']['skins'])) {
945 // loop over all registered skins
946 foreach ($GLOBALS['TBE_STYLES']['skins'] as $skinExtKey => $skin) {
947 $skinStylesheetDirs = $this->stylesheetsSkins;
948 // Skins can add custom stylesheetDirectories using
949 // $GLOBALS['TBE_STYLES']['skins'][$_EXTKEY]['stylesheetDirectories']
950 if (is_array($skin['stylesheetDirectories'])) {
951 $skinStylesheetDirs = array_merge($skinStylesheetDirs, $skin['stylesheetDirectories']);
952 }
953 // Add all registered directories
954 foreach ($skinStylesheetDirs as $stylesheetDir) {
955 // for EXT:myskin/stylesheets/ syntax
956 if (substr($stylesheetDir, 0, 4) === 'EXT:') {
957 list($extKey, $path) = explode('/', substr($stylesheetDir, 4), 2);
958 if (!empty($extKey) && ExtensionManagementUtility::isLoaded($extKey) && !empty($path)) {
959 $stylesheetDirectories[] = ExtensionManagementUtility::extRelPath($extKey) . $path;
960 }
961 } else {
962 // For relative paths
963 $stylesheetDirectories[] = ExtensionManagementUtility::extRelPath($skinExtKey) . $stylesheetDir;
964 }
965 }
966 }
967 }
968 return $stylesheetDirectories;
969 }
970
971 /**
972 * Returns generator meta tag
973 *
974 * @return string <meta> tag with name "generator
975 */
976 public function generator()
977 {
978 $str = 'TYPO3 CMS, ' . TYPO3_URL_GENERAL . ', &#169; Kasper Sk&#229;rh&#248;j ' . TYPO3_copyright_year . ', extensions are copyright of their respective owners.';
979 return '<meta name="generator" content="' . $str . '" />';
980 }
981
982 /**
983 * Returns X-UA-Compatible meta tag
984 *
985 * @param string $content Content of the compatible tag (default: IE-8)
986 * @return string <meta http-equiv="X-UA-Compatible" content="???" />
987 */
988 public function xUaCompatible($content = 'IE=8')
989 {
990 return '<meta http-equiv="X-UA-Compatible" content="' . $content . '" />';
991 }
992
993 /*****************************************
994 *
995 * OTHER ELEMENTS
996 * Tables, buttons, formatting dimmed/red strings
997 *
998 ******************************************/
999 /**
1000 * Returns an image-tag with an 18x16 icon of the following types:
1001 *
1002 * $type:
1003 * -1: OK icon (Check-mark)
1004 * 1: Notice (Speach-bubble)
1005 * 2: Warning (Yellow triangle)
1006 * 3: Fatal error (Red stop sign)
1007 *
1008 * @param int $type See description
1009 * @param string $styleAttribValue Value for style attribute
1010 * @return string HTML image tag (if applicable)
1011 */
1012 public function icons($type, $styleAttribValue = '')
1013 {
1014 switch ($type) {
1015 case self::STATUS_ICON_ERROR:
1016 $icon = 'status-dialog-error';
1017 break;
1018 case self::STATUS_ICON_WARNING:
1019 $icon = 'status-dialog-warning';
1020 break;
1021 case self::STATUS_ICON_NOTIFICATION:
1022 $icon = 'status-dialog-notification';
1023 break;
1024 case self::STATUS_ICON_OK:
1025 $icon = 'status-dialog-ok';
1026 break;
1027 default:
1028 // Do nothing
1029 }
1030 if ($icon) {
1031 return $this->iconFactory->getIcon($icon, Icon::SIZE_SMALL)->render();
1032 }
1033 }
1034
1035 /**
1036 * Returns an <input> button with the $onClick action and $label
1037 *
1038 * @param string $onClick The value of the onclick attribute of the input tag (submit type)
1039 * @param string $label The label for the button (which will be htmlspecialchar'ed)
1040 * @return string A <input> tag of the type "submit
1041 */
1042 public function t3Button($onClick, $label)
1043 {
1044 $button = '<input class="btn btn-default" type="submit" onclick="' . htmlspecialchars($onClick) . '; return false;" value="' . htmlspecialchars($label) . '" />';
1045 return $button;
1046 }
1047
1048 /**
1049 * Returns string wrapped in CDATA "tags" for XML / XHTML (wrap content of <script> and <style> sections in those!)
1050 *
1051 * @param string $string Input string
1052 * @return string Output string
1053 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9.
1054 */
1055 public function wrapInCData($string)
1056 {
1057 GeneralUtility::logDeprecatedFunction();
1058 $string = '/*<![CDATA[*/' . $string . '/*]]>*/';
1059 return $string;
1060 }
1061
1062 /**
1063 * Wraps the input string in script tags.
1064 * Automatic re-identing of the JS code is done by using the first line as ident reference.
1065 * This is nice for identing JS code with PHP code on the same level.
1066 *
1067 * @param string $string Input string
1068 * @param bool $linebreak Wrap script element in linebreaks? Default is TRUE.
1069 * @return string Output string
1070 */
1071 public function wrapScriptTags($string, $linebreak = true)
1072 {
1073 if (trim($string)) {
1074 // <script wrapped in nl?
1075 $cr = $linebreak ? LF : '';
1076 // Remove nl from the beginning
1077 $string = ltrim($string, LF);
1078 // Re-ident to one tab using the first line as reference
1079 if ($string[0] === TAB) {
1080 $string = TAB . ltrim($string, TAB);
1081 }
1082 $string = $cr . '<script type="text/javascript">
1083 /*<![CDATA[*/
1084 ' . $string . '
1085 /*]]>*/
1086 </script>' . $cr;
1087 }
1088 return trim($string);
1089 }
1090
1091 /**
1092 * Returns a one-row/two-celled table with $content and $menu side by side.
1093 * The table is a 100% width table and each cell is aligned left / right
1094 *
1095 * @param string $content Content cell content (left)
1096 * @param string $menu Menu cell content (right)
1097 * @return string HTML output
1098 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9.
1099 */
1100 public function funcMenu($content, $menu)
1101 {
1102 GeneralUtility::logDeprecatedFunction();
1103 return '
1104 <table border="0" cellpadding="0" cellspacing="0" width="100%" id="typo3-funcmenu">
1105 <tr>
1106 <td valign="top" nowrap="nowrap">' . $content . '</td>
1107 <td valign="top" align="right" nowrap="nowrap">' . $menu . '</td>
1108 </tr>
1109 </table>';
1110 }
1111
1112 /**
1113 * Includes a javascript library that exists in the core /typo3/ directory
1114 *
1115 * @param string $lib: Library name. Call it with the full path like "sysext/core/Resources/Public/JavaScript/QueryGenerator.js" to load it
1116 * @return void
1117 */
1118 public function loadJavascriptLib($lib)
1119 {
1120 // @todo: maybe we can remove this one as well
1121 $this->pageRenderer->addJsFile($lib);
1122 }
1123
1124 /**
1125 * Includes the necessary Javascript function for the clickmenu (context sensitive menus) in the document
1126 *
1127 * @return void
1128 */
1129 public function getContextMenuCode()
1130 {
1131 $this->pageRenderer->loadJquery();
1132 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ClickMenu');
1133 }
1134
1135 /**
1136 * Includes the necessary javascript file for use on pages which have the
1137 * drag and drop functionality (legacy folder tree)
1138 *
1139 * @param string $table indicator of which table the drag and drop function should work on (pages or folders)
1140 * @param string $additionalJavaScriptCode adds more code to the additional javascript code
1141 * @return void
1142 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9.
1143 */
1144 public function getDragDropCode($table, $additionalJavaScriptCode = '')
1145 {
1146 GeneralUtility::logDeprecatedFunction();
1147 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LegacyTree', 'function() {
1148 DragDrop.table = "' . $table . '";
1149 ' . $additionalJavaScriptCode . '
1150 }');
1151 }
1152
1153 /**
1154 * Creates a tab menu from an array definition
1155 *
1156 * Returns a tab menu for a module
1157 * Requires the JS function jumpToUrl() to be available
1158 *
1159 * @param mixed $mainParams is the "&id=" parameter value to be sent to the module, but it can be also a parameter array which will be passed instead of the &id=...
1160 * @param string $elementName it the form elements name, probably something like "SET[...]
1161 * @param string $currentValue is the value to be selected currently.
1162 * @param array $menuItems is an array with the menu items for the selector box
1163 * @param string $script is the script to send the &id to, if empty it's automatically found
1164 * @param string $addparams is additional parameters to pass to the script.
1165 * @return string HTML code for tab menu
1166 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9.
1167 */
1168 public function getTabMenu($mainParams, $elementName, $currentValue, $menuItems, $script = '', $addparams = '')
1169 {
1170 GeneralUtility::logDeprecatedFunction();
1171 $content = '';
1172 if (is_array($menuItems)) {
1173 if (!is_array($mainParams)) {
1174 $mainParams = array('id' => $mainParams);
1175 }
1176 $mainParams = GeneralUtility::implodeArrayForUrl('', $mainParams);
1177 if (!$script) {
1178 $script = basename(PATH_thisScript);
1179 }
1180 $menuDef = array();
1181 foreach ($menuItems as $value => $label) {
1182 $menuDef[$value]['isActive'] = (string)$currentValue === (string)$value;
1183 $menuDef[$value]['label'] = htmlspecialchars($label, ENT_COMPAT, 'UTF-8', false);
1184 $menuDef[$value]['url'] = $script . '?' . $mainParams . $addparams . '&' . $elementName . '=' . $value;
1185 }
1186 $content = $this->getTabMenuRaw($menuDef);
1187 }
1188 return $content;
1189 }
1190
1191 /**
1192 * Creates the HTML content for the tab menu
1193 *
1194 * @param array $menuItems Menu items for tabs
1195 * @return string Table HTML
1196 * @access private
1197 */
1198 public function getTabMenuRaw($menuItems)
1199 {
1200 if (!is_array($menuItems)) {
1201 return '';
1202 }
1203
1204 $options = '';
1205 foreach ($menuItems as $id => $def) {
1206 $class = $def['isActive'] ? 'active' : '';
1207 $label = $def['label'];
1208 $url = htmlspecialchars($def['url']);
1209 $params = $def['addParams'];
1210
1211 $options .= '<li class="' . $class . '">' .
1212 '<a href="' . $url . '" ' . $params . '>' . $label . '</a>' .
1213 '</li>';
1214 }
1215
1216 return '<ul class="nav nav-tabs" role="tablist">' .
1217 $options .
1218 '</ul>';
1219 }
1220
1221 /**
1222 * Creates the version selector for the page id inputted.
1223 * Requires the core version management extension, "version" to be loaded.
1224 *
1225 * @param int $id Page id to create selector for.
1226 * @param bool $noAction If set, there will be no button for swapping page.
1227 * @return string
1228 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9.
1229 */
1230 public function getVersionSelector($id, $noAction = false)
1231 {
1232 GeneralUtility::logDeprecatedFunction();
1233 if (
1234 ExtensionManagementUtility::isLoaded('version') &&
1235 !ExtensionManagementUtility::isLoaded('workspaces')
1236 ) {
1237 $versionGuiObj = GeneralUtility::makeInstance(\TYPO3\CMS\Version\View\VersionView::class);
1238 return $versionGuiObj->getVersionSelector($id, $noAction);
1239 }
1240 }
1241
1242 /**
1243 * Function to load a HTML template file with markers.
1244 * When calling from own extension, use syntax getHtmlTemplate('EXT:extkey/template.html')
1245 *
1246 * @param string $filename tmpl name, usually in the typo3/template/ directory
1247 * @return string HTML of template
1248 */
1249 public function getHtmlTemplate($filename)
1250 {
1251 // setting the name of the original HTML template
1252 $this->moduleTemplateFilename = $filename;
1253 if ($GLOBALS['TBE_STYLES']['htmlTemplates'][$filename]) {
1254 $filename = $GLOBALS['TBE_STYLES']['htmlTemplates'][$filename];
1255 }
1256 if (GeneralUtility::isFirstPartOfStr($filename, 'EXT:')) {
1257 $filename = GeneralUtility::getFileAbsFileName($filename, true, true);
1258 } elseif (!GeneralUtility::isAbsPath($filename)) {
1259 $filename = GeneralUtility::resolveBackPath($filename);
1260 } elseif (!GeneralUtility::isAllowedAbsPath($filename)) {
1261 $filename = '';
1262 }
1263 $htmlTemplate = '';
1264 if ($filename !== '') {
1265 $htmlTemplate = GeneralUtility::getUrl($filename);
1266 }
1267 return $htmlTemplate;
1268 }
1269
1270 /**
1271 * Define the template for the module
1272 *
1273 * @param string $filename filename
1274 * @return void
1275 */
1276 public function setModuleTemplate($filename)
1277 {
1278 $this->moduleTemplate = $this->getHtmlTemplate($filename);
1279 }
1280
1281 /**
1282 * Put together the various elements for the module <body> using a static HTML
1283 * template
1284 *
1285 * @param array $pageRecord Record of the current page, used for page path and info
1286 * @param array $buttons HTML for all buttons
1287 * @param array $markerArray HTML for all other markers
1288 * @param array $subpartArray HTML for the subparts
1289 * @return string Composite HTML
1290 */
1291 public function moduleBody($pageRecord = array(), $buttons = array(), $markerArray = array(), $subpartArray = array())
1292 {
1293 // Get the HTML template for the module
1294 $moduleBody = $this->templateService->getSubpart($this->moduleTemplate, '###FULLDOC###');
1295 // Add CSS
1296 $this->inDocStylesArray[] = 'html { overflow: hidden; }';
1297 // Get the page path for the docheader
1298 $markerArray['PAGEPATH'] = $this->getPagePath($pageRecord);
1299 // Get the page info for the docheader
1300 $markerArray['PAGEINFO'] = $this->getPageInfo($pageRecord);
1301 // Get all the buttons for the docheader
1302 $docHeaderButtons = $this->getDocHeaderButtons($buttons);
1303 // Merge docheader buttons with the marker array
1304 $markerArray = array_merge($markerArray, $docHeaderButtons);
1305 // replacing subparts
1306 foreach ($subpartArray as $marker => $content) {
1307 $moduleBody = $this->templateService->substituteSubpart($moduleBody, $marker, $content);
1308 }
1309 // adding flash messages
1310 if ($this->showFlashMessages) {
1311 $flashMessages = $this->getFlashMessages();
1312 if (!empty($flashMessages)) {
1313 $markerArray['FLASHMESSAGES'] = $flashMessages;
1314 // If there is no dedicated marker for the messages present
1315 // then force them to appear before the content
1316 if (strpos($moduleBody, '###FLASHMESSAGES###') === false) {
1317 $moduleBody = str_replace('###CONTENT###', '###FLASHMESSAGES######CONTENT###', $moduleBody);
1318 }
1319 }
1320 }
1321 // Hook for adding more markers/content to the page, like the version selector
1322 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['moduleBodyPostProcess'])) {
1323 $params = array(
1324 'moduleTemplateFilename' => &$this->moduleTemplateFilename,
1325 'moduleTemplate' => &$this->moduleTemplate,
1326 'moduleBody' => &$moduleBody,
1327 'markers' => &$markerArray,
1328 'parentObject' => &$this
1329 );
1330 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['moduleBodyPostProcess'] as $funcRef) {
1331 GeneralUtility::callUserFunction($funcRef, $params, $this);
1332 }
1333 }
1334 // Replacing all markers with the finished markers and return the HTML content
1335 return $this->templateService->substituteMarkerArray($moduleBody, $markerArray, '###|###');
1336 }
1337
1338 /**
1339 * Get the default rendered FlashMessages from queue
1340 *
1341 * @return string
1342 */
1343 public function getFlashMessages()
1344 {
1345 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
1346 $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
1347 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
1348 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1349 return $defaultFlashMessageQueue->renderFlashMessages();
1350 }
1351
1352 /**
1353 * Renders the FlashMessages from queue and returns them as AJAX.
1354 *
1355 * @param ServerRequestInterface $request
1356 * @param ResponseInterface $response
1357 * @return ResponseInterface
1358 */
1359 public function renderQueuedFlashMessages(ServerRequestInterface $request, ResponseInterface $response)
1360 {
1361 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
1362 $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
1363 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
1364 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1365 $flashMessages = $defaultFlashMessageQueue->getAllMessagesAndFlush();
1366
1367 $messages = [];
1368 foreach ($flashMessages as $flashMessage) {
1369 $messages[] = [
1370 'title' => $flashMessage->getTitle(),
1371 'message' => $flashMessage->getMessage(),
1372 'severity' => $flashMessage->getSeverity()
1373 ];
1374 }
1375
1376 $response->getBody()->write(json_encode($messages));
1377 return $response;
1378 }
1379
1380 /**
1381 * Fill the button lists with the defined HTML
1382 *
1383 * @param array $buttons HTML for all buttons
1384 * @return array Containing HTML for both buttonlists
1385 */
1386 protected function getDocHeaderButtons($buttons)
1387 {
1388 $markers = array();
1389 // Fill buttons for left and right float
1390 $floats = array('left', 'right');
1391 foreach ($floats as $key) {
1392 // Get the template for each float
1393 $buttonTemplate = $this->templateService->getSubpart($this->moduleTemplate, '###BUTTON_GROUPS_' . strtoupper($key) . '###');
1394 // Fill the button markers in this float
1395 $buttonTemplate = $this->templateService->substituteMarkerArray($buttonTemplate, $buttons, '###|###', true);
1396 // getting the wrap for each group
1397 $buttonWrap = $this->templateService->getSubpart($this->moduleTemplate, '###BUTTON_GROUP_WRAP###');
1398 // looping through the groups (max 6) and remove the empty groups
1399 for ($groupNumber = 1; $groupNumber < 6; $groupNumber++) {
1400 $buttonMarker = '###BUTTON_GROUP' . $groupNumber . '###';
1401 $buttonGroup = $this->templateService->getSubpart($buttonTemplate, $buttonMarker);
1402 if (trim($buttonGroup)) {
1403 if ($buttonWrap) {
1404 $buttonGroup = $this->templateService->substituteMarker($buttonWrap, '###BUTTONS###', $buttonGroup);
1405 }
1406 $buttonTemplate = $this->templateService->substituteSubpart($buttonTemplate, $buttonMarker, trim($buttonGroup));
1407 }
1408 }
1409 // Replace the marker with the template and remove all line breaks (for IE compat)
1410 $markers['BUTTONLIST_' . strtoupper($key)] = str_replace(LF, '', $buttonTemplate);
1411 }
1412 // Hook for manipulating docHeaderButtons
1413 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['docHeaderButtonsHook'])) {
1414 $params = array(
1415 'buttons' => $buttons,
1416 'markers' => &$markers,
1417 'pObj' => &$this
1418 );
1419 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['docHeaderButtonsHook'] as $funcRef) {
1420 GeneralUtility::callUserFunction($funcRef, $params, $this);
1421 }
1422 }
1423 return $markers;
1424 }
1425
1426 /**
1427 * Generate the page path for docheader
1428 *
1429 * @param array $pageRecord Current page
1430 * @return string Page path
1431 */
1432 protected function getPagePath($pageRecord)
1433 {
1434 // Is this a real page
1435 if (is_array($pageRecord) && $pageRecord['uid']) {
1436 $title = substr($pageRecord['_thePathFull'], 0, -1);
1437 // Remove current page title
1438 $pos = strrpos($title, $pageRecord['title']);
1439 if ($pos !== false) {
1440 $title = substr($title, 0, $pos);
1441 }
1442 } else {
1443 $title = '';
1444 }
1445 // Setting the path of the page
1446 $pagePath = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.path', true) . ': <span class="typo3-docheader-pagePath">';
1447 // crop the title to title limit (or 50, if not defined)
1448 $cropLength = empty($GLOBALS['BE_USER']->uc['titleLen']) ? 50 : $GLOBALS['BE_USER']->uc['titleLen'];
1449 $croppedTitle = GeneralUtility::fixed_lgd_cs($title, -$cropLength);
1450 if ($croppedTitle !== $title) {
1451 $pagePath .= '<abbr title="' . htmlspecialchars($title) . '">' . htmlspecialchars($croppedTitle) . '</abbr>';
1452 } else {
1453 $pagePath .= htmlspecialchars($title);
1454 }
1455 $pagePath .= '</span>';
1456 return $pagePath;
1457 }
1458
1459 /**
1460 * Setting page icon with clickmenu + uid for docheader
1461 *
1462 * @param array $pageRecord Current page
1463 * @return string Page info
1464 */
1465 protected function getPageInfo($pageRecord)
1466 {
1467 // Add icon with clickmenu, etc:
1468 // If there IS a real page
1469 if (is_array($pageRecord) && $pageRecord['uid']) {
1470 $alttext = BackendUtility::getRecordIconAltText($pageRecord, 'pages');
1471 $iconImg = '<span title="' . htmlspecialchars($alttext) . '">' . $this->iconFactory->getIconForRecord('pages', $pageRecord, Icon::SIZE_SMALL)->render() . '</span>';
1472 // Make Icon:
1473 $theIcon = BackendUtility::wrapClickMenuOnIcon($iconImg, 'pages', $pageRecord['uid']);
1474 $uid = $pageRecord['uid'];
1475 $title = BackendUtility::getRecordTitle('pages', $pageRecord);
1476 } else {
1477 // On root-level of page tree
1478 // Make Icon
1479 $iconImg = '<span title="' . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) . '">' . $this->iconFactory->getIcon('apps-pagetree-root', Icon::SIZE_SMALL)->render() . '</span>';
1480 if ($GLOBALS['BE_USER']->user['admin']) {
1481 $theIcon = BackendUtility::wrapClickMenuOnIcon($iconImg, 'pages', 0);
1482 } else {
1483 $theIcon = $iconImg;
1484 }
1485 $uid = '0';
1486 $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
1487 }
1488 // Setting icon with clickmenu + uid
1489 $pageInfo = $theIcon . '<strong>' . htmlspecialchars($title) . '&nbsp;[' . $uid . ']</strong>';
1490 return $pageInfo;
1491 }
1492
1493 /**
1494 * Retrieves configured favicon for backend (with fallback)
1495 *
1496 * @return string
1497 */
1498 protected function getBackendFavicon()
1499 {
1500 return $GLOBALS['TBE_STYLES']['favicon'] ?: ExtensionManagementUtility::extRelPath('backend') . 'Resources/Public/Icons/favicon.ico';
1501 }
1502 }