TYPO3  7.6
PageRenderer.php
Go to the documentation of this file.
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 
24 
30 {
31  // Constants for the part to be rendered
32  const PART_COMPLETE = 0;
33  const PART_HEADER = 1;
34  const PART_FOOTER = 2;
35  // jQuery Core version that is shipped with TYPO3
36  const JQUERY_VERSION_LATEST = '2.1.4';
37  // jQuery namespace options
38  const JQUERY_NAMESPACE_NONE = 'none';
39  const JQUERY_NAMESPACE_DEFAULT = 'jQuery';
40  const JQUERY_NAMESPACE_DEFAULT_NOCONFLICT = 'defaultNoConflict';
41 
45  protected $compressJavascript = false;
46 
50  protected $compressCss = false;
51 
55  protected $removeLineBreaksFromTemplate = false;
56 
60  protected $concatenateFiles = false;
61 
65  protected $concatenateJavascript = false;
66 
70  protected $concatenateCss = false;
71 
75  protected $moveJsFromHeaderToFooter = false;
76 
80  protected $csConvObj;
81 
85  protected $locales;
86 
93  protected $lang;
94 
101  protected $languageDependencies = array();
102 
106  protected $compressor;
107 
108  // Arrays containing associative array for the included files
112  protected $jsFiles = array();
113 
117  protected $jsFooterFiles = array();
118 
122  protected $jsLibs = array();
123 
127  protected $jsFooterLibs = array();
128 
132  protected $cssFiles = array();
133 
137  protected $cssLibs = array();
138 
144  protected $title;
145 
151  protected $charSet;
152 
156  protected $favIcon;
157 
161  protected $baseUrl;
162 
166  protected $renderXhtml = true;
167 
168  // Static header blocks
172  protected $xmlPrologAndDocType = '';
173 
177  protected $metaTags = array();
178 
182  protected $inlineComments = array();
183 
187  protected $headerData = array();
188 
192  protected $footerData = array();
193 
197  protected $titleTag = '<title>|</title>';
198 
202  protected $metaCharsetTag = '<meta http-equiv="Content-Type" content="text/html; charset=|" />';
203 
207  protected $htmlTag = '<html>';
208 
212  protected $headTag = '<head>';
213 
217  protected $baseUrlTag = '<base href="|" />';
218 
222  protected $iconMimeType = '';
223 
227  protected $shortcutTag = '<link rel="shortcut icon" href="%1$s"%2$s />';
228 
229  // Static inline code blocks
233  protected $jsInline = array();
234 
238  protected $jsFooterInline = array();
239 
243  protected $extOnReadyCode = array();
244 
248  protected $cssInline = array();
249 
253  protected $bodyContent;
254 
258  protected $templateFile;
259 
263  protected $jsLibraryNames = array('extjs');
264 
265  // Paths to contibuted libraries
266 
271  protected $requireJsPath = 'sysext/core/Resources/Public/JavaScript/Contrib/';
272 
276  protected $extJsPath = 'sysext/core/Resources/Public/JavaScript/Contrib/extjs/';
277 
283  protected $jQueryPath = 'sysext/core/Resources/Public/JavaScript/Contrib/jquery/';
284 
285  // Internal flags for JS-libraries
303  protected $jQueryVersions = array();
304 
310  protected $availableLocalJqueryVersions = array(
311  self::JQUERY_VERSION_LATEST
312  );
313 
319  protected $jQueryCdnUrls = array(
320  'google' => 'https://ajax.googleapis.com/ajax/libs/jquery/%1$s/jquery%2$s.js',
321  'msn' => 'https://ajax.aspnetcdn.com/ajax/jQuery/jquery-%1$s%2$s.js',
322  'jquery' => 'https://code.jquery.com/jquery-%1$s%2$s.js',
323  'cloudflare' => 'https://cdnjs.cloudflare.com/ajax/libs/jquery/%1$s/jquery%2$s.js'
324  );
325 
330  protected $addRequireJs = false;
331 
336  protected $requireJsConfig = array();
337 
341  protected $addExtJS = false;
342 
346  protected $extDirectCodeAdded = false;
347 
351  protected $enableExtJsDebug = false;
352 
356  protected $enableJqueryDebug = false;
357 
361  protected $extJStheme = true;
362 
366  protected $extJScss = true;
367 
371  protected $inlineLanguageLabels = array();
372 
376  protected $inlineLanguageLabelFiles = array();
377 
381  protected $inlineSettings = array();
382 
386  protected $inlineJavascriptWrap = array();
387 
393  protected $compressError = '';
394 
400  protected $endingSlash = '';
401 
407  public $backPath;
408 
413  public function __construct($templateFile = '', $backPath = null)
414  {
415  $this->reset();
416  $this->csConvObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Charset\CharsetConverter::class);
417  $this->locales = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\Locales::class);
418  if ($templateFile !== '') {
419  $this->templateFile = $templateFile;
420  }
421  $this->backPath = isset($backPath) ? $backPath : $GLOBALS['BACK_PATH'];
422  $this->inlineJavascriptWrap = array(
423  '<script type="text/javascript">' . LF . '/*<![CDATA[*/' . LF,
424  '/*]]>*/' . LF . '</script>' . LF
425  );
426  $this->inlineCssWrap = array(
427  '<style type="text/css">' . LF . '/*<![CDATA[*/' . LF . '<!-- ' . LF,
428  '-->' . LF . '/*]]>*/' . LF . '</style>' . LF
429  );
430  }
431 
437  protected function reset()
438  {
439  $this->templateFile = 'EXT:core/Resources/Private/Templates/PageRenderer.html';
440  $this->jsFiles = array();
441  $this->jsFooterFiles = array();
442  $this->jsInline = array();
443  $this->jsFooterInline = array();
444  $this->jsLibs = array();
445  $this->cssFiles = array();
446  $this->cssInline = array();
447  $this->metaTags = array();
448  $this->inlineComments = array();
449  $this->headerData = array();
450  $this->footerData = array();
451  $this->extOnReadyCode = array();
452  $this->jQueryVersions = array();
453  }
454 
455  /*****************************************************/
456  /* */
457  /* Public Setters */
458  /* */
459  /* */
460  /*****************************************************/
467  public function setTitle($title)
468  {
469  $this->title = $title;
470  }
471 
478  public function setRenderXhtml($enable)
479  {
480  $this->renderXhtml = $enable;
481  }
482 
490  {
491  $this->xmlPrologAndDocType = $xmlPrologAndDocType;
492  }
493 
500  public function setCharSet($charSet)
501  {
502  $this->charSet = $charSet;
503  }
504 
511  public function setLanguage($lang)
512  {
513  $this->lang = $lang;
514  $this->languageDependencies = array();
515 
516  // Language is found. Configure it:
517  if (in_array($this->lang, $this->locales->getLocales())) {
518  $this->languageDependencies[] = $this->lang;
519  foreach ($this->locales->getLocaleDependencies($this->lang) as $language) {
520  $this->languageDependencies[] = $language;
521  }
522  }
523  }
524 
532  {
533  $this->metaCharsetTag = $metaCharsetTag;
534  }
535 
542  public function setHtmlTag($htmlTag)
543  {
544  $this->htmlTag = $htmlTag;
545  }
546 
553  public function setHeadTag($headTag)
554  {
555  $this->headTag = $headTag;
556  }
557 
564  public function setFavIcon($favIcon)
565  {
566  $this->favIcon = $favIcon;
567  }
568 
576  {
577  $this->iconMimeType = $iconMimeType;
578  }
579 
586  public function setBaseUrl($baseUrl)
587  {
588  $this->baseUrl = $baseUrl;
589  }
590 
597  public function setTemplateFile($file)
598  {
599  $this->templateFile = $file;
600  }
601 
608  public function setBackPath($backPath)
609  {
610  $this->backPath = $backPath;
611  }
612 
619  public function setBodyContent($content)
620  {
621  $this->bodyContent = $content;
622  }
623 
630  public function setRequireJsPath($path)
631  {
632  $this->requireJsPath = $path;
633  }
634 
641  public function setExtJsPath($path)
642  {
643  $this->extJsPath = $path;
644  }
645 
646  /*****************************************************/
647  /* */
648  /* Public Enablers / Disablers */
649  /* */
650  /* */
651  /*****************************************************/
658  {
659  $this->moveJsFromHeaderToFooter = true;
660  }
661 
668  {
669  $this->moveJsFromHeaderToFooter = false;
670  }
671 
677  public function enableCompressJavascript()
678  {
679  $this->compressJavascript = true;
680  }
681 
687  public function disableCompressJavascript()
688  {
689  $this->compressJavascript = false;
690  }
691 
697  public function enableCompressCss()
698  {
699  $this->compressCss = true;
700  }
701 
707  public function disableCompressCss()
708  {
709  $this->compressCss = false;
710  }
711 
717  public function enableConcatenateFiles()
718  {
719  $this->concatenateFiles = true;
720  }
721 
727  public function disableConcatenateFiles()
728  {
729  $this->concatenateFiles = false;
730  }
731 
737  public function enableConcatenateJavascript()
738  {
739  $this->concatenateJavascript = true;
740  }
741 
748  {
749  $this->concatenateJavascript = false;
750  }
751 
757  public function enableConcatenateCss()
758  {
759  $this->concatenateCss = true;
760  }
761 
767  public function disableConcatenateCss()
768  {
769  $this->concatenateCss = false;
770  }
771 
778  {
779  $this->removeLineBreaksFromTemplate = true;
780  }
781 
788  {
789  $this->removeLineBreaksFromTemplate = false;
790  }
791 
798  public function enableDebugMode()
799  {
800  $this->compressJavascript = false;
801  $this->compressCss = false;
802  $this->concatenateFiles = false;
803  $this->removeLineBreaksFromTemplate = false;
804  $this->enableExtJsDebug = true;
805  $this->enableJqueryDebug = true;
806  }
807 
808  /*****************************************************/
809  /* */
810  /* Public Getters */
811  /* */
812  /* */
813  /*****************************************************/
819  public function getTitle()
820  {
821  return $this->title;
822  }
823 
829  public function getCharSet()
830  {
831  return $this->charSet;
832  }
833 
839  public function getLanguage()
840  {
841  return $this->lang;
842  }
843 
849  public function getRenderXhtml()
850  {
851  return $this->renderXhtml;
852  }
853 
859  public function getHtmlTag()
860  {
861  return $this->htmlTag;
862  }
863 
869  public function getMetaCharsetTag()
870  {
871  return $this->metaCharsetTag;
872  }
873 
879  public function getHeadTag()
880  {
881  return $this->headTag;
882  }
883 
889  public function getFavIcon()
890  {
891  return $this->favIcon;
892  }
893 
899  public function getIconMimeType()
900  {
901  return $this->iconMimeType;
902  }
903 
909  public function getBaseUrl()
910  {
911  return $this->baseUrl;
912  }
913 
919  public function getTemplateFile()
920  {
921  return $this->templateFile;
922  }
923 
929  public function getMoveJsFromHeaderToFooter()
930  {
932  }
933 
939  public function getCompressJavascript()
940  {
942  }
943 
949  public function getCompressCss()
950  {
951  return $this->compressCss;
952  }
953 
959  public function getConcatenateFiles()
960  {
962  }
963 
969  public function getConcatenateJavascript()
970  {
972  }
973 
979  public function getConcatenateCss()
980  {
981  return $this->concatenateCss;
982  }
983 
990  {
992  }
993 
999  public function getBodyContent()
1000  {
1001  return $this->bodyContent;
1002  }
1003 
1009  public function getExtJsPath()
1010  {
1011  return $this->extJsPath;
1012  }
1013 
1019  public function getInlineLanguageLabels()
1020  {
1022  }
1023 
1030  {
1032  }
1033 
1034  /*****************************************************/
1035  /* */
1036  /* Public Functions to add Data */
1037  /* */
1038  /* */
1039  /*****************************************************/
1046  public function addMetaTag($meta)
1047  {
1048  if (!in_array($meta, $this->metaTags)) {
1049  $this->metaTags[] = $meta;
1050  }
1051  }
1052 
1059  public function addInlineComment($comment)
1060  {
1061  if (!in_array($comment, $this->inlineComments)) {
1062  $this->inlineComments[] = $comment;
1063  }
1064  }
1065 
1072  public function addHeaderData($data)
1073  {
1074  if (!in_array($data, $this->headerData)) {
1075  $this->headerData[] = $data;
1076  }
1077  }
1078 
1085  public function addFooterData($data)
1086  {
1087  if (!in_array($data, $this->footerData)) {
1088  $this->footerData[] = $data;
1089  }
1090  }
1091 
1107  public function addJsLibrary($name, $file, $type = 'text/javascript', $compress = false, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1108  {
1109  if (!$type) {
1110  $type = 'text/javascript';
1111  }
1112  if (!in_array(strtolower($name), $this->jsLibs)) {
1113  $this->jsLibs[strtolower($name)] = array(
1114  'file' => $file,
1115  'type' => $type,
1116  'section' => self::PART_HEADER,
1117  'compress' => $compress,
1118  'forceOnTop' => $forceOnTop,
1119  'allWrap' => $allWrap,
1120  'excludeFromConcatenation' => $excludeFromConcatenation,
1121  'splitChar' => $splitChar,
1122  'async' => $async,
1123  'integrity' => $integrity,
1124  );
1125  }
1126  }
1127 
1143  public function addJsFooterLibrary($name, $file, $type = 'text/javascript', $compress = false, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1144  {
1145  if (!$type) {
1146  $type = 'text/javascript';
1147  }
1148  if (!in_array(strtolower($name), $this->jsLibs)) {
1149  $this->jsLibs[strtolower($name)] = array(
1150  'file' => $file,
1151  'type' => $type,
1152  'section' => self::PART_FOOTER,
1153  'compress' => $compress,
1154  'forceOnTop' => $forceOnTop,
1155  'allWrap' => $allWrap,
1156  'excludeFromConcatenation' => $excludeFromConcatenation,
1157  'splitChar' => $splitChar,
1158  'async' => $async,
1159  'integrity' => $integrity,
1160  );
1161  }
1162  }
1163 
1178  public function addJsFile($file, $type = 'text/javascript', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1179  {
1180  if (!$type) {
1181  $type = 'text/javascript';
1182  }
1183  if (!isset($this->jsFiles[$file])) {
1184  $this->jsFiles[$file] = array(
1185  'file' => $file,
1186  'type' => $type,
1187  'section' => self::PART_HEADER,
1188  'compress' => $compress,
1189  'forceOnTop' => $forceOnTop,
1190  'allWrap' => $allWrap,
1191  'excludeFromConcatenation' => $excludeFromConcatenation,
1192  'splitChar' => $splitChar,
1193  'async' => $async,
1194  'integrity' => $integrity,
1195  );
1196  }
1197  }
1198 
1213  public function addJsFooterFile($file, $type = 'text/javascript', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1214  {
1215  if (!$type) {
1216  $type = 'text/javascript';
1217  }
1218  if (!isset($this->jsFiles[$file])) {
1219  $this->jsFiles[$file] = array(
1220  'file' => $file,
1221  'type' => $type,
1222  'section' => self::PART_FOOTER,
1223  'compress' => $compress,
1224  'forceOnTop' => $forceOnTop,
1225  'allWrap' => $allWrap,
1226  'excludeFromConcatenation' => $excludeFromConcatenation,
1227  'splitChar' => $splitChar,
1228  'async' => $async,
1229  'integrity' => $integrity,
1230  );
1231  }
1232  }
1233 
1243  public function addJsInlineCode($name, $block, $compress = true, $forceOnTop = false)
1244  {
1245  if (!isset($this->jsInline[$name]) && !empty($block)) {
1246  $this->jsInline[$name] = array(
1247  'code' => $block . LF,
1248  'section' => self::PART_HEADER,
1249  'compress' => $compress,
1250  'forceOnTop' => $forceOnTop
1251  );
1252  }
1253  }
1254 
1264  public function addJsFooterInlineCode($name, $block, $compress = true, $forceOnTop = false)
1265  {
1266  if (!isset($this->jsInline[$name]) && !empty($block)) {
1267  $this->jsInline[$name] = array(
1268  'code' => $block . LF,
1269  'section' => self::PART_FOOTER,
1270  'compress' => $compress,
1271  'forceOnTop' => $forceOnTop
1272  );
1273  }
1274  }
1275 
1283  public function addExtOnReadyCode($block, $forceOnTop = false)
1284  {
1285  if (!in_array($block, $this->extOnReadyCode)) {
1286  if ($forceOnTop) {
1287  array_unshift($this->extOnReadyCode, $block);
1288  } else {
1289  $this->extOnReadyCode[] = $block;
1290  }
1291  }
1292  }
1293 
1300  public function addExtDirectCode(array $filterNamespaces = array())
1301  {
1302  if ($this->extDirectCodeAdded) {
1303  return;
1304  }
1305  $this->extDirectCodeAdded = true;
1306  if (empty($filterNamespaces)) {
1307  $filterNamespaces = array('TYPO3');
1308  }
1309  // @deprecated since TYPO3 CMS 7, will be removed in TYPO3 CMS 8
1310  // add compatibility mapping for the old flashmessage API
1311  $this->addJsFile(GeneralUtility::resolveBackPath($this->backPath .
1312  'sysext/backend/Resources/Public/JavaScript/flashmessage_compatibility.js'));
1313 
1314  // Add language labels for ExtDirect
1315  if (TYPO3_MODE === 'FE') {
1316  $this->addInlineLanguageLabelArray(array(
1317  'extDirect_timeoutHeader' => $this->getTypoScriptFrontendController()->sL('LLL:EXT:lang/locallang_misc.xlf:extDirect_timeoutHeader'),
1318  'extDirect_timeoutMessage' => $this->getTypoScriptFrontendController()->sL('LLL:EXT:lang/locallang_misc.xlf:extDirect_timeoutMessage')
1319  ));
1320  } else {
1321  $this->addInlineLanguageLabelArray(array(
1322  'extDirect_timeoutHeader' => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_misc.xlf:extDirect_timeoutHeader'),
1323  'extDirect_timeoutMessage' => $this->getLanguageService()->sL('LLL:EXT:lang/locallang_misc.xlf:extDirect_timeoutMessage')
1324  ));
1325  }
1326 
1327  $token = ($api = '');
1328  if (TYPO3_MODE === 'BE') {
1329  $formprotection = \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get();
1330  $token = $formprotection->generateToken('extDirect');
1331 
1332  // Debugger Console strings
1333  $this->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/debugger.xlf');
1334  }
1336  $extDirect = GeneralUtility::makeInstance(\TYPO3\CMS\Core\ExtDirect\ExtDirectApi::class);
1337  $api = $extDirect->getApiPhp($filterNamespaces);
1338  if ($api) {
1339  $this->addJsInlineCode('TYPO3ExtDirectAPI', $api, false);
1340  }
1341  // Note: we need to iterate thru the object, because the addProvider method
1342  // does this only with multiple arguments
1343  $this->addExtOnReadyCode('
1344  (function() {
1345  TYPO3.ExtDirectToken = "' . $token . '";
1346  for (var api in Ext.app.ExtDirectAPI) {
1347  var provider = Ext.Direct.addProvider(Ext.app.ExtDirectAPI[api]);
1348  provider.on("beforecall", function(provider, transaction, meta) {
1349  if (transaction.data) {
1350  transaction.data[transaction.data.length] = TYPO3.ExtDirectToken;
1351  } else {
1352  transaction.data = [TYPO3.ExtDirectToken];
1353  }
1354  });
1355 
1356  provider.on("call", function(provider, transaction, meta) {
1357  if (transaction.isForm) {
1358  transaction.params.securityToken = TYPO3.ExtDirectToken;
1359  }
1360  });
1361  }
1362  })();
1363 
1364  var extDirectDebug = function(message, header, group) {
1365  var DebugConsole = null;
1366 
1367  if (top && top.TYPO3 && typeof top.TYPO3.DebugConsole === "object") {
1368  DebugConsole = top.TYPO3.DebugConsole;
1369  } else if (typeof TYPO3 === "object" && typeof TYPO3.DebugConsole === "object") {
1370  DebugConsole = TYPO3.DebugConsole;
1371  }
1372 
1373  if (DebugConsole !== null) {
1374  DebugConsole.add(message, header, group);
1375  } else if (typeof console === "object") {
1376  console.log(message);
1377  } else {
1378  document.write(message);
1379  }
1380  };
1381 
1382  Ext.Direct.on("exception", function(event) {
1383  if (event.code === Ext.Direct.exceptions.TRANSPORT && !event.where) {
1384  top.TYPO3.Notification.error(
1385  TYPO3.l10n.localize("extDirect_timeoutHeader"),
1386  TYPO3.l10n.localize("extDirect_timeoutMessage")
1387  );
1388  } else {
1389  var backtrace = "";
1390  if (event.code === "parse") {
1391  extDirectDebug(
1392  "<p>" + event.xhr.responseText + "<\\/p>",
1393  event.type,
1394  "ExtDirect - Exception"
1395  );
1396  } else if (event.code === "router") {
1397  top.TYPO3.Notification.error(
1398  event.code,
1399  event.message
1400  );
1401  } else if (event.where) {
1402  backtrace = "<p style=\\"margin-top: 20px;\\">" +
1403  "<strong>Backtrace:<\\/strong><br \\/>" +
1404  event.where.replace(/#/g, "<br \\/>#") +
1405  "<\\/p>";
1406  extDirectDebug(
1407  "<p>" + event.message + "<\\/p>" + backtrace,
1408  event.method,
1409  "ExtDirect - Exception"
1410  );
1411  }
1412 
1413 
1414  }
1415  });
1416 
1417  Ext.Direct.on("event", function(event, provider) {
1418  if (typeof event.debug !== "undefined" && event.debug !== "") {
1419  extDirectDebug(event.debug, event.method, "ExtDirect - Debug");
1420  }
1421  });
1422  ', true);
1423  }
1424 
1439  public function addCssFile($file, $rel = 'stylesheet', $media = 'all', $title = '', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|')
1440  {
1441  if (!isset($this->cssFiles[$file])) {
1442  $this->cssFiles[$file] = array(
1443  'file' => $file,
1444  'rel' => $rel,
1445  'media' => $media,
1446  'title' => $title,
1447  'compress' => $compress,
1448  'forceOnTop' => $forceOnTop,
1449  'allWrap' => $allWrap,
1450  'excludeFromConcatenation' => $excludeFromConcatenation,
1451  'splitChar' => $splitChar
1452  );
1453  }
1454  }
1455 
1470  public function addCssLibrary($file, $rel = 'stylesheet', $media = 'all', $title = '', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|')
1471  {
1472  if (!isset($this->cssLibs[$file])) {
1473  $this->cssLibs[$file] = array(
1474  'file' => $file,
1475  'rel' => $rel,
1476  'media' => $media,
1477  'title' => $title,
1478  'compress' => $compress,
1479  'forceOnTop' => $forceOnTop,
1480  'allWrap' => $allWrap,
1481  'excludeFromConcatenation' => $excludeFromConcatenation,
1482  'splitChar' => $splitChar
1483  );
1484  }
1485  }
1486 
1496  public function addCssInlineBlock($name, $block, $compress = false, $forceOnTop = false)
1497  {
1498  if (!isset($this->cssInline[$name]) && !empty($block)) {
1499  $this->cssInline[$name] = array(
1500  'code' => $block,
1501  'compress' => $compress,
1502  'forceOnTop' => $forceOnTop
1503  );
1504  }
1505  }
1506 
1516  public function loadJquery($version = null, $source = null, $namespace = self::JQUERY_NAMESPACE_DEFAULT)
1517  {
1518  // Set it to the version that is shipped with the TYPO3 core
1519  if ($version === null || $version === 'latest') {
1520  $version = self::JQUERY_VERSION_LATEST;
1521  }
1522  // Check if the source is set, otherwise set it to "default"
1523  if ($source === null) {
1524  $source = 'local';
1525  }
1526  if ($source === 'local' && !in_array($version, $this->availableLocalJqueryVersions)) {
1527  throw new \UnexpectedValueException('The requested jQuery version is not available in the local filesystem.', 1341505305);
1528  }
1529  if (!preg_match('/^[a-zA-Z0-9]+$/', $namespace)) {
1530  throw new \UnexpectedValueException('The requested namespace contains non alphanumeric characters.', 1341571604);
1531  }
1532  $this->jQueryVersions[$namespace] = array(
1533  'version' => $version,
1534  'source' => $source
1535  );
1536  }
1537 
1546  public function loadRequireJs()
1547  {
1548 
1549  // load all paths to map to package names / namespaces
1550  if (empty($this->requireJsConfig)) {
1551  // In order to avoid browser caching of JS files, adding a GET parameter to the files loaded via requireJS
1552  if (GeneralUtility::getApplicationContext()->isDevelopment()) {
1553  $this->requireJsConfig['urlArgs'] = 'bust=' . $GLOBALS['EXEC_TIME'];
1554  } else {
1555  $this->requireJsConfig['urlArgs'] = 'bust=' . GeneralUtility::hmac(TYPO3_version . PATH_site);
1556  }
1557  // first, load all paths for the namespaces, and configure contrib libs.
1558  $this->requireJsConfig['paths'] = array(
1559  'jquery-ui' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/jquery-ui',
1560  'datatables' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/jquery.dataTables',
1561  'nprogress' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/nprogress',
1562  'moment' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/moment',
1563  'cropper' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/cropper.min',
1564  'imagesloaded' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/imagesloaded.pkgd.min',
1565  'bootstrap' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/bootstrap/bootstrap',
1566  'twbs/bootstrap-datetimepicker' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/bootstrap-datetimepicker',
1567  'autosize' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/autosize',
1568  'taboverride' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/taboverride.min',
1569  'twbs/bootstrap-slider' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/bootstrap-slider.min',
1570  'jquery/autocomplete' => $this->backPath . 'sysext/core/Resources/Public/JavaScript/Contrib/jquery.autocomplete',
1571  );
1572  // get all extensions that are loaded
1573  $loadedExtensions = \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::getLoadedExtensionListArray();
1574  foreach ($loadedExtensions as $packageName) {
1575  $fullJsPath = 'EXT:' . $packageName . '/Resources/Public/JavaScript/';
1576  $fullJsPath = GeneralUtility::getFileAbsFileName($fullJsPath);
1577  $fullJsPath = \TYPO3\CMS\Core\Utility\PathUtility::getRelativePath(PATH_typo3, $fullJsPath);
1578  $fullJsPath = rtrim($fullJsPath, '/');
1579  if ($fullJsPath) {
1580  $this->requireJsConfig['paths']['TYPO3/CMS/' . GeneralUtility::underscoredToUpperCamelCase($packageName)] = $this->backPath . $fullJsPath;
1581  }
1582  }
1583 
1584  // check if additional AMD modules need to be loaded if a single AMD module is initialized
1585  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RequireJS']['postInitializationModules'])) {
1586  $this->addInlineSettingArray('RequireJS.PostInitializationModules', $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RequireJS']['postInitializationModules']);
1587  }
1588  }
1589 
1590  $this->addRequireJs = true;
1591  }
1592 
1606  public function addRequireJsConfiguration(array $configuration)
1607  {
1608  \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($this->requireJsConfig, $configuration);
1609  }
1610 
1628  public function loadRequireJsModule($mainModuleName, $callBackFunction = null)
1629  {
1630  $inlineCodeKey = $mainModuleName;
1631  // make sure requireJS is initialized
1632  $this->loadRequireJs();
1633 
1634  // execute the main module, and load a possible callback function
1635  $javaScriptCode = 'require(["' . $mainModuleName . '"]';
1636  if ($callBackFunction !== null) {
1637  $inlineCodeKey .= sha1($callBackFunction);
1638  $javaScriptCode .= ', ' . $callBackFunction;
1639  }
1640  $javaScriptCode .= ');';
1641  $this->addJsInlineCode('RequireJS-Module-' . $inlineCodeKey, $javaScriptCode);
1642  }
1643 
1644 
1652  public function loadExtJS($css = true, $theme = true)
1653  {
1654  $this->addExtJS = true;
1655  $this->extJStheme = $theme;
1656  $this->extJScss = $css;
1657  }
1658 
1664  public function enableExtJsDebug()
1665  {
1666  $this->enableExtJsDebug = true;
1667  }
1668 
1678  public function addInlineLanguageLabel($key, $value)
1679  {
1680  $this->inlineLanguageLabels[$key] = $value;
1681  }
1682 
1693  public function addInlineLanguageLabelArray(array $array, $parseWithLanguageService = false)
1694  {
1695  if ($parseWithLanguageService === true) {
1696  foreach ($array as $key => $value) {
1697  if (TYPO3_MODE === 'FE') {
1698  $array[$key] = $this->getTypoScriptFrontendController()->sL($value);
1699  } else {
1700  $array[$key] = $this->getLanguageService()->sL($value);
1701  }
1702  }
1703  }
1704 
1705  $this->inlineLanguageLabels = array_merge($this->inlineLanguageLabels, $array);
1706  }
1707 
1717  public function addInlineLanguageLabelFile($fileRef, $selectionPrefix = '', $stripFromSelectionName = '', $errorMode = 0)
1718  {
1719  $index = md5($fileRef . $selectionPrefix . $stripFromSelectionName);
1720  if ($fileRef && !isset($this->inlineLanguageLabelFiles[$index])) {
1721  $this->inlineLanguageLabelFiles[$index] = array(
1722  'fileRef' => $fileRef,
1723  'selectionPrefix' => $selectionPrefix,
1724  'stripFromSelectionName' => $stripFromSelectionName,
1725  'errorMode' => $errorMode
1726  );
1727  }
1728  }
1729 
1740  public function addInlineSetting($namespace, $key, $value)
1741  {
1742  if ($namespace) {
1743  if (strpos($namespace, '.')) {
1744  $parts = explode('.', $namespace);
1745  $a = &$this->inlineSettings;
1746  foreach ($parts as $part) {
1747  $a = &$a[$part];
1748  }
1749  $a[$key] = $value;
1750  } else {
1751  $this->inlineSettings[$namespace][$key] = $value;
1752  }
1753  } else {
1754  $this->inlineSettings[$key] = $value;
1755  }
1756  }
1757 
1768  public function addInlineSettingArray($namespace, array $array)
1769  {
1770  if ($namespace) {
1771  if (strpos($namespace, '.')) {
1772  $parts = explode('.', $namespace);
1773  $a = &$this->inlineSettings;
1774  foreach ($parts as $part) {
1775  $a = &$a[$part];
1776  }
1777  $a = array_merge((array)$a, $array);
1778  } else {
1779  $this->inlineSettings[$namespace] = array_merge((array)$this->inlineSettings[$namespace], $array);
1780  }
1781  } else {
1782  $this->inlineSettings = array_merge($this->inlineSettings, $array);
1783  }
1784  }
1785 
1792  public function addBodyContent($content)
1793  {
1794  $this->bodyContent .= $content;
1795  }
1796 
1797  /*****************************************************/
1798  /* */
1799  /* Render Functions */
1800  /* */
1801  /*****************************************************/
1808  public function render($part = self::PART_COMPLETE)
1809  {
1810  $this->prepareRendering();
1812  $metaTags = implode(LF, $this->metaTags);
1814  $template = $this->getTemplateForPart($part);
1815 
1816  // The page renderer needs a full reset, even when only rendering one part of the page
1817  // This means that you can only register footer files *after* the header has been already rendered.
1818  // In case you render the footer part first, header files can only be added *after* the footer has been rendered
1819  $this->reset();
1820  $templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1821  return trim($templateService->substituteMarkerArray($template, $markerArray, '###|###'));
1822  }
1823 
1831  public function renderPageWithUncachedObjects($substituteHash)
1832  {
1833  $this->prepareRendering();
1834  $markerArray = $this->getPreparedMarkerArrayForPageWithUncachedObjects($substituteHash);
1835  $template = $this->getTemplateForPart(self::PART_COMPLETE);
1836  $templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1837  return trim($templateService->substituteMarkerArray($template, $markerArray, '###|###'));
1838  }
1839 
1849  public function renderJavaScriptAndCssForProcessingOfUncachedContentObjects($cachedPageContent, $substituteHash)
1850  {
1851  $this->prepareRendering();
1853  $title = $this->title ? str_replace('|', htmlspecialchars($this->title), $this->titleTag) : '';
1854  $markerArray = array(
1855  '<!-- ###TITLE' . $substituteHash . '### -->' => $title,
1856  '<!-- ###CSS_LIBS' . $substituteHash . '### -->' => $cssLibs,
1857  '<!-- ###CSS_INCLUDE' . $substituteHash . '### -->' => $cssFiles,
1858  '<!-- ###CSS_INLINE' . $substituteHash . '### -->' => $cssInline,
1859  '<!-- ###JS_INLINE' . $substituteHash . '### -->' => $jsInline,
1860  '<!-- ###JS_INCLUDE' . $substituteHash . '### -->' => $jsFiles,
1861  '<!-- ###JS_LIBS' . $substituteHash . '### -->' => $jsLibs,
1862  '<!-- ###HEADERDATA' . $substituteHash . '### -->' => implode(LF, $this->headerData),
1863  '<!-- ###FOOTERDATA' . $substituteHash . '### -->' => implode(LF, $this->footerData),
1864  '<!-- ###JS_LIBS_FOOTER' . $substituteHash . '### -->' => $jsFooterLibs,
1865  '<!-- ###JS_INCLUDE_FOOTER' . $substituteHash . '### -->' => $jsFooterFiles,
1866  '<!-- ###JS_INLINE_FOOTER' . $substituteHash . '### -->' => $jsFooterInline
1867  );
1868  foreach ($markerArray as $placeHolder => $content) {
1869  $cachedPageContent = str_replace($placeHolder, $content, $cachedPageContent);
1870  }
1871  $this->reset();
1872  return $cachedPageContent;
1873  }
1874 
1882  protected function prepareRendering()
1883  {
1884  if ($this->getRenderXhtml()) {
1885  $this->endingSlash = ' /';
1886  } else {
1887  $this->metaCharsetTag = str_replace(' />', '>', $this->metaCharsetTag);
1888  $this->baseUrlTag = str_replace(' />', '>', $this->baseUrlTag);
1889  $this->shortcutTag = str_replace(' />', '>', $this->shortcutTag);
1890  $this->endingSlash = '';
1891  }
1892  }
1893 
1899  protected function renderJavaScriptAndCss()
1900  {
1901  $this->executePreRenderHook();
1902  $mainJsLibs = $this->renderMainJavaScriptLibraries();
1903  if ($this->concatenateFiles || $this->concatenateJavascript || $this->concatenateCss) {
1904  // Do the file concatenation
1905  $this->doConcatenate();
1906  }
1907  if ($this->compressCss || $this->compressJavascript) {
1908  // Do the file compression
1909  $this->doCompress();
1910  }
1912  $cssLibs = $this->renderCssLibraries();
1913  $cssFiles = $this->renderCssFiles();
1914  $cssInline = $this->renderCssInline();
1916  list($jsFiles, $jsFooterFiles) = $this->renderJavaScriptFiles();
1918  $jsLibs = $mainJsLibs . $jsLibs;
1919  if ($this->moveJsFromHeaderToFooter) {
1921  $jsLibs = '';
1923  $jsFiles = '';
1925  $jsInline = '';
1926  }
1929  }
1930 
1947  {
1948  $markerArray = array(
1949  'XMLPROLOG_DOCTYPE' => $this->xmlPrologAndDocType,
1950  'HTMLTAG' => $this->htmlTag,
1951  'HEADTAG' => $this->headTag,
1952  'METACHARSET' => $this->charSet ? str_replace('|', htmlspecialchars($this->charSet), $this->metaCharsetTag) : '',
1953  'INLINECOMMENT' => $this->inlineComments ? LF . LF . '<!-- ' . LF . implode(LF, $this->inlineComments) . '-->' . LF . LF : '',
1954  'BASEURL' => $this->baseUrl ? str_replace('|', $this->baseUrl, $this->baseUrlTag) : '',
1955  'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) : '',
1956  'CSS_LIBS' => $cssLibs,
1957  'CSS_INCLUDE' => $cssFiles,
1958  'CSS_INLINE' => $cssInline,
1959  'JS_INLINE' => $jsInline,
1960  'JS_INCLUDE' => $jsFiles,
1961  'JS_LIBS' => $jsLibs,
1962  'TITLE' => $this->title ? str_replace('|', htmlspecialchars($this->title), $this->titleTag) : '',
1963  'META' => $metaTags,
1964  'HEADERDATA' => $this->headerData ? implode(LF, $this->headerData) : '',
1965  'FOOTERDATA' => $this->footerData ? implode(LF, $this->footerData) : '',
1966  'JS_LIBS_FOOTER' => $jsFooterLibs,
1967  'JS_INCLUDE_FOOTER' => $jsFooterFiles,
1968  'JS_INLINE_FOOTER' => $jsFooterInline,
1969  'BODY' => $this->bodyContent
1970  );
1971  $markerArray = array_map('trim', $markerArray);
1972  return $markerArray;
1973  }
1974 
1981  protected function getPreparedMarkerArrayForPageWithUncachedObjects($substituteHash)
1982  {
1983  $markerArray = array(
1984  'XMLPROLOG_DOCTYPE' => $this->xmlPrologAndDocType,
1985  'HTMLTAG' => $this->htmlTag,
1986  'HEADTAG' => $this->headTag,
1987  'METACHARSET' => $this->charSet ? str_replace('|', htmlspecialchars($this->charSet), $this->metaCharsetTag) : '',
1988  'INLINECOMMENT' => $this->inlineComments ? LF . LF . '<!-- ' . LF . implode(LF, $this->inlineComments) . '-->' . LF . LF : '',
1989  'BASEURL' => $this->baseUrl ? str_replace('|', $this->baseUrl, $this->baseUrlTag) : '',
1990  'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) : '',
1991  'META' => implode(LF, $this->metaTags),
1992  'BODY' => $this->bodyContent,
1993  'TITLE' => '<!-- ###TITLE' . $substituteHash . '### -->',
1994  'CSS_LIBS' => '<!-- ###CSS_LIBS' . $substituteHash . '### -->',
1995  'CSS_INCLUDE' => '<!-- ###CSS_INCLUDE' . $substituteHash . '### -->',
1996  'CSS_INLINE' => '<!-- ###CSS_INLINE' . $substituteHash . '### -->',
1997  'JS_INLINE' => '<!-- ###JS_INLINE' . $substituteHash . '### -->',
1998  'JS_INCLUDE' => '<!-- ###JS_INCLUDE' . $substituteHash . '### -->',
1999  'JS_LIBS' => '<!-- ###JS_LIBS' . $substituteHash . '### -->',
2000  'HEADERDATA' => '<!-- ###HEADERDATA' . $substituteHash . '### -->',
2001  'FOOTERDATA' => '<!-- ###FOOTERDATA' . $substituteHash . '### -->',
2002  'JS_LIBS_FOOTER' => '<!-- ###JS_LIBS_FOOTER' . $substituteHash . '### -->',
2003  'JS_INCLUDE_FOOTER' => '<!-- ###JS_INCLUDE_FOOTER' . $substituteHash . '### -->',
2004  'JS_INLINE_FOOTER' => '<!-- ###JS_INLINE_FOOTER' . $substituteHash . '### -->'
2005  );
2006  $markerArray = array_map('trim', $markerArray);
2007  return $markerArray;
2008  }
2009 
2016  protected function getTemplateForPart($part)
2017  {
2018  $templateFile = GeneralUtility::getFileAbsFileName($this->templateFile, true);
2020  if ($this->removeLineBreaksFromTemplate) {
2021  $template = strtr($template, array(LF => '', CR => ''));
2022  }
2023  if ($part !== self::PART_COMPLETE) {
2024  $templatePart = explode('###BODY###', $template);
2025  $template = $templatePart[$part - 1];
2026  }
2027  return $template;
2028  }
2029 
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->backPath . $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->backPath . $this->extJsPath . 'adapter/ext-base' . ($this->enableExtJsDebug ? '-debug' : '') . '.js')) . '" type="text/javascript"></script>' . LF;
2058  $out .= '<script src="' . $this->processJsFile(($this->backPath . $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  // @todo autoconvert file from UTF8 to current BE charset if necessary!!!!
2072  $extJsLocaleFile = $this->extJsPath . 'locale/ext-lang-' . $extJsLang . '.js';
2073  if (file_exists(PATH_typo3 . $extJsLocaleFile)) {
2074  $out .= '<script src="' . $this->processJsFile(($this->backPath . $extJsLocaleFile)) . '" type="text/javascript" charset="utf-8"></script>' . LF;
2075  }
2076  // Remove extjs from JScodeLibArray
2077  unset($this->jsFiles[$this->backPath . $this->extJsPath . 'ext-all.js'], $this->jsFiles[$this->backPath . $this->extJsPath . 'ext-all-debug.js']);
2078  }
2080  if (TYPO3_MODE === 'BE') {
2081  $this->addAjaxUrlsToInlineSettings();
2082  }
2083  $inlineSettings = $this->inlineLanguageLabels ? 'TYPO3.lang = ' . json_encode($this->inlineLanguageLabels) . ';' : '';
2084  $inlineSettings .= $this->inlineSettings ? 'TYPO3.settings = ' . json_encode($this->inlineSettings) . ';' : '';
2085  if ($this->addExtJS) {
2086  // Set clear.gif, move it on top, add handler code
2087  $code = '';
2088  if (!empty($this->extOnReadyCode)) {
2089  foreach ($this->extOnReadyCode as $block) {
2090  $code .= $block;
2091  }
2092  }
2093  $out .= $this->inlineJavascriptWrap[0] . '
2094  Ext.ns("TYPO3");
2095  Ext.BLANK_IMAGE_URL = "' . htmlspecialchars(GeneralUtility::locationHeaderUrl($this->backPath . 'sysext/t3skin/icons/gfx/clear.gif')) . '";
2096  Ext.SSL_SECURE_URL = "' . htmlspecialchars(GeneralUtility::locationHeaderUrl($this->backPath . 'sysext/t3skin/icons/gfx/clear.gif')) . '";' . 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(($this->backPath . 'sysext/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($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['all'], 'stylesheet', 'all', '', true);
2110  } else {
2111  $this->addCssLibrary($this->backPath . $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($this->backPath . $GLOBALS['TBE_STYLES']['extJS']['theme'], 'stylesheet', 'all', '', true);
2117  } else {
2118  $this->addCssLibrary($this->backPath . $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 
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 
2161  protected function addAjaxUrlsToInlineSettings()
2162  {
2163  $ajaxUrls = array();
2164  foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'] as $ajaxHandler => $_) {
2165  $ajaxUrls[$ajaxHandler] = BackendUtility::getAjaxUrl($ajaxHandler);
2166  }
2167 
2168  // also add the ajax-based routes
2170  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
2172  $router = GeneralUtility::makeInstance(Router::class);
2173  $routes = $router->getRoutes();
2174  foreach ($routes as $routeIdentifier => $route) {
2175  if ($route->getOption('ajax')) {
2176  $uri = (string)$uriBuilder->buildUriFromRoute($routeIdentifier);
2177  // use the shortened value in order to use this in JavaScript
2178  $routeIdentifier = str_replace('ajax_', '', $routeIdentifier);
2179  $ajaxUrls[$routeIdentifier] = $uri;
2180  }
2181  }
2182 
2183  $this->inlineSettings['ajaxUrls'] = $ajaxUrls;
2184  }
2185 
2194  protected function renderJqueryScriptTag($version, $source, $namespace)
2195  {
2196  switch (true) {
2197  case isset($this->jQueryCdnUrls[$source]):
2198  if ($this->enableJqueryDebug) {
2199  $minifyPart = '';
2200  } else {
2201  $minifyPart = '.min';
2202  }
2203  $jQueryFileName = sprintf($this->jQueryCdnUrls[$source], $version, $minifyPart);
2204  break;
2205  case $source === 'local':
2206  $jQueryFileName = $this->backPath . $this->jQueryPath . 'jquery-' . rawurlencode($version);
2207  if ($this->enableJqueryDebug) {
2208  $jQueryFileName .= '.js';
2209  } else {
2210  $jQueryFileName .= '.min.js';
2211  }
2212  break;
2213  default:
2214  $jQueryFileName = $source;
2215  }
2216  // Include the jQuery Core
2217  $scriptTag = '<script src="' . htmlspecialchars($jQueryFileName) . '" type="text/javascript"></script>' . LF;
2218  // Set the noConflict mode to be available via "TYPO3.jQuery" in all installations
2219  switch ($namespace) {
2220  case self::JQUERY_NAMESPACE_DEFAULT_NOCONFLICT:
2221  $scriptTag .= GeneralUtility::wrapJS('jQuery.noConflict();') . LF;
2222  break;
2223  case self::JQUERY_NAMESPACE_NONE:
2224  break;
2225  case self::JQUERY_NAMESPACE_DEFAULT:
2226 
2227  default:
2228  $scriptTag .= GeneralUtility::wrapJS('var TYPO3 = TYPO3 || {}; TYPO3.' . $namespace . ' = jQuery.noConflict(true);') . LF;
2229  }
2230  return $scriptTag;
2231  }
2232 
2238  protected function renderCssLibraries()
2239  {
2240  $cssFiles = '';
2241  if (!empty($this->cssLibs)) {
2242  foreach ($this->cssLibs as $file => $properties) {
2243  $file = GeneralUtility::resolveBackPath($file);
2245  $tag = '<link rel="' . htmlspecialchars($properties['rel'])
2246  . '" type="text/css" href="' . htmlspecialchars($file)
2247  . '" media="' . htmlspecialchars($properties['media']) . '"'
2248  . ($properties['title'] ? ' title="' . htmlspecialchars($properties['title']) . '"' : '')
2249  . $this->endingSlash . '>';
2250  if ($properties['allWrap']) {
2251  $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2252  $tag = $wrapArr[0] . $tag . $wrapArr[1];
2253  }
2254  $tag .= LF;
2255  if ($properties['forceOnTop']) {
2256  $cssFiles = $tag . $cssFiles;
2257  } else {
2258  $cssFiles .= $tag;
2259  }
2260  }
2261  }
2262  return $cssFiles;
2263  }
2264 
2270  protected function renderCssFiles()
2271  {
2272  $cssFiles = '';
2273  if (!empty($this->cssFiles)) {
2274  foreach ($this->cssFiles as $file => $properties) {
2275  $file = GeneralUtility::resolveBackPath($file);
2277  $tag = '<link rel="' . htmlspecialchars($properties['rel'])
2278  . '" type="text/css" href="' . htmlspecialchars($file)
2279  . '" media="' . htmlspecialchars($properties['media']) . '"'
2280  . ($properties['title'] ? ' title="' . htmlspecialchars($properties['title']) . '"' : '')
2281  . $this->endingSlash . '>';
2282  if ($properties['allWrap']) {
2283  $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2284  $tag = $wrapArr[0] . $tag . $wrapArr[1];
2285  }
2286  $tag .= LF;
2287  if ($properties['forceOnTop']) {
2288  $cssFiles = $tag . $cssFiles;
2289  } else {
2290  $cssFiles .= $tag;
2291  }
2292  }
2293  }
2294  return $cssFiles;
2295  }
2296 
2302  protected function renderCssInline()
2303  {
2304  $cssInline = '';
2305  if (!empty($this->cssInline)) {
2306  foreach ($this->cssInline as $name => $properties) {
2307  $cssCode = '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF;
2308  if ($properties['forceOnTop']) {
2309  $cssInline = $cssCode . $cssInline;
2310  } else {
2311  $cssInline .= $cssCode;
2312  }
2313  }
2314  $cssInline = $this->inlineCssWrap[0] . $cssInline . $this->inlineCssWrap[1];
2315  }
2316  return $cssInline;
2317  }
2318 
2325  {
2326  $jsLibs = '';
2327  $jsFooterLibs = '';
2328  if (!empty($this->jsLibs)) {
2329  foreach ($this->jsLibs as $properties) {
2330  $properties['file'] = GeneralUtility::resolveBackPath($properties['file']);
2331  $properties['file'] = GeneralUtility::createVersionNumberedFilename($properties['file']);
2332  $async = ($properties['async']) ? ' async="async"' : '';
2333  $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '"' : '';
2334  $tag = '<script src="' . htmlspecialchars($properties['file']) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $integrity . '></script>';
2335  if ($properties['allWrap']) {
2336  $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2337  $tag = $wrapArr[0] . $tag . $wrapArr[1];
2338  }
2339  $tag .= LF;
2340  if ($properties['forceOnTop']) {
2341  if ($properties['section'] === self::PART_HEADER) {
2342  $jsLibs = $tag . $jsLibs;
2343  } else {
2344  $jsFooterLibs = $tag . $jsFooterLibs;
2345  }
2346  } else {
2347  if ($properties['section'] === self::PART_HEADER) {
2348  $jsLibs .= $tag;
2349  } else {
2350  $jsFooterLibs .= $tag;
2351  }
2352  }
2353  }
2354  }
2355  if ($this->moveJsFromHeaderToFooter) {
2357  $jsLibs = '';
2358  }
2359  return array($jsLibs, $jsFooterLibs);
2360  }
2361 
2367  protected function renderJavaScriptFiles()
2368  {
2369  $jsFiles = '';
2370  $jsFooterFiles = '';
2371  if (!empty($this->jsFiles)) {
2372  foreach ($this->jsFiles as $file => $properties) {
2373  $file = GeneralUtility::resolveBackPath($file);
2375  $async = ($properties['async']) ? ' async="async"' : '';
2376  $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '"' : '';
2377  $tag = '<script src="' . htmlspecialchars($file) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $integrity . '></script>';
2378  if ($properties['allWrap']) {
2379  $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2380  $tag = $wrapArr[0] . $tag . $wrapArr[1];
2381  }
2382  $tag .= LF;
2383  if ($properties['forceOnTop']) {
2384  if ($properties['section'] === self::PART_HEADER) {
2385  $jsFiles = $tag . $jsFiles;
2386  } else {
2387  $jsFooterFiles = $tag . $jsFooterFiles;
2388  }
2389  } else {
2390  if ($properties['section'] === self::PART_HEADER) {
2391  $jsFiles .= $tag;
2392  } else {
2393  $jsFooterFiles .= $tag;
2394  }
2395  }
2396  }
2397  }
2398  if ($this->moveJsFromHeaderToFooter) {
2400  $jsFiles = '';
2401  }
2402  return array($jsFiles, $jsFooterFiles);
2403  }
2404 
2410  protected function renderInlineJavaScript()
2411  {
2412  $jsInline = '';
2413  $jsFooterInline = '';
2414  if (!empty($this->jsInline)) {
2415  foreach ($this->jsInline as $name => $properties) {
2416  $jsCode = '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF;
2417  if ($properties['forceOnTop']) {
2418  if ($properties['section'] === self::PART_HEADER) {
2419  $jsInline = $jsCode . $jsInline;
2420  } else {
2421  $jsFooterInline = $jsCode . $jsFooterInline;
2422  }
2423  } else {
2424  if ($properties['section'] === self::PART_HEADER) {
2425  $jsInline .= $jsCode;
2426  } else {
2427  $jsFooterInline .= $jsCode;
2428  }
2429  }
2430  }
2431  }
2432  if ($jsInline) {
2433  $jsInline = $this->inlineJavascriptWrap[0] . $jsInline . $this->inlineJavascriptWrap[1];
2434  }
2435  if ($jsFooterInline) {
2436  $jsFooterInline = $this->inlineJavascriptWrap[0] . $jsFooterInline . $this->inlineJavascriptWrap[1];
2437  }
2438  if ($this->moveJsFromHeaderToFooter) {
2440  $jsInline = '';
2441  }
2442  return array($jsInline, $jsFooterInline);
2443  }
2444 
2455  protected function includeLanguageFileForInline($fileRef, $selectionPrefix = '', $stripFromSelectionName = '', $errorMode = 0)
2456  {
2457  if (!isset($this->lang) || !isset($this->charSet)) {
2458  throw new \RuntimeException('Language and character encoding are not set.', 1284906026);
2459  }
2460  $labelsFromFile = array();
2461  $allLabels = $this->readLLfile($fileRef, $errorMode);
2462  if ($allLabels !== false) {
2463  // Merge language specific translations:
2464  if ($this->lang !== 'default' && isset($allLabels[$this->lang])) {
2465  $labels = array_merge($allLabels['default'], $allLabels[$this->lang]);
2466  } else {
2467  $labels = $allLabels['default'];
2468  }
2469  // Iterate through all locallang labels:
2470  foreach ($labels as $label => $value) {
2471  // If $selectionPrefix is set, only respect labels that start with $selectionPrefix
2472  if ($selectionPrefix === '' || strpos($label, $selectionPrefix) === 0) {
2473  // Remove substring $stripFromSelectionName from label
2474  $label = str_replace($stripFromSelectionName, '', $label);
2475  $labelsFromFile[$label] = $value;
2476  }
2477  }
2478  $this->inlineLanguageLabels = array_merge($this->inlineLanguageLabels, $labelsFromFile);
2479  }
2480  }
2481 
2489  protected function readLLfile($fileRef, $errorMode = 0)
2490  {
2492  $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
2493 
2494  if ($this->lang !== 'default') {
2495  $languages = array_reverse($this->languageDependencies);
2496  // At least we need to have English
2497  if (empty($languages)) {
2498  $languages[] = 'default';
2499  }
2500  } else {
2501  $languages = array('default');
2502  }
2503 
2504  $localLanguage = array();
2505  foreach ($languages as $language) {
2506  $tempLL = $languageFactory->getParsedData($fileRef, $language, $this->charSet, $errorMode);
2507 
2508  $localLanguage['default'] = $tempLL['default'];
2509  if (!isset($localLanguage[$this->lang])) {
2510  $localLanguage[$this->lang] = $localLanguage['default'];
2511  }
2512  if ($this->lang !== 'default' && isset($tempLL[$language])) {
2513  // Merge current language labels onto labels from previous language
2514  // This way we have a labels with fall back applied
2515  \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($localLanguage[$this->lang], $tempLL[$language], true, false);
2516  }
2517  }
2518 
2519  return $localLanguage;
2520  }
2521 
2522 
2523  /*****************************************************/
2524  /* */
2525  /* Tools */
2526  /* */
2527  /*****************************************************/
2534  protected function doConcatenate()
2535  {
2536  $this->doConcatenateCss();
2537  $this->doConcatenateJavaScript();
2538  }
2539 
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 
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 
2599  protected function doCompress()
2600  {
2601  $this->doCompressJavaScript();
2602  $this->doCompressCss();
2603  }
2604 
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 
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 
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 
2691  protected function processJsFile($filename)
2692  {
2693  switch (TYPO3_MODE) {
2694  case 'FE':
2695  if ($this->compressJavascript) {
2696  $filename = $this->getCompressor()->compressJsFile($filename);
2697  } else {
2699  }
2700  break;
2701  case 'BE':
2702  if ($this->compressJavascript) {
2703  $filename = $this->getCompressor()->compressJsFile($filename);
2704  }
2705  break;
2706  }
2707  return $filename;
2708  }
2709 
2715  protected function getTypoScriptFrontendController()
2716  {
2717  return $GLOBALS['TSFE'];
2718  }
2719 
2725  protected function getLanguageService()
2726  {
2727  return $GLOBALS['LANG'];
2728  }
2729 
2730  /*****************************************************/
2731  /* */
2732  /* Hooks */
2733  /* */
2734  /*****************************************************/
2740  protected function executePreRenderHook()
2741  {
2742  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-preProcess'])) {
2743  $params = array(
2744  'jsLibs' => &$this->jsLibs,
2745  'jsFooterLibs' => &$this->jsFooterLibs,
2746  'jsFiles' => &$this->jsFiles,
2747  'jsFooterFiles' => &$this->jsFooterFiles,
2748  'cssFiles' => &$this->cssFiles,
2749  'headerData' => &$this->headerData,
2750  'footerData' => &$this->footerData,
2751  'jsInline' => &$this->jsInline,
2752  'jsFooterInline' => &$this->jsFooterInline,
2753  'cssInline' => &$this->cssInline
2754  );
2755  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-preProcess'] as $hook) {
2756  GeneralUtility::callUserFunction($hook, $params, $this);
2757  }
2758  }
2759  }
2760 
2766  protected function executeRenderPostTransformHook()
2767  {
2768  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postTransform'])) {
2769  $params = array(
2770  'jsLibs' => &$this->jsLibs,
2771  'jsFooterLibs' => &$this->jsFooterLibs,
2772  'jsFiles' => &$this->jsFiles,
2773  'jsFooterFiles' => &$this->jsFooterFiles,
2774  'cssFiles' => &$this->cssFiles,
2775  'headerData' => &$this->headerData,
2776  'footerData' => &$this->footerData,
2777  'jsInline' => &$this->jsInline,
2778  'jsFooterInline' => &$this->jsFooterInline,
2779  'cssInline' => &$this->cssInline
2780  );
2781  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postTransform'] as $hook) {
2782  GeneralUtility::callUserFunction($hook, $params, $this);
2783  }
2784  }
2785  }
2786 
2802  {
2803  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'])) {
2804  $params = array(
2805  'jsLibs' => &$jsLibs,
2806  'jsFiles' => &$jsFiles,
2807  'jsFooterFiles' => &$jsFooterFiles,
2808  'cssLibs' => &$cssLibs,
2809  'cssFiles' => &$cssFiles,
2810  'headerData' => &$this->headerData,
2811  'footerData' => &$this->footerData,
2812  'jsInline' => &$jsInline,
2813  'cssInline' => &$cssInline,
2814  'xmlPrologAndDocType' => &$this->xmlPrologAndDocType,
2815  'htmlTag' => &$this->htmlTag,
2816  'headTag' => &$this->headTag,
2817  'charSet' => &$this->charSet,
2818  'metaCharsetTag' => &$this->metaCharsetTag,
2819  'shortcutTag' => &$this->shortcutTag,
2820  'inlineComments' => &$this->inlineComments,
2821  'baseUrl' => &$this->baseUrl,
2822  'baseUrlTag' => &$this->baseUrlTag,
2823  'favIcon' => &$this->favIcon,
2824  'iconMimeType' => &$this->iconMimeType,
2825  'titleTag' => &$this->titleTag,
2826  'title' => &$this->title,
2827  'metaTags' => &$this->metaTags,
2828  'jsFooterInline' => &$jsFooterInline,
2829  'jsFooterLibs' => &$jsFooterLibs,
2830  'bodyContent' => &$this->bodyContent
2831  );
2832  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'] as $hook) {
2833  GeneralUtility::callUserFunction($hook, $params, $this);
2834  }
2835  }
2836  }
2837 }