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