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