[TASK] Streamline about modules and about
[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.2.1';
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 'matchheight' => $corePath . 'jquery.matchHeight-min',
1554 'nprogress' => $corePath . 'nprogress',
1555 'moment' => $corePath . 'moment',
1556 'cropper' => $corePath . 'cropper.min',
1557 'imagesloaded' => $corePath . 'imagesloaded.pkgd.min',
1558 'bootstrap' => $corePath . 'bootstrap/bootstrap',
1559 'twbs/bootstrap-datetimepicker' => $corePath . 'bootstrap-datetimepicker',
1560 'autosize' => $corePath . 'autosize',
1561 'taboverride' => $corePath . 'taboverride.min',
1562 'twbs/bootstrap-slider' => $corePath . 'bootstrap-slider.min',
1563 'jquery/autocomplete' => $corePath . 'jquery.autocomplete',
1564 );
1565 // get all extensions that are loaded
1566 $loadedExtensions = ExtensionManagementUtility::getLoadedExtensionListArray();
1567 foreach ($loadedExtensions as $packageName) {
1568 $fullJsPath = 'EXT:' . $packageName . '/Resources/Public/JavaScript/';
1569 $fullJsPath = GeneralUtility::getFileAbsFileName($fullJsPath);
1570 $fullJsPath = PathUtility::getAbsoluteWebPath($fullJsPath);
1571 $fullJsPath = rtrim($fullJsPath, '/');
1572 if ($fullJsPath) {
1573 $this->requireJsConfig['paths']['TYPO3/CMS/' . GeneralUtility::underscoredToUpperCamelCase($packageName)] = $fullJsPath;
1574 }
1575 }
1576
1577 // check if additional AMD modules need to be loaded if a single AMD module is initialized
1578 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RequireJS']['postInitializationModules'])) {
1579 $this->addInlineSettingArray('RequireJS.PostInitializationModules', $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RequireJS']['postInitializationModules']);
1580 }
1581 }
1582
1583 $this->addRequireJs = true;
1584 }
1585
1586 /**
1587 * Add additional configuration to require js.
1588 *
1589 * Configuration will be merged recursive with overrule.
1590 *
1591 * To add another path mapping deliver the following configuration:
1592 * 'paths' => array(
1593 * 'EXTERN/mybootstrapjs' => 'sysext/.../twbs/bootstrap.min',
1594 * ),
1595 *
1596 * @param array $configuration The configuration that will be merged with existing one.
1597 * @return void
1598 */
1599 public function addRequireJsConfiguration(array $configuration)
1600 {
1601 if (TYPO3_MODE === 'BE') {
1602 // Load RequireJS in backend context at first. Doing this in FE could break the output
1603 $this->loadRequireJs();
1604 }
1605 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($this->requireJsConfig, $configuration);
1606 }
1607
1608 /**
1609 * includes an AMD-compatible JS file by resolving the ModuleName, and then requires the file via a requireJS request,
1610 * additionally allowing to execute JavaScript code afterwards
1611 *
1612 * this function only works for AMD-ready JS modules, used like "define('TYPO3/CMS/Backend/FormEngine..."
1613 * in the JS file
1614 *
1615 * TYPO3/CMS/Backend/FormEngine =>
1616 * "TYPO3": Vendor Name
1617 * "CMS": Product Name
1618 * "Backend": Extension Name
1619 * "FormEngine": FileName in the Resources/Public/JavaScript folder
1620 *
1621 * @param string $mainModuleName Must be in the form of "TYPO3/CMS/PackageName/ModuleName" e.g. "TYPO3/CMS/Backend/FormEngine"
1622 * @param string $callBackFunction loaded right after the requireJS loading, must be wrapped in function() {}
1623 * @return void
1624 */
1625 public function loadRequireJsModule($mainModuleName, $callBackFunction = null)
1626 {
1627 $inlineCodeKey = $mainModuleName;
1628 // make sure requireJS is initialized
1629 $this->loadRequireJs();
1630
1631 // execute the main module, and load a possible callback function
1632 $javaScriptCode = 'require(["' . $mainModuleName . '"]';
1633 if ($callBackFunction !== null) {
1634 $inlineCodeKey .= sha1($callBackFunction);
1635 $javaScriptCode .= ', ' . $callBackFunction;
1636 }
1637 $javaScriptCode .= ');';
1638 $this->addJsInlineCode('RequireJS-Module-' . $inlineCodeKey, $javaScriptCode);
1639 }
1640
1641 /**
1642 * call this function if you need the extJS library
1643 *
1644 * @param bool $css Flag, if set the ext-css will be loaded
1645 * @param bool $theme Flag, if set the ext-theme "grey" will be loaded
1646 * @return void
1647 */
1648 public function loadExtJS($css = true, $theme = true)
1649 {
1650 $this->addExtJS = true;
1651 $this->extJStheme = $theme;
1652 $this->extJScss = $css;
1653 }
1654
1655 /**
1656 * Call this function to load debug version of ExtJS. Use this for development only
1657 *
1658 * @return void
1659 */
1660 public function enableExtJsDebug()
1661 {
1662 $this->enableExtJsDebug = true;
1663 }
1664
1665 /**
1666 * Adds Javascript Inline Label. This will occur in TYPO3.lang - object
1667 * The label can be used in scripts with TYPO3.lang.<key>
1668 * Need extJs loaded
1669 *
1670 * @param string $key
1671 * @param string $value
1672 * @return void
1673 */
1674 public function addInlineLanguageLabel($key, $value)
1675 {
1676 $this->inlineLanguageLabels[$key] = $value;
1677 }
1678
1679 /**
1680 * Adds Javascript Inline Label Array. This will occur in TYPO3.lang - object
1681 * The label can be used in scripts with TYPO3.lang.<key>
1682 * Array will be merged with existing array.
1683 * Need extJs loaded
1684 *
1685 * @param array $array
1686 * @param bool $parseWithLanguageService
1687 * @return void
1688 */
1689 public function addInlineLanguageLabelArray(array $array, $parseWithLanguageService = false)
1690 {
1691 if ($parseWithLanguageService === true) {
1692 foreach ($array as $key => $value) {
1693 if (TYPO3_MODE === 'FE') {
1694 $array[$key] = $this->getTypoScriptFrontendController()->sL($value);
1695 } else {
1696 $array[$key] = $this->getLanguageService()->sL($value);
1697 }
1698 }
1699 }
1700
1701 $this->inlineLanguageLabels = array_merge($this->inlineLanguageLabels, $array);
1702 }
1703
1704 /**
1705 * Gets labels to be used in JavaScript fetched from a locallang file.
1706 *
1707 * @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.
1708 * @param string $selectionPrefix Prefix to select the correct labels (default: '')
1709 * @param string $stripFromSelectionName String to be removed from the label names in the output. (default: '')
1710 * @param int $errorMode Error mode (when file could not be found): 0 - syslog entry, 1 - do nothing, 2 - throw an exception
1711 * @return void
1712 */
1713 public function addInlineLanguageLabelFile($fileRef, $selectionPrefix = '', $stripFromSelectionName = '', $errorMode = 0)
1714 {
1715 $index = md5($fileRef . $selectionPrefix . $stripFromSelectionName);
1716 if ($fileRef && !isset($this->inlineLanguageLabelFiles[$index])) {
1717 $this->inlineLanguageLabelFiles[$index] = array(
1718 'fileRef' => $fileRef,
1719 'selectionPrefix' => $selectionPrefix,
1720 'stripFromSelectionName' => $stripFromSelectionName,
1721 'errorMode' => $errorMode
1722 );
1723 }
1724 }
1725
1726 /**
1727 * Adds Javascript Inline Setting. This will occur in TYPO3.settings - object
1728 * The label can be used in scripts with TYPO3.setting.<key>
1729 * Need extJs loaded
1730 *
1731 * @param string $namespace
1732 * @param string $key
1733 * @param string $value
1734 * @return void
1735 */
1736 public function addInlineSetting($namespace, $key, $value)
1737 {
1738 if ($namespace) {
1739 if (strpos($namespace, '.')) {
1740 $parts = explode('.', $namespace);
1741 $a = &$this->inlineSettings;
1742 foreach ($parts as $part) {
1743 $a = &$a[$part];
1744 }
1745 $a[$key] = $value;
1746 } else {
1747 $this->inlineSettings[$namespace][$key] = $value;
1748 }
1749 } else {
1750 $this->inlineSettings[$key] = $value;
1751 }
1752 }
1753
1754 /**
1755 * Adds Javascript Inline Setting. This will occur in TYPO3.settings - object
1756 * The label can be used in scripts with TYPO3.setting.<key>
1757 * Array will be merged with existing array.
1758 * Need extJs loaded
1759 *
1760 * @param string $namespace
1761 * @param array $array
1762 * @return void
1763 */
1764 public function addInlineSettingArray($namespace, array $array)
1765 {
1766 if ($namespace) {
1767 if (strpos($namespace, '.')) {
1768 $parts = explode('.', $namespace);
1769 $a = &$this->inlineSettings;
1770 foreach ($parts as $part) {
1771 $a = &$a[$part];
1772 }
1773 $a = array_merge((array)$a, $array);
1774 } else {
1775 $this->inlineSettings[$namespace] = array_merge((array)$this->inlineSettings[$namespace], $array);
1776 }
1777 } else {
1778 $this->inlineSettings = array_merge($this->inlineSettings, $array);
1779 }
1780 }
1781
1782 /**
1783 * Adds content to body content
1784 *
1785 * @param string $content
1786 * @return void
1787 */
1788 public function addBodyContent($content)
1789 {
1790 $this->bodyContent .= $content;
1791 }
1792
1793 /*****************************************************/
1794 /* */
1795 /* Render Functions */
1796 /* */
1797 /*****************************************************/
1798 /**
1799 * Render the section (Header or Footer)
1800 *
1801 * @param int $part Section which should be rendered: self::PART_COMPLETE, self::PART_HEADER or self::PART_FOOTER
1802 * @return string Content of rendered section
1803 */
1804 public function render($part = self::PART_COMPLETE)
1805 {
1806 $this->prepareRendering();
1807 list($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs) = $this->renderJavaScriptAndCss();
1808 $metaTags = implode(LF, $this->metaTags);
1809 $markerArray = $this->getPreparedMarkerArray($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs, $metaTags);
1810 $template = $this->getTemplateForPart($part);
1811
1812 // The page renderer needs a full reset, even when only rendering one part of the page
1813 // This means that you can only register footer files *after* the header has been already rendered.
1814 // In case you render the footer part first, header files can only be added *after* the footer has been rendered
1815 $this->reset();
1816 $templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1817 return trim($templateService->substituteMarkerArray($template, $markerArray, '###|###'));
1818 }
1819
1820 /**
1821 * Render the page but not the JavaScript and CSS Files
1822 *
1823 * @param string $substituteHash The hash that is used for the placehoder markers
1824 * @access private
1825 * @return string Content of rendered section
1826 */
1827 public function renderPageWithUncachedObjects($substituteHash)
1828 {
1829 $this->prepareRendering();
1830 $markerArray = $this->getPreparedMarkerArrayForPageWithUncachedObjects($substituteHash);
1831 $template = $this->getTemplateForPart(self::PART_COMPLETE);
1832 $templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1833 return trim($templateService->substituteMarkerArray($template, $markerArray, '###|###'));
1834 }
1835
1836 /**
1837 * Renders the JavaScript and CSS files that have been added during processing
1838 * of uncached content objects (USER_INT, COA_INT)
1839 *
1840 * @param string $cachedPageContent
1841 * @param string $substituteHash The hash that is used for the placehoder markers
1842 * @access private
1843 * @return string
1844 */
1845 public function renderJavaScriptAndCssForProcessingOfUncachedContentObjects($cachedPageContent, $substituteHash)
1846 {
1847 $this->prepareRendering();
1848 list($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs) = $this->renderJavaScriptAndCss();
1849 $title = $this->title ? str_replace('|', htmlspecialchars($this->title), $this->titleTag) : '';
1850 $markerArray = array(
1851 '<!-- ###TITLE' . $substituteHash . '### -->' => $title,
1852 '<!-- ###CSS_LIBS' . $substituteHash . '### -->' => $cssLibs,
1853 '<!-- ###CSS_INCLUDE' . $substituteHash . '### -->' => $cssFiles,
1854 '<!-- ###CSS_INLINE' . $substituteHash . '### -->' => $cssInline,
1855 '<!-- ###JS_INLINE' . $substituteHash . '### -->' => $jsInline,
1856 '<!-- ###JS_INCLUDE' . $substituteHash . '### -->' => $jsFiles,
1857 '<!-- ###JS_LIBS' . $substituteHash . '### -->' => $jsLibs,
1858 '<!-- ###HEADERDATA' . $substituteHash . '### -->' => implode(LF, $this->headerData),
1859 '<!-- ###FOOTERDATA' . $substituteHash . '### -->' => implode(LF, $this->footerData),
1860 '<!-- ###JS_LIBS_FOOTER' . $substituteHash . '### -->' => $jsFooterLibs,
1861 '<!-- ###JS_INCLUDE_FOOTER' . $substituteHash . '### -->' => $jsFooterFiles,
1862 '<!-- ###JS_INLINE_FOOTER' . $substituteHash . '### -->' => $jsFooterInline
1863 );
1864 foreach ($markerArray as $placeHolder => $content) {
1865 $cachedPageContent = str_replace($placeHolder, $content, $cachedPageContent);
1866 }
1867 $this->reset();
1868 return $cachedPageContent;
1869 }
1870
1871 /**
1872 * Remove ending slashes from static header block
1873 * if the page is beeing rendered as html (not xhtml)
1874 * and define property $this->endingSlash for further use
1875 *
1876 * @return void
1877 */
1878 protected function prepareRendering()
1879 {
1880 if ($this->getRenderXhtml()) {
1881 $this->endingSlash = ' /';
1882 } else {
1883 $this->metaCharsetTag = str_replace(' />', '>', $this->metaCharsetTag);
1884 $this->baseUrlTag = str_replace(' />', '>', $this->baseUrlTag);
1885 $this->shortcutTag = str_replace(' />', '>', $this->shortcutTag);
1886 $this->endingSlash = '';
1887 }
1888 }
1889
1890 /**
1891 * Renders all JavaScript and CSS
1892 *
1893 * @return array<string>
1894 */
1895 protected function renderJavaScriptAndCss()
1896 {
1897 $this->executePreRenderHook();
1898 $mainJsLibs = $this->renderMainJavaScriptLibraries();
1899 if ($this->concatenateFiles || $this->concatenateJavascript || $this->concatenateCss) {
1900 // Do the file concatenation
1901 $this->doConcatenate();
1902 }
1903 if ($this->compressCss || $this->compressJavascript) {
1904 // Do the file compression
1905 $this->doCompress();
1906 }
1907 $this->executeRenderPostTransformHook();
1908 $cssLibs = $this->renderCssLibraries();
1909 $cssFiles = $this->renderCssFiles();
1910 $cssInline = $this->renderCssInline();
1911 list($jsLibs, $jsFooterLibs) = $this->renderAdditionalJavaScriptLibraries();
1912 list($jsFiles, $jsFooterFiles) = $this->renderJavaScriptFiles();
1913 list($jsInline, $jsFooterInline) = $this->renderInlineJavaScript();
1914 $jsLibs = $mainJsLibs . $jsLibs;
1915 if ($this->moveJsFromHeaderToFooter) {
1916 $jsFooterLibs = $jsLibs . LF . $jsFooterLibs;
1917 $jsLibs = '';
1918 $jsFooterFiles = $jsFiles . LF . $jsFooterFiles;
1919 $jsFiles = '';
1920 $jsFooterInline = $jsInline . LF . $jsFooterInline;
1921 $jsInline = '';
1922 }
1923 $this->executePostRenderHook($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs);
1924 return array($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs);
1925 }
1926
1927 /**
1928 * Fills the marker array with the given strings and trims each value
1929 *
1930 * @param $jsLibs string
1931 * @param $jsFiles string
1932 * @param $jsFooterFiles string
1933 * @param $cssLibs string
1934 * @param $cssFiles string
1935 * @param $jsInline string
1936 * @param $cssInline string
1937 * @param $jsFooterInline string
1938 * @param $jsFooterLibs string
1939 * @param $metaTags string
1940 * @return array Marker array
1941 */
1942 protected function getPreparedMarkerArray($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs, $metaTags)
1943 {
1944 $markerArray = array(
1945 'XMLPROLOG_DOCTYPE' => $this->xmlPrologAndDocType,
1946 'HTMLTAG' => $this->htmlTag,
1947 'HEADTAG' => $this->headTag,
1948 'METACHARSET' => $this->charSet ? str_replace('|', htmlspecialchars($this->charSet), $this->metaCharsetTag) : '',
1949 'INLINECOMMENT' => $this->inlineComments ? LF . LF . '<!-- ' . LF . implode(LF, $this->inlineComments) . '-->' . LF . LF : '',
1950 'BASEURL' => $this->baseUrl ? str_replace('|', $this->baseUrl, $this->baseUrlTag) : '',
1951 'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) : '',
1952 'CSS_LIBS' => $cssLibs,
1953 'CSS_INCLUDE' => $cssFiles,
1954 'CSS_INLINE' => $cssInline,
1955 'JS_INLINE' => $jsInline,
1956 'JS_INCLUDE' => $jsFiles,
1957 'JS_LIBS' => $jsLibs,
1958 'TITLE' => $this->title ? str_replace('|', htmlspecialchars($this->title), $this->titleTag) : '',
1959 'META' => $metaTags,
1960 'HEADERDATA' => $this->headerData ? implode(LF, $this->headerData) : '',
1961 'FOOTERDATA' => $this->footerData ? implode(LF, $this->footerData) : '',
1962 'JS_LIBS_FOOTER' => $jsFooterLibs,
1963 'JS_INCLUDE_FOOTER' => $jsFooterFiles,
1964 'JS_INLINE_FOOTER' => $jsFooterInline,
1965 'BODY' => $this->bodyContent
1966 );
1967 $markerArray = array_map('trim', $markerArray);
1968 return $markerArray;
1969 }
1970
1971 /**
1972 * Fills the marker array with the given strings and trims each value
1973 *
1974 * @param string $substituteHash The hash that is used for the placehoder markers
1975 * @return array Marker array
1976 */
1977 protected function getPreparedMarkerArrayForPageWithUncachedObjects($substituteHash)
1978 {
1979 $markerArray = array(
1980 'XMLPROLOG_DOCTYPE' => $this->xmlPrologAndDocType,
1981 'HTMLTAG' => $this->htmlTag,
1982 'HEADTAG' => $this->headTag,
1983 'METACHARSET' => $this->charSet ? str_replace('|', htmlspecialchars($this->charSet), $this->metaCharsetTag) : '',
1984 'INLINECOMMENT' => $this->inlineComments ? LF . LF . '<!-- ' . LF . implode(LF, $this->inlineComments) . '-->' . LF . LF : '',
1985 'BASEURL' => $this->baseUrl ? str_replace('|', $this->baseUrl, $this->baseUrlTag) : '',
1986 'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) : '',
1987 'META' => implode(LF, $this->metaTags),
1988 'BODY' => $this->bodyContent,
1989 'TITLE' => '<!-- ###TITLE' . $substituteHash . '### -->',
1990 'CSS_LIBS' => '<!-- ###CSS_LIBS' . $substituteHash . '### -->',
1991 'CSS_INCLUDE' => '<!-- ###CSS_INCLUDE' . $substituteHash . '### -->',
1992 'CSS_INLINE' => '<!-- ###CSS_INLINE' . $substituteHash . '### -->',
1993 'JS_INLINE' => '<!-- ###JS_INLINE' . $substituteHash . '### -->',
1994 'JS_INCLUDE' => '<!-- ###JS_INCLUDE' . $substituteHash . '### -->',
1995 'JS_LIBS' => '<!-- ###JS_LIBS' . $substituteHash . '### -->',
1996 'HEADERDATA' => '<!-- ###HEADERDATA' . $substituteHash . '### -->',
1997 'FOOTERDATA' => '<!-- ###FOOTERDATA' . $substituteHash . '### -->',
1998 'JS_LIBS_FOOTER' => '<!-- ###JS_LIBS_FOOTER' . $substituteHash . '### -->',
1999 'JS_INCLUDE_FOOTER' => '<!-- ###JS_INCLUDE_FOOTER' . $substituteHash . '### -->',
2000 'JS_INLINE_FOOTER' => '<!-- ###JS_INLINE_FOOTER' . $substituteHash . '### -->'
2001 );
2002 $markerArray = array_map('trim', $markerArray);
2003 return $markerArray;
2004 }
2005
2006 /**
2007 * Reads the template file and returns the requested part as string
2008 *
2009 * @param int $part
2010 * @return string
2011 */
2012 protected function getTemplateForPart($part)
2013 {
2014 $templateFile = GeneralUtility::getFileAbsFileName($this->templateFile);
2015 if (is_file($templateFile)) {
2016 $template = file_get_contents($templateFile);
2017 if ($this->removeLineBreaksFromTemplate) {
2018 $template = strtr($template, array(LF => '', CR => ''));
2019 }
2020 if ($part !== self::PART_COMPLETE) {
2021 $templatePart = explode('###BODY###', $template);
2022 $template = $templatePart[$part - 1];
2023 }
2024 } else {
2025 $template = '';
2026 }
2027 return $template;
2028 }
2029
2030 /**
2031 * Helper function for render the main JavaScript libraries,
2032 * currently: RequireJS, jQuery, ExtJS
2033 *
2034 * @return string Content with JavaScript libraries
2035 */
2036 protected function renderMainJavaScriptLibraries()
2037 {
2038 $out = '';
2039
2040 // Include RequireJS
2041 if ($this->addRequireJs) {
2042 // load the paths of the requireJS configuration
2043 $out .= GeneralUtility::wrapJS('var require = ' . json_encode($this->requireJsConfig)) . LF;
2044 // directly after that, include the require.js file
2045 $out .= '<script src="' . $this->processJsFile($this->requireJsPath . 'require.js') . '" type="text/javascript"></script>' . LF;
2046 }
2047
2048 // Include jQuery Core for each namespace, depending on the version and source
2049 if (!empty($this->jQueryVersions)) {
2050 foreach ($this->jQueryVersions as $namespace => $jQueryVersion) {
2051 $out .= $this->renderJqueryScriptTag($jQueryVersion['version'], $jQueryVersion['source'], $namespace);
2052 }
2053 }
2054 // Include extJS
2055 if ($this->addExtJS) {
2056 // Use the base adapter all the time
2057 $out .= '<script src="' . $this->processJsFile($this->extJsPath . 'adapter/ext-base' . ($this->enableExtJsDebug ? '-debug' : '') . '.js') . '" type="text/javascript"></script>' . LF;
2058 $out .= '<script src="' . $this->processJsFile($this->extJsPath . 'ext-all' . ($this->enableExtJsDebug ? '-debug' : '') . '.js') . '" type="text/javascript"></script>' . LF;
2059 // Add extJS localization
2060 // Load standard ISO mapping and modify for use with ExtJS
2061 $localeMap = $this->locales->getIsoMapping();
2062 $localeMap[''] = 'en';
2063 $localeMap['default'] = 'en';
2064 // Greek
2065 $localeMap['gr'] = 'el_GR';
2066 // Norwegian Bokmaal
2067 $localeMap['no'] = 'no_BO';
2068 // Swedish
2069 $localeMap['se'] = 'se_SV';
2070 $extJsLang = isset($localeMap[$this->lang]) ? $localeMap[$this->lang] : $this->lang;
2071 $extJsLocaleFile = $this->extJsPath . 'locale/ext-lang-' . $extJsLang . '.js';
2072 if (file_exists(PATH_site . $extJsLocaleFile)) {
2073 $out .= '<script src="' . $this->processJsFile($extJsLocaleFile) . '" type="text/javascript" charset="utf-8"></script>' . LF;
2074 }
2075 // Remove extjs from JScodeLibArray
2076 unset($this->jsFiles[$this->extJsPath . 'ext-all.js'], $this->jsFiles[$this->extJsPath . 'ext-all-debug.js']);
2077 }
2078 $this->loadJavaScriptLanguageStrings();
2079 if (TYPO3_MODE === 'BE') {
2080 $this->addAjaxUrlsToInlineSettings();
2081 }
2082 $inlineSettings = $this->inlineLanguageLabels ? 'TYPO3.lang = ' . json_encode($this->inlineLanguageLabels) . ';' : '';
2083 $inlineSettings .= $this->inlineSettings ? 'TYPO3.settings = ' . json_encode($this->inlineSettings) . ';' : '';
2084 if ($this->addExtJS) {
2085 // Set clear.gif, move it on top, add handler code
2086 $code = '';
2087 if (!empty($this->extOnReadyCode)) {
2088 foreach ($this->extOnReadyCode as $block) {
2089 $code .= $block;
2090 }
2091 }
2092 $clearGifPath = htmlspecialchars(GeneralUtility::locationHeaderUrl(ExtensionManagementUtility::extRelPath('backend') . 'Resources/Public/Images/clear.gif'));
2093 $out .= $this->inlineJavascriptWrap[0] . '
2094 Ext.ns("TYPO3");
2095 Ext.BLANK_IMAGE_URL = "' . $clearGifPath . '";
2096 Ext.SSL_SECURE_URL = "' . $clearGifPath . '";' . LF
2097 . $inlineSettings
2098 . 'Ext.onReady(function() {'
2099 . $code
2100 . ' });'
2101 . $this->inlineJavascriptWrap[1];
2102 $this->extOnReadyCode = array();
2103 // Include TYPO3.l10n object
2104 if (TYPO3_MODE === 'BE') {
2105 $out .= '<script src="' . $this->processJsFile(ExtensionManagementUtility::extRelPath('lang') . 'Resources/Public/JavaScript/Typo3Lang.js') . '" type="text/javascript" charset="utf-8"></script>' . LF;
2106 }
2107 if ($this->extJScss) {
2108 if (isset($GLOBALS['TBE_STYLES']['extJS']['all'])) {
2109 $this->addCssLibrary($GLOBALS['TBE_STYLES']['extJS']['all'], 'stylesheet', 'all', '', true);
2110 } else {
2111 $this->addCssLibrary($this->extJsPath . 'resources/css/ext-all-notheme.css', 'stylesheet', 'all', '', true);
2112 }
2113 }
2114 if ($this->extJStheme) {
2115 if (isset($GLOBALS['TBE_STYLES']['extJS']['theme'])) {
2116 $this->addCssLibrary($GLOBALS['TBE_STYLES']['extJS']['theme'], 'stylesheet', 'all', '', true);
2117 } else {
2118 $this->addCssLibrary($this->extJsPath . 'resources/css/xtheme-blue.css', 'stylesheet', 'all', '', true);
2119 }
2120 }
2121 } else {
2122 // no extJS loaded, but still inline settings
2123 if ($inlineSettings !== '') {
2124 // make sure the global TYPO3 is available
2125 $inlineSettings = 'var TYPO3 = TYPO3 || {};' . CRLF . $inlineSettings;
2126 $out .= $this->inlineJavascriptWrap[0] . $inlineSettings . $this->inlineJavascriptWrap[1];
2127 // Add language module only if also jquery is guaranteed to be there
2128 if (TYPO3_MODE === 'BE' && !empty($this->jQueryVersions)) {
2129 $this->loadRequireJsModule('TYPO3/CMS/Lang/Lang');
2130 }
2131 }
2132 }
2133 return $out;
2134 }
2135
2136 /**
2137 * Load the language strings into JavaScript
2138 */
2139 protected function loadJavaScriptLanguageStrings()
2140 {
2141 if (!empty($this->inlineLanguageLabelFiles)) {
2142 foreach ($this->inlineLanguageLabelFiles as $languageLabelFile) {
2143 $this->includeLanguageFileForInline($languageLabelFile['fileRef'], $languageLabelFile['selectionPrefix'], $languageLabelFile['stripFromSelectionName'], $languageLabelFile['errorMode']);
2144 }
2145 }
2146 $this->inlineLanguageLabelFiles = array();
2147 // Convert labels/settings back to UTF-8 since json_encode() only works with UTF-8:
2148 if (TYPO3_MODE === 'FE' && $this->getCharSet() !== 'utf-8') {
2149 if ($this->inlineLanguageLabels) {
2150 $this->csConvObj->convArray($this->inlineLanguageLabels, $this->getCharSet(), 'utf-8');
2151 }
2152 if ($this->inlineSettings) {
2153 $this->csConvObj->convArray($this->inlineSettings, $this->getCharSet(), 'utf-8');
2154 }
2155 }
2156 }
2157
2158 /**
2159 * Make URLs to all backend ajax handlers available as inline setting.
2160 */
2161 protected function addAjaxUrlsToInlineSettings()
2162 {
2163 $ajaxUrls = array();
2164 // Note: this method of adding Ajax URLs is @deprecated as of TYPO3 v8, and will be removed in TYPO3 v9
2165 foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'] as $ajaxHandler => $_) {
2166 $ajaxUrls[$ajaxHandler] = BackendUtility::getAjaxUrl($ajaxHandler);
2167 }
2168
2169 // also add the ajax-based routes
2170 /** @var UriBuilder $uriBuilder */
2171 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
2172 /** @var Router $router */
2173 $router = GeneralUtility::makeInstance(Router::class);
2174 $routes = $router->getRoutes();
2175 foreach ($routes as $routeIdentifier => $route) {
2176 if ($route->getOption('ajax')) {
2177 $uri = (string)$uriBuilder->buildUriFromRoute($routeIdentifier);
2178 // use the shortened value in order to use this in JavaScript
2179 $routeIdentifier = str_replace('ajax_', '', $routeIdentifier);
2180 $ajaxUrls[$routeIdentifier] = $uri;
2181 }
2182 }
2183
2184 $this->inlineSettings['ajaxUrls'] = $ajaxUrls;
2185 }
2186
2187 /**
2188 * Renders the HTML script tag for the given jQuery version.
2189 *
2190 * @param string $version The jQuery version that should be included, either "latest" or any available version
2191 * @param string $source The location of the jQuery source, can be "local", "google", "msn" or "jquery
2192 * @param string $namespace The namespace in which the jQuery object of the specific version should be stored
2193 * @return string
2194 */
2195 protected function renderJqueryScriptTag($version, $source, $namespace)
2196 {
2197 switch (true) {
2198 case isset($this->jQueryCdnUrls[$source]):
2199 if ($this->enableJqueryDebug) {
2200 $minifyPart = '';
2201 } else {
2202 $minifyPart = '.min';
2203 }
2204 $jQueryFileName = sprintf($this->jQueryCdnUrls[$source], $version, $minifyPart);
2205 break;
2206 case $source === 'local':
2207 $jQueryFileName = $this->jQueryPath . 'jquery-' . rawurlencode($version);
2208 if ($this->enableJqueryDebug) {
2209 $jQueryFileName .= '.js';
2210 } else {
2211 $jQueryFileName .= '.min.js';
2212 }
2213 break;
2214 default:
2215 $jQueryFileName = $source;
2216 }
2217 // Include the jQuery Core
2218 $scriptTag = '<script src="' . htmlspecialchars($jQueryFileName) . '" type="text/javascript"></script>' . LF;
2219 // Set the noConflict mode to be available via "TYPO3.jQuery" in all installations
2220 switch ($namespace) {
2221 case self::JQUERY_NAMESPACE_DEFAULT_NOCONFLICT:
2222 $scriptTag .= GeneralUtility::wrapJS('jQuery.noConflict();') . LF;
2223 break;
2224 case self::JQUERY_NAMESPACE_NONE:
2225 break;
2226 case self::JQUERY_NAMESPACE_DEFAULT:
2227
2228 default:
2229 $scriptTag .= GeneralUtility::wrapJS('var TYPO3 = TYPO3 || {}; TYPO3.' . $namespace . ' = jQuery.noConflict(true);') . LF;
2230 }
2231 return $scriptTag;
2232 }
2233
2234 /**
2235 * Render CSS library files
2236 *
2237 * @return string
2238 */
2239 protected function renderCssLibraries()
2240 {
2241 $cssFiles = '';
2242 if (!empty($this->cssLibs)) {
2243 foreach ($this->cssLibs as $file => $properties) {
2244 $file = GeneralUtility::resolveBackPath($file);
2245 $file = GeneralUtility::createVersionNumberedFilename($file);
2246 $tag = '<link rel="' . htmlspecialchars($properties['rel'])
2247 . '" type="text/css" href="' . htmlspecialchars($file)
2248 . '" media="' . htmlspecialchars($properties['media']) . '"'
2249 . ($properties['title'] ? ' title="' . htmlspecialchars($properties['title']) . '"' : '')
2250 . $this->endingSlash . '>';
2251 if ($properties['allWrap']) {
2252 $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2253 $tag = $wrapArr[0] . $tag . $wrapArr[1];
2254 }
2255 $tag .= LF;
2256 if ($properties['forceOnTop']) {
2257 $cssFiles = $tag . $cssFiles;
2258 } else {
2259 $cssFiles .= $tag;
2260 }
2261 }
2262 }
2263 return $cssFiles;
2264 }
2265
2266 /**
2267 * Render CSS files
2268 *
2269 * @return string
2270 */
2271 protected function renderCssFiles()
2272 {
2273 $cssFiles = '';
2274 if (!empty($this->cssFiles)) {
2275 foreach ($this->cssFiles as $file => $properties) {
2276 $file = GeneralUtility::resolveBackPath($file);
2277 $file = GeneralUtility::createVersionNumberedFilename($file);
2278 $tag = '<link rel="' . htmlspecialchars($properties['rel'])
2279 . '" type="text/css" href="' . htmlspecialchars($file)
2280 . '" media="' . htmlspecialchars($properties['media']) . '"'
2281 . ($properties['title'] ? ' title="' . htmlspecialchars($properties['title']) . '"' : '')
2282 . $this->endingSlash . '>';
2283 if ($properties['allWrap']) {
2284 $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2285 $tag = $wrapArr[0] . $tag . $wrapArr[1];
2286 }
2287 $tag .= LF;
2288 if ($properties['forceOnTop']) {
2289 $cssFiles = $tag . $cssFiles;
2290 } else {
2291 $cssFiles .= $tag;
2292 }
2293 }
2294 }
2295 return $cssFiles;
2296 }
2297
2298 /**
2299 * Render inline CSS
2300 *
2301 * @return string
2302 */
2303 protected function renderCssInline()
2304 {
2305 $cssInline = '';
2306 if (!empty($this->cssInline)) {
2307 foreach ($this->cssInline as $name => $properties) {
2308 $cssCode = '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF;
2309 if ($properties['forceOnTop']) {
2310 $cssInline = $cssCode . $cssInline;
2311 } else {
2312 $cssInline .= $cssCode;
2313 }
2314 }
2315 $cssInline = $this->inlineCssWrap[0] . $cssInline . $this->inlineCssWrap[1];
2316 }
2317 return $cssInline;
2318 }
2319
2320 /**
2321 * Render JavaScipt libraries
2322 *
2323 * @return array<string> jsLibs and jsFooterLibs strings
2324 */
2325 protected function renderAdditionalJavaScriptLibraries()
2326 {
2327 $jsLibs = '';
2328 $jsFooterLibs = '';
2329 if (!empty($this->jsLibs)) {
2330 foreach ($this->jsLibs as $properties) {
2331 $properties['file'] = GeneralUtility::resolveBackPath($properties['file']);
2332 $properties['file'] = GeneralUtility::createVersionNumberedFilename($properties['file']);
2333 $async = ($properties['async']) ? ' async="async"' : '';
2334 $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '"' : '';
2335 $tag = '<script src="' . htmlspecialchars($properties['file']) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $integrity . '></script>';
2336 if ($properties['allWrap']) {
2337 $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2338 $tag = $wrapArr[0] . $tag . $wrapArr[1];
2339 }
2340 $tag .= LF;
2341 if ($properties['forceOnTop']) {
2342 if ($properties['section'] === self::PART_HEADER) {
2343 $jsLibs = $tag . $jsLibs;
2344 } else {
2345 $jsFooterLibs = $tag . $jsFooterLibs;
2346 }
2347 } else {
2348 if ($properties['section'] === self::PART_HEADER) {
2349 $jsLibs .= $tag;
2350 } else {
2351 $jsFooterLibs .= $tag;
2352 }
2353 }
2354 }
2355 }
2356 if ($this->moveJsFromHeaderToFooter) {
2357 $jsFooterLibs = $jsLibs . LF . $jsFooterLibs;
2358 $jsLibs = '';
2359 }
2360 return array($jsLibs, $jsFooterLibs);
2361 }
2362
2363 /**
2364 * Render JavaScript files
2365 *
2366 * @return array<string> jsFiles and jsFooterFiles strings
2367 */
2368 protected function renderJavaScriptFiles()
2369 {
2370 $jsFiles = '';
2371 $jsFooterFiles = '';
2372 if (!empty($this->jsFiles)) {
2373 foreach ($this->jsFiles as $file => $properties) {
2374 $file = GeneralUtility::resolveBackPath($file);
2375 $file = GeneralUtility::createVersionNumberedFilename($file);
2376 $async = ($properties['async']) ? ' async="async"' : '';
2377 $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '"' : '';
2378 $tag = '<script src="' . htmlspecialchars($file) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $integrity . '></script>';
2379 if ($properties['allWrap']) {
2380 $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2381 $tag = $wrapArr[0] . $tag . $wrapArr[1];
2382 }
2383 $tag .= LF;
2384 if ($properties['forceOnTop']) {
2385 if ($properties['section'] === self::PART_HEADER) {
2386 $jsFiles = $tag . $jsFiles;
2387 } else {
2388 $jsFooterFiles = $tag . $jsFooterFiles;
2389 }
2390 } else {
2391 if ($properties['section'] === self::PART_HEADER) {
2392 $jsFiles .= $tag;
2393 } else {
2394 $jsFooterFiles .= $tag;
2395 }
2396 }
2397 }
2398 }
2399 if ($this->moveJsFromHeaderToFooter) {
2400 $jsFooterFiles = $jsFiles . $jsFooterFiles;
2401 $jsFiles = '';
2402 }
2403 return array($jsFiles, $jsFooterFiles);
2404 }
2405
2406 /**
2407 * Render inline JavaScript
2408 *
2409 * @return array<string> jsInline and jsFooterInline string
2410 */
2411 protected function renderInlineJavaScript()
2412 {
2413 $jsInline = '';
2414 $jsFooterInline = '';
2415 if (!empty($this->jsInline)) {
2416 foreach ($this->jsInline as $name => $properties) {
2417 $jsCode = '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF;
2418 if ($properties['forceOnTop']) {
2419 if ($properties['section'] === self::PART_HEADER) {
2420 $jsInline = $jsCode . $jsInline;
2421 } else {
2422 $jsFooterInline = $jsCode . $jsFooterInline;
2423 }
2424 } else {
2425 if ($properties['section'] === self::PART_HEADER) {
2426 $jsInline .= $jsCode;
2427 } else {
2428 $jsFooterInline .= $jsCode;
2429 }
2430 }
2431 }
2432 }
2433 if ($jsInline) {
2434 $jsInline = $this->inlineJavascriptWrap[0] . $jsInline . $this->inlineJavascriptWrap[1];
2435 }
2436 if ($jsFooterInline) {
2437 $jsFooterInline = $this->inlineJavascriptWrap[0] . $jsFooterInline . $this->inlineJavascriptWrap[1];
2438 }
2439 if ($this->moveJsFromHeaderToFooter) {
2440 $jsFooterInline = $jsInline . $jsFooterInline;
2441 $jsInline = '';
2442 }
2443 return array($jsInline, $jsFooterInline);
2444 }
2445
2446 /**
2447 * Include language file for inline usage
2448 *
2449 * @param string $fileRef
2450 * @param string $selectionPrefix
2451 * @param string $stripFromSelectionName
2452 * @param int $errorMode
2453 * @return void
2454 * @throws \RuntimeException
2455 */
2456 protected function includeLanguageFileForInline($fileRef, $selectionPrefix = '', $stripFromSelectionName = '', $errorMode = 0)
2457 {
2458 if (!isset($this->lang) || !isset($this->charSet)) {
2459 throw new \RuntimeException('Language and character encoding are not set.', 1284906026);
2460 }
2461 $labelsFromFile = array();
2462 $allLabels = $this->readLLfile($fileRef, $errorMode);
2463 if ($allLabels !== false) {
2464 // Merge language specific translations:
2465 if ($this->lang !== 'default' && isset($allLabels[$this->lang])) {
2466 $labels = array_merge($allLabels['default'], $allLabels[$this->lang]);
2467 } else {
2468 $labels = $allLabels['default'];
2469 }
2470 // Iterate through all locallang labels:
2471 foreach ($labels as $label => $value) {
2472 // If $selectionPrefix is set, only respect labels that start with $selectionPrefix
2473 if ($selectionPrefix === '' || strpos($label, $selectionPrefix) === 0) {
2474 // Remove substring $stripFromSelectionName from label
2475 $label = str_replace($stripFromSelectionName, '', $label);
2476 $labelsFromFile[$label] = $value;
2477 }
2478 }
2479 $this->inlineLanguageLabels = array_merge($this->inlineLanguageLabels, $labelsFromFile);
2480 }
2481 }
2482
2483 /**
2484 * Reads a locallang file.
2485 *
2486 * @param string $fileRef Reference to a relative filename to include.
2487 * @param int $errorMode Error mode (when file could not be found): 0 - syslog entry, 1 - do nothing, 2 - throw an exception
2488 * @return array Returns the $LOCAL_LANG array found in the file. If no array found, returns empty array.
2489 */
2490 protected function readLLfile($fileRef, $errorMode = 0)
2491 {
2492 /** @var $languageFactory LocalizationFactory */
2493 $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
2494
2495 if ($this->lang !== 'default') {
2496 $languages = array_reverse($this->languageDependencies);
2497 // At least we need to have English
2498 if (empty($languages)) {
2499 $languages[] = 'default';
2500 }
2501 } else {
2502 $languages = array('default');
2503 }
2504
2505 $localLanguage = array();
2506 foreach ($languages as $language) {
2507 $tempLL = $languageFactory->getParsedData($fileRef, $language, $this->charSet, $errorMode);
2508
2509 $localLanguage['default'] = $tempLL['default'];
2510 if (!isset($localLanguage[$this->lang])) {
2511 $localLanguage[$this->lang] = $localLanguage['default'];
2512 }
2513 if ($this->lang !== 'default' && isset($tempLL[$language])) {
2514 // Merge current language labels onto labels from previous language
2515 // This way we have a labels with fall back applied
2516 \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($localLanguage[$this->lang], $tempLL[$language], true, false);
2517 }
2518 }
2519
2520 return $localLanguage;
2521 }
2522
2523 /*****************************************************/
2524 /* */
2525 /* Tools */
2526 /* */
2527 /*****************************************************/
2528 /**
2529 * Concatenate files into one file
2530 * registered handler
2531 *
2532 * @return void
2533 */
2534 protected function doConcatenate()
2535 {
2536 $this->doConcatenateCss();
2537 $this->doConcatenateJavaScript();
2538 }
2539
2540 /**
2541 * Concatenate JavaScript files according to the configuration.
2542 *
2543 * @return void
2544 */
2545 protected function doConcatenateJavaScript()
2546 {
2547 if ($this->concatenateFiles || $this->concatenateJavascript) {
2548 if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsConcatenateHandler'])) {
2549 // use external concatenation routine
2550 $params = array(
2551 'jsLibs' => &$this->jsLibs,
2552 'jsFiles' => &$this->jsFiles,
2553 'jsFooterFiles' => &$this->jsFooterFiles,
2554 'headerData' => &$this->headerData,
2555 'footerData' => &$this->footerData
2556 );
2557 GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsConcatenateHandler'], $params, $this);
2558 } else {
2559 $this->jsLibs = $this->getCompressor()->concatenateJsFiles($this->jsLibs);
2560 $this->jsFiles = $this->getCompressor()->concatenateJsFiles($this->jsFiles);
2561 $this->jsFooterFiles = $this->getCompressor()->concatenateJsFiles($this->jsFooterFiles);
2562 }
2563 }
2564 }
2565
2566 /**
2567 * Concatenate CSS files according to configuration.
2568 *
2569 * @return void
2570 */
2571 protected function doConcatenateCss()
2572 {
2573 if ($this->concatenateFiles || $this->concatenateCss) {
2574 if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssConcatenateHandler'])) {
2575 // use external concatenation routine
2576 $params = array(
2577 'cssFiles' => &$this->cssFiles,
2578 'cssLibs' => &$this->cssLibs,
2579 'headerData' => &$this->headerData,
2580 'footerData' => &$this->footerData
2581 );
2582 GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssConcatenateHandler'], $params, $this);
2583 } else {
2584 $cssOptions = array();
2585 if (TYPO3_MODE === 'BE') {
2586 $cssOptions = array('baseDirectories' => $GLOBALS['TBE_TEMPLATE']->getSkinStylesheetDirectories());
2587 }
2588 $this->cssLibs = $this->getCompressor()->concatenateCssFiles($this->cssLibs, $cssOptions);
2589 $this->cssFiles = $this->getCompressor()->concatenateCssFiles($this->cssFiles, $cssOptions);
2590 }
2591 }
2592 }
2593
2594 /**
2595 * Compresses inline code
2596 *
2597 * @return void
2598 */
2599 protected function doCompress()
2600 {
2601 $this->doCompressJavaScript();
2602 $this->doCompressCss();
2603 }
2604
2605 /**
2606 * Compresses CSS according to configuration.
2607 *
2608 * @return void
2609 */
2610 protected function doCompressCss()
2611 {
2612 if ($this->compressCss) {
2613 if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'])) {
2614 // Use external compression routine
2615 $params = array(
2616 'cssInline' => &$this->cssInline,
2617 'cssFiles' => &$this->cssFiles,
2618 'cssLibs' => &$this->cssLibs,
2619 'headerData' => &$this->headerData,
2620 'footerData' => &$this->footerData
2621 );
2622 GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'], $params, $this);
2623 } else {
2624 $this->cssLibs = $this->getCompressor()->compressCssFiles($this->cssLibs);
2625 $this->cssFiles = $this->getCompressor()->compressCssFiles($this->cssFiles);
2626 }
2627 }
2628 }
2629
2630 /**
2631 * Compresses JavaScript according to configuration.
2632 *
2633 * @return void
2634 */
2635 protected function doCompressJavaScript()
2636 {
2637 if ($this->compressJavascript) {
2638 if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler'])) {
2639 // Use external compression routine
2640 $params = array(
2641 'jsInline' => &$this->jsInline,
2642 'jsFooterInline' => &$this->jsFooterInline,
2643 'jsLibs' => &$this->jsLibs,
2644 'jsFiles' => &$this->jsFiles,
2645 'jsFooterFiles' => &$this->jsFooterFiles,
2646 'headerData' => &$this->headerData,
2647 'footerData' => &$this->footerData
2648 );
2649 GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler'], $params, $this);
2650 } else {
2651 // Traverse the arrays, compress files
2652 if (!empty($this->jsInline)) {
2653 foreach ($this->jsInline as $name => $properties) {
2654 if ($properties['compress']) {
2655 $error = '';
2656 $this->jsInline[$name]['code'] = GeneralUtility::minifyJavaScript($properties['code'], $error);
2657 if ($error) {
2658 $this->compressError .= 'Error with minify JS Inline Block "' . $name . '": ' . $error . LF;
2659 }
2660 }
2661 }
2662 }
2663 $this->jsLibs = $this->getCompressor()->compressJsFiles($this->jsLibs);
2664 $this->jsFiles = $this->getCompressor()->compressJsFiles($this->jsFiles);
2665 $this->jsFooterFiles = $this->getCompressor()->compressJsFiles($this->jsFooterFiles);
2666 }
2667 }
2668 }
2669
2670 /**
2671 * Returns instance of \TYPO3\CMS\Core\Resource\ResourceCompressor
2672 *
2673 * @return \TYPO3\CMS\Core\Resource\ResourceCompressor
2674 */
2675 protected function getCompressor()
2676 {
2677 if ($this->compressor === null) {
2678 $this->compressor = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\ResourceCompressor::class);
2679 }
2680 return $this->compressor;
2681 }
2682
2683 /**
2684 * Processes a Javascript file dependent on the current context
2685 *
2686 * Adds the version number for Frontend, compresses the file for Backend
2687 *
2688 * @param string $filename Filename
2689 * @return string New filename
2690 */
2691 protected function processJsFile($filename)
2692 {
2693 if ($this->compressJavascript) {
2694 return $this->getCompressor()->compressJsFile($filename);
2695 } elseif (TYPO3_MODE === 'FE') {
2696 return GeneralUtility::createVersionNumberedFilename($filename);
2697 } else {
2698 return $filename;
2699 }
2700 }
2701
2702 /**
2703 * Returns global frontend controller
2704 *
2705 * @return TypoScriptFrontendController
2706 */
2707 protected function getTypoScriptFrontendController()
2708 {
2709 return $GLOBALS['TSFE'];
2710 }
2711
2712 /**
2713 * Returns global language service instance
2714 *
2715 * @return \TYPO3\CMS\Lang\LanguageService
2716 */
2717 protected function getLanguageService()
2718 {
2719 return $GLOBALS['LANG'];
2720 }
2721
2722 /*****************************************************/
2723 /* */
2724 /* Hooks */
2725 /* */
2726 /*****************************************************/
2727 /**
2728 * Execute PreRenderHook for possible manipulation
2729 *
2730 * @return void
2731 */
2732 protected function executePreRenderHook()
2733 {
2734 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-preProcess'])) {
2735 $params = array(
2736 'jsLibs' => &$this->jsLibs,
2737 'jsFooterLibs' => &$this->jsFooterLibs,
2738 'jsFiles' => &$this->jsFiles,
2739 'jsFooterFiles' => &$this->jsFooterFiles,
2740 'cssFiles' => &$this->cssFiles,
2741 'headerData' => &$this->headerData,
2742 'footerData' => &$this->footerData,
2743 'jsInline' => &$this->jsInline,
2744 'jsFooterInline' => &$this->jsFooterInline,
2745 'cssInline' => &$this->cssInline
2746 );
2747 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-preProcess'] as $hook) {
2748 GeneralUtility::callUserFunction($hook, $params, $this);
2749 }
2750 }
2751 }
2752
2753 /**
2754 * PostTransform for possible manipulation of concatenated and compressed files
2755 *
2756 * @return void
2757 */
2758 protected function executeRenderPostTransformHook()
2759 {
2760 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postTransform'])) {
2761 $params = array(
2762 'jsLibs' => &$this->jsLibs,
2763 'jsFooterLibs' => &$this->jsFooterLibs,
2764 'jsFiles' => &$this->jsFiles,
2765 'jsFooterFiles' => &$this->jsFooterFiles,
2766 'cssFiles' => &$this->cssFiles,
2767 'headerData' => &$this->headerData,
2768 'footerData' => &$this->footerData,
2769 'jsInline' => &$this->jsInline,
2770 'jsFooterInline' => &$this->jsFooterInline,
2771 'cssInline' => &$this->cssInline
2772 );
2773 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postTransform'] as $hook) {
2774 GeneralUtility::callUserFunction($hook, $params, $this);
2775 }
2776 }
2777 }
2778
2779 /**
2780 * Execute postRenderHook for possible manipulation
2781 *
2782 * @param $jsLibs string
2783 * @param $jsFiles string
2784 * @param $jsFooterFiles string
2785 * @param $cssLibs string
2786 * @param $cssFiles string
2787 * @param $jsInline string
2788 * @param $cssInline string
2789 * @param $jsFooterInline string
2790 * @param $jsFooterLibs string
2791 * @return void
2792 */
2793 protected function executePostRenderHook(&$jsLibs, &$jsFiles, &$jsFooterFiles, &$cssLibs, &$cssFiles, &$jsInline, &$cssInline, &$jsFooterInline, &$jsFooterLibs)
2794 {
2795 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'])) {
2796 $params = array(
2797 'jsLibs' => &$jsLibs,
2798 'jsFiles' => &$jsFiles,
2799 'jsFooterFiles' => &$jsFooterFiles,
2800 'cssLibs' => &$cssLibs,
2801 'cssFiles' => &$cssFiles,
2802 'headerData' => &$this->headerData,
2803 'footerData' => &$this->footerData,
2804 'jsInline' => &$jsInline,
2805 'cssInline' => &$cssInline,
2806 'xmlPrologAndDocType' => &$this->xmlPrologAndDocType,
2807 'htmlTag' => &$this->htmlTag,
2808 'headTag' => &$this->headTag,
2809 'charSet' => &$this->charSet,
2810 'metaCharsetTag' => &$this->metaCharsetTag,
2811 'shortcutTag' => &$this->shortcutTag,
2812 'inlineComments' => &$this->inlineComments,
2813 'baseUrl' => &$this->baseUrl,
2814 'baseUrlTag' => &$this->baseUrlTag,
2815 'favIcon' => &$this->favIcon,
2816 'iconMimeType' => &$this->iconMimeType,
2817 'titleTag' => &$this->titleTag,
2818 'title' => &$this->title,
2819 'metaTags' => &$this->metaTags,
2820 'jsFooterInline' => &$jsFooterInline,
2821 'jsFooterLibs' => &$jsFooterLibs,
2822 'bodyContent' => &$this->bodyContent
2823 );
2824 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'] as $hook) {
2825 GeneralUtility::callUserFunction($hook, $params, $this);
2826 }
2827 }
2828 }
2829 }