[BUGFIX] Move issueCommand to BackendUtility
[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\Authentication\BackendUserAuthentication;
21 use TYPO3\CMS\Core\Imaging\Icon;
22 use TYPO3\CMS\Core\Imaging\IconFactory;
23 use TYPO3\CMS\Core\Page\PageRenderer;
24 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
25 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27 use TYPO3\CMS\Fluid\View\StandaloneView;
28
29 /**
30 * TYPO3 Backend Template Class
31 *
32 * This class contains functions for starting and ending the HTML of backend modules
33 * It also contains methods for outputting sections of content.
34 * Further there are functions for making icons, links, setting form-field widths etc.
35 * Color scheme and stylesheet definitions are also available here.
36 * Finally this file includes the language class for TYPO3's backend.
37 *
38 * After this file $LANG and $TBE_TEMPLATE are global variables / instances of their respective classes.
39 *
40 * Please refer to Inside TYPO3 for a discussion of how to use this API.
41 */
42 class DocumentTemplate
43 {
44 // Vars you typically might want to/should set from outside after making instance of this class:
45 /**
46 * This can be set to the HTML-code for a formtag.
47 * Useful when you need a form to span the whole page; Inserted exactly after the body-tag.
48 *
49 * @var string
50 */
51 public $form = '';
52
53 /**
54 * Similar to $JScode (see below) but used as an associative array to prevent double inclusion of JS code.
55 * This is used to include certain external Javascript libraries before the inline JS code.
56 * <script>-Tags are not wrapped around automatically
57 *
58 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use pageRenderer directly
59 */
60 public $JScodeLibArray = array();
61
62 /**
63 * Additional header code (eg. a JavaScript section) could be accommulated in this var. It will be directly outputted in the header.
64 *
65 * @var string
66 */
67 public $JScode = '';
68
69 /**
70 * Additional header code for ExtJS. It will be included in document header and inserted in a Ext.onReady(function()
71 *
72 * @var string
73 */
74 public $extJScode = '';
75
76 /**
77 * 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.
78 *
79 * @var array
80 */
81 public $JScodeArray = array('jumpToUrl' => '
82 function jumpToUrl(URL) {
83 window.location.href = URL;
84 return false;
85 }
86 ');
87
88 /**
89 * 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.
90 *
91 * @var string
92 */
93 public $postCode = '';
94
95 /**
96 * Doc-type used in the header. Default is xhtml_trans. You can also set it to 'html_3', 'xhtml_strict' or 'xhtml_frames'.
97 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, as it is HTML5
98 */
99 public $docType = '';
100
101 /**
102 * HTML template with markers for module
103 *
104 * @var string
105 */
106 public $moduleTemplate = '';
107
108 /**
109 * The base file (not overlaid by TBE_STYLES) for the current module, useful for hooks when finding out which modules is rendered currently
110 *
111 * @var string
112 */
113 protected $moduleTemplateFilename = '';
114
115 /**
116 * Script ID
117 *
118 * @var string
119 */
120 public $scriptID = '';
121
122 /**
123 * Id which can be set for the body tag. Default value is based on script ID
124 *
125 * @var string
126 */
127 public $bodyTagId = '';
128
129 /**
130 * You can add additional attributes to the body-tag through this variable.
131 *
132 * @var string
133 */
134 public $bodyTagAdditions = '';
135
136 /**
137 * Additional CSS styles which will be added to the <style> section in the header
138 *
139 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use the pageRenderer property for adding CSS styles
140 */
141 public $inDocStyles = '';
142
143 /**
144 * Additional CSS styles which will be added to the <style> section in the header
145 * used as array with associative keys to prevent double inclusion of CSS code
146 *
147 * @var array
148 */
149 public $inDocStylesArray = array();
150
151 /**
152 * Compensation for large documents (used in \TYPO3\CMS\Backend\Form\FormEngine)
153 *
154 * @var float
155 */
156 public $form_largeComp = 1.33;
157
158 /**
159 * If set, then a JavaScript section will be outputted in the bottom of page which will try and update the top.busy session expiry object.
160 *
161 * @var int
162 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
163 */
164 public $endJS = 1;
165
166 // TYPO3 Colorscheme.
167 // If you want to change this, please do so through a skin using the global var $GLOBALS['TBE_STYLES']
168
169 /**
170 * Light background color
171 *
172 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
173 */
174 public $bgColor = '#F7F3EF';
175
176 /**
177 * Steel-blue
178 *
179 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
180 */
181 public $bgColor2 = '#9BA1A8';
182
183 /**
184 * dok.color
185 *
186 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
187 */
188 public $bgColor3 = '#F6F2E6';
189
190 /**
191 * light tablerow background, brownish
192 *
193 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
194 */
195 public $bgColor4 = '#D9D5C9';
196
197 /**
198 * light tablerow background, greenish
199 *
200 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
201 */
202 public $bgColor5 = '#ABBBB4';
203
204 /**
205 * light tablerow background, yellowish, for section headers. Light.
206 *
207 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
208 */
209 public $bgColor6 = '#E7DBA8';
210
211 /**
212 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
213 */
214 public $hoverColor = '#254D7B';
215
216 /**
217 * Filename of stylesheet (relative to PATH_typo3)
218 *
219 * @var string
220 */
221 public $styleSheetFile = '';
222
223 /**
224 * Filename of stylesheet #2 - linked to right after the $this->styleSheetFile script (relative to PATH_typo3)
225 *
226 * @var string
227 */
228 public $styleSheetFile2 = '';
229
230 /**
231 * Filename of a post-stylesheet - included right after all inline styles.
232 *
233 * @var string
234 */
235 public $styleSheetFile_post = '';
236
237 /**
238 * Background image of page (relative to PATH_typo3)
239 *
240 * @var string
241 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use a stylesheet instead
242 */
243 public $backGroundImage = '';
244
245 /**
246 * Inline css styling set from TBE_STYLES array
247 *
248 * @var string
249 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use inDocStylesArray['TBEstyle']
250 */
251 public $inDocStyles_TBEstyle = '';
252
253 /**
254 * Whether to use the X-UA-Compatible meta tag
255 *
256 * @var bool
257 */
258 protected $useCompatibilityTag = true;
259
260 /**
261 * X-Ua-Compatible version output in meta tag
262 *
263 * @var string
264 */
265 protected $xUaCompatibilityVersion = 'IE=edge';
266
267 // Skinning
268 /**
269 * stylesheets from core
270 *
271 * @var array
272 */
273 protected $stylesheetsCore = array(
274 'generatedSprites' => '../typo3temp/sprites/'
275 );
276
277 /**
278 * Include these CSS directories from skins by default
279 *
280 * @var array
281 */
282 protected $stylesheetsSkins = array(
283 'structure' => 'Resources/Public/Css/structure/',
284 'visual' => 'Resources/Public/Css/visual/'
285 );
286
287 /**
288 * JavaScript files loaded for every page in the Backend
289 *
290 * @var array
291 */
292 protected $jsFiles = array();
293
294 /**
295 * JavaScript files loaded for every page in the Backend, but explicitly excluded from concatenation (useful for libraries etc.)
296 *
297 * @var array
298 */
299 protected $jsFilesNoConcatenation = array();
300
301 /**
302 * Will output the parsetime of the scripts in milliseconds (for admin-users).
303 * Set this to FALSE when releasing TYPO3. Only for dev.
304 *
305 * @var bool
306 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
307 */
308 public $parseTimeFlag = false;
309
310 /**
311 * internal character set, nowadays utf-8 for everything
312 *
313 * @var string
314 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, as it is always utf-8
315 */
316 protected $charset = 'utf-8';
317
318 /**
319 * Indicates if a <div>-output section is open
320 *
321 * @var int
322 * @internal
323 */
324 public $sectionFlag = 0;
325
326 /**
327 * (Default) Class for wrapping <DIV>-tag of page. Is set in class extensions.
328 *
329 * @var string
330 */
331 public $divClass = '';
332
333 /**
334 * @var string
335 */
336 public $pageHeaderBlock = '';
337
338 /**
339 * @var string
340 */
341 public $endOfPageJsBlock = '';
342
343 /**
344 * @var bool
345 */
346 public $hasDocheader = true;
347
348 /**
349 * @var PageRenderer
350 */
351 protected $pageRenderer = null;
352
353 /**
354 * Alternative template file
355 *
356 * @var string
357 */
358 protected $pageHeaderFooterTemplateFile = '';
359
360 /**
361 * @var bool
362 */
363 protected $extDirectStateProvider = false;
364
365 /**
366 * Whether flashmessages should be rendered or not
367 *
368 * @var bool $showFlashMessages
369 */
370 public $showFlashMessages = true;
371
372 /**
373 * @var IconFactory
374 */
375 protected $iconFactory;
376
377 /**
378 * @var MarkerBasedTemplateService
379 */
380 protected $templateService;
381
382 const STATUS_ICON_ERROR = 3;
383 const STATUS_ICON_WARNING = 2;
384 const STATUS_ICON_NOTIFICATION = 1;
385 const STATUS_ICON_OK = -1;
386
387 /**
388 * Constructor
389 */
390 public function __construct()
391 {
392 // Initializes the page rendering object:
393 $this->initPageRenderer();
394
395 // load Legacy CSS Support
396 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LegacyCssClasses');
397 $this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
398
399 // initialize Marker Support
400 $this->templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
401
402 // Setting default scriptID:
403 if (($temp_M = (string)GeneralUtility::_GET('M')) && $GLOBALS['TBE_MODULES']['_PATHS'][$temp_M]) {
404 $this->scriptID = preg_replace('/^.*\\/(sysext|ext)\\//', 'ext/', $GLOBALS['TBE_MODULES']['_PATHS'][$temp_M] . 'index.php');
405 } else {
406 $this->scriptID = preg_replace('/^.*\\/(sysext|ext)\\//', 'ext/', \TYPO3\CMS\Core\Utility\PathUtility::stripPathSitePrefix(PATH_thisScript));
407 }
408 if (TYPO3_mainDir != 'typo3/' && substr($this->scriptID, 0, strlen(TYPO3_mainDir)) == TYPO3_mainDir) {
409 // This fixes if TYPO3_mainDir has been changed so the script ids are STILL "typo3/..."
410 $this->scriptID = 'typo3/' . substr($this->scriptID, strlen(TYPO3_mainDir));
411 }
412 $this->bodyTagId = preg_replace('/[^A-Za-z0-9-]/', '-', $this->scriptID);
413 // Individual configuration per script? If so, make a recursive merge of the arrays:
414 if (is_array($GLOBALS['TBE_STYLES']['scriptIDindex'][$this->scriptID])) {
415 // Make copy
416 $ovr = $GLOBALS['TBE_STYLES']['scriptIDindex'][$this->scriptID];
417 // merge styles.
418 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($GLOBALS['TBE_STYLES'], $ovr);
419 // Have to unset - otherwise the second instantiation will do it again!
420 unset($GLOBALS['TBE_STYLES']['scriptIDindex'][$this->scriptID]);
421 }
422 // Main Stylesheets:
423 if ($GLOBALS['TBE_STYLES']['stylesheet']) {
424 $this->styleSheetFile = $GLOBALS['TBE_STYLES']['stylesheet'];
425 }
426 if ($GLOBALS['TBE_STYLES']['stylesheet2']) {
427 $this->styleSheetFile2 = $GLOBALS['TBE_STYLES']['stylesheet2'];
428 }
429 if ($GLOBALS['TBE_STYLES']['styleSheetFile_post']) {
430 $this->styleSheetFile_post = $GLOBALS['TBE_STYLES']['styleSheetFile_post'];
431 }
432 if ($GLOBALS['TBE_STYLES']['inDocStyles_TBEstyle']) {
433 $this->inDocStylesArray['TBEstyle'] = $GLOBALS['TBE_STYLES']['inDocStyles_TBEstyle'];
434 }
435 // include all stylesheets
436 foreach ($this->getSkinStylesheetDirectories() as $stylesheetDirectory) {
437 $this->addStylesheetDirectory($stylesheetDirectory);
438 }
439 // Background image
440 if ($GLOBALS['TBE_STYLES']['background']) {
441 GeneralUtility::deprecationLog('Usage of $TBE_STYLES["background"] is deprecated. Please use stylesheets directly.');
442 }
443 }
444
445 /**
446 * Initializes the page renderer object
447 */
448 protected function initPageRenderer()
449 {
450 if ($this->pageRenderer !== null) {
451 return;
452 }
453 $this->pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
454 $this->pageRenderer->setLanguage($GLOBALS['LANG']->lang);
455 $this->pageRenderer->enableConcatenateFiles();
456 $this->pageRenderer->enableCompressCss();
457 $this->pageRenderer->enableCompressJavascript();
458 // Add all JavaScript files defined in $this->jsFiles to the PageRenderer
459 foreach ($this->jsFilesNoConcatenation as $file) {
460 $this->pageRenderer->addJsFile(
461 $GLOBALS['BACK_PATH'] . $file,
462 'text/javascript',
463 true,
464 false,
465 '',
466 true
467 );
468 }
469 // Add all JavaScript files defined in $this->jsFiles to the PageRenderer
470 foreach ($this->jsFiles as $file) {
471 $this->pageRenderer->addJsFile($GLOBALS['BACK_PATH'] . $file);
472 }
473 if ((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['debug'] === 1) {
474 $this->pageRenderer->enableDebugMode();
475 }
476 }
477
478 /**
479 * Gets instance of PageRenderer configured with the current language, file references and debug settings
480 *
481 * @return PageRenderer
482 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8.
483 */
484 public function getPageRenderer()
485 {
486 GeneralUtility::logDeprecatedFunction();
487 $this->initPageRenderer();
488
489 return $this->pageRenderer;
490 }
491
492 /**
493 * Sets inclusion of StateProvider
494 *
495 * @return void
496 */
497 public function setExtDirectStateProvider()
498 {
499 $this->extDirectStateProvider = true;
500 }
501
502 /*****************************************
503 *
504 * EVALUATION FUNCTIONS
505 * Various centralized processing
506 *
507 *****************************************/
508 /**
509 * Makes click menu link (context sensitive menu)
510 * Returns $str (possibly an <|img> tag/icon) wrapped in a link which will activate the context sensitive menu for the record ($table/$uid) or file ($table = file)
511 * The link will load the top frame with the parameter "&item" which is the table,uid and listFr arguments imploded by "|": rawurlencode($table.'|'.$uid.'|'.$listFr)
512 *
513 * @param string $str String to be wrapped in link, typ. image tag.
514 * @param string $table Table name/File path. If the icon is for a database record, enter the tablename from $GLOBALS['TCA']. If a file then enter the absolute filepath
515 * @param int $uid If icon is for database record this is the UID for the record from $table
516 * @param bool $listFr Tells the top frame script that the link is coming from a "list" frame which means a frame from within the backend content frame.
517 * @param string $addParams Additional GET parameters for the link to the ClickMenu AJAX request
518 * @param string $enDisItems Enable / Disable click menu items. Example: "+new,view" will display ONLY these two items (and any spacers in between), "new,view" will display all BUT these two items.
519 * @param bool $returnTagParameters If set, will return only the onclick JavaScript, not the whole link.
520 * @return string The link-wrapped input string.
521 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use BackendUtility::wrapClickMenuOnIcon() instead
522 */
523 public function wrapClickMenuOnIcon($content, $table, $uid = 0, $listFr = true, $addParams = '', $enDisItems = '', $returnTagParameters = false)
524 {
525 GeneralUtility::logDeprecatedFunction();
526 return BackendUtility::wrapClickMenuOnIcon($content, $table, $uid, $listFr, $addParams, $enDisItems, $returnTagParameters);
527 }
528
529 /**
530 * Makes link to page $id in frontend (view page)
531 * Returns an icon which links to the frontend index.php document for viewing the page with id $id
532 * $id must be a page-uid
533 * If the BE_USER has access to Web>List then a link to that module is shown as well (with return-url)
534 *
535 * @param int $id The page id
536 * @param string $_ @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
537 * @return string HTML string with linked icon(s)
538 */
539 public function viewPageIcon($id, $_ = '')
540 {
541 // If access to Web>List for user, then link to that module.
542 $str = BackendUtility::getListViewLink(array(
543 'id' => $id,
544 'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
545 ), $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.showList'));
546 // Make link to view page
547 $str .= '<a href="#" onclick="' . htmlspecialchars(BackendUtility::viewOnClick($id, '', BackendUtility::BEgetRootLine($id))) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.showPage', true) . '">' . $this->iconFactory->getIcon('actions-document-view', Icon::SIZE_SMALL)->render() . '</a>';
548 return $str;
549 }
550
551 /**
552 * Returns a URL with a command to TYPO3 Core Engine (tce_db.php)
553 * See description of the API elsewhere.
554 *
555 * @param string $params is a set of GET params to send to tce_db.php. Example: "&cmd[tt_content][123][move]=456" or "&data[tt_content][123][hidden]=1&data[tt_content][123][title]=Hello%20World
556 * @param string|int $redirectUrl Redirect URL, default is to use GeneralUtility::getIndpEnv('REQUEST_URI'), -1 means to generate an URL for JavaScript using T3_THIS_LOCATION
557 * @return string URL to BackendUtility::getModuleUrl('tce_db') + parameters
558 * @see \TYPO3\CMS\Backend\Utility\BackendUtility::editOnClick()
559 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use BackendUtility::getLinkToDataHandlerAction() instead
560 */
561 public function issueCommand($params, $redirectUrl = '')
562 {
563 GeneralUtility::logDeprecatedFunction();
564 return BackendUtility::getLinkToDataHandlerAction($params, $redirectUrl);
565 }
566
567 /**
568 * Makes the header (icon+title) for a page (or other record). Used in most modules under Web>*
569 * $table and $row must be a tablename/record from that table
570 * $path will be shown as alt-text for the icon.
571 * The title will be truncated to 45 chars.
572 *
573 * @param string $table Table name
574 * @param array $row Record row
575 * @param string $path Alt text
576 * @param bool $noViewPageIcon Set $noViewPageIcon TRUE if you don't want a magnifier-icon for viewing the page in the frontend
577 * @param array $tWrap is an array with indexes 0 and 1 each representing HTML-tags (start/end) which will wrap the title
578 * @param bool $enableClickMenu If TRUE, render click menu code around icon image
579 * @return string HTML content
580 */
581 public function getHeader($table, $row, $path, $noViewPageIcon = false, $tWrap = array('', ''), $enableClickMenu = true)
582 {
583 $viewPage = '';
584 if (is_array($row) && $row['uid']) {
585 $iconImgTag = '<span title="' . htmlspecialchars($path) . '">' . $this->iconFactory->getIconForRecord($table, $row, Icon::SIZE_SMALL)->render() . '</span>';
586 $title = strip_tags(BackendUtility::getRecordTitle($table, $row));
587 $viewPage = $noViewPageIcon ? '' : $this->viewPageIcon($row['uid']);
588 } else {
589 $iconImgTag = '<span title="' . htmlspecialchars($path) . '">' . $this->iconFactory->getIcon('apps-pagetree-page-domain', Icon::SIZE_SMALL)->render() . '</span>';
590 $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
591 }
592
593 if ($enableClickMenu) {
594 $iconImgTag = BackendUtility::wrapClickMenuOnIcon($iconImgTag, $table, $row['uid']);
595 }
596
597 return '<span class="typo3-moduleHeader">' . $iconImgTag . $viewPage . $tWrap[0] . htmlspecialchars(GeneralUtility::fixed_lgd_cs($title, 45)) . $tWrap[1] . '</span>';
598 }
599
600 /**
601 * Like ->getHeader() but for files and folders
602 * Returns the icon with the path of the file/folder set in the alt/title attribute. Shows the name after the icon.
603 *
604 * @param \TYPO3\CMS\Core\Resource\ResourceInterface $resource
605 * @param array $tWrap is an array with indexes 0 and 1 each representing HTML-tags (start/end) which will wrap the title
606 * @param bool $enableClickMenu If TRUE, render click menu code around icon image
607 * @return string
608 */
609 public function getResourceHeader(\TYPO3\CMS\Core\Resource\ResourceInterface $resource, $tWrap = array('', ''), $enableClickMenu = true)
610 {
611 try {
612 $path = $resource->getStorage()->getName() . $resource->getParentFolder()->getIdentifier();
613 $iconImgTag = '<span title="' . htmlspecialchars($path) . '">' . $this->iconFactory->getIconForResource($resource, Icon::SIZE_SMALL)->render() . '</span>';
614 } catch (\TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException $e) {
615 $iconImgTag = '';
616 }
617
618 if ($enableClickMenu && ($resource instanceof \TYPO3\CMS\Core\Resource\File)) {
619 $metaData = $resource->_getMetaData();
620 $iconImgTag = BackendUtility::wrapClickMenuOnIcon($iconImgTag, 'sys_file_metadata', $metaData['uid']);
621 }
622
623 return '<span class="typo3-moduleHeader">' . $iconImgTag . $tWrap[0] . htmlspecialchars(GeneralUtility::fixed_lgd_cs($resource->getName(), 45)) . $tWrap[1] . '</span>';
624 }
625
626 /**
627 * Returns a linked shortcut-icon which will call the shortcut frame and set a shortcut there back to the calling page/module
628 *
629 * @param string $gvList Is the list of GET variables to store (if any)
630 * @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
631 * @param string $modName Module name string
632 * @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.
633 * @param string $classes
634 * @return string HTML content
635 */
636 public function makeShortcutIcon($gvList, $setList, $modName, $motherModName = '', $classes = '')
637 {
638 $gvList = 'route,' . $gvList;
639 $storeUrl = $this->makeShortcutUrl($gvList, $setList);
640 $pathInfo = parse_url(GeneralUtility::getIndpEnv('REQUEST_URI'));
641 // Fallback for alt_mod. We still pass in the old xMOD... stuff, but TBE_MODULES only knows about "record_edit".
642 // We still need to pass the xMOD name to createShortcut below, since this is used for icons.
643 $moduleName = $modName === 'xMOD_alt_doc.php' ? 'record_edit' : $modName;
644 // Add the module identifier automatically if typo3/index.php is used:
645 if (GeneralUtility::_GET('M') !== null && isset($GLOBALS['TBE_MODULES']['_PATHS'][$moduleName])) {
646 $storeUrl = '&M=' . $moduleName . $storeUrl;
647 }
648 if ((int)$motherModName === 1) {
649 $motherModule = 'top.currentModuleLoaded';
650 } elseif ($motherModName) {
651 $motherModule = GeneralUtility::quoteJSvalue($motherModName);
652 } else {
653 $motherModule = '\'\'';
654 }
655 $confirmationText = GeneralUtility::quoteJSvalue($GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.makeBookmark'));
656
657 $shortcutUrl = $pathInfo['path'] . '?' . $storeUrl;
658 $shortcutExist = BackendUtility::shortcutExists($shortcutUrl);
659
660 if ($shortcutExist) {
661 return '<a class="active ' . htmlspecialchars($classes) . '" title="">' .
662 $this->iconFactory->getIcon('actions-system-shortcut-active', Icon::SIZE_SMALL)->render() . '</a>';
663 }
664 $url = GeneralUtility::quoteJSvalue(rawurlencode($shortcutUrl));
665 $onClick = 'top.TYPO3.ShortcutMenu.createShortcut(' . GeneralUtility::quoteJSvalue(rawurlencode($modName)) .
666 ', ' . $url . ', ' . $confirmationText . ', ' . $motherModule . ', this);return false;';
667
668 return '<a href="#" class="' . htmlspecialchars($classes) . '" onclick="' . htmlspecialchars($onClick) . '" title="' .
669 $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.makeBookmark', true) . '">' .
670 $this->iconFactory->getIcon('actions-system-shortcut-new', Icon::SIZE_SMALL)->render() . '</a>';
671 }
672
673 /**
674 * MAKE url for storing
675 * Internal func
676 *
677 * @param string $gvList Is the list of GET variables to store (if any)
678 * @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
679 * @return string
680 * @access private
681 * @see makeShortcutIcon()
682 */
683 public function makeShortcutUrl($gvList, $setList)
684 {
685 $GET = GeneralUtility::_GET();
686 $storeArray = array_merge(GeneralUtility::compileSelectedGetVarsFromArray($gvList, $GET), array('SET' => GeneralUtility::compileSelectedGetVarsFromArray($setList, (array)$GLOBALS['SOBE']->MOD_SETTINGS)));
687 $storeUrl = GeneralUtility::implodeArrayForUrl('', $storeArray);
688 return $storeUrl;
689 }
690
691 /**
692 * Returns <input> attributes to set the width of an text-type input field.
693 * For client browsers with no CSS support the cols/size attribute is returned.
694 * For CSS compliant browsers (recommended) a ' style="width: ...px;"' is returned.
695 *
696 * @param int $size A relative number which multiplied with approx. 10 will lead to the width in pixels
697 * @param bool $textarea A flag you can set for textareas - DEPRECATED as there is no difference any more between the two
698 * @param string $styleOverride A string which will be returned as attribute-value for style="" instead of the calculated width (if CSS is enabled)
699 * @return string Tag attributes for an <input> tag (regarding width)
700 */
701 public function formWidth($size = 48, $textarea = false, $styleOverride = '')
702 {
703 return ' style="' . ($styleOverride ?: 'width:' . ceil($size * 9.58) . 'px;') . '"';
704 }
705
706 /**
707 * Returns JavaScript variables setting the returnUrl and thisScript location for use by JavaScript on the page.
708 * Used in fx. db_list.php (Web>List)
709 *
710 * @param string $thisLocation URL to "this location" / current script
711 * @return string Urls are returned as JavaScript variables T3_RETURN_URL and T3_THIS_LOCATION
712 * @see typo3/db_list.php
713 */
714 public function redirectUrls($thisLocation = '')
715 {
716 $thisLocation = $thisLocation ? $thisLocation : GeneralUtility::linkThisScript(array(
717 'CB' => '',
718 'SET' => '',
719 'cmd' => '',
720 'popViewId' => ''
721 ));
722 $out = '
723 var T3_RETURN_URL = ' . GeneralUtility::quoteJSvalue(str_replace('%20', '', rawurlencode(GeneralUtility::sanitizeLocalUrl(GeneralUtility::_GP('returnUrl'))))) . ';
724 var T3_THIS_LOCATION = ' . GeneralUtility::quoteJSvalue(str_replace('%20', '', rawurlencode($thisLocation))) . '
725 ';
726 return $out;
727 }
728
729 /**
730 * Returns a formatted string of $tstamp
731 * Uses $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'] and $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] to format date and time
732 *
733 * @param int $tstamp UNIX timestamp, seconds since 1970
734 * @param int $type How much data to show: $type = 1: hhmm, $type = 10: ddmmmyy
735 * @return string Formatted timestamp
736 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use the corresponding methods in BackendUtility
737 */
738 public function formatTime($tstamp, $type)
739 {
740 GeneralUtility::logDeprecatedFunction();
741 $dateStr = '';
742 switch ($type) {
743 case 1:
744 $dateStr = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'], $tstamp);
745 break;
746 case 10:
747 $dateStr = date($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'], $tstamp);
748 break;
749 }
750 return $dateStr;
751 }
752
753 /**
754 * Returns script parsetime IF ->parseTimeFlag is set and user is "admin"
755 * Automatically outputted in page end
756 *
757 * @return string HTML formated with <p>-tags
758 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
759 */
760 public function parseTime()
761 {
762 GeneralUtility::logDeprecatedFunction();
763 if ($this->parseTimeFlag && $GLOBALS['BE_USER']->isAdmin()) {
764 return '<p>(ParseTime: ' . (GeneralUtility::milliseconds() - $GLOBALS['PARSETIME_START']) . ' ms</p>
765 <p>REQUEST_URI-length: ' . strlen(GeneralUtility::getIndpEnv('REQUEST_URI')) . ')</p>';
766 }
767 }
768
769 /**
770 * Defines whether to use the X-UA-Compatible meta tag.
771 *
772 * @param bool $useCompatibilityTag Whether to use the tag
773 * @return void
774 */
775 public function useCompatibilityTag($useCompatibilityTag = true)
776 {
777 $this->useCompatibilityTag = (bool)$useCompatibilityTag;
778 }
779
780 /*****************************************
781 *
782 * PAGE BUILDING FUNCTIONS.
783 * Use this to build the HTML of your backend modules
784 *
785 *****************************************/
786 /**
787 * Returns page start
788 * This includes the proper header with charset, title, meta tag and beginning body-tag.
789 *
790 * @param string $title HTML Page title for the header
791 * @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)
792 * @see endPage()
793 */
794 public function startPage($title)
795 {
796 // hook pre start page
797 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['preStartPageHook'])) {
798 $preStartPageHook = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['preStartPageHook'];
799 if (is_array($preStartPageHook)) {
800 $hookParameters = array(
801 'title' => &$title
802 );
803 foreach ($preStartPageHook as $hookFunction) {
804 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
805 }
806 }
807 }
808 // alternative template for Header and Footer
809 if ($this->pageHeaderFooterTemplateFile) {
810 $file = GeneralUtility::getFileAbsFileName($this->pageHeaderFooterTemplateFile, true);
811 if ($file) {
812 $this->pageRenderer->setTemplateFile($file);
813 }
814 }
815
816 // Disable rendering of XHTML tags
817 $this->pageRenderer->setRenderXhtml(false);
818
819 $languageCode = $this->pageRenderer->getLanguage() === 'default' ? 'en' : $this->pageRenderer->getLanguage();
820 $this->pageRenderer->setHtmlTag('<html lang="' . $languageCode . '">');
821
822 $headerStart = '<!DOCTYPE html>';
823 $this->pageRenderer->setXmlPrologAndDocType($headerStart);
824 $this->pageRenderer->setHeadTag('<head>' . LF . '<!-- TYPO3 Script ID: ' . htmlspecialchars($this->scriptID) . ' -->');
825 header('Content-Type:text/html;charset=utf-8');
826 $this->pageRenderer->setCharSet('utf-8');
827 $this->pageRenderer->addMetaTag($this->generator());
828 $this->pageRenderer->addMetaTag('<meta name="robots" content="noindex,follow">');
829 $this->pageRenderer->addMetaTag('<meta charset="utf-8">');
830 $this->pageRenderer->addMetaTag('<meta name="viewport" content="width=device-width, initial-scale=1">');
831 $this->pageRenderer->setFavIcon($this->getBackendFavicon());
832 if ($this->useCompatibilityTag) {
833 $this->pageRenderer->addMetaTag($this->xUaCompatible($this->xUaCompatibilityVersion));
834 }
835 $this->pageRenderer->setTitle($title);
836 // add docstyles
837 $this->docStyle();
838 if ($this->extDirectStateProvider) {
839 $this->pageRenderer->addJsFile('sysext/backend/Resources/Public/JavaScript/ExtDirect.StateProvider.js');
840 }
841 // Add jsCode for overriding the console with a debug panel connection
842 $this->pageRenderer->addJsInlineCode('consoleOverrideWithDebugPanel', 'if (typeof top.Ext === "object") {
843 top.Ext.onReady(function() {
844 if (typeof console === "undefined") {
845 if (top && top.TYPO3 && top.TYPO3.Backend && top.TYPO3.Backend.DebugConsole) {
846 console = top.TYPO3.Backend.DebugConsole;
847 } else {
848 console = {
849 log: Ext.log,
850 info: Ext.log,
851 warn: Ext.log,
852 error: Ext.log
853 };
854 }
855 }
856 });
857 }
858 ', false);
859 $this->pageRenderer->addHeaderData($this->JScode);
860 foreach ($this->JScodeArray as $name => $code) {
861 $this->pageRenderer->addJsInlineCode($name, $code, false);
862 }
863 if (!empty($this->JScodeLibArray)) {
864 GeneralUtility::deprecationLog('DocumentTemplate->JScodeLibArray is deprecated since TYPO3 CMS 7. Use the functionality within pageRenderer directly');
865 foreach ($this->JScodeLibArray as $library) {
866 $this->pageRenderer->addHeaderData($library);
867 }
868 }
869 if ($this->extJScode) {
870 $this->pageRenderer->addExtOnReadyCode($this->extJScode);
871 }
872
873 // Load jquery and twbs JS libraries on every backend request
874 $this->pageRenderer->loadJquery();
875 // Note: please do not reference "bootstrap" outside of the TYPO3 Core (not in your own extensions)
876 // as this is preliminary as long as Twitter bootstrap does not support AMD modules
877 // this logic will be changed once Twitter bootstrap 4 is included
878 $this->pageRenderer->addJsFile('sysext/core/Resources/Public/JavaScript/Contrib/bootstrap/bootstrap.js');
879
880 // hook for additional headerData
881 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['preHeaderRenderHook'])) {
882 $preHeaderRenderHook = &$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['preHeaderRenderHook'];
883 if (is_array($preHeaderRenderHook)) {
884 $hookParameters = array(
885 'pageRenderer' => &$this->pageRenderer
886 );
887 foreach ($preHeaderRenderHook as $hookFunction) {
888 GeneralUtility::callUserFunction($hookFunction, $hookParameters, $this);
889 }
890 }
891 }
892 // Construct page header.
893 $str = $this->pageRenderer->render(PageRenderer::PART_HEADER);
894 $this->JScode = ($this->extJScode = '');
895 $this->JScodeArray = array();
896 $this->endOfPageJsBlock = $this->pageRenderer->render(PageRenderer::PART_FOOTER);
897 $str .= $this->docBodyTagBegin() . ($this->divClass ? '
898
899 <!-- Wrapping DIV-section for whole page BEGIN -->
900 <div class="' . $this->divClass . '">
901 ' : '') . trim($this->form);
902 return $str;
903 }
904
905 /**
906 * Returns page end; This includes finishing form, div, body and html tags.
907 *
908 * @return string The HTML end of a page
909 * @see startPage()
910 */
911 public function endPage()
912 {
913 $str = $this->sectionEnd() . $this->postCode . $this->wrapScriptTags(BackendUtility::getUpdateSignalCode()) . ($this->form ? '
914 </form>' : '');
915 // If something is in buffer like debug, put it to end of page
916 if (ob_get_contents()) {
917 $str .= ob_get_clean();
918 if (!headers_sent()) {
919 header('Content-Encoding: None');
920 }
921 }
922 $str .= ($this->divClass ? '
923
924 <!-- Wrapping DIV-section for whole page END -->
925 </div>' : '') . $this->endOfPageJsBlock;
926
927 // Logging: Can't find better place to put it:
928 if (TYPO3_DLOG) {
929 GeneralUtility::devLog('END of BACKEND session', \TYPO3\CMS\Backend\Template\DocumentTemplate::class, 0, array('_FLUSH' => true));
930 }
931 return $str;
932 }
933
934 /**
935 * Shortcut for render the complete page of a module
936 *
937 * @param string $title page title
938 * @param string $content page content
939 * @return string complete page
940 */
941 public function render($title, $content)
942 {
943 $pageContent = $this->startPage($title);
944 $pageContent .= $content;
945 $pageContent .= $this->endPage();
946 return $this->insertStylesAndJS($pageContent);
947 }
948
949 /**
950 * Returns the header-bar in the top of most backend modules
951 * Closes section if open.
952 *
953 * @param string $text The text string for the header
954 * @return string HTML content
955 */
956 public function header($text)
957 {
958 $str = '
959
960 <!-- MAIN Header in page top -->
961 <h1 class="t3js-title-inlineedit">' . htmlspecialchars($text) . '</h1>
962 ';
963 return $this->sectionEnd() . $str;
964 }
965
966 /**
967 * Begins an output section and sets header and content
968 *
969 * @param string $label The header
970 * @param string $text The HTML-content
971 * @param bool $nostrtoupper A flag that will prevent the header from being converted to uppercase
972 * @param bool $sH Defines the type of header (if set, "<h3>" rather than the default "h4")
973 * @param int $type The number of an icon to show with the header (see the icon-function). -1,1,2,3
974 * @param bool $allowHTMLinHeader If set, HTML tags are allowed in $label (otherwise this value is by default htmlspecialchars()'ed)
975 * @return string HTML content
976 * @see icons(), sectionHeader()
977 */
978 public function section($label, $text, $nostrtoupper = false, $sH = false, $type = 0, $allowHTMLinHeader = false)
979 {
980 $str = '';
981 // Setting header
982 if ($label) {
983 if (!$allowHTMLinHeader) {
984 $label = htmlspecialchars($label);
985 }
986 $str .= $this->sectionHeader($this->icons($type) . $label, $sH, $nostrtoupper ? '' : ' class="uppercase"');
987 }
988 // Setting content
989 $str .= '
990
991 <!-- Section content -->
992 ' . $text;
993 return $this->sectionBegin() . $str;
994 }
995
996 /**
997 * Inserts a divider image
998 * Ends a section (if open) before inserting the image
999 *
1000 * @param int $dist The margin-top/-bottom of the <hr> ruler.
1001 * @return string HTML content
1002 */
1003 public function divider($dist)
1004 {
1005 $dist = (int)$dist;
1006 $str = '
1007
1008 <!-- DIVIDER -->
1009 <hr style="margin-top: ' . $dist . 'px; margin-bottom: ' . $dist . 'px;" />
1010 ';
1011 return $this->sectionEnd() . $str;
1012 }
1013
1014 /**
1015 * Returns a blank <div>-section with a height
1016 *
1017 * @param int $dist Padding-top for the div-section (should be margin-top but konqueror (3.1) doesn't like it :-(
1018 * @return string HTML content
1019 */
1020 public function spacer($dist)
1021 {
1022 if ($dist > 0) {
1023 return '
1024
1025 <!-- Spacer element -->
1026 <div style="padding-top: ' . (int)$dist . 'px;"></div>
1027 ';
1028 }
1029 }
1030
1031 /**
1032 * Make a section header.
1033 * Begins a section if not already open.
1034 *
1035 * @param string $label The label between the <h3> or <h4> tags. (Allows HTML)
1036 * @param bool $sH If set, <h3> is used, otherwise <h4>
1037 * @param string $addAttrib Additional attributes to h-tag, eg. ' class=""'
1038 * @return string HTML content
1039 */
1040 public function sectionHeader($label, $sH = false, $addAttrib = '')
1041 {
1042 $tag = $sH ? 'h2' : 'h3';
1043 if ($addAttrib && $addAttrib[0] !== ' ') {
1044 $addAttrib = ' ' . $addAttrib;
1045 }
1046 $str = '
1047
1048 <!-- Section header -->
1049 <' . $tag . $addAttrib . '>' . $label . '</' . $tag . '>
1050 ';
1051 return $this->sectionBegin() . $str;
1052 }
1053
1054 /**
1055 * Begins an output section.
1056 * Returns the <div>-begin tag AND sets the ->sectionFlag TRUE (if the ->sectionFlag is not already set!)
1057 * 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!
1058 *
1059 * @return string HTML content
1060 */
1061 public function sectionBegin()
1062 {
1063 if (!$this->sectionFlag) {
1064 $this->sectionFlag = 1;
1065 $str = '
1066
1067 <!-- ***********************
1068 Begin output section.
1069 *********************** -->
1070 <div>
1071 ';
1072 return $str;
1073 } else {
1074 return '';
1075 }
1076 }
1077
1078 /**
1079 * Ends and output section
1080 * Returns the </div>-end tag AND clears the ->sectionFlag (but does so only IF the sectionFlag is set - that is a section is 'open')
1081 * See sectionBegin() also.
1082 *
1083 * @return string HTML content
1084 */
1085 public function sectionEnd()
1086 {
1087 if ($this->sectionFlag) {
1088 $this->sectionFlag = 0;
1089 return '
1090 </div>
1091 <!-- *********************
1092 End output section.
1093 ********************* -->
1094 ';
1095 } else {
1096 return '';
1097 }
1098 }
1099
1100 /**
1101 * If a form-tag is defined in ->form then and end-tag for that <form> element is outputted
1102 * Further a JavaScript section is outputted which will update the top.busy session-expiry object (unless $this->endJS is set to FALSE)
1103 *
1104 * @return string HTML content (<script> tag section)
1105 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, nothing there to output anymore
1106 */
1107 public function endPageJS()
1108 {
1109 GeneralUtility::logDeprecatedFunction();
1110 return '';
1111 }
1112
1113 /**
1114 * Creates the bodyTag.
1115 * You can add to the bodyTag by $this->bodyTagAdditions
1116 *
1117 * @return string HTML body tag
1118 */
1119 public function docBodyTagBegin()
1120 {
1121 return '<body ' . trim($this->bodyTagAdditions . ($this->bodyTagId ? ' id="' . $this->bodyTagId . '"' : '')) . '>';
1122 }
1123
1124 /**
1125 * Outputting document style
1126 *
1127 * @return string HTML style section/link tags
1128 */
1129 public function docStyle()
1130 {
1131 // Implode it all:
1132 $inDocStyles = implode(LF, $this->inDocStylesArray);
1133
1134 // Reset styles so they won't be added again in insertStylesAndJS()
1135 $this->inDocStylesArray = array();
1136
1137 if ($this->styleSheetFile) {
1138 $this->pageRenderer->addCssFile($this->styleSheetFile);
1139 }
1140 if ($this->styleSheetFile2) {
1141 $this->pageRenderer->addCssFile($this->styleSheetFile2);
1142 }
1143
1144 if ($inDocStyles !== '') {
1145 $this->pageRenderer->addCssInlineBlock('inDocStyles', $inDocStyles . LF . '/*###POSTCSSMARKER###*/');
1146 }
1147
1148 if ($this->styleSheetFile_post) {
1149 $this->pageRenderer->addCssFile($this->styleSheetFile_post);
1150 }
1151 }
1152
1153 /**
1154 * Insert additional style sheet link
1155 *
1156 * @param string $key some key identifying the style sheet
1157 * @param string $href uri to the style sheet file
1158 * @param string $title value for the title attribute of the link element
1159 * @param string $relation value for the rel attribute of the link element
1160 * @return void
1161 */
1162 public function addStyleSheet($key, $href, $title = '', $relation = 'stylesheet')
1163 {
1164 $this->pageRenderer->addCssFile($href, $relation, 'screen', $title);
1165 }
1166
1167 /**
1168 * Add all *.css files of the directory $path to the stylesheets
1169 *
1170 * @param string $path directory to add
1171 * @return void
1172 */
1173 public function addStyleSheetDirectory($path)
1174 {
1175 // Calculation needed, when TYPO3 source is used via a symlink
1176 // absolute path to the stylesheets
1177 $filePath = dirname(GeneralUtility::getIndpEnv('SCRIPT_FILENAME')) . '/' . $GLOBALS['BACK_PATH'] . $path;
1178 // Clean the path
1179 $resolvedPath = GeneralUtility::resolveBackPath($filePath);
1180 // Read all files in directory and sort them alphabetically
1181 $files = GeneralUtility::getFilesInDir($resolvedPath, 'css', false, 1);
1182 foreach ($files as $file) {
1183 $this->pageRenderer->addCssFile($GLOBALS['BACK_PATH'] . $path . $file, 'stylesheet', 'all');
1184 }
1185 }
1186
1187 /**
1188 * Insert post rendering document style into already rendered content
1189 * This is needed for extobjbase
1190 *
1191 * @param string $content style-content to insert.
1192 * @return string content with inserted styles
1193 */
1194 public function insertStylesAndJS($content)
1195 {
1196 $styles = LF . implode(LF, $this->inDocStylesArray);
1197 $content = str_replace('/*###POSTCSSMARKER###*/', $styles, $content);
1198
1199 // Insert accumulated JS
1200 $jscode = $this->JScode . LF . $this->wrapScriptTags(implode(LF, $this->JScodeArray));
1201 $content = str_replace('<!--###POSTJSMARKER###-->', $jscode, $content);
1202 return $content;
1203 }
1204
1205 /**
1206 * Returns an array of all stylesheet directories belonging to core and skins
1207 *
1208 * @return array Stylesheet directories
1209 */
1210 public function getSkinStylesheetDirectories()
1211 {
1212 $stylesheetDirectories = array();
1213 // Add default core stylesheets
1214 foreach ($this->stylesheetsCore as $stylesheetDir) {
1215 $stylesheetDirectories[] = $stylesheetDir;
1216 }
1217 // Stylesheets from skins
1218 // merge default css directories ($this->stylesheetsSkin) with additional ones and include them
1219 if (is_array($GLOBALS['TBE_STYLES']['skins'])) {
1220 // loop over all registered skins
1221 foreach ($GLOBALS['TBE_STYLES']['skins'] as $skinExtKey => $skin) {
1222 $skinStylesheetDirs = $this->stylesheetsSkins;
1223 // Skins can add custom stylesheetDirectories using
1224 // $GLOBALS['TBE_STYLES']['skins'][$_EXTKEY]['stylesheetDirectories']
1225 if (is_array($skin['stylesheetDirectories'])) {
1226 $skinStylesheetDirs = array_merge($skinStylesheetDirs, $skin['stylesheetDirectories']);
1227 }
1228 // Add all registered directories
1229 foreach ($skinStylesheetDirs as $stylesheetDir) {
1230 // for EXT:myskin/stylesheets/ syntax
1231 if (substr($stylesheetDir, 0, 4) === 'EXT:') {
1232 list($extKey, $path) = explode('/', substr($stylesheetDir, 4), 2);
1233 if (!empty($extKey) && ExtensionManagementUtility::isLoaded($extKey) && !empty($path)) {
1234 $stylesheetDirectories[] = ExtensionManagementUtility::extRelPath($extKey) . $path;
1235 }
1236 } else {
1237 // For relative paths
1238 $stylesheetDirectories[] = ExtensionManagementUtility::extRelPath($skinExtKey) . $stylesheetDir;
1239 }
1240 }
1241 }
1242 }
1243 return $stylesheetDirectories;
1244 }
1245
1246 /**
1247 * Returns generator meta tag
1248 *
1249 * @return string <meta> tag with name "generator
1250 */
1251 public function generator()
1252 {
1253 $str = 'TYPO3 CMS, ' . TYPO3_URL_GENERAL . ', &#169; Kasper Sk&#229;rh&#248;j ' . TYPO3_copyright_year . ', extensions are copyright of their respective owners.';
1254 return '<meta name="generator" content="' . $str . '" />';
1255 }
1256
1257 /**
1258 * Returns X-UA-Compatible meta tag
1259 *
1260 * @param string $content Content of the compatible tag (default: IE-8)
1261 * @return string <meta http-equiv="X-UA-Compatible" content="???" />
1262 */
1263 public function xUaCompatible($content = 'IE=8')
1264 {
1265 return '<meta http-equiv="X-UA-Compatible" content="' . $content . '" />';
1266 }
1267
1268 /*****************************************
1269 *
1270 * OTHER ELEMENTS
1271 * Tables, buttons, formatting dimmed/red strings
1272 *
1273 ******************************************/
1274 /**
1275 * Returns an image-tag with an 18x16 icon of the following types:
1276 *
1277 * $type:
1278 * -1: OK icon (Check-mark)
1279 * 1: Notice (Speach-bubble)
1280 * 2: Warning (Yellow triangle)
1281 * 3: Fatal error (Red stop sign)
1282 *
1283 * @param int $type See description
1284 * @param string $styleAttribValue Value for style attribute
1285 * @return string HTML image tag (if applicable)
1286 */
1287 public function icons($type, $styleAttribValue = '')
1288 {
1289 switch ($type) {
1290 case self::STATUS_ICON_ERROR:
1291 $icon = 'status-dialog-error';
1292 break;
1293 case self::STATUS_ICON_WARNING:
1294 $icon = 'status-dialog-warning';
1295 break;
1296 case self::STATUS_ICON_NOTIFICATION:
1297 $icon = 'status-dialog-notification';
1298 break;
1299 case self::STATUS_ICON_OK:
1300 $icon = 'status-dialog-ok';
1301 break;
1302 default:
1303 // Do nothing
1304 }
1305 if ($icon) {
1306 return $this->iconFactory->getIcon($icon, Icon::SIZE_SMALL)->render();
1307 }
1308 }
1309
1310 /**
1311 * Returns an <input> button with the $onClick action and $label
1312 *
1313 * @param string $onClick The value of the onclick attribute of the input tag (submit type)
1314 * @param string $label The label for the button (which will be htmlspecialchar'ed)
1315 * @return string A <input> tag of the type "submit
1316 */
1317 public function t3Button($onClick, $label)
1318 {
1319 $button = '<input class="btn btn-default" type="submit" onclick="' . htmlspecialchars($onClick) . '; return false;" value="' . htmlspecialchars($label) . '" />';
1320 return $button;
1321 }
1322
1323 /**
1324 * Dimmed-fontwrap. Returns the string wrapped in a <span>-tag defining the color to be gray/dimmed
1325 *
1326 * @param string $string Input string
1327 * @return string Output string
1328 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use proper HTML directly
1329 */
1330 public function dfw($string)
1331 {
1332 GeneralUtility::logDeprecatedFunction();
1333 return '<span class="text-muted">' . $string . '</span>';
1334 }
1335
1336 /**
1337 * red-fontwrap. Returns the string wrapped in a <span>-tag defining the color to be red
1338 *
1339 * @param string $string Input string
1340 * @return string Output string
1341 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8, use proper HTML directly
1342 */
1343 public function rfw($string)
1344 {
1345 GeneralUtility::logDeprecatedFunction();
1346 return '<span class="text-danger">' . $string . '</span>';
1347 }
1348
1349 /**
1350 * Returns string wrapped in CDATA "tags" for XML / XHTML (wrap content of <script> and <style> sections in those!)
1351 *
1352 * @param string $string Input string
1353 * @return string Output string
1354 */
1355 public function wrapInCData($string)
1356 {
1357 $string = '/*<![CDATA[*/' . $string . '/*]]>*/';
1358 return $string;
1359 }
1360
1361 /**
1362 * Wraps the input string in script tags.
1363 * Automatic re-identing of the JS code is done by using the first line as ident reference.
1364 * This is nice for identing JS code with PHP code on the same level.
1365 *
1366 * @param string $string Input string
1367 * @param bool $linebreak Wrap script element in linebreaks? Default is TRUE.
1368 * @return string Output string
1369 */
1370 public function wrapScriptTags($string, $linebreak = true)
1371 {
1372 if (trim($string)) {
1373 // <script wrapped in nl?
1374 $cr = $linebreak ? LF : '';
1375 // Remove nl from the beginning
1376 $string = ltrim($string, LF);
1377 // Re-ident to one tab using the first line as reference
1378 if ($string[0] === TAB) {
1379 $string = TAB . ltrim($string, TAB);
1380 }
1381 $string = $cr . '<script type="text/javascript">
1382 /*<![CDATA[*/
1383 ' . $string . '
1384 /*]]>*/
1385 </script>' . $cr;
1386 }
1387 return trim($string);
1388 }
1389
1390 // These vars defines the layout for the table produced by the table() function.
1391 // You can override these values from outside if you like.
1392 public $tableLayout = array(
1393 'defRow' => array(
1394 'defCol' => array('<td valign="top">', '</td>')
1395 )
1396 );
1397
1398 public $table_TR = '<tr>';
1399
1400 public $table_TABLE = '<table border="0" cellspacing="0" cellpadding="0" class="typo3-dblist" id="typo3-tmpltable">';
1401
1402 /**
1403 * Returns a table based on the input $data
1404 *
1405 * @param array $data Multidim array with first levels = rows, second levels = cells
1406 * @param array $layout If set, then this provides an alternative layout array instead of $this->tableLayout
1407 * @return string The HTML table.
1408 * @internal
1409 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
1410 */
1411 public function table($data, $layout = null)
1412 {
1413 GeneralUtility::logDeprecatedFunction();
1414 $result = '';
1415 if (is_array($data)) {
1416 $tableLayout = is_array($layout) ? $layout : $this->tableLayout;
1417 $rowCount = 0;
1418 foreach ($data as $tableRow) {
1419 if ($rowCount % 2) {
1420 $layout = is_array($tableLayout['defRowOdd']) ? $tableLayout['defRowOdd'] : $tableLayout['defRow'];
1421 } else {
1422 $layout = is_array($tableLayout['defRowEven']) ? $tableLayout['defRowEven'] : $tableLayout['defRow'];
1423 }
1424 $rowLayout = is_array($tableLayout[$rowCount]) ? $tableLayout[$rowCount] : $layout;
1425 $rowResult = '';
1426 if (is_array($tableRow)) {
1427 $cellCount = 0;
1428 foreach ($tableRow as $tableCell) {
1429 $cellWrap = is_array($layout[$cellCount]) ? $layout[$cellCount] : $layout['defCol'];
1430 $cellWrap = is_array($rowLayout['defCol']) ? $rowLayout['defCol'] : $cellWrap;
1431 $cellWrap = is_array($rowLayout[$cellCount]) ? $rowLayout[$cellCount] : $cellWrap;
1432 $rowResult .= $cellWrap[0] . $tableCell . $cellWrap[1];
1433 $cellCount++;
1434 }
1435 }
1436 $rowWrap = is_array($layout['tr']) ? $layout['tr'] : array($this->table_TR, '</tr>');
1437 $rowWrap = is_array($rowLayout['tr']) ? $rowLayout['tr'] : $rowWrap;
1438 $result .= $rowWrap[0] . $rowResult . $rowWrap[1];
1439 $rowCount++;
1440 }
1441 $tableWrap = is_array($tableLayout['table']) ? $tableLayout['table'] : array($this->table_TABLE, '</table>');
1442 $result = $tableWrap[0] . $result . $tableWrap[1];
1443 }
1444 return $result;
1445 }
1446
1447 /**
1448 * Constructs a table with content from the $arr1, $arr2 and $arr3.
1449 *
1450 * @param array $arr1 Menu elements on first level
1451 * @param array $arr2 Secondary items
1452 * @param array $arr3 Third-level items
1453 * @return string HTML content, <table>...</table>
1454 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
1455 */
1456 public function menuTable($arr1, $arr2 = array(), $arr3 = array())
1457 {
1458 GeneralUtility::logDeprecatedFunction();
1459 $rows = max(array(count($arr1), count($arr2), count($arr3)));
1460 $menu = '
1461 <table border="0" cellpadding="0" cellspacing="0" id="typo3-tablemenu">';
1462 for ($a = 0; $a < $rows; $a++) {
1463 $menu .= '<tr>';
1464 $cls = array();
1465 $valign = 'middle';
1466 $cls[] = '<td valign="' . $valign . '">' . $arr1[$a][0] . '</td><td>' . $arr1[$a][1] . '</td>';
1467 if (!empty($arr2)) {
1468 $cls[] = '<td valign="' . $valign . '">' . $arr2[$a][0] . '</td><td>' . $arr2[$a][1] . '</td>';
1469 if (!empty($arr3)) {
1470 $cls[] = '<td valign="' . $valign . '">' . $arr3[$a][0] . '</td><td>' . $arr3[$a][1] . '</td>';
1471 }
1472 }
1473 $menu .= implode($cls, '<td>&nbsp;&nbsp;</td>');
1474 $menu .= '</tr>';
1475 }
1476 $menu .= '
1477 </table>
1478 ';
1479 return $menu;
1480 }
1481
1482 /**
1483 * Returns a one-row/two-celled table with $content and $menu side by side.
1484 * The table is a 100% width table and each cell is aligned left / right
1485 *
1486 * @param string $content Content cell content (left)
1487 * @param string $menu Menu cell content (right)
1488 * @return string HTML output
1489 */
1490 public function funcMenu($content, $menu)
1491 {
1492 return '
1493 <table border="0" cellpadding="0" cellspacing="0" width="100%" id="typo3-funcmenu">
1494 <tr>
1495 <td valign="top" nowrap="nowrap">' . $content . '</td>
1496 <td valign="top" align="right" nowrap="nowrap">' . $menu . '</td>
1497 </tr>
1498 </table>';
1499 }
1500
1501 /**
1502 * Includes a javascript library that exists in the core /typo3/ directory
1503 *
1504 * @param string $lib: Library name. Call it with the full path like "sysext/core/Resources/Public/JavaScript/QueryGenerator.js" to load it
1505 * @return void
1506 */
1507 public function loadJavascriptLib($lib)
1508 {
1509 // @todo: maybe we can remove this one as well
1510 $this->pageRenderer->addJsFile($lib);
1511 }
1512
1513 /**
1514 * Includes the necessary Javascript function for the clickmenu (context sensitive menus) in the document
1515 *
1516 * @return void
1517 */
1518 public function getContextMenuCode()
1519 {
1520 $this->pageRenderer->loadJquery();
1521 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/ClickMenu');
1522 }
1523
1524 /**
1525 * Includes the necessary javascript file for use on pages which have the
1526 * drag and drop functionality (legacy folder tree)
1527 *
1528 * @param string $table indicator of which table the drag and drop function should work on (pages or folders)
1529 * @param string $additionalJavaScriptCode adds more code to the additional javascript code
1530 * @return void
1531 */
1532 public function getDragDropCode($table, $additionalJavaScriptCode = '')
1533 {
1534 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/LegacyTree', 'function() {
1535 DragDrop.table = "' . $table . '";
1536 ' . $additionalJavaScriptCode . '
1537 }');
1538 }
1539
1540 /**
1541 * Creates a tab menu from an array definition
1542 *
1543 * Returns a tab menu for a module
1544 * Requires the JS function jumpToUrl() to be available
1545 *
1546 * @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=...
1547 * @param string $elementName it the form elements name, probably something like "SET[...]
1548 * @param string $currentValue is the value to be selected currently.
1549 * @param array $menuItems is an array with the menu items for the selector box
1550 * @param string $script is the script to send the &id to, if empty it's automatically found
1551 * @param string $addparams is additional parameters to pass to the script.
1552 * @return string HTML code for tab menu
1553 */
1554 public function getTabMenu($mainParams, $elementName, $currentValue, $menuItems, $script = '', $addparams = '')
1555 {
1556 $content = '';
1557 if (is_array($menuItems)) {
1558 if (!is_array($mainParams)) {
1559 $mainParams = array('id' => $mainParams);
1560 }
1561 $mainParams = GeneralUtility::implodeArrayForUrl('', $mainParams);
1562 if (!$script) {
1563 $script = basename(PATH_thisScript);
1564 }
1565 $menuDef = array();
1566 foreach ($menuItems as $value => $label) {
1567 $menuDef[$value]['isActive'] = (string)$currentValue === (string)$value;
1568 $menuDef[$value]['label'] = htmlspecialchars($label, ENT_COMPAT, 'UTF-8', false);
1569 $menuDef[$value]['url'] = $script . '?' . $mainParams . $addparams . '&' . $elementName . '=' . $value;
1570 }
1571 $content = $this->getTabMenuRaw($menuDef);
1572 }
1573 return $content;
1574 }
1575
1576 /**
1577 * Creates the HTML content for the tab menu
1578 *
1579 * @param array $menuItems Menu items for tabs
1580 * @return string Table HTML
1581 * @access private
1582 */
1583 public function getTabMenuRaw($menuItems)
1584 {
1585 if (!is_array($menuItems)) {
1586 return '';
1587 }
1588
1589 $options = '';
1590 foreach ($menuItems as $id => $def) {
1591 $class = $def['isActive'] ? 'active' : '';
1592 $label = $def['label'];
1593 $url = htmlspecialchars($def['url']);
1594 $params = $def['addParams'];
1595
1596 $options .= '<li class="' . $class . '">' .
1597 '<a href="' . $url . '" ' . $params . '>' . $label . '</a>' .
1598 '</li>';
1599 }
1600
1601 return '<ul class="nav nav-tabs" role="tablist">' .
1602 $options .
1603 '</ul>';
1604 }
1605
1606 /**
1607 * Creates a DYNAMIC tab-menu where the tabs or collapseable are rendered with bootstrap markup
1608 *
1609 * @param array $menuItems Numeric array where each entry is an array in itself with associative keys: "label" contains the label for the TAB, "content" contains the HTML content that goes into the div-layer of the tabs content. "description" contains description text to be shown in the layer. "linkTitle" is short text for the title attribute of the tab-menu link (mouse-over text of tab). "stateIcon" indicates a standard status icon (see ->icon(), values: -1, 1, 2, 3). "icon" is an image tag placed before the text.
1610 * @param string $identString Identification string. This should be unique for every instance of a dynamic menu!
1611 * @param int $defaultTabIndex Default tab to open (for toggle <=0). Value corresponds to integer-array index + 1 (index zero is "1", index "1" is 2 etc.). A value of zero (or something non-existing) will result in no default tab open.
1612 * @param bool $collapseable If set, the tabs are rendered as headers instead over each sheet. Effectively this means there is no tab menu, but rather a foldout/foldin menu.
1613 * @param bool $wrapContent If set, the content is wrapped in div structure which provides a padding and border style. Set this FALSE to get unstyled content pane with fullsize content area.
1614 * @param bool $storeLastActiveTab If set, the last open tab is stored in local storage and will be re-open again. If you don't need this feature, e.g. for wizards like import/export you can disable this behaviour.
1615 * @return string
1616 */
1617 public function getDynamicTabMenu(array $menuItems, $identString, $defaultTabIndex = 1, $collapseable = false, $wrapContent = true, $storeLastActiveTab = true)
1618 {
1619 $this->pageRenderer->loadRequireJsModule('TYPO3/CMS/Backend/Tabs');
1620 $templatePathAndFileName = 'EXT:backend/Resources/Private/Templates/DocumentTemplate/' . ($collapseable ? 'Collapse.html' : 'Tabs.html');
1621 $view = GeneralUtility::makeInstance(StandaloneView::class);
1622 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($templatePathAndFileName));
1623 $view->assignMultiple(array(
1624 'id' => $this->getDynTabMenuId($identString),
1625 'items' => $menuItems,
1626 'defaultTabIndex' => $defaultTabIndex,
1627 'wrapContent' => $wrapContent,
1628 'storeLastActiveTab' => $storeLastActiveTab,
1629 'BACK_PATH' => $GLOBALS['BACK_PATH']
1630 ));
1631 return $view->render();
1632 }
1633
1634 /**
1635 * Creates a DYNAMIC tab-menu where the tabs are switched between with DHTML.
1636 * Should work in MSIE, Mozilla, Opera and Konqueror. On Konqueror I did find a serious problem: <textarea> fields loose their content when you switch tabs!
1637 *
1638 * @param array $menuItems Numeric array where each entry is an array in itself with associative keys: "label" contains the label for the TAB, "content" contains the HTML content that goes into the div-layer of the tabs content. "description" contains description text to be shown in the layer. "linkTitle" is short text for the title attribute of the tab-menu link (mouse-over text of tab). "stateIcon" indicates a standard status icon (see ->icon(), values: -1, 1, 2, 3). "icon" is an image tag placed before the text.
1639 * @param string $identString Identification string. This should be unique for every instance of a dynamic menu!
1640 * @param int $toggle If "1", then enabling one tab does not hide the others - they simply toggles each sheet on/off. This makes most sense together with the $foldout option. If "-1" then it acts normally where only one tab can be active at a time BUT you can click a tab and it will close so you have no active tabs.
1641 * @param bool $foldout If set, the tabs are rendered as headers instead over each sheet. Effectively this means there is no tab menu, but rather a foldout/foldin menu. Make sure to set $toggle as well for this option.
1642 * @param bool $noWrap Deprecated - delivered by CSS
1643 * @param bool $fullWidth If set, the tabs will span the full width of their position
1644 * @param int $defaultTabIndex Default tab to open (for toggle <=0). Value corresponds to integer-array index + 1 (index zero is "1", index "1" is 2 etc.). A value of zero (or something non-existing) will result in no default tab open.
1645 * @param int $tabBehaviour If set to '1' empty tabs will be remove, If set to '2' empty tabs will be disabled. setting this option to '2' is deprecated since TYPO3 CMS 7, and will be removed iwth CMS 8
1646 * @return string JavaScript section for the HTML header.
1647 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
1648 */
1649 public function getDynTabMenu($menuItems, $identString, $toggle = 0, $foldout = false, $noWrap = true, $fullWidth = false, $defaultTabIndex = 1, $tabBehaviour = 1)
1650 {
1651 GeneralUtility::logDeprecatedFunction();
1652 return $this->getDynamicTabMenu($menuItems, $identString, $defaultTabIndex, $foldout, $noWrap);
1653 }
1654
1655 /**
1656 * Creates the id for dynTabMenus.
1657 *
1658 * @param string $identString Identification string. This should be unique for every instance of a dynamic menu!
1659 * @return string The id with a short MD5 of $identString and prefixed "DTM-", like "DTM-2e8791854a
1660 */
1661 public function getDynTabMenuId($identString)
1662 {
1663 $id = 'DTM-' . GeneralUtility::shortMD5($identString);
1664 return $id;
1665 }
1666
1667 /**
1668 * Creates the version selector for the page id inputted.
1669 * Requires the core version management extension, "version" to be loaded.
1670 *
1671 * @param int $id Page id to create selector for.
1672 * @param bool $noAction If set, there will be no button for swapping page.
1673 * @return string
1674 */
1675 public function getVersionSelector($id, $noAction = false)
1676 {
1677 if (
1678 ExtensionManagementUtility::isLoaded('version') &&
1679 !ExtensionManagementUtility::isLoaded('workspaces')
1680 ) {
1681 $versionGuiObj = GeneralUtility::makeInstance(\TYPO3\CMS\Version\View\VersionView::class);
1682 return $versionGuiObj->getVersionSelector($id, $noAction);
1683 }
1684 }
1685
1686 /**
1687 * Function to load a HTML template file with markers.
1688 * When calling from own extension, use syntax getHtmlTemplate('EXT:extkey/template.html')
1689 *
1690 * @param string $filename tmpl name, usually in the typo3/template/ directory
1691 * @return string HTML of template
1692 */
1693 public function getHtmlTemplate($filename)
1694 {
1695 // setting the name of the original HTML template
1696 $this->moduleTemplateFilename = $filename;
1697 if ($GLOBALS['TBE_STYLES']['htmlTemplates'][$filename]) {
1698 $filename = $GLOBALS['TBE_STYLES']['htmlTemplates'][$filename];
1699 }
1700 if (GeneralUtility::isFirstPartOfStr($filename, 'EXT:')) {
1701 $filename = GeneralUtility::getFileAbsFileName($filename, true, true);
1702 } elseif (!GeneralUtility::isAbsPath($filename)) {
1703 $filename = GeneralUtility::resolveBackPath($filename);
1704 } elseif (!GeneralUtility::isAllowedAbsPath($filename)) {
1705 $filename = '';
1706 }
1707 $htmlTemplate = '';
1708 if ($filename !== '') {
1709 $htmlTemplate = GeneralUtility::getUrl($filename);
1710 }
1711 return $htmlTemplate;
1712 }
1713
1714 /**
1715 * Define the template for the module
1716 *
1717 * @param string $filename filename
1718 * @return void
1719 */
1720 public function setModuleTemplate($filename)
1721 {
1722 $this->moduleTemplate = $this->getHtmlTemplate($filename);
1723 }
1724
1725 /**
1726 * Put together the various elements for the module <body> using a static HTML
1727 * template
1728 *
1729 * @param array $pageRecord Record of the current page, used for page path and info
1730 * @param array $buttons HTML for all buttons
1731 * @param array $markerArray HTML for all other markers
1732 * @param array $subpartArray HTML for the subparts
1733 * @return string Composite HTML
1734 */
1735 public function moduleBody($pageRecord = array(), $buttons = array(), $markerArray = array(), $subpartArray = array())
1736 {
1737 // Get the HTML template for the module
1738 $moduleBody = $this->templateService->getSubpart($this->moduleTemplate, '###FULLDOC###');
1739 // Add CSS
1740 $this->inDocStylesArray[] = 'html { overflow: hidden; }';
1741 // Get the page path for the docheader
1742 $markerArray['PAGEPATH'] = $this->getPagePath($pageRecord);
1743 // Get the page info for the docheader
1744 $markerArray['PAGEINFO'] = $this->getPageInfo($pageRecord);
1745 // Get all the buttons for the docheader
1746 $docHeaderButtons = $this->getDocHeaderButtons($buttons);
1747 // Merge docheader buttons with the marker array
1748 $markerArray = array_merge($markerArray, $docHeaderButtons);
1749 // replacing subparts
1750 foreach ($subpartArray as $marker => $content) {
1751 $moduleBody = $this->templateService->substituteSubpart($moduleBody, $marker, $content);
1752 }
1753 // adding flash messages
1754 if ($this->showFlashMessages) {
1755 $flashMessages = $this->getFlashMessages();
1756 if (!empty($flashMessages)) {
1757 $markerArray['FLASHMESSAGES'] = $flashMessages;
1758 // If there is no dedicated marker for the messages present
1759 // then force them to appear before the content
1760 if (strpos($moduleBody, '###FLASHMESSAGES###') === false) {
1761 $moduleBody = str_replace('###CONTENT###', '###FLASHMESSAGES######CONTENT###', $moduleBody);
1762 }
1763 }
1764 }
1765 // Hook for adding more markers/content to the page, like the version selector
1766 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['moduleBodyPostProcess'])) {
1767 $params = array(
1768 'moduleTemplateFilename' => &$this->moduleTemplateFilename,
1769 'moduleTemplate' => &$this->moduleTemplate,
1770 'moduleBody' => &$moduleBody,
1771 'markers' => &$markerArray,
1772 'parentObject' => &$this
1773 );
1774 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['moduleBodyPostProcess'] as $funcRef) {
1775 GeneralUtility::callUserFunction($funcRef, $params, $this);
1776 }
1777 }
1778 // Replacing all markers with the finished markers and return the HTML content
1779 return $this->templateService->substituteMarkerArray($moduleBody, $markerArray, '###|###');
1780 }
1781
1782 /**
1783 * Get the default rendered FlashMessages from queue
1784 *
1785 * @return string
1786 */
1787 public function getFlashMessages()
1788 {
1789 /** @var $flashMessageService \TYPO3\CMS\Core\Messaging\FlashMessageService */
1790 $flashMessageService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Messaging\FlashMessageService::class);
1791 /** @var $defaultFlashMessageQueue \TYPO3\CMS\Core\Messaging\FlashMessageQueue */
1792 $defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
1793 return $defaultFlashMessageQueue->renderFlashMessages();
1794 }
1795
1796 /**
1797 * Renders the FlashMessages from queue and returns them as AJAX.
1798 *
1799 * @param ServerRequestInterface $request
1800 * @param ResponseInterface $response
1801 * @return ResponseInterface
1802 */
1803 public function renderQueuedFlashMessages(ServerRequestInterface $request, ResponseInterface $response)
1804 {
1805 $response->getBody()->write($this->getFlashMessages());
1806 return $response;
1807 }
1808
1809 /**
1810 * Fill the button lists with the defined HTML
1811 *
1812 * @param array $buttons HTML for all buttons
1813 * @return array Containing HTML for both buttonlists
1814 */
1815 protected function getDocHeaderButtons($buttons)
1816 {
1817 $markers = array();
1818 // Fill buttons for left and right float
1819 $floats = array('left', 'right');
1820 foreach ($floats as $key) {
1821 // Get the template for each float
1822 $buttonTemplate = $this->templateService->getSubpart($this->moduleTemplate, '###BUTTON_GROUPS_' . strtoupper($key) . '###');
1823 // Fill the button markers in this float
1824 $buttonTemplate = $this->templateService->substituteMarkerArray($buttonTemplate, $buttons, '###|###', true);
1825 // getting the wrap for each group
1826 $buttonWrap = $this->templateService->getSubpart($this->moduleTemplate, '###BUTTON_GROUP_WRAP###');
1827 // looping through the groups (max 6) and remove the empty groups
1828 for ($groupNumber = 1; $groupNumber < 6; $groupNumber++) {
1829 $buttonMarker = '###BUTTON_GROUP' . $groupNumber . '###';
1830 $buttonGroup = $this->templateService->getSubpart($buttonTemplate, $buttonMarker);
1831 if (trim($buttonGroup)) {
1832 if ($buttonWrap) {
1833 $buttonGroup = $this->templateService->substituteMarker($buttonWrap, '###BUTTONS###', $buttonGroup);
1834 }
1835 $buttonTemplate = $this->templateService->substituteSubpart($buttonTemplate, $buttonMarker, trim($buttonGroup));
1836 }
1837 }
1838 // Replace the marker with the template and remove all line breaks (for IE compat)
1839 $markers['BUTTONLIST_' . strtoupper($key)] = str_replace(LF, '', $buttonTemplate);
1840 }
1841 // Hook for manipulating docHeaderButtons
1842 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['docHeaderButtonsHook'])) {
1843 $params = array(
1844 'buttons' => $buttons,
1845 'markers' => &$markers,
1846 'pObj' => &$this
1847 );
1848 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['typo3/template.php']['docHeaderButtonsHook'] as $funcRef) {
1849 GeneralUtility::callUserFunction($funcRef, $params, $this);
1850 }
1851 }
1852 return $markers;
1853 }
1854
1855 /**
1856 * Generate the page path for docheader
1857 *
1858 * @param array $pageRecord Current page
1859 * @return string Page path
1860 */
1861 protected function getPagePath($pageRecord)
1862 {
1863 // Is this a real page
1864 if (is_array($pageRecord) && $pageRecord['uid']) {
1865 $title = substr($pageRecord['_thePathFull'], 0, -1);
1866 // Remove current page title
1867 $pos = strrpos($title, $pageRecord['title']);
1868 if ($pos !== false) {
1869 $title = substr($title, 0, $pos);
1870 }
1871 } else {
1872 $title = '';
1873 }
1874 // Setting the path of the page
1875 $pagePath = $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:labels.path', true) . ': <span class="typo3-docheader-pagePath">';
1876 // crop the title to title limit (or 50, if not defined)
1877 $cropLength = empty($GLOBALS['BE_USER']->uc['titleLen']) ? 50 : $GLOBALS['BE_USER']->uc['titleLen'];
1878 $croppedTitle = GeneralUtility::fixed_lgd_cs($title, -$cropLength);
1879 if ($croppedTitle !== $title) {
1880 $pagePath .= '<abbr title="' . htmlspecialchars($title) . '">' . htmlspecialchars($croppedTitle) . '</abbr>';
1881 } else {
1882 $pagePath .= htmlspecialchars($title);
1883 }
1884 $pagePath .= '</span>';
1885 return $pagePath;
1886 }
1887
1888 /**
1889 * Setting page icon with clickmenu + uid for docheader
1890 *
1891 * @param array $pageRecord Current page
1892 * @return string Page info
1893 */
1894 protected function getPageInfo($pageRecord)
1895 {
1896 // Add icon with clickmenu, etc:
1897 // If there IS a real page
1898 if (is_array($pageRecord) && $pageRecord['uid']) {
1899 $alttext = BackendUtility::getRecordIconAltText($pageRecord, 'pages');
1900 $iconImg = '<span title="' . htmlspecialchars($alttext) . '">' . $this->iconFactory->getIconForRecord('pages', $pageRecord, Icon::SIZE_SMALL)->render() . '</span>';
1901 // Make Icon:
1902 $theIcon = BackendUtility::wrapClickMenuOnIcon($iconImg, 'pages', $pageRecord['uid']);
1903 $uid = $pageRecord['uid'];
1904 $title = BackendUtility::getRecordTitle('pages', $pageRecord);
1905 } else {
1906 // On root-level of page tree
1907 // Make Icon
1908 $iconImg = '<span title="' . htmlspecialchars($GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']) . '">' . $this->iconFactory->getIcon('apps-pagetree-root', Icon::SIZE_SMALL)->render() . '</span>';
1909 if ($GLOBALS['BE_USER']->user['admin']) {
1910 $theIcon = BackendUtility::wrapClickMenuOnIcon($iconImg, 'pages', 0);
1911 } else {
1912 $theIcon = $iconImg;
1913 }
1914 $uid = '0';
1915 $title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
1916 }
1917 // Setting icon with clickmenu + uid
1918 $pageInfo = $theIcon . '<strong>' . htmlspecialchars($title) . '&nbsp;[' . $uid . ']</strong>';
1919 return $pageInfo;
1920 }
1921
1922 /**
1923 * Makes a collapseable section. See reports module for an example
1924 *
1925 * @param string $title
1926 * @param string $html
1927 * @param string $id
1928 * @param string $saveStatePointer
1929 * @return string
1930 * @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8. Use HTML bootstrap classes, localStorage etc.
1931 */
1932 public function collapseableSection($title, $html, $id, $saveStatePointer = '')
1933 {
1934 GeneralUtility::logDeprecatedFunction();
1935 $hasSave = (bool)$saveStatePointer;
1936 $collapsedStyle = ($collapsedClass = '');
1937 if ($hasSave) {
1938 /** @var $userSettingsController \TYPO3\CMS\Backend\Controller\UserSettingsController */
1939 $userSettingsController = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Controller\UserSettingsController::class);
1940 $value = $userSettingsController->process('get', $saveStatePointer . '.' . $id);
1941 if ($value) {
1942 $collapsedStyle = ' style="display: none"';
1943 $collapsedClass = ' collapsed';
1944 } else {
1945 $collapsedStyle = '';
1946 $collapsedClass = ' expanded';
1947 }
1948 }
1949 $this->pageRenderer->loadExtJS();
1950 $this->pageRenderer->addExtOnReadyCode('
1951 Ext.select("h2.section-header").each(function(element){
1952 element.on("click", function(event, tag) {
1953 var state = 0,
1954 el = Ext.fly(tag),
1955 div = el.next("div"),
1956 saveKey = el.getAttribute("rel");
1957 if (el.hasClass("collapsed")) {
1958 el.removeClass("collapsed").addClass("expanded");
1959 div.slideIn("t", {
1960 easing: "easeIn",
1961 duration: .5
1962 });
1963 } else {
1964 el.removeClass("expanded").addClass("collapsed");
1965 div.slideOut("t", {
1966 easing: "easeOut",
1967 duration: .5,
1968 remove: false,
1969 useDisplay: true
1970 });
1971 state = 1;
1972 }
1973 if (saveKey) {
1974 try {
1975 top.TYPO3.Storage.Persistent.set(saveKey + "." + tag.id, state);
1976 } catch(e) {}
1977 }
1978 });
1979 });
1980 ');
1981 return '
1982 <h2 id="' . $id . '" class="section-header' . $collapsedClass . '" rel="' . $saveStatePointer . '"> ' . $title . '</h2>
1983 <div' . $collapsedStyle . '>' . $html . '</div>
1984 ';
1985 }
1986
1987 /**
1988 * Retrieves configured favicon for backend (with fallback)
1989 *
1990 * @return string
1991 */
1992 protected function getBackendFavicon()
1993 {
1994 return $GLOBALS['TBE_STYLES']['favicon'] ?: 'sysext/backend/Resources/Public/Icons/favicon.ico';
1995 }
1996 }