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