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