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