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