[!!!][TASK] Deprecate various GeneralUtility methods
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Page / PageRenderer.php
1 <?php
2 namespace TYPO3\CMS\Core\Page;
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\Routing\Router;
18 use TYPO3\CMS\Backend\Routing\UriBuilder;
19 use TYPO3\CMS\Backend\Utility\BackendUtility;
20 use TYPO3\CMS\Core\Localization\LocalizationFactory;
21 use TYPO3\CMS\Core\Service\MarkerBasedTemplateService;
22 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Core\Utility\PathUtility;
25 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
26
27 /**
28 * TYPO3 pageRender class (new in TYPO3 4.3.0)
29 * This class render the HTML of a webpage, usable for BE and FE
30 */
31 class PageRenderer implements \TYPO3\CMS\Core\SingletonInterface
32 {
33 // Constants for the part to be rendered
34 const PART_COMPLETE = 0;
35 const PART_HEADER = 1;
36 const PART_FOOTER = 2;
37 // jQuery Core version that is shipped with TYPO3
38 const JQUERY_VERSION_LATEST = '2.1.4';
39 // jQuery namespace options
40 const JQUERY_NAMESPACE_NONE = 'none';
41 const JQUERY_NAMESPACE_DEFAULT = 'jQuery';
42 const JQUERY_NAMESPACE_DEFAULT_NOCONFLICT = 'defaultNoConflict';
43
44 /**
45 * @var bool
46 */
47 protected $compressJavascript = false;
48
49 /**
50 * @var bool
51 */
52 protected $compressCss = false;
53
54 /**
55 * @var bool
56 */
57 protected $removeLineBreaksFromTemplate = false;
58
59 /**
60 * @var bool
61 */
62 protected $concatenateFiles = false;
63
64 /**
65 * @var bool
66 */
67 protected $concatenateJavascript = false;
68
69 /**
70 * @var bool
71 */
72 protected $concatenateCss = false;
73
74 /**
75 * @var bool
76 */
77 protected $moveJsFromHeaderToFooter = false;
78
79 /**
80 * @var \TYPO3\CMS\Core\Charset\CharsetConverter
81 */
82 protected $csConvObj;
83
84 /**
85 * @var \TYPO3\CMS\Core\Localization\Locales
86 */
87 protected $locales;
88
89 /**
90 * The language key
91 * Two character string or 'default'
92 *
93 * @var string
94 */
95 protected $lang;
96
97 /**
98 * List of language dependencies for actual language. This is used for local variants of a language
99 * that depend on their "main" language, like Brazilian Portuguese or Canadian French.
100 *
101 * @var array
102 */
103 protected $languageDependencies = array();
104
105 /**
106 * @var \TYPO3\CMS\Core\Resource\ResourceCompressor
107 */
108 protected $compressor;
109
110 // Arrays containing associative array for the included files
111 /**
112 * @var array
113 */
114 protected $jsFiles = array();
115
116 /**
117 * @var array
118 */
119 protected $jsFooterFiles = array();
120
121 /**
122 * @var array
123 */
124 protected $jsLibs = array();
125
126 /**
127 * @var array
128 */
129 protected $jsFooterLibs = array();
130
131 /**
132 * @var array
133 */
134 protected $cssFiles = array();
135
136 /**
137 * @var array
138 */
139 protected $cssLibs = array();
140
141 /**
142 * The title of the page
143 *
144 * @var string
145 */
146 protected $title;
147
148 /**
149 * Charset for the rendering
150 *
151 * @var string
152 */
153 protected $charSet;
154
155 /**
156 * @var string
157 */
158 protected $favIcon;
159
160 /**
161 * @var string
162 */
163 protected $baseUrl;
164
165 /**
166 * @var bool
167 */
168 protected $renderXhtml = true;
169
170 // Static header blocks
171 /**
172 * @var string
173 */
174 protected $xmlPrologAndDocType = '';
175
176 /**
177 * @var array
178 */
179 protected $metaTags = array();
180
181 /**
182 * @var array
183 */
184 protected $inlineComments = array();
185
186 /**
187 * @var array
188 */
189 protected $headerData = array();
190
191 /**
192 * @var array
193 */
194 protected $footerData = array();
195
196 /**
197 * @var string
198 */
199 protected $titleTag = '<title>|</title>';
200
201 /**
202 * @var string
203 */
204 protected $metaCharsetTag = '<meta http-equiv="Content-Type" content="text/html; charset=|" />';
205
206 /**
207 * @var string
208 */
209 protected $htmlTag = '<html>';
210
211 /**
212 * @var string
213 */
214 protected $headTag = '<head>';
215
216 /**
217 * @var string
218 */
219 protected $baseUrlTag = '<base href="|" />';
220
221 /**
222 * @var string
223 */
224 protected $iconMimeType = '';
225
226 /**
227 * @var string
228 */
229 protected $shortcutTag = '<link rel="shortcut icon" href="%1$s"%2$s />';
230
231 // Static inline code blocks
232 /**
233 * @var array
234 */
235 protected $jsInline = array();
236
237 /**
238 * @var array
239 */
240 protected $jsFooterInline = array();
241
242 /**
243 * @var array
244 */
245 protected $extOnReadyCode = array();
246
247 /**
248 * @var array
249 */
250 protected $cssInline = array();
251
252 /**
253 * @var string
254 */
255 protected $bodyContent;
256
257 /**
258 * @var string
259 */
260 protected $templateFile;
261
262 /**
263 * @var array
264 */
265 protected $jsLibraryNames = array('extjs');
266
267 // Paths to contributed libraries
268
269 /**
270 * default path to the requireJS library, relative to the typo3/ directory
271 * @var string
272 */
273 protected $requireJsPath = 'Resources/Public/JavaScript/Contrib/';
274
275 /**
276 * @var string
277 */
278 protected $extJsPath = 'Resources/Public/JavaScript/Contrib/extjs/';
279
280 /**
281 * The local directory where one can find jQuery versions and plugins
282 *
283 * @var string
284 */
285 protected $jQueryPath = 'Resources/Public/JavaScript/Contrib/jquery/';
286
287 // Internal flags for JS-libraries
288 /**
289 * This array holds all jQuery versions that should be included in the
290 * current page.
291 * Each version is described by "source", "version" and "namespace"
292 *
293 * The namespace of every particular version is the key
294 * of that array, because only one version per namespace can exist.
295 *
296 * The type "source" describes where the jQuery core should be included from
297 * currently, TYPO3 supports "local" (make use of jQuery path), "google",
298 * "jquery", "msn" and "cloudflare".
299 *
300 * Currently there are downsides to "local" which supports only the latest/shipped
301 * jQuery core out of the box.
302 *
303 * @var array
304 */
305 protected $jQueryVersions = array();
306
307 /**
308 * Array of jQuery version numbers shipped with the core
309 *
310 * @var array
311 */
312 protected $availableLocalJqueryVersions = array(
313 self::JQUERY_VERSION_LATEST
314 );
315
316 /**
317 * Array of jQuery CDNs with placeholders
318 *
319 * @var array
320 */
321 protected $jQueryCdnUrls = array(
322 'google' => 'https://ajax.googleapis.com/ajax/libs/jquery/%1$s/jquery%2$s.js',
323 'msn' => 'https://ajax.aspnetcdn.com/ajax/jQuery/jquery-%1$s%2$s.js',
324 'jquery' => 'https://code.jquery.com/jquery-%1$s%2$s.js',
325 'cloudflare' => 'https://cdnjs.cloudflare.com/ajax/libs/jquery/%1$s/jquery%2$s.js'
326 );
327
328 /**
329 * if set, the requireJS library is included
330 * @var bool
331 */
332 protected $addRequireJs = false;
333
334 /**
335 * inline configuration for requireJS
336 * @var array
337 */
338 protected $requireJsConfig = array();
339
340 /**
341 * @var bool
342 */
343 protected $addExtJS = false;
344
345 /**
346 * @var bool
347 */
348 protected $extDirectCodeAdded = false;
349
350 /**
351 * @var bool
352 */
353 protected $enableExtJsDebug = false;
354
355 /**
356 * @var bool
357 */
358 protected $enableJqueryDebug = false;
359
360 /**
361 * @var bool
362 */
363 protected $extJStheme = true;
364
365 /**
366 * @var bool
367 */
368 protected $extJScss = true;
369
370 /**
371 * @var array
372 */
373 protected $inlineLanguageLabels = array();
374
375 /**
376 * @var array
377 */
378 protected $inlineLanguageLabelFiles = array();
379
380 /**
381 * @var array
382 */
383 protected $inlineSettings = array();
384
385 /**
386 * @var array
387 */
388 protected $inlineJavascriptWrap = array();
389
390 /**
391 * Saves error messages generated during compression
392 *
393 * @var string
394 */
395 protected $compressError = '';
396
397 /**
398 * Is empty string for HTML and ' /' for XHTML rendering
399 *
400 * @var string
401 */
402 protected $endingSlash = '';
403
404 /**
405 * @param string $templateFile Declare the used template file. Omit this parameter will use default template
406 */
407 public function __construct($templateFile = '')
408 {
409 $this->reset();
410
411 if (TYPO3_MODE === 'FE') {
412 $coreRelPath = ExtensionManagementUtility::siteRelPath('core');
413 } else {
414 $coreRelPath = ExtensionManagementUtility::extRelPath('core');
415 }
416 $this->requireJsPath = $coreRelPath . $this->requireJsPath;
417 $this->extJsPath = $coreRelPath . $this->extJsPath;
418 $this->jQueryPath = $coreRelPath . $this->jQueryPath;
419
420 $this->csConvObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Charset\CharsetConverter::class);
421 $this->locales = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\Locales::class);
422 if ($templateFile !== '') {
423 $this->templateFile = $templateFile;
424 }
425 $this->inlineJavascriptWrap = array(
426 '<script type="text/javascript">' . LF . '/*<![CDATA[*/' . LF,
427 '/*]]>*/' . LF . '</script>' . LF
428 );
429 $this->inlineCssWrap = array(
430 '<style type="text/css">' . LF . '/*<![CDATA[*/' . LF . '<!-- ' . LF,
431 '-->' . LF . '/*]]>*/' . LF . '</style>' . LF
432 );
433 }
434
435 /**
436 * Reset all vars to initial values
437 *
438 * @return void
439 */
440 protected function reset()
441 {
442 $this->templateFile = 'EXT:core/Resources/Private/Templates/PageRenderer.html';
443 $this->jsFiles = array();
444 $this->jsFooterFiles = array();
445 $this->jsInline = array();
446 $this->jsFooterInline = array();
447 $this->jsLibs = array();
448 $this->cssFiles = array();
449 $this->cssInline = array();
450 $this->metaTags = array();
451 $this->inlineComments = array();
452 $this->headerData = array();
453 $this->footerData = array();
454 $this->extOnReadyCode = array();
455 $this->jQueryVersions = array();
456 }
457
458 /*****************************************************/
459 /* */
460 /* Public Setters */
461 /* */
462 /* */
463 /*****************************************************/
464 /**
465 * Sets the title
466 *
467 * @param string $title title of webpage
468 * @return void
469 */
470 public function setTitle($title)
471 {
472 $this->title = $title;
473 }
474
475 /**
476 * Enables/disables rendering of XHTML code
477 *
478 * @param bool $enable Enable XHTML
479 * @return void
480 */
481 public function setRenderXhtml($enable)
482 {
483 $this->renderXhtml = $enable;
484 }
485
486 /**
487 * Sets xml prolog and docType
488 *
489 * @param string $xmlPrologAndDocType Complete tags for xml prolog and docType
490 * @return void
491 */
492 public function setXmlPrologAndDocType($xmlPrologAndDocType)
493 {
494 $this->xmlPrologAndDocType = $xmlPrologAndDocType;
495 }
496
497 /**
498 * Sets meta charset
499 *
500 * @param string $charSet Used charset
501 * @return void
502 */
503 public function setCharSet($charSet)
504 {
505 $this->charSet = $charSet;
506 }
507
508 /**
509 * Sets language
510 *
511 * @param string $lang Used language
512 * @return void
513 */
514 public function setLanguage($lang)
515 {
516 $this->lang = $lang;
517 $this->languageDependencies = array();
518
519 // Language is found. Configure it:
520 if (in_array($this->lang, $this->locales->getLocales())) {
521 $this->languageDependencies[] = $this->lang;
522 foreach ($this->locales->getLocaleDependencies($this->lang) as $language) {
523 $this->languageDependencies[] = $language;
524 }
525 }
526 }
527
528 /**
529 * Set the meta charset tag
530 *
531 * @param string $metaCharsetTag
532 * @return void
533 */
534 public function setMetaCharsetTag($metaCharsetTag)
535 {
536 $this->metaCharsetTag = $metaCharsetTag;
537 }
538
539 /**
540 * Sets html tag
541 *
542 * @param string $htmlTag Html tag
543 * @return void
544 */
545 public function setHtmlTag($htmlTag)
546 {
547 $this->htmlTag = $htmlTag;
548 }
549
550 /**
551 * Sets HTML head tag
552 *
553 * @param string $headTag HTML head tag
554 * @return void
555 */
556 public function setHeadTag($headTag)
557 {
558 $this->headTag = $headTag;
559 }
560
561 /**
562 * Sets favicon
563 *
564 * @param string $favIcon
565 * @return void
566 */
567 public function setFavIcon($favIcon)
568 {
569 $this->favIcon = $favIcon;
570 }
571
572 /**
573 * Sets icon mime type
574 *
575 * @param string $iconMimeType
576 * @return void
577 */
578 public function setIconMimeType($iconMimeType)
579 {
580 $this->iconMimeType = $iconMimeType;
581 }
582
583 /**
584 * Sets HTML base URL
585 *
586 * @param string $baseUrl HTML base URL
587 * @return void
588 */
589 public function setBaseUrl($baseUrl)
590 {
591 $this->baseUrl = $baseUrl;
592 }
593
594 /**
595 * Sets template file
596 *
597 * @param string $file
598 * @return void
599 */
600 public function setTemplateFile($file)
601 {
602 $this->templateFile = $file;
603 }
604
605 /**
606 * Sets Content for Body
607 *
608 * @param string $content
609 * @return void
610 */
611 public function setBodyContent($content)
612 {
613 $this->bodyContent = $content;
614 }
615
616 /**
617 * Sets path to requireJS library (relative to typo3 directory)
618 *
619 * @param string $path Path to requireJS library
620 * @return void
621 */
622 public function setRequireJsPath($path)
623 {
624 $this->requireJsPath = $path;
625 }
626
627 /**
628 * Sets Path for ExtJs library (relative to typo3 directory)
629 *
630 * @param string $path
631 * @return void
632 */
633 public function setExtJsPath($path)
634 {
635 $this->extJsPath = $path;
636 }
637
638 /*****************************************************/
639 /* */
640 /* Public Enablers / Disablers */
641 /* */
642 /* */
643 /*****************************************************/
644 /**
645 * Enables MoveJsFromHeaderToFooter
646 *
647 * @return void
648 */
649 public function enableMoveJsFromHeaderToFooter()
650 {
651 $this->moveJsFromHeaderToFooter = true;
652 }
653
654 /**
655 * Disables MoveJsFromHeaderToFooter
656 *
657 * @return void
658 */
659 public function disableMoveJsFromHeaderToFooter()
660 {
661 $this->moveJsFromHeaderToFooter = false;
662 }
663
664 /**
665 * Enables compression of javascript
666 *
667 * @return void
668 */
669 public function enableCompressJavascript()
670 {
671 $this->compressJavascript = true;
672 }
673
674 /**
675 * Disables compression of javascript
676 *
677 * @return void
678 */
679 public function disableCompressJavascript()
680 {
681 $this->compressJavascript = false;
682 }
683
684 /**
685 * Enables compression of css
686 *
687 * @return void
688 */
689 public function enableCompressCss()
690 {
691 $this->compressCss = true;
692 }
693
694 /**
695 * Disables compression of css
696 *
697 * @return void
698 */
699 public function disableCompressCss()
700 {
701 $this->compressCss = false;
702 }
703
704 /**
705 * Enables concatenation of js and css files
706 *
707 * @return void
708 */
709 public function enableConcatenateFiles()
710 {
711 $this->concatenateFiles = true;
712 }
713
714 /**
715 * Disables concatenation of js and css files
716 *
717 * @return void
718 */
719 public function disableConcatenateFiles()
720 {
721 $this->concatenateFiles = false;
722 }
723
724 /**
725 * Enables concatenation of js files
726 *
727 * @return void
728 */
729 public function enableConcatenateJavascript()
730 {
731 $this->concatenateJavascript = true;
732 }
733
734 /**
735 * Disables concatenation of js files
736 *
737 * @return void
738 */
739 public function disableConcatenateJavascript()
740 {
741 $this->concatenateJavascript = false;
742 }
743
744 /**
745 * Enables concatenation of css files
746 *
747 * @return void
748 */
749 public function enableConcatenateCss()
750 {
751 $this->concatenateCss = true;
752 }
753
754 /**
755 * Disables concatenation of css files
756 *
757 * @return void
758 */
759 public function disableConcatenateCss()
760 {
761 $this->concatenateCss = false;
762 }
763
764 /**
765 * Sets removal of all line breaks in template
766 *
767 * @return void
768 */
769 public function enableRemoveLineBreaksFromTemplate()
770 {
771 $this->removeLineBreaksFromTemplate = true;
772 }
773
774 /**
775 * Unsets removal of all line breaks in template
776 *
777 * @return void
778 */
779 public function disableRemoveLineBreaksFromTemplate()
780 {
781 $this->removeLineBreaksFromTemplate = false;
782 }
783
784 /**
785 * Enables Debug Mode
786 * This is a shortcut to switch off all compress/concatenate features to enable easier debug
787 *
788 * @return void
789 */
790 public function enableDebugMode()
791 {
792 $this->compressJavascript = false;
793 $this->compressCss = false;
794 $this->concatenateFiles = false;
795 $this->removeLineBreaksFromTemplate = false;
796 $this->enableExtJsDebug = true;
797 $this->enableJqueryDebug = true;
798 }
799
800 /*****************************************************/
801 /* */
802 /* Public Getters */
803 /* */
804 /* */
805 /*****************************************************/
806 /**
807 * Gets the title
808 *
809 * @return string $title Title of webpage
810 */
811 public function getTitle()
812 {
813 return $this->title;
814 }
815
816 /**
817 * Gets the charSet
818 *
819 * @return string $charSet
820 */
821 public function getCharSet()
822 {
823 return $this->charSet;
824 }
825
826 /**
827 * Gets the language
828 *
829 * @return string $lang
830 */
831 public function getLanguage()
832 {
833 return $this->lang;
834 }
835
836 /**
837 * Returns rendering mode XHTML or HTML
838 *
839 * @return bool TRUE if XHTML, FALSE if HTML
840 */
841 public function getRenderXhtml()
842 {
843 return $this->renderXhtml;
844 }
845
846 /**
847 * Gets html tag
848 *
849 * @return string $htmlTag Html tag
850 */
851 public function getHtmlTag()
852 {
853 return $this->htmlTag;
854 }
855
856 /**
857 * Get meta charset
858 *
859 * @return string
860 */
861 public function getMetaCharsetTag()
862 {
863 return $this->metaCharsetTag;
864 }
865
866 /**
867 * Gets head tag
868 *
869 * @return string $tag Head tag
870 */
871 public function getHeadTag()
872 {
873 return $this->headTag;
874 }
875
876 /**
877 * Gets favicon
878 *
879 * @return string $favIcon
880 */
881 public function getFavIcon()
882 {
883 return $this->favIcon;
884 }
885
886 /**
887 * Gets icon mime type
888 *
889 * @return string $iconMimeType
890 */
891 public function getIconMimeType()
892 {
893 return $this->iconMimeType;
894 }
895
896 /**
897 * Gets HTML base URL
898 *
899 * @return string $url
900 */
901 public function getBaseUrl()
902 {
903 return $this->baseUrl;
904 }
905
906 /**
907 * Gets template file
908 *
909 * @return string
910 */
911 public function getTemplateFile()
912 {
913 return $this->templateFile;
914 }
915
916 /**
917 * Gets MoveJsFromHeaderToFooter
918 *
919 * @return bool
920 */
921 public function getMoveJsFromHeaderToFooter()
922 {
923 return $this->moveJsFromHeaderToFooter;
924 }
925
926 /**
927 * Gets compress of javascript
928 *
929 * @return bool
930 */
931 public function getCompressJavascript()
932 {
933 return $this->compressJavascript;
934 }
935
936 /**
937 * Gets compress of css
938 *
939 * @return bool
940 */
941 public function getCompressCss()
942 {
943 return $this->compressCss;
944 }
945
946 /**
947 * Gets concatenate of js and css files
948 *
949 * @return bool
950 */
951 public function getConcatenateFiles()
952 {
953 return $this->concatenateFiles;
954 }
955
956 /**
957 * Gets concatenate of js files
958 *
959 * @return bool
960 */
961 public function getConcatenateJavascript()
962 {
963 return $this->concatenateJavascript;
964 }
965
966 /**
967 * Gets concatenate of css files
968 *
969 * @return bool
970 */
971 public function getConcatenateCss()
972 {
973 return $this->concatenateCss;
974 }
975
976 /**
977 * Gets remove of empty lines from template
978 *
979 * @return bool
980 */
981 public function getRemoveLineBreaksFromTemplate()
982 {
983 return $this->removeLineBreaksFromTemplate;
984 }
985
986 /**
987 * Gets content for body
988 *
989 * @return string
990 */
991 public function getBodyContent()
992 {
993 return $this->bodyContent;
994 }
995
996 /**
997 * Gets Path for ExtJs library (relative to typo3 directory)
998 *
999 * @return string
1000 */
1001 public function getExtJsPath()
1002 {
1003 return $this->extJsPath;
1004 }
1005
1006 /**
1007 * Gets the inline language labels.
1008 *
1009 * @return array The inline language labels
1010 */
1011 public function getInlineLanguageLabels()
1012 {
1013 return $this->inlineLanguageLabels;
1014 }
1015
1016 /**
1017 * Gets the inline language files
1018 *
1019 * @return array
1020 */
1021 public function getInlineLanguageLabelFiles()
1022 {
1023 return $this->inlineLanguageLabelFiles;
1024 }
1025
1026 /*****************************************************/
1027 /* */
1028 /* Public Functions to add Data */
1029 /* */
1030 /* */
1031 /*****************************************************/
1032 /**
1033 * Adds meta data
1034 *
1035 * @param string $meta Meta data (complete metatag)
1036 * @return void
1037 */
1038 public function addMetaTag($meta)
1039 {
1040 if (!in_array($meta, $this->metaTags)) {
1041 $this->metaTags[] = $meta;
1042 }
1043 }
1044
1045 /**
1046 * Adds inline HTML comment
1047 *
1048 * @param string $comment
1049 * @return void
1050 */
1051 public function addInlineComment($comment)
1052 {
1053 if (!in_array($comment, $this->inlineComments)) {
1054 $this->inlineComments[] = $comment;
1055 }
1056 }
1057
1058 /**
1059 * Adds header data
1060 *
1061 * @param string $data Free header data for HTML header
1062 * @return void
1063 */
1064 public function addHeaderData($data)
1065 {
1066 if (!in_array($data, $this->headerData)) {
1067 $this->headerData[] = $data;
1068 }
1069 }
1070
1071 /**
1072 * Adds footer data
1073 *
1074 * @param string $data Free header data for HTML header
1075 * @return void
1076 */
1077 public function addFooterData($data)
1078 {
1079 if (!in_array($data, $this->footerData)) {
1080 $this->footerData[] = $data;
1081 }
1082 }
1083
1084 /**
1085 * Adds JS Library. JS Library block is rendered on top of the JS files.
1086 *
1087 * @param string $name Arbitrary identifier
1088 * @param string $file File name
1089 * @param string $type Content Type
1090 * @param bool $compress Flag if library should be compressed
1091 * @param bool $forceOnTop Flag if added library should be inserted at begin of this block
1092 * @param string $allWrap
1093 * @param bool $excludeFromConcatenation
1094 * @param string $splitChar The char used to split the allWrap value, default is "|"
1095 * @param bool $async Flag if property 'async="async"' should be added to JavaScript tags
1096 * @param string $integrity Subresource Integrity (SRI)
1097 * @return void
1098 */
1099 public function addJsLibrary($name, $file, $type = 'text/javascript', $compress = false, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1100 {
1101 if (!$type) {
1102 $type = 'text/javascript';
1103 }
1104 if (!in_array(strtolower($name), $this->jsLibs)) {
1105 $this->jsLibs[strtolower($name)] = array(
1106 'file' => $file,
1107 'type' => $type,
1108 'section' => self::PART_HEADER,
1109 'compress' => $compress,
1110 'forceOnTop' => $forceOnTop,
1111 'allWrap' => $allWrap,
1112 'excludeFromConcatenation' => $excludeFromConcatenation,
1113 'splitChar' => $splitChar,
1114 'async' => $async,
1115 'integrity' => $integrity,
1116 );
1117 }
1118 }
1119
1120 /**
1121 * Adds JS Library to Footer. JS Library block is rendered on top of the Footer JS files.
1122 *
1123 * @param string $name Arbitrary identifier
1124 * @param string $file File name
1125 * @param string $type Content Type
1126 * @param bool $compress Flag if library should be compressed
1127 * @param bool $forceOnTop Flag if added library should be inserted at begin of this block
1128 * @param string $allWrap
1129 * @param bool $excludeFromConcatenation
1130 * @param string $splitChar The char used to split the allWrap value, default is "|"
1131 * @param bool $async Flag if property 'async="async"' should be added to JavaScript tags
1132 * @param string $integrity Subresource Integrity (SRI)
1133 * @return void
1134 */
1135 public function addJsFooterLibrary($name, $file, $type = 'text/javascript', $compress = false, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1136 {
1137 if (!$type) {
1138 $type = 'text/javascript';
1139 }
1140 if (!in_array(strtolower($name), $this->jsLibs)) {
1141 $this->jsLibs[strtolower($name)] = array(
1142 'file' => $file,
1143 'type' => $type,
1144 'section' => self::PART_FOOTER,
1145 'compress' => $compress,
1146 'forceOnTop' => $forceOnTop,
1147 'allWrap' => $allWrap,
1148 'excludeFromConcatenation' => $excludeFromConcatenation,
1149 'splitChar' => $splitChar,
1150 'async' => $async,
1151 'integrity' => $integrity,
1152 );
1153 }
1154 }
1155
1156 /**
1157 * Adds JS file
1158 *
1159 * @param string $file File name
1160 * @param string $type Content Type
1161 * @param bool $compress
1162 * @param bool $forceOnTop
1163 * @param string $allWrap
1164 * @param bool $excludeFromConcatenation
1165 * @param string $splitChar The char used to split the allWrap value, default is "|"
1166 * @param bool $async Flag if property 'async="async"' should be added to JavaScript tags
1167 * @param string $integrity Subresource Integrity (SRI)
1168 * @return void
1169 */
1170 public function addJsFile($file, $type = 'text/javascript', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1171 {
1172 if (!$type) {
1173 $type = 'text/javascript';
1174 }
1175 if (!isset($this->jsFiles[$file])) {
1176 $this->jsFiles[$file] = array(
1177 'file' => $file,
1178 'type' => $type,
1179 'section' => self::PART_HEADER,
1180 'compress' => $compress,
1181 'forceOnTop' => $forceOnTop,
1182 'allWrap' => $allWrap,
1183 'excludeFromConcatenation' => $excludeFromConcatenation,
1184 'splitChar' => $splitChar,
1185 'async' => $async,
1186 'integrity' => $integrity,
1187 );
1188 }
1189 }
1190
1191 /**
1192 * Adds JS file to footer
1193 *
1194 * @param string $file File name
1195 * @param string $type Content Type
1196 * @param bool $compress
1197 * @param bool $forceOnTop
1198 * @param string $allWrap
1199 * @param bool $excludeFromConcatenation
1200 * @param string $splitChar The char used to split the allWrap value, default is "|"
1201 * @param bool $async Flag if property 'async="async"' should be added to JavaScript tags
1202 * @param string $integrity Subresource Integrity (SRI)
1203 * @return void
1204 */
1205 public function addJsFooterFile($file, $type = 'text/javascript', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1206 {
1207 if (!$type) {
1208 $type = 'text/javascript';
1209 }
1210 if (!isset($this->jsFiles[$file])) {
1211 $this->jsFiles[$file] = array(
1212 'file' => $file,
1213 'type' => $type,
1214 'section' => self::PART_FOOTER,
1215 'compress' => $compress,
1216 'forceOnTop' => $forceOnTop,
1217 'allWrap' => $allWrap,
1218 'excludeFromConcatenation' => $excludeFromConcatenation,
1219 'splitChar' => $splitChar,
1220 'async' => $async,
1221 'integrity' => $integrity,
1222 );
1223 }
1224 }
1225
1226 /**
1227 * Adds JS inline code
1228 *
1229 * @param string $name
1230 * @param string $block
1231 * @param bool $compress
1232 * @param bool $forceOnTop
1233 * @return void
1234 */
1235 public function addJsInlineCode($name, $block, $compress = true, $forceOnTop = false)
1236 {
1237 if (!isset($this->jsInline[$name]) && !empty($block)) {
1238 $this->jsInline[$name] = array(
1239 'code' => $block . LF,
1240 'section' => self::PART_HEADER,
1241 'compress' => $compress,
1242 'forceOnTop' => $forceOnTop
1243 );
1244 }
1245 }
1246
1247 /**
1248 * Adds JS inline code to footer
1249 *
1250 * @param string $name
1251 * @param string $block
1252 * @param bool $compress
1253 * @param bool $forceOnTop
1254 * @return void
1255 */
1256 public function addJsFooterInlineCode($name, $block, $compress = true, $forceOnTop = false)
1257 {
1258 if (!isset($this->jsInline[$name]) && !empty($block)) {
1259 $this->jsInline[$name] = array(
1260 'code' => $block . LF,
1261 'section' => self::PART_FOOTER,
1262 'compress' => $compress,
1263 'forceOnTop' => $forceOnTop
1264 );
1265 }
1266 }
1267
1268 /**
1269 * Adds Ext.onready code, which will be wrapped in Ext.onReady(function() {...});
1270 *
1271 * @param string $block Javascript code
1272 * @param bool $forceOnTop Position of the javascript code (TRUE for putting it on top, default is FALSE = bottom)
1273 * @return void
1274 */
1275 public function addExtOnReadyCode($block, $forceOnTop = false)
1276 {
1277 if (!in_array($block, $this->extOnReadyCode)) {
1278 if ($forceOnTop) {
1279 array_unshift($this->extOnReadyCode, $block);
1280 } else {
1281 $this->extOnReadyCode[] = $block;
1282 }
1283 }
1284 }
1285
1286 /**
1287 * Adds the ExtDirect code
1288 *
1289 * @param array $filterNamespaces Limit the output to defined namespaces. If empty, all namespaces are generated
1290 * @return void
1291 */
1292 public function addExtDirectCode(array $filterNamespaces = array())
1293 {
1294 if ($this->extDirectCodeAdded) {
1295 return;
1296 }
1297 $this->extDirectCodeAdded = true;
1298 if (empty($filterNamespaces)) {
1299 $filterNamespaces = array('TYPO3');
1300 }
1301
1302 // Add language labels for ExtDirect
1303 if (TYPO3_MODE === 'FE') {
1304 $this->addInlineLanguageLabelArray(array(
1305 'extDirect_timeoutHeader' => $this->getTypoScriptFrontendController()->sL('LLL:EXT:lang/locallang_misc.xlf:extDirect_timeoutHeader'),
1306 'extDirect_timeoutMessage' => $this->getTypoScriptFrontendController()->sL('LLL:EXT:lang/locallang_misc.xlf:extDirect_timeoutMessage')
1307 ));
1308 } else {
1309 $this->addInlineLanguageLabelArray(array(
1310 'extDirect_timeoutHeader' => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_misc.xlf:extDirect_timeoutHeader'),
1311 'extDirect_timeoutMessage' => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_misc.xlf:extDirect_timeoutMessage')
1312 ));
1313 }
1314
1315 $token = ($api = '');
1316 if (TYPO3_MODE === 'BE') {
1317 $formprotection = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get();
1318 $token = $formprotection->generateToken('extDirect');
1319
1320 // Debugger Console strings
1321 $this->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/debugger.xlf');
1322
1323 $this->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/wizard.xlf');
1324 }
1325 /** @var $extDirect \TYPO3\CMS\Core\ExtDirect\ExtDirectApi */
1326 $extDirect = GeneralUtility::makeInstance(\TYPO3\CMS\Core\ExtDirect\ExtDirectApi::class);
1327 $api = $extDirect->getApiPhp($filterNamespaces);
1328 if ($api) {
1329 $this->addJsInlineCode('TYPO3ExtDirectAPI', $api, false);
1330 }
1331 // Note: we need to iterate thru the object, because the addProvider method
1332 // does this only with multiple arguments
1333 $this->addExtOnReadyCode('
1334 (function() {
1335 TYPO3.ExtDirectToken = "' . $token . '";
1336 for (var api in Ext.app.ExtDirectAPI) {
1337 var provider = Ext.Direct.addProvider(Ext.app.ExtDirectAPI[api]);
1338 provider.on("beforecall", function(provider, transaction, meta) {
1339 if (transaction.data) {
1340 transaction.data[transaction.data.length] = TYPO3.ExtDirectToken;
1341 } else {
1342 transaction.data = [TYPO3.ExtDirectToken];
1343 }
1344 });
1345
1346 provider.on("call", function(provider, transaction, meta) {
1347 if (transaction.isForm) {
1348 transaction.params.securityToken = TYPO3.ExtDirectToken;
1349 }
1350 });
1351 }
1352 })();
1353
1354 var extDirectDebug = function(message, header, group) {
1355 var DebugConsole = null;
1356
1357 if (top && top.TYPO3 && typeof top.TYPO3.DebugConsole === "object") {
1358 DebugConsole = top.TYPO3.DebugConsole;
1359 } else if (typeof TYPO3 === "object" && typeof TYPO3.DebugConsole === "object") {
1360 DebugConsole = TYPO3.DebugConsole;
1361 }
1362
1363 if (DebugConsole !== null) {
1364 DebugConsole.add(message, header, group);
1365 } else if (typeof console === "object") {
1366 console.log(message);
1367 } else {
1368 document.write(message);
1369 }
1370 };
1371
1372 Ext.Direct.on("exception", function(event) {
1373 if (event.code === Ext.Direct.exceptions.TRANSPORT && !event.where) {
1374 top.TYPO3.Notification.error(
1375 TYPO3.l10n.localize("extDirect_timeoutHeader"),
1376 TYPO3.l10n.localize("extDirect_timeoutMessage")
1377 );
1378 } else {
1379 var backtrace = "";
1380 if (event.code === "parse") {
1381 extDirectDebug(
1382 "<p>" + event.xhr.responseText + "<\\/p>",
1383 event.type,
1384 "ExtDirect - Exception"
1385 );
1386 } else if (event.code === "router") {
1387 top.TYPO3.Notification.error(
1388 event.code,
1389 event.message
1390 );
1391 } else if (event.where) {
1392 backtrace = "<p style=\\"margin-top: 20px;\\">" +
1393 "<strong>Backtrace:<\\/strong><br \\/>" +
1394 event.where.replace(/#/g, "<br \\/>#") +
1395 "<\\/p>";
1396 extDirectDebug(
1397 "<p>" + event.message + "<\\/p>" + backtrace,
1398 event.method,
1399 "ExtDirect - Exception"
1400 );
1401 }
1402
1403
1404 }
1405 });
1406
1407 Ext.Direct.on("event", function(event, provider) {
1408 if (typeof event.debug !== "undefined" && event.debug !== "") {
1409 extDirectDebug(event.debug, event.method, "ExtDirect - Debug");
1410 }
1411 });
1412 ', true);
1413 }
1414
1415 /**
1416 * Adds CSS file
1417 *
1418 * @param string $file
1419 * @param string $rel
1420 * @param string $media
1421 * @param string $title
1422 * @param bool $compress
1423 * @param bool $forceOnTop
1424 * @param string $allWrap
1425 * @param bool $excludeFromConcatenation
1426 * @param string $splitChar The char used to split the allWrap value, default is "|"
1427 * @return void
1428 */
1429 public function addCssFile($file, $rel = 'stylesheet', $media = 'all', $title = '', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|')
1430 {
1431 if (!isset($this->cssFiles[$file])) {
1432 $this->cssFiles[$file] = array(
1433 'file' => $file,
1434 'rel' => $rel,
1435 'media' => $media,
1436 'title' => $title,
1437 'compress' => $compress,
1438 'forceOnTop' => $forceOnTop,
1439 'allWrap' => $allWrap,
1440 'excludeFromConcatenation' => $excludeFromConcatenation,
1441 'splitChar' => $splitChar
1442 );
1443 }
1444 }
1445
1446 /**
1447 * Adds CSS file
1448 *
1449 * @param string $file
1450 * @param string $rel
1451 * @param string $media
1452 * @param string $title
1453 * @param bool $compress
1454 * @param bool $forceOnTop
1455 * @param string $allWrap
1456 * @param bool $excludeFromConcatenation
1457 * @param string $splitChar The char used to split the allWrap value, default is "|"
1458 * @return void
1459 */
1460 public function addCssLibrary($file, $rel = 'stylesheet', $media = 'all', $title = '', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|')
1461 {
1462 if (!isset($this->cssLibs[$file])) {
1463 $this->cssLibs[$file] = array(
1464 'file' => $file,
1465 'rel' => $rel,
1466 'media' => $media,
1467 'title' => $title,
1468 'compress' => $compress,
1469 'forceOnTop' => $forceOnTop,
1470 'allWrap' => $allWrap,
1471 'excludeFromConcatenation' => $excludeFromConcatenation,
1472 'splitChar' => $splitChar
1473 );
1474 }
1475 }
1476
1477 /**
1478 * Adds CSS inline code
1479 *
1480 * @param string $name
1481 * @param string $block
1482 * @param bool $compress
1483 * @param bool $forceOnTop
1484 * @return void
1485 */
1486 public function addCssInlineBlock($name, $block, $compress = false, $forceOnTop = false)
1487 {
1488 if (!isset($this->cssInline[$name]) && !empty($block)) {
1489 $this->cssInline[$name] = array(
1490 'code' => $block,
1491 'compress' => $compress,
1492 'forceOnTop' => $forceOnTop
1493 );
1494 }
1495 }
1496
1497 /**
1498 * Call this function if you need to include the jQuery library
1499 *
1500 * @param null|string $version The jQuery version that should be included, either "latest" or any available version
1501 * @param null|string $source The location of the jQuery source, can be "local", "google", "msn", "jquery" or just an URL to your jQuery lib
1502 * @param string $namespace The namespace in which the jQuery object of the specific version should be stored.
1503 * @return void
1504 * @throws \UnexpectedValueException
1505 */
1506 public function loadJquery($version = null, $source = null, $namespace = self::JQUERY_NAMESPACE_DEFAULT)
1507 {
1508 // Set it to the version that is shipped with the TYPO3 core
1509 if ($version === null || $version === 'latest') {
1510 $version = self::JQUERY_VERSION_LATEST;
1511 }
1512 // Check if the source is set, otherwise set it to "default"
1513 if ($source === null) {
1514 $source = 'local';
1515 }
1516 if ($source === 'local' && !in_array($version, $this->availableLocalJqueryVersions)) {
1517 throw new \UnexpectedValueException('The requested jQuery version is not available in the local filesystem.', 1341505305);
1518 }
1519 if (!preg_match('/^[a-zA-Z0-9]+$/', $namespace)) {
1520 throw new \UnexpectedValueException('The requested namespace contains non alphanumeric characters.', 1341571604);
1521 }
1522 $this->jQueryVersions[$namespace] = array(
1523 'version' => $version,
1524 'source' => $source
1525 );
1526 }
1527
1528 /**
1529 * Call function if you need the requireJS library
1530 * this automatically adds the JavaScript path of all loaded extensions in the requireJS path option
1531 * so it resolves names like TYPO3/CMS/MyExtension/MyJsFile to EXT:MyExtension/Resources/Public/JavaScript/MyJsFile.js
1532 * when using requireJS
1533 *
1534 * @return void
1535 */
1536 public function loadRequireJs()
1537 {
1538
1539 // load all paths to map to package names / namespaces
1540 if (empty($this->requireJsConfig)) {
1541 // In order to avoid browser caching of JS files, adding a GET parameter to the files loaded via requireJS
1542 if (GeneralUtility::getApplicationContext()->isDevelopment()) {
1543 $this->requireJsConfig['urlArgs'] = 'bust=' . $GLOBALS['EXEC_TIME'];
1544 } else {
1545 $this->requireJsConfig['urlArgs'] = 'bust=' . GeneralUtility::hmac(TYPO3_version . PATH_site);
1546 }
1547 $corePath = ExtensionManagementUtility::extPath('core', 'Resources/Public/JavaScript/Contrib/');
1548 $corePath = PathUtility::getAbsoluteWebPath($corePath);
1549 // first, load all paths for the namespaces, and configure contrib libs.
1550 $this->requireJsConfig['paths'] = array(
1551 'jquery-ui' => $corePath . 'jquery-ui',
1552 'datatables' => $corePath . 'jquery.dataTables',
1553 'nprogress' => $corePath . 'nprogress',
1554 'moment' => $corePath . 'moment',
1555 'cropper' => $corePath . 'cropper.min',
1556 'imagesloaded' => $corePath . 'imagesloaded.pkgd.min',
1557 'bootstrap' => $corePath . 'bootstrap/bootstrap',
1558 'twbs/bootstrap-datetimepicker' => $corePath . 'bootstrap-datetimepicker',
1559 'autosize' => $corePath . 'autosize',
1560 'taboverride' => $corePath . 'taboverride.min',
1561 'twbs/bootstrap-slider' => $corePath . 'bootstrap-slider.min',
1562 'jquery/autocomplete' => $corePath . 'jquery.autocomplete',
1563 );
1564 // get all extensions that are loaded
1565 $loadedExtensions = ExtensionManagementUtility::getLoadedExtensionListArray();
1566 foreach ($loadedExtensions as $packageName) {
1567 $fullJsPath = 'EXT:' . $packageName . '/Resources/Public/JavaScript/';
1568 $fullJsPath = GeneralUtility::getFileAbsFileName($fullJsPath);
1569 $fullJsPath = PathUtility::getAbsoluteWebPath($fullJsPath);
1570 $fullJsPath = rtrim($fullJsPath, '/');
1571 if ($fullJsPath) {
1572 $this->requireJsConfig['paths']['TYPO3/CMS/' . GeneralUtility::underscoredToUpperCamelCase($packageName)] = $fullJsPath;
1573 }
1574 }
1575
1576 // check if additional AMD modules need to be loaded if a single AMD module is initialized
1577 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RequireJS']['postInitializationModules'])) {
1578 $this->addInlineSettingArray('RequireJS.PostInitializationModules', $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RequireJS']['postInitializationModules']);
1579 }
1580 }
1581
1582 $this->addRequireJs = true;
1583 }
1584
1585 /**
1586 * Add additional configuration to require js.
1587 *
1588 * Configuration will be merged recursive with overrule.
1589 *
1590 * To add another path mapping deliver the following configuration:
1591 * 'paths' => array(
1592 * 'EXTERN/mybootstrapjs' => 'sysext/.../twbs/bootstrap.min',
1593 * ),
1594 *
1595 * @param array $configuration The configuration that will be merged with existing one.
1596 * @return void
1597 */
1598 public function addRequireJsConfiguration(array $configuration)
1599 {
1600 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($this->requireJsConfig, $configuration);
1601 }
1602
1603 /**
1604 * includes an AMD-compatible JS file by resolving the ModuleName, and then requires the file via a requireJS request,
1605 * additionally allowing to execute JavaScript code afterwards
1606 *
1607 * this function only works for AMD-ready JS modules, used like "define('TYPO3/CMS/Backend/FormEngine..."
1608 * in the JS file
1609 *
1610 * TYPO3/CMS/Backend/FormEngine =>
1611 * "TYPO3": Vendor Name
1612 * "CMS": Product Name
1613 * "Backend": Extension Name
1614 * "FormEngine": FileName in the Resources/Public/JavaScript folder
1615 *
1616 * @param string $mainModuleName Must be in the form of "TYPO3/CMS/PackageName/ModuleName" e.g. "TYPO3/CMS/Backend/FormEngine"
1617 * @param string $callBackFunction loaded right after the requireJS loading, must be wrapped in function() {}
1618 * @return void
1619 */
1620 public function loadRequireJsModule($mainModuleName, $callBackFunction = null)
1621 {
1622 $inlineCodeKey = $mainModuleName;
1623 // make sure requireJS is initialized
1624 $this->loadRequireJs();
1625
1626 // execute the main module, and load a possible callback function
1627 $javaScriptCode = 'require(["' . $mainModuleName . '"]';
1628 if ($callBackFunction !== null) {
1629 $inlineCodeKey .= sha1($callBackFunction);
1630 $javaScriptCode .= ', ' . $callBackFunction;
1631 }
1632 $javaScriptCode .= ');';
1633 $this->addJsInlineCode('RequireJS-Module-' . $inlineCodeKey, $javaScriptCode);
1634 }
1635
1636 /**
1637 * call this function if you need the extJS library
1638 *
1639 * @param bool $css Flag, if set the ext-css will be loaded
1640 * @param bool $theme Flag, if set the ext-theme "grey" will be loaded
1641 * @return void
1642 */
1643 public function loadExtJS($css = true, $theme = true)
1644 {
1645 $this->addExtJS = true;
1646 $this->extJStheme = $theme;
1647 $this->extJScss = $css;
1648 }
1649
1650 /**
1651 * Call this function to load debug version of ExtJS. Use this for development only
1652 *
1653 * @return void
1654 */
1655 public function enableExtJsDebug()
1656 {
1657 $this->enableExtJsDebug = true;
1658 }
1659
1660 /**
1661 * Adds Javascript Inline Label. This will occur in TYPO3.lang - object
1662 * The label can be used in scripts with TYPO3.lang.<key>
1663 * Need extJs loaded
1664 *
1665 * @param string $key
1666 * @param string $value
1667 * @return void
1668 */
1669 public function addInlineLanguageLabel($key, $value)
1670 {
1671 $this->inlineLanguageLabels[$key] = $value;
1672 }
1673
1674 /**
1675 * Adds Javascript Inline Label Array. This will occur in TYPO3.lang - object
1676 * The label can be used in scripts with TYPO3.lang.<key>
1677 * Array will be merged with existing array.
1678 * Need extJs loaded
1679 *
1680 * @param array $array
1681 * @param bool $parseWithLanguageService
1682 * @return void
1683 */
1684 public function addInlineLanguageLabelArray(array $array, $parseWithLanguageService = false)
1685 {
1686 if ($parseWithLanguageService === true) {
1687 foreach ($array as $key => $value) {
1688 if (TYPO3_MODE === 'FE') {
1689 $array[$key] = $this->getTypoScriptFrontendController()->sL($value);
1690 } else {
1691 $array[$key] = $this->getLanguageService()->sL($value);
1692 }
1693 }
1694 }
1695
1696 $this->inlineLanguageLabels = array_merge($this->inlineLanguageLabels, $array);
1697 }
1698
1699 /**
1700 * Gets labels to be used in JavaScript fetched from a locallang file.
1701 *
1702 * @param string $fileRef Input is a file-reference (see GeneralUtility::getFileAbsFileName). That file is expected to be a 'locallang.xlf' file containing a valid XML TYPO3 language structure.
1703 * @param string $selectionPrefix Prefix to select the correct labels (default: '')
1704 * @param string $stripFromSelectionName String to be removed from the label names in the output. (default: '')
1705 * @param int $errorMode Error mode (when file could not be found): 0 - syslog entry, 1 - do nothing, 2 - throw an exception
1706 * @return void
1707 */
1708 public function addInlineLanguageLabelFile($fileRef, $selectionPrefix = '', $stripFromSelectionName = '', $errorMode = 0)
1709 {
1710 $index = md5($fileRef . $selectionPrefix . $stripFromSelectionName);
1711 if ($fileRef && !isset($this->inlineLanguageLabelFiles[$index])) {
1712 $this->inlineLanguageLabelFiles[$index] = array(
1713 'fileRef' => $fileRef,
1714 'selectionPrefix' => $selectionPrefix,
1715 'stripFromSelectionName' => $stripFromSelectionName,
1716 'errorMode' => $errorMode
1717 );
1718 }
1719 }
1720
1721 /**
1722 * Adds Javascript Inline Setting. This will occur in TYPO3.settings - object
1723 * The label can be used in scripts with TYPO3.setting.<key>
1724 * Need extJs loaded
1725 *
1726 * @param string $namespace
1727 * @param string $key
1728 * @param string $value
1729 * @return void
1730 */
1731 public function addInlineSetting($namespace, $key, $value)
1732 {
1733 if ($namespace) {
1734 if (strpos($namespace, '.')) {
1735 $parts = explode('.', $namespace);
1736 $a = &$this->inlineSettings;
1737 foreach ($parts as $part) {
1738 $a = &$a[$part];
1739 }
1740 $a[$key] = $value;
1741 } else {
1742 $this->inlineSettings[$namespace][$key] = $value;
1743 }
1744 } else {
1745 $this->inlineSettings[$key] = $value;
1746 }
1747 }
1748
1749 /**
1750 * Adds Javascript Inline Setting. This will occur in TYPO3.settings - object
1751 * The label can be used in scripts with TYPO3.setting.<key>
1752 * Array will be merged with existing array.
1753 * Need extJs loaded
1754 *
1755 * @param string $namespace
1756 * @param array $array
1757 * @return void
1758 */
1759 public function addInlineSettingArray($namespace, array $array)
1760 {
1761 if ($namespace) {
1762 if (strpos($namespace, '.')) {
1763 $parts = explode('.', $namespace);
1764 $a = &$this->inlineSettings;
1765 foreach ($parts as $part) {
1766 $a = &$a[$part];
1767 }
1768 $a = array_merge((array)$a, $array);
1769 } else {
1770 $this->inlineSettings[$namespace] = array_merge((array)$this->inlineSettings[$namespace], $array);
1771 }
1772 } else {
1773 $this->inlineSettings = array_merge($this->inlineSettings, $array);
1774 }
1775 }
1776
1777 /**
1778 * Adds content to body content
1779 *
1780 * @param string $content
1781 * @return void
1782 */
1783 public function addBodyContent($content)
1784 {
1785 $this->bodyContent .= $content;
1786 }
1787
1788 /*****************************************************/
1789 /* */
1790 /* Render Functions */
1791 /* */
1792 /*****************************************************/
1793 /**
1794 * Render the section (Header or Footer)
1795 *
1796 * @param int $part Section which should be rendered: self::PART_COMPLETE, self::PART_HEADER or self::PART_FOOTER
1797 * @return string Content of rendered section
1798 */
1799 public function render($part = self::PART_COMPLETE)
1800 {
1801 $this->prepareRendering();
1802 list($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs) = $this->renderJavaScriptAndCss();
1803 $metaTags = implode(LF, $this->metaTags);
1804 $markerArray = $this->getPreparedMarkerArray($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs, $metaTags);
1805 $template = $this->getTemplateForPart($part);
1806
1807 // The page renderer needs a full reset, even when only rendering one part of the page
1808 // This means that you can only register footer files *after* the header has been already rendered.
1809 // In case you render the footer part first, header files can only be added *after* the footer has been rendered
1810 $this->reset();
1811 $templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1812 return trim($templateService->substituteMarkerArray($template, $markerArray, '###|###'));
1813 }
1814
1815 /**
1816 * Render the page but not the JavaScript and CSS Files
1817 *
1818 * @param string $substituteHash The hash that is used for the placehoder markers
1819 * @access private
1820 * @return string Content of rendered section
1821 */
1822 public function renderPageWithUncachedObjects($substituteHash)
1823 {
1824 $this->prepareRendering();
1825 $markerArray = $this->getPreparedMarkerArrayForPageWithUncachedObjects($substituteHash);
1826 $template = $this->getTemplateForPart(self::PART_COMPLETE);
1827 $templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1828 return trim($templateService->substituteMarkerArray($template, $markerArray, '###|###'));
1829 }
1830
1831 /**
1832 * Renders the JavaScript and CSS files that have been added during processing
1833 * of uncached content objects (USER_INT, COA_INT)
1834 *
1835 * @param string $cachedPageContent
1836 * @param string $substituteHash The hash that is used for the placehoder markers
1837 * @access private
1838 * @return string
1839 */
1840 public function renderJavaScriptAndCssForProcessingOfUncachedContentObjects($cachedPageContent, $substituteHash)
1841 {
1842 $this->prepareRendering();
1843 list($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs) = $this->renderJavaScriptAndCss();
1844 $title = $this->title ? str_replace('|', htmlspecialchars($this->title), $this->titleTag) : '';
1845 $markerArray = array(
1846 '<!-- ###TITLE' . $substituteHash . '### -->' => $title,
1847 '<!-- ###CSS_LIBS' . $substituteHash . '### -->' => $cssLibs,
1848 '<!-- ###CSS_INCLUDE' . $substituteHash . '### -->' => $cssFiles,
1849 '<!-- ###CSS_INLINE' . $substituteHash . '### -->' => $cssInline,
1850 '<!-- ###JS_INLINE' . $substituteHash . '### -->' => $jsInline,
1851 '<!-- ###JS_INCLUDE' . $substituteHash . '### -->' => $jsFiles,
1852 '<!-- ###JS_LIBS' . $substituteHash . '### -->' => $jsLibs,
1853 '<!-- ###HEADERDATA' . $substituteHash . '### -->' => implode(LF, $this->headerData),
1854 '<!-- ###FOOTERDATA' . $substituteHash . '### -->' => implode(LF, $this->footerData),
1855 '<!-- ###JS_LIBS_FOOTER' . $substituteHash . '### -->' => $jsFooterLibs,
1856 '<!-- ###JS_INCLUDE_FOOTER' . $substituteHash . '### -->' => $jsFooterFiles,
1857 '<!-- ###JS_INLINE_FOOTER' . $substituteHash . '### -->' => $jsFooterInline
1858 );
1859 foreach ($markerArray as $placeHolder => $content) {
1860 $cachedPageContent = str_replace($placeHolder, $content, $cachedPageContent);
1861 }
1862 $this->reset();
1863 return $cachedPageContent;
1864 }
1865
1866 /**
1867 * Remove ending slashes from static header block
1868 * if the page is beeing rendered as html (not xhtml)
1869 * and define property $this->endingSlash for further use
1870 *
1871 * @return void
1872 */
1873 protected function prepareRendering()
1874 {
1875 if ($this->getRenderXhtml()) {
1876 $this->endingSlash = ' /';
1877 } else {
1878 $this->metaCharsetTag = str_replace(' />', '>', $this->metaCharsetTag);
1879 $this->baseUrlTag = str_replace(' />', '>', $this->baseUrlTag);
1880 $this->shortcutTag = str_replace(' />', '>', $this->shortcutTag);
1881 $this->endingSlash = '';
1882 }
1883 }
1884
1885 /**
1886 * Renders all JavaScript and CSS
1887 *
1888 * @return array<string>
1889 */
1890 protected function renderJavaScriptAndCss()
1891 {
1892 $this->executePreRenderHook();
1893 $mainJsLibs = $this->renderMainJavaScriptLibraries();
1894 if ($this->concatenateFiles || $this->concatenateJavascript || $this->concatenateCss) {
1895 // Do the file concatenation
1896 $this->doConcatenate();
1897 }
1898 if ($this->compressCss || $this->compressJavascript) {
1899 // Do the file compression
1900 $this->doCompress();
1901 }
1902 $this->executeRenderPostTransformHook();
1903 $cssLibs = $this->renderCssLibraries();
1904 $cssFiles = $this->renderCssFiles();
1905 $cssInline = $this->renderCssInline();
1906 list($jsLibs, $jsFooterLibs) = $this->renderAdditionalJavaScriptLibraries();
1907 list($jsFiles, $jsFooterFiles) = $this->renderJavaScriptFiles();
1908 list($jsInline, $jsFooterInline) = $this->renderInlineJavaScript();
1909 $jsLibs = $mainJsLibs . $jsLibs;
1910 if ($this->moveJsFromHeaderToFooter) {
1911 $jsFooterLibs = $jsLibs . LF . $jsFooterLibs;
1912 $jsLibs = '';
1913 $jsFooterFiles = $jsFiles . LF . $jsFooterFiles;
1914 $jsFiles = '';
1915 $jsFooterInline = $jsInline . LF . $jsFooterInline;
1916 $jsInline = '';
1917 }
1918 $this->executePostRenderHook($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs);
1919 return array($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs);
1920 }
1921
1922 /**
1923 * Fills the marker array with the given strings and trims each value
1924 *
1925 * @param $jsLibs string
1926 * @param $jsFiles string
1927 * @param $jsFooterFiles string
1928 * @param $cssLibs string
1929 * @param $cssFiles string
1930 * @param $jsInline string
1931 * @param $cssInline string
1932 * @param $jsFooterInline string
1933 * @param $jsFooterLibs string
1934 * @param $metaTags string
1935 * @return array Marker array
1936 */
1937 protected function getPreparedMarkerArray($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs, $metaTags)
1938 {
1939 $markerArray = array(
1940 'XMLPROLOG_DOCTYPE' => $this->xmlPrologAndDocType,
1941 'HTMLTAG' => $this->htmlTag,
1942 'HEADTAG' => $this->headTag,
1943 'METACHARSET' => $this->charSet ? str_replace('|', htmlspecialchars($this->charSet), $this->metaCharsetTag) : '',
1944 'INLINECOMMENT' => $this->inlineComments ? LF . LF . '<!-- ' . LF . implode(LF, $this->inlineComments) . '-->' . LF . LF : '',
1945 'BASEURL' => $this->baseUrl ? str_replace('|', $this->baseUrl, $this->baseUrlTag) : '',
1946 'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) : '',
1947 'CSS_LIBS' => $cssLibs,
1948 'CSS_INCLUDE' => $cssFiles,
1949 'CSS_INLINE' => $cssInline,
1950 'JS_INLINE' => $jsInline,
1951 'JS_INCLUDE' => $jsFiles,
1952 'JS_LIBS' => $jsLibs,
1953 'TITLE' => $this->title ? str_replace('|', htmlspecialchars($this->title), $this->titleTag) : '',
1954 'META' => $metaTags,
1955 'HEADERDATA' => $this->headerData ? implode(LF, $this->headerData) : '',
1956 'FOOTERDATA' => $this->footerData ? implode(LF, $this->footerData) : '',
1957 'JS_LIBS_FOOTER' => $jsFooterLibs,
1958 'JS_INCLUDE_FOOTER' => $jsFooterFiles,
1959 'JS_INLINE_FOOTER' => $jsFooterInline,
1960 'BODY' => $this->bodyContent
1961 );
1962 $markerArray = array_map('trim', $markerArray);
1963 return $markerArray;
1964 }
1965
1966 /**
1967 * Fills the marker array with the given strings and trims each value
1968 *
1969 * @param string $substituteHash The hash that is used for the placehoder markers
1970 * @return array Marker array
1971 */
1972 protected function getPreparedMarkerArrayForPageWithUncachedObjects($substituteHash)
1973 {
1974 $markerArray = array(
1975 'XMLPROLOG_DOCTYPE' => $this->xmlPrologAndDocType,
1976 'HTMLTAG' => $this->htmlTag,
1977 'HEADTAG' => $this->headTag,
1978 'METACHARSET' => $this->charSet ? str_replace('|', htmlspecialchars($this->charSet), $this->metaCharsetTag) : '',
1979 'INLINECOMMENT' => $this->inlineComments ? LF . LF . '<!-- ' . LF . implode(LF, $this->inlineComments) . '-->' . LF . LF : '',
1980 'BASEURL' => $this->baseUrl ? str_replace('|', $this->baseUrl, $this->baseUrlTag) : '',
1981 'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) : '',
1982 'META' => implode(LF, $this->metaTags),
1983 'BODY' => $this->bodyContent,
1984 'TITLE' => '<!-- ###TITLE' . $substituteHash . '### -->',
1985 'CSS_LIBS' => '<!-- ###CSS_LIBS' . $substituteHash . '### -->',
1986 'CSS_INCLUDE' => '<!-- ###CSS_INCLUDE' . $substituteHash . '### -->',
1987 'CSS_INLINE' => '<!-- ###CSS_INLINE' . $substituteHash . '### -->',
1988 'JS_INLINE' => '<!-- ###JS_INLINE' . $substituteHash . '### -->',
1989 'JS_INCLUDE' => '<!-- ###JS_INCLUDE' . $substituteHash . '### -->',
1990 'JS_LIBS' => '<!-- ###JS_LIBS' . $substituteHash . '### -->',
1991 'HEADERDATA' => '<!-- ###HEADERDATA' . $substituteHash . '### -->',
1992 'FOOTERDATA' => '<!-- ###FOOTERDATA' . $substituteHash . '### -->',
1993 'JS_LIBS_FOOTER' => '<!-- ###JS_LIBS_FOOTER' . $substituteHash . '### -->',
1994 'JS_INCLUDE_FOOTER' => '<!-- ###JS_INCLUDE_FOOTER' . $substituteHash . '### -->',
1995 'JS_INLINE_FOOTER' => '<!-- ###JS_INLINE_FOOTER' . $substituteHash . '### -->'
1996 );
1997 $markerArray = array_map('trim', $markerArray);
1998 return $markerArray;
1999 }
2000
2001 /**
2002 * Reads the template file and returns the requested part as string
2003 *
2004 * @param int $part
2005 * @return string
2006 */
2007 protected function getTemplateForPart($part)
2008 {
2009 $templateFile = GeneralUtility::getFileAbsFileName($this->templateFile);
2010 $template = GeneralUtility::getUrl($templateFile);
2011 if ($this->removeLineBreaksFromTemplate) {
2012 $template = strtr($template, array(LF => '', CR => ''));
2013 }
2014 if ($part !== self::PART_COMPLETE) {
2015 $templatePart = explode('###BODY###', $template);
2016 $template = $templatePart[$part - 1];
2017 }
2018 return $template;
2019 }
2020
2021 /**
2022 * Helper function for render the main JavaScript libraries,
2023 * currently: RequireJS, jQuery, ExtJS
2024 *
2025 * @return string Content with JavaScript libraries
2026 */
2027 protected function renderMainJavaScriptLibraries()
2028 {
2029 $out = '';
2030
2031 // Include RequireJS
2032 if ($this->addRequireJs) {
2033 // load the paths of the requireJS configuration
2034 $out .= GeneralUtility::wrapJS('var require = ' . json_encode($this->requireJsConfig)) . LF;
2035 // directly after that, include the require.js file
2036 $out .= '<script src="' . $this->processJsFile($this->requireJsPath . 'require.js') . '" type="text/javascript"></script>' . LF;
2037 }
2038
2039 // Include jQuery Core for each namespace, depending on the version and source
2040 if (!empty($this->jQueryVersions)) {
2041 foreach ($this->jQueryVersions as $namespace => $jQueryVersion) {
2042 $out .= $this->renderJqueryScriptTag($jQueryVersion['version'], $jQueryVersion['source'], $namespace);
2043 }
2044 }
2045 // Include extJS
2046 if ($this->addExtJS) {
2047 // Use the base adapter all the time
2048 $out .= '<script src="' . $this->processJsFile($this->extJsPath . 'adapter/ext-base' . ($this->enableExtJsDebug ? '-debug' : '') . '.js') . '" type="text/javascript"></script>' . LF;
2049 $out .= '<script src="' . $this->processJsFile($this->extJsPath . 'ext-all' . ($this->enableExtJsDebug ? '-debug' : '') . '.js') . '" type="text/javascript"></script>' . LF;
2050 // Add extJS localization
2051 // Load standard ISO mapping and modify for use with ExtJS
2052 $localeMap = $this->locales->getIsoMapping();
2053 $localeMap[''] = 'en';
2054 $localeMap['default'] = 'en';
2055 // Greek
2056 $localeMap['gr'] = 'el_GR';
2057 // Norwegian Bokmaal
2058 $localeMap['no'] = 'no_BO';
2059 // Swedish
2060 $localeMap['se'] = 'se_SV';
2061 $extJsLang = isset($localeMap[$this->lang]) ? $localeMap[$this->lang] : $this->lang;
2062 $extJsLocaleFile = $this->extJsPath . 'locale/ext-lang-' . $extJsLang . '.js';
2063 if (file_exists(PATH_site . $extJsLocaleFile)) {
2064 $out .= '<script src="' . $this->processJsFile($extJsLocaleFile) . '" type="text/javascript" charset="utf-8"></script>' . LF;
2065 }
2066 // Remove extjs from JScodeLibArray
2067 unset($this->jsFiles[$this->extJsPath . 'ext-all.js'], $this->jsFiles[$this->extJsPath . 'ext-all-debug.js']);
2068 }
2069 $this->loadJavaScriptLanguageStrings();
2070 if (TYPO3_MODE === 'BE') {
2071 $this->addAjaxUrlsToInlineSettings();
2072 }
2073 $inlineSettings = $this->inlineLanguageLabels ? 'TYPO3.lang = ' . json_encode($this->inlineLanguageLabels) . ';' : '';
2074 $inlineSettings .= $this->inlineSettings ? 'TYPO3.settings = ' . json_encode($this->inlineSettings) . ';' : '';
2075 if ($this->addExtJS) {
2076 // Set clear.gif, move it on top, add handler code
2077 $code = '';
2078 if (!empty($this->extOnReadyCode)) {
2079 foreach ($this->extOnReadyCode as $block) {
2080 $code .= $block;
2081 }
2082 }
2083 $clearGifPath = htmlspecialchars(GeneralUtility::locationHeaderUrl(ExtensionManagementUtility::extRelPath('backend') . 'Resources/Public/Images/clear.gif'));
2084 $out .= $this->inlineJavascriptWrap[0] . '
2085 Ext.ns("TYPO3");
2086 Ext.BLANK_IMAGE_URL = "' . $clearGifPath . '";
2087 Ext.SSL_SECURE_URL = "' . $clearGifPath . '";' . LF
2088 . $inlineSettings
2089 . 'Ext.onReady(function() {'
2090 . $code
2091 . ' });'
2092 . $this->inlineJavascriptWrap[1];
2093 $this->extOnReadyCode = array();
2094 // Include TYPO3.l10n object
2095 if (TYPO3_MODE === 'BE') {
2096 $out .= '<script src="' . $this->processJsFile(ExtensionManagementUtility::extRelPath('lang') . 'Resources/Public/JavaScript/Typo3Lang.js') . '" type="text/javascript" charset="utf-8"></script>' . LF;
2097 }
2098 if ($this->extJScss) {
2099 if (isset($GLOBALS['TBE_STYLES']['extJS']['all'])) {
2100 $this->addCssLibrary($GLOBALS['TBE_STYLES']['extJS']['all'], 'stylesheet', 'all', '', true);
2101 } else {
2102 $this->addCssLibrary($this->extJsPath . 'resources/css/ext-all-notheme.css', 'stylesheet', 'all', '', true);
2103 }
2104 }
2105 if ($this->extJStheme) {
2106 if (isset($GLOBALS['TBE_STYLES']['extJS']['theme'])) {
2107 $this->addCssLibrary($GLOBALS['TBE_STYLES']['extJS']['theme'], 'stylesheet', 'all', '', true);
2108 } else {
2109 $this->addCssLibrary($this->extJsPath . 'resources/css/xtheme-blue.css', 'stylesheet', 'all', '', true);
2110 }
2111 }
2112 } else {
2113 // no extJS loaded, but still inline settings
2114 if ($inlineSettings !== '') {
2115 // make sure the global TYPO3 is available
2116 $inlineSettings = 'var TYPO3 = TYPO3 || {};' . CRLF . $inlineSettings;
2117 $out .= $this->inlineJavascriptWrap[0] . $inlineSettings . $this->inlineJavascriptWrap[1];
2118 // Add language module only if also jquery is guaranteed to be there
2119 if (TYPO3_MODE === 'BE' && !empty($this->jQueryVersions)) {
2120 $this->loadRequireJsModule('TYPO3/CMS/Lang/Lang');
2121 }
2122 }
2123 }
2124 return $out;
2125 }
2126
2127 /**
2128 * Load the language strings into JavaScript
2129 */
2130 protected function loadJavaScriptLanguageStrings()
2131 {
2132 if (!empty($this->inlineLanguageLabelFiles)) {
2133 foreach ($this->inlineLanguageLabelFiles as $languageLabelFile) {
2134 $this->includeLanguageFileForInline($languageLabelFile['fileRef'], $languageLabelFile['selectionPrefix'], $languageLabelFile['stripFromSelectionName'], $languageLabelFile['errorMode']);
2135 }
2136 }
2137 $this->inlineLanguageLabelFiles = array();
2138 // Convert labels/settings back to UTF-8 since json_encode() only works with UTF-8:
2139 if (TYPO3_MODE === 'FE' && $this->getCharSet() !== 'utf-8') {
2140 if ($this->inlineLanguageLabels) {
2141 $this->csConvObj->convArray($this->inlineLanguageLabels, $this->getCharSet(), 'utf-8');
2142 }
2143 if ($this->inlineSettings) {
2144 $this->csConvObj->convArray($this->inlineSettings, $this->getCharSet(), 'utf-8');
2145 }
2146 }
2147 }
2148
2149 /**
2150 * Make URLs to all backend ajax handlers available as inline setting.
2151 */
2152 protected function addAjaxUrlsToInlineSettings()
2153 {
2154 $ajaxUrls = array();
2155 foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'] as $ajaxHandler => $_) {
2156 $ajaxUrls[$ajaxHandler] = BackendUtility::getAjaxUrl($ajaxHandler);
2157 }
2158
2159 // also add the ajax-based routes
2160 /** @var UriBuilder $uriBuilder */
2161 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
2162 /** @var Router $router */
2163 $router = GeneralUtility::makeInstance(Router::class);
2164 $routes = $router->getRoutes();
2165 foreach ($routes as $routeIdentifier => $route) {
2166 if ($route->getOption('ajax')) {
2167 $uri = (string)$uriBuilder->buildUriFromRoute($routeIdentifier);
2168 // use the shortened value in order to use this in JavaScript
2169 $routeIdentifier = str_replace('ajax_', '', $routeIdentifier);
2170 $ajaxUrls[$routeIdentifier] = $uri;
2171 }
2172 }
2173
2174 $this->inlineSettings['ajaxUrls'] = $ajaxUrls;
2175 }
2176
2177 /**
2178 * Renders the HTML script tag for the given jQuery version.
2179 *
2180 * @param string $version The jQuery version that should be included, either "latest" or any available version
2181 * @param string $source The location of the jQuery source, can be "local", "google", "msn" or "jquery
2182 * @param string $namespace The namespace in which the jQuery object of the specific version should be stored
2183 * @return string
2184 */
2185 protected function renderJqueryScriptTag($version, $source, $namespace)
2186 {
2187 switch (true) {
2188 case isset($this->jQueryCdnUrls[$source]):
2189 if ($this->enableJqueryDebug) {
2190 $minifyPart = '';
2191 } else {
2192 $minifyPart = '.min';
2193 }
2194 $jQueryFileName = sprintf($this->jQueryCdnUrls[$source], $version, $minifyPart);
2195 break;
2196 case $source === 'local':
2197 $jQueryFileName = $this->jQueryPath . 'jquery-' . rawurlencode($version);
2198 if ($this->enableJqueryDebug) {
2199 $jQueryFileName .= '.js';
2200 } else {
2201 $jQueryFileName .= '.min.js';
2202 }
2203 break;
2204 default:
2205 $jQueryFileName = $source;
2206 }
2207 // Include the jQuery Core
2208 $scriptTag = '<script src="' . htmlspecialchars($jQueryFileName) . '" type="text/javascript"></script>' . LF;
2209 // Set the noConflict mode to be available via "TYPO3.jQuery" in all installations
2210 switch ($namespace) {
2211 case self::JQUERY_NAMESPACE_DEFAULT_NOCONFLICT:
2212 $scriptTag .= GeneralUtility::wrapJS('jQuery.noConflict();') . LF;
2213 break;
2214 case self::JQUERY_NAMESPACE_NONE:
2215 break;
2216 case self::JQUERY_NAMESPACE_DEFAULT:
2217
2218 default:
2219 $scriptTag .= GeneralUtility::wrapJS('var TYPO3 = TYPO3 || {}; TYPO3.' . $namespace . ' = jQuery.noConflict(true);') . LF;
2220 }
2221 return $scriptTag;
2222 }
2223
2224 /**
2225 * Render CSS library files
2226 *
2227 * @return string
2228 */
2229 protected function renderCssLibraries()
2230 {
2231 $cssFiles = '';
2232 if (!empty($this->cssLibs)) {
2233 foreach ($this->cssLibs as $file => $properties) {
2234 $file = GeneralUtility::resolveBackPath($file);
2235 $file = GeneralUtility::createVersionNumberedFilename($file);
2236 $tag = '<link rel="' . htmlspecialchars($properties['rel'])
2237 . '" type="text/css" href="' . htmlspecialchars($file)
2238 . '" media="' . htmlspecialchars($properties['media']) . '"'
2239 . ($properties['title'] ? ' title="' . htmlspecialchars($properties['title']) . '"' : '')
2240 . $this->endingSlash . '>';
2241 if ($properties['allWrap']) {
2242 $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2243 $tag = $wrapArr[0] . $tag . $wrapArr[1];
2244 }
2245 $tag .= LF;
2246 if ($properties['forceOnTop']) {
2247 $cssFiles = $tag . $cssFiles;
2248 } else {
2249 $cssFiles .= $tag;
2250 }
2251 }
2252 }
2253 return $cssFiles;
2254 }
2255
2256 /**
2257 * Render CSS files
2258 *
2259 * @return string
2260 */
2261 protected function renderCssFiles()
2262 {
2263 $cssFiles = '';
2264 if (!empty($this->cssFiles)) {
2265 foreach ($this->cssFiles as $file => $properties) {
2266 $file = GeneralUtility::resolveBackPath($file);
2267 $file = GeneralUtility::createVersionNumberedFilename($file);
2268 $tag = '<link rel="' . htmlspecialchars($properties['rel'])
2269 . '" type="text/css" href="' . htmlspecialchars($file)
2270 . '" media="' . htmlspecialchars($properties['media']) . '"'
2271 . ($properties['title'] ? ' title="' . htmlspecialchars($properties['title']) . '"' : '')
2272 . $this->endingSlash . '>';
2273 if ($properties['allWrap']) {
2274 $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2275 $tag = $wrapArr[0] . $tag . $wrapArr[1];
2276 }
2277 $tag .= LF;
2278 if ($properties['forceOnTop']) {
2279 $cssFiles = $tag . $cssFiles;
2280 } else {
2281 $cssFiles .= $tag;
2282 }
2283 }
2284 }
2285 return $cssFiles;
2286 }
2287
2288 /**
2289 * Render inline CSS
2290 *
2291 * @return string
2292 */
2293 protected function renderCssInline()
2294 {
2295 $cssInline = '';
2296 if (!empty($this->cssInline)) {
2297 foreach ($this->cssInline as $name => $properties) {
2298 $cssCode = '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF;
2299 if ($properties['forceOnTop']) {
2300 $cssInline = $cssCode . $cssInline;
2301 } else {
2302 $cssInline .= $cssCode;
2303 }
2304 }
2305 $cssInline = $this->inlineCssWrap[0] . $cssInline . $this->inlineCssWrap[1];
2306 }
2307 return $cssInline;
2308 }
2309
2310 /**
2311 * Render JavaScipt libraries
2312 *
2313 * @return array<string> jsLibs and jsFooterLibs strings
2314 */
2315 protected function renderAdditionalJavaScriptLibraries()
2316 {
2317 $jsLibs = '';
2318 $jsFooterLibs = '';
2319 if (!empty($this->jsLibs)) {
2320 foreach ($this->jsLibs as $properties) {
2321 $properties['file'] = GeneralUtility::resolveBackPath($properties['file']);
2322 $properties['file'] = GeneralUtility::createVersionNumberedFilename($properties['file']);
2323 $async = ($properties['async']) ? ' async="async"' : '';
2324 $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '"' : '';
2325 $tag = '<script src="' . htmlspecialchars($properties['file']) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $integrity . '></script>';
2326 if ($properties['allWrap']) {
2327 $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2328 $tag = $wrapArr[0] . $tag . $wrapArr[1];
2329 }
2330 $tag .= LF;
2331 if ($properties['forceOnTop']) {
2332 if ($properties['section'] === self::PART_HEADER) {
2333 $jsLibs = $tag . $jsLibs;
2334 } else {
2335 $jsFooterLibs = $tag . $jsFooterLibs;
2336 }
2337 } else {
2338 if ($properties['section'] === self::PART_HEADER) {
2339 $jsLibs .= $tag;
2340 } else {
2341 $jsFooterLibs .= $tag;
2342 }
2343 }
2344 }
2345 }
2346 if ($this->moveJsFromHeaderToFooter) {
2347 $jsFooterLibs = $jsLibs . LF . $jsFooterLibs;
2348 $jsLibs = '';
2349 }
2350 return array($jsLibs, $jsFooterLibs);
2351 }
2352
2353 /**
2354 * Render JavaScript files
2355 *
2356 * @return array<string> jsFiles and jsFooterFiles strings
2357 */
2358 protected function renderJavaScriptFiles()
2359 {
2360 $jsFiles = '';
2361 $jsFooterFiles = '';
2362 if (!empty($this->jsFiles)) {
2363 foreach ($this->jsFiles as $file => $properties) {
2364 $file = GeneralUtility::resolveBackPath($file);
2365 $file = GeneralUtility::createVersionNumberedFilename($file);
2366 $async = ($properties['async']) ? ' async="async"' : '';
2367 $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '"' : '';
2368 $tag = '<script src="' . htmlspecialchars($file) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $integrity . '></script>';
2369 if ($properties['allWrap']) {
2370 $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2371 $tag = $wrapArr[0] . $tag . $wrapArr[1];
2372 }
2373 $tag .= LF;
2374 if ($properties['forceOnTop']) {
2375 if ($properties['section'] === self::PART_HEADER) {
2376 $jsFiles = $tag . $jsFiles;
2377 } else {
2378 $jsFooterFiles = $tag . $jsFooterFiles;
2379 }
2380 } else {
2381 if ($properties['section'] === self::PART_HEADER) {
2382 $jsFiles .= $tag;
2383 } else {
2384 $jsFooterFiles .= $tag;
2385 }
2386 }
2387 }
2388 }
2389 if ($this->moveJsFromHeaderToFooter) {
2390 $jsFooterFiles = $jsFiles . $jsFooterFiles;
2391 $jsFiles = '';
2392 }
2393 return array($jsFiles, $jsFooterFiles);
2394 }
2395
2396 /**
2397 * Render inline JavaScript
2398 *
2399 * @return array<string> jsInline and jsFooterInline string
2400 */
2401 protected function renderInlineJavaScript()
2402 {
2403 $jsInline = '';
2404 $jsFooterInline = '';
2405 if (!empty($this->jsInline)) {
2406 foreach ($this->jsInline as $name => $properties) {
2407 $jsCode = '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF;
2408 if ($properties['forceOnTop']) {
2409 if ($properties['section'] === self::PART_HEADER) {
2410 $jsInline = $jsCode . $jsInline;
2411 } else {
2412 $jsFooterInline = $jsCode . $jsFooterInline;
2413 }
2414 } else {
2415 if ($properties['section'] === self::PART_HEADER) {
2416 $jsInline .= $jsCode;
2417 } else {
2418 $jsFooterInline .= $jsCode;
2419 }
2420 }
2421 }
2422 }
2423 if ($jsInline) {
2424 $jsInline = $this->inlineJavascriptWrap[0] . $jsInline . $this->inlineJavascriptWrap[1];
2425 }
2426 if ($jsFooterInline) {
2427 $jsFooterInline = $this->inlineJavascriptWrap[0] . $jsFooterInline . $this->inlineJavascriptWrap[1];
2428 }
2429 if ($this->moveJsFromHeaderToFooter) {
2430 $jsFooterInline = $jsInline . $jsFooterInline;
2431 $jsInline = '';
2432 }
2433 return array($jsInline, $jsFooterInline);
2434 }
2435
2436 /**
2437 * Include language file for inline usage
2438 *
2439 * @param string $fileRef
2440 * @param string $selectionPrefix
2441 * @param string $stripFromSelectionName
2442 * @param int $errorMode
2443 * @return void
2444 * @throws \RuntimeException
2445 */
2446 protected function includeLanguageFileForInline($fileRef, $selectionPrefix = '', $stripFromSelectionName = '', $errorMode = 0)
2447 {
2448 if (!isset($this->lang) || !isset($this->charSet)) {
2449 throw new \RuntimeException('Language and character encoding are not set.', 1284906026);
2450 }
2451 $labelsFromFile = array();
2452 $allLabels = $this->readLLfile($fileRef, $errorMode);
2453 if ($allLabels !== false) {
2454 // Merge language specific translations:
2455 if ($this->lang !== 'default' && isset($allLabels[$this->lang])) {
2456 $labels = array_merge($allLabels['default'], $allLabels[$this->lang]);
2457 } else {
2458 $labels = $allLabels['default'];
2459 }
2460 // Iterate through all locallang labels:
2461 foreach ($labels as $label => $value) {
2462 // If $selectionPrefix is set, only respect labels that start with $selectionPrefix
2463 if ($selectionPrefix === '' || strpos($label, $selectionPrefix) === 0) {
2464 // Remove substring $stripFromSelectionName from label
2465 $label = str_replace($stripFromSelectionName, '', $label);
2466 $labelsFromFile[$label] = $value;
2467 }
2468 }
2469 $this->inlineLanguageLabels = array_merge($this->inlineLanguageLabels, $labelsFromFile);
2470 }
2471 }
2472
2473 /**
2474 * Reads a locallang file.
2475 *
2476 * @param string $fileRef Reference to a relative filename to include.
2477 * @param int $errorMode Error mode (when file could not be found): 0 - syslog entry, 1 - do nothing, 2 - throw an exception
2478 * @return array Returns the $LOCAL_LANG array found in the file. If no array found, returns empty array.
2479 */
2480 protected function readLLfile($fileRef, $errorMode = 0)
2481 {
2482 /** @var $languageFactory LocalizationFactory */
2483 $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
2484
2485 if ($this->lang !== 'default') {
2486 $languages = array_reverse($this->languageDependencies);
2487 // At least we need to have English
2488 if (empty($languages)) {
2489 $languages[] = 'default';
2490 }
2491 } else {
2492 $languages = array('default');
2493 }
2494
2495 $localLanguage = array();
2496 foreach ($languages as $language) {
2497 $tempLL = $languageFactory->getParsedData($fileRef, $language, $this->charSet, $errorMode);
2498
2499 $localLanguage['default'] = $tempLL['default'];
2500 if (!isset($localLanguage[$this->lang])) {
2501 $localLanguage[$this->lang] = $localLanguage['default'];
2502 }
2503 if ($this->lang !== 'default' && isset($tempLL[$language])) {
2504 // Merge current language labels onto labels from previous language
2505 // This way we have a labels with fall back applied
2506 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($localLanguage[$this->lang], $tempLL[$language], true, false);
2507 }
2508 }
2509
2510 return $localLanguage;
2511 }
2512
2513 /*****************************************************/
2514 /* */
2515 /* Tools */
2516 /* */
2517 /*****************************************************/
2518 /**
2519 * Concatenate files into one file
2520 * registered handler
2521 *
2522 * @return void
2523 */
2524 protected function doConcatenate()
2525 {
2526 $this->doConcatenateCss();
2527 $this->doConcatenateJavaScript();
2528 }
2529
2530 /**
2531 * Concatenate JavaScript files according to the configuration.
2532 *
2533 * @return void
2534 */
2535 protected function doConcatenateJavaScript()
2536 {
2537 if ($this->concatenateFiles || $this->concatenateJavascript) {
2538 if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsConcatenateHandler'])) {
2539 // use external concatenation routine
2540 $params = array(
2541 'jsLibs' => &$this->jsLibs,
2542 'jsFiles' => &$this->jsFiles,
2543 'jsFooterFiles' => &$this->jsFooterFiles,
2544 'headerData' => &$this->headerData,
2545 'footerData' => &$this->footerData
2546 );
2547 GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsConcatenateHandler'], $params, $this);
2548 } else {
2549 $this->jsLibs = $this->getCompressor()->concatenateJsFiles($this->jsLibs);
2550 $this->jsFiles = $this->getCompressor()->concatenateJsFiles($this->jsFiles);
2551 $this->jsFooterFiles = $this->getCompressor()->concatenateJsFiles($this->jsFooterFiles);
2552 }
2553 }
2554 }
2555
2556 /**
2557 * Concatenate CSS files according to configuration.
2558 *
2559 * @return void
2560 */
2561 protected function doConcatenateCss()
2562 {
2563 if ($this->concatenateFiles || $this->concatenateCss) {
2564 if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssConcatenateHandler'])) {
2565 // use external concatenation routine
2566 $params = array(
2567 'cssFiles' => &$this->cssFiles,
2568 'cssLibs' => &$this->cssLibs,
2569 'headerData' => &$this->headerData,
2570 'footerData' => &$this->footerData
2571 );
2572 GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssConcatenateHandler'], $params, $this);
2573 } else {
2574 $cssOptions = array();
2575 if (TYPO3_MODE === 'BE') {
2576 $cssOptions = array('baseDirectories' => $GLOBALS['TBE_TEMPLATE']->getSkinStylesheetDirectories());
2577 }
2578 $this->cssLibs = $this->getCompressor()->concatenateCssFiles($this->cssLibs, $cssOptions);
2579 $this->cssFiles = $this->getCompressor()->concatenateCssFiles($this->cssFiles, $cssOptions);
2580 }
2581 }
2582 }
2583
2584 /**
2585 * Compresses inline code
2586 *
2587 * @return void
2588 */
2589 protected function doCompress()
2590 {
2591 $this->doCompressJavaScript();
2592 $this->doCompressCss();
2593 }
2594
2595 /**
2596 * Compresses CSS according to configuration.
2597 *
2598 * @return void
2599 */
2600 protected function doCompressCss()
2601 {
2602 if ($this->compressCss) {
2603 if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'])) {
2604 // Use external compression routine
2605 $params = array(
2606 'cssInline' => &$this->cssInline,
2607 'cssFiles' => &$this->cssFiles,
2608 'cssLibs' => &$this->cssLibs,
2609 'headerData' => &$this->headerData,
2610 'footerData' => &$this->footerData
2611 );
2612 GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'], $params, $this);
2613 } else {
2614 $this->cssLibs = $this->getCompressor()->compressCssFiles($this->cssLibs);
2615 $this->cssFiles = $this->getCompressor()->compressCssFiles($this->cssFiles);
2616 }
2617 }
2618 }
2619
2620 /**
2621 * Compresses JavaScript according to configuration.
2622 *
2623 * @return void
2624 */
2625 protected function doCompressJavaScript()
2626 {
2627 if ($this->compressJavascript) {
2628 if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler'])) {
2629 // Use external compression routine
2630 $params = array(
2631 'jsInline' => &$this->jsInline,
2632 'jsFooterInline' => &$this->jsFooterInline,
2633 'jsLibs' => &$this->jsLibs,
2634 'jsFiles' => &$this->jsFiles,
2635 'jsFooterFiles' => &$this->jsFooterFiles,
2636 'headerData' => &$this->headerData,
2637 'footerData' => &$this->footerData
2638 );
2639 GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler'], $params, $this);
2640 } else {
2641 // Traverse the arrays, compress files
2642 if (!empty($this->jsInline)) {
2643 foreach ($this->jsInline as $name => $properties) {
2644 if ($properties['compress']) {
2645 $error = '';
2646 $this->jsInline[$name]['code'] = GeneralUtility::minifyJavaScript($properties['code'], $error);
2647 if ($error) {
2648 $this->compressError .= 'Error with minify JS Inline Block "' . $name . '": ' . $error . LF;
2649 }
2650 }
2651 }
2652 }
2653 $this->jsLibs = $this->getCompressor()->compressJsFiles($this->jsLibs);
2654 $this->jsFiles = $this->getCompressor()->compressJsFiles($this->jsFiles);
2655 $this->jsFooterFiles = $this->getCompressor()->compressJsFiles($this->jsFooterFiles);
2656 }
2657 }
2658 }
2659
2660 /**
2661 * Returns instance of \TYPO3\CMS\Core\Resource\ResourceCompressor
2662 *
2663 * @return \TYPO3\CMS\Core\Resource\ResourceCompressor
2664 */
2665 protected function getCompressor()
2666 {
2667 if ($this->compressor === null) {
2668 $this->compressor = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\ResourceCompressor::class);
2669 }
2670 return $this->compressor;
2671 }
2672
2673 /**
2674 * Processes a Javascript file dependent on the current context
2675 *
2676 * Adds the version number for Frontend, compresses the file for Backend
2677 *
2678 * @param string $filename Filename
2679 * @return string New filename
2680 */
2681 protected function processJsFile($filename)
2682 {
2683 if ($this->compressJavascript) {
2684 return $this->getCompressor()->compressJsFile($filename);
2685 } elseif (TYPO3_MODE === 'FE') {
2686 return GeneralUtility::createVersionNumberedFilename($filename);
2687 } else {
2688 return $filename;
2689 }
2690 }
2691
2692 /**
2693 * Returns global frontend controller
2694 *
2695 * @return TypoScriptFrontendController
2696 */
2697 protected function getTypoScriptFrontendController()
2698 {
2699 return $GLOBALS['TSFE'];
2700 }
2701
2702 /**
2703 * Returns global language service instance
2704 *
2705 * @return \TYPO3\CMS\Lang\LanguageService
2706 */
2707 protected function getLanguageService()
2708 {
2709 return $GLOBALS['LANG'];
2710 }
2711
2712 /*****************************************************/
2713 /* */
2714 /* Hooks */
2715 /* */
2716 /*****************************************************/
2717 /**
2718 * Execute PreRenderHook for possible manipulation
2719 *
2720 * @return void
2721 */
2722 protected function executePreRenderHook()
2723 {
2724 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-preProcess'])) {
2725 $params = array(
2726 'jsLibs' => &$this->jsLibs,
2727 'jsFooterLibs' => &$this->jsFooterLibs,
2728 'jsFiles' => &$this->jsFiles,
2729 'jsFooterFiles' => &$this->jsFooterFiles,
2730 'cssFiles' => &$this->cssFiles,
2731 'headerData' => &$this->headerData,
2732 'footerData' => &$this->footerData,
2733 'jsInline' => &$this->jsInline,
2734 'jsFooterInline' => &$this->jsFooterInline,
2735 'cssInline' => &$this->cssInline
2736 );
2737 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-preProcess'] as $hook) {
2738 GeneralUtility::callUserFunction($hook, $params, $this);
2739 }
2740 }
2741 }
2742
2743 /**
2744 * PostTransform for possible manipulation of concatenated and compressed files
2745 *
2746 * @return void
2747 */
2748 protected function executeRenderPostTransformHook()
2749 {
2750 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postTransform'])) {
2751 $params = array(
2752 'jsLibs' => &$this->jsLibs,
2753 'jsFooterLibs' => &$this->jsFooterLibs,
2754 'jsFiles' => &$this->jsFiles,
2755 'jsFooterFiles' => &$this->jsFooterFiles,
2756 'cssFiles' => &$this->cssFiles,
2757 'headerData' => &$this->headerData,
2758 'footerData' => &$this->footerData,
2759 'jsInline' => &$this->jsInline,
2760 'jsFooterInline' => &$this->jsFooterInline,
2761 'cssInline' => &$this->cssInline
2762 );
2763 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postTransform'] as $hook) {
2764 GeneralUtility::callUserFunction($hook, $params, $this);
2765 }
2766 }
2767 }
2768
2769 /**
2770 * Execute postRenderHook for possible manipulation
2771 *
2772 * @param $jsLibs string
2773 * @param $jsFiles string
2774 * @param $jsFooterFiles string
2775 * @param $cssLibs string
2776 * @param $cssFiles string
2777 * @param $jsInline string
2778 * @param $cssInline string
2779 * @param $jsFooterInline string
2780 * @param $jsFooterLibs string
2781 * @return void
2782 */
2783 protected function executePostRenderHook(&$jsLibs, &$jsFiles, &$jsFooterFiles, &$cssLibs, &$cssFiles, &$jsInline, &$cssInline, &$jsFooterInline, &$jsFooterLibs)
2784 {
2785 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'])) {
2786 $params = array(
2787 'jsLibs' => &$jsLibs,
2788 'jsFiles' => &$jsFiles,
2789 'jsFooterFiles' => &$jsFooterFiles,
2790 'cssLibs' => &$cssLibs,
2791 'cssFiles' => &$cssFiles,
2792 'headerData' => &$this->headerData,
2793 'footerData' => &$this->footerData,
2794 'jsInline' => &$jsInline,
2795 'cssInline' => &$cssInline,
2796 'xmlPrologAndDocType' => &$this->xmlPrologAndDocType,
2797 'htmlTag' => &$this->htmlTag,
2798 'headTag' => &$this->headTag,
2799 'charSet' => &$this->charSet,
2800 'metaCharsetTag' => &$this->metaCharsetTag,
2801 'shortcutTag' => &$this->shortcutTag,
2802 'inlineComments' => &$this->inlineComments,
2803 'baseUrl' => &$this->baseUrl,
2804 'baseUrlTag' => &$this->baseUrlTag,
2805 'favIcon' => &$this->favIcon,
2806 'iconMimeType' => &$this->iconMimeType,
2807 'titleTag' => &$this->titleTag,
2808 'title' => &$this->title,
2809 'metaTags' => &$this->metaTags,
2810 'jsFooterInline' => &$jsFooterInline,
2811 'jsFooterLibs' => &$jsFooterLibs,
2812 'bodyContent' => &$this->bodyContent
2813 );
2814 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'] as $hook) {
2815 GeneralUtility::callUserFunction($hook, $params, $this);
2816 }
2817 }
2818 }
2819 }