TYPO3 CMS  TYPO3_8-7
PageRenderer.php
Go to the documentation of this file.
1 <?php
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 
28 
34 {
35  // Constants for the part to be rendered
36  const PART_COMPLETE = 0;
37  const PART_HEADER = 1;
38  const PART_FOOTER = 2;
39  // jQuery Core version that is shipped with TYPO3
40  const JQUERY_VERSION_LATEST = '3.2.1';
41  // jQuery namespace options
42  const JQUERY_NAMESPACE_NONE = 'none';
43  const JQUERY_NAMESPACE_DEFAULT = 'jQuery';
44  const JQUERY_NAMESPACE_DEFAULT_NOCONFLICT = 'defaultNoConflict';
45 
46  const REQUIREJS_SCOPE_CONFIG = 'config';
47  const REQUIREJS_SCOPE_RESOLVE = 'resolve';
48 
52  protected $compressJavascript = false;
53 
57  protected $compressCss = false;
58 
62  protected $removeLineBreaksFromTemplate = false;
63 
67  protected $concatenateFiles = false;
68 
72  protected $concatenateJavascript = false;
73 
77  protected $concatenateCss = false;
78 
82  protected $moveJsFromHeaderToFooter = false;
83 
87  protected $csConvObj;
88 
92  protected $locales;
93 
100  protected $lang;
101 
108  protected $languageDependencies = [];
109 
113  protected $compressor;
114 
115  // Arrays containing associative array for the included files
119  protected $jsFiles = [];
120 
124  protected $jsFooterFiles = [];
125 
129  protected $jsLibs = [];
130 
134  protected $jsFooterLibs = [];
135 
139  protected $cssFiles = [];
140 
144  protected $cssLibs = [];
145 
151  protected $title;
152 
158  protected $charSet;
159 
163  protected $favIcon;
164 
168  protected $baseUrl;
169 
173  protected $renderXhtml = true;
174 
175  // Static header blocks
179  protected $xmlPrologAndDocType = '';
180 
184  protected $metaTags = [];
185 
189  protected $inlineComments = [];
190 
194  protected $headerData = [];
195 
199  protected $footerData = [];
200 
204  protected $titleTag = '<title>|</title>';
205 
209  protected $metaCharsetTag = '<meta http-equiv="Content-Type" content="text/html; charset=|" />';
210 
214  protected $htmlTag = '<html>';
215 
219  protected $headTag = '<head>';
220 
224  protected $baseUrlTag = '<base href="|" />';
225 
229  protected $iconMimeType = '';
230 
234  protected $shortcutTag = '<link rel="shortcut icon" href="%1$s"%2$s />';
235 
236  // Static inline code blocks
240  protected $jsInline = [];
241 
245  protected $jsFooterInline = [];
246 
250  protected $extOnReadyCode = [];
251 
255  protected $cssInline = [];
256 
260  protected $bodyContent;
261 
265  protected $templateFile;
266 
270  protected $jsLibraryNames = ['extjs'];
271 
272  // Paths to contributed libraries
273 
278  protected $requireJsPath = 'EXT:core/Resources/Public/JavaScript/Contrib/';
279 
283  protected $extJsPath = 'EXT:core/Resources/Public/JavaScript/Contrib/extjs/';
284 
290  protected $jQueryPath = 'EXT:core/Resources/Public/JavaScript/Contrib/jquery/';
291 
292  // Internal flags for JS-libraries
310  protected $jQueryVersions = [];
311 
318  self::JQUERY_VERSION_LATEST
319  ];
320 
326  protected $jQueryCdnUrls = [
327  'google' => 'https://ajax.googleapis.com/ajax/libs/jquery/%1$s/jquery%2$s.js',
328  'msn' => 'https://ajax.aspnetcdn.com/ajax/jQuery/jquery-%1$s%2$s.js',
329  'jquery' => 'https://code.jquery.com/jquery-%1$s%2$s.js',
330  'cloudflare' => 'https://cdnjs.cloudflare.com/ajax/libs/jquery/%1$s/jquery%2$s.js'
331  ];
332 
337  protected $addRequireJs = false;
338 
343  protected $requireJsConfig = [];
344 
350 
355  protected $publicRequireJsConfig = [];
356 
360  protected $addExtJS = false;
361 
365  protected $extDirectCodeAdded = false;
366 
370  protected $enableExtJsDebug = false;
371 
375  protected $enableJqueryDebug = false;
376 
380  protected $extJStheme = true;
381 
385  protected $extJScss = true;
386 
390  protected $inlineLanguageLabels = [];
391 
396 
400  protected $inlineSettings = [];
401 
405  protected $inlineJavascriptWrap = [];
406 
410  protected $inlineCssWrap = [];
411 
417  protected $compressError = '';
418 
424  protected $endingSlash = '';
425 
429  public function __construct($templateFile = '')
430  {
431  $this->reset();
432  $this->csConvObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Charset\CharsetConverter::class);
433  $this->locales = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\Locales::class);
434  if ($templateFile !== '') {
435  $this->templateFile = $templateFile;
436  }
437  $this->inlineJavascriptWrap = [
438  '<script type="text/javascript">' . LF . '/*<![CDATA[*/' . LF,
439  '/*]]>*/' . LF . '</script>' . LF
440  ];
441  $this->inlineCssWrap = [
442  '<style type="text/css">' . LF . '/*<![CDATA[*/' . LF . '<!-- ' . LF,
443  '-->' . LF . '/*]]>*/' . LF . '</style>' . LF
444  ];
445  }
446 
450  protected function reset()
451  {
452  $this->templateFile = 'EXT:core/Resources/Private/Templates/PageRenderer.html';
453  $this->jsFiles = [];
454  $this->jsFooterFiles = [];
455  $this->jsInline = [];
456  $this->jsFooterInline = [];
457  $this->jsLibs = [];
458  $this->cssFiles = [];
459  $this->cssInline = [];
460  $this->metaTags = [];
461  $this->inlineComments = [];
462  $this->headerData = [];
463  $this->footerData = [];
464  $this->extOnReadyCode = [];
465  $this->jQueryVersions = [];
466  }
467 
468  /*****************************************************/
469  /* */
470  /* Public Setters */
471  /* */
472  /* */
473  /*****************************************************/
479  public function setTitle($title)
480  {
481  $this->title = $title;
482  }
483 
489  public function setRenderXhtml($enable)
490  {
491  $this->renderXhtml = $enable;
492  }
493 
500  {
501  $this->xmlPrologAndDocType = $xmlPrologAndDocType;
502  }
503 
509  public function setCharSet($charSet)
510  {
511  $this->charSet = $charSet;
512  }
513 
519  public function setLanguage($lang)
520  {
521  $this->lang = $lang;
522  $this->languageDependencies = [];
523 
524  // Language is found. Configure it:
525  if (in_array($this->lang, $this->locales->getLocales())) {
526  $this->languageDependencies[] = $this->lang;
527  foreach ($this->locales->getLocaleDependencies($this->lang) as $language) {
528  $this->languageDependencies[] = $language;
529  }
530  }
531  }
532 
539  {
540  $this->metaCharsetTag = $metaCharsetTag;
541  }
542 
548  public function setHtmlTag($htmlTag)
549  {
550  $this->htmlTag = $htmlTag;
551  }
552 
558  public function setHeadTag($headTag)
559  {
560  $this->headTag = $headTag;
561  }
562 
568  public function setFavIcon($favIcon)
569  {
570  $this->favIcon = $favIcon;
571  }
572 
579  {
580  $this->iconMimeType = $iconMimeType;
581  }
582 
588  public function setBaseUrl($baseUrl)
589  {
590  $this->baseUrl = $baseUrl;
591  }
592 
598  public function setTemplateFile($file)
599  {
600  $this->templateFile = $file;
601  }
602 
608  public function setBodyContent($content)
609  {
610  $this->bodyContent = $content;
611  }
612 
618  public function setRequireJsPath($path)
619  {
620  $this->requireJsPath = $path;
621  }
622 
628  public function setExtJsPath($path)
629  {
630  $this->extJsPath = $path;
631  }
632 
637  public function getRequireJsConfig(string $scope = null): array
638  {
639  // return basic RequireJS configuration without shim, paths and packages
640  if ($scope === static::REQUIREJS_SCOPE_CONFIG) {
641  return array_replace_recursive(
642  $this->publicRequireJsConfig,
643  $this->filterArrayKeys(
644  $this->requireJsConfig,
645  ['shim', 'paths', 'packages'],
646  false
647  )
648  );
649  }
650  // return RequireJS configuration for resolving only shim, paths and packages
651  if ($scope === static::REQUIREJS_SCOPE_RESOLVE) {
652  return $this->filterArrayKeys(
653  $this->requireJsConfig,
654  ['shim', 'paths', 'packages'],
655  true
656  );
657  }
658  return [];
659  }
660 
661  /*****************************************************/
662  /* */
663  /* Public Enablers / Disablers */
664  /* */
665  /* */
666  /*****************************************************/
671  {
672  $this->moveJsFromHeaderToFooter = true;
673  }
674 
679  {
680  $this->moveJsFromHeaderToFooter = false;
681  }
682 
686  public function enableCompressJavascript()
687  {
688  $this->compressJavascript = true;
689  }
690 
694  public function disableCompressJavascript()
695  {
696  $this->compressJavascript = false;
697  }
698 
702  public function enableCompressCss()
703  {
704  $this->compressCss = true;
705  }
706 
710  public function disableCompressCss()
711  {
712  $this->compressCss = false;
713  }
714 
718  public function enableConcatenateFiles()
719  {
720  $this->concatenateFiles = true;
721  }
722 
726  public function disableConcatenateFiles()
727  {
728  $this->concatenateFiles = false;
729  }
730 
734  public function enableConcatenateJavascript()
735  {
736  $this->concatenateJavascript = true;
737  }
738 
743  {
744  $this->concatenateJavascript = false;
745  }
746 
750  public function enableConcatenateCss()
751  {
752  $this->concatenateCss = true;
753  }
754 
758  public function disableConcatenateCss()
759  {
760  $this->concatenateCss = false;
761  }
762 
767  {
768  $this->removeLineBreaksFromTemplate = true;
769  }
770 
775  {
776  $this->removeLineBreaksFromTemplate = false;
777  }
778 
783  public function enableDebugMode()
784  {
785  $this->compressJavascript = false;
786  $this->compressCss = false;
787  $this->concatenateFiles = false;
788  $this->removeLineBreaksFromTemplate = false;
789  $this->enableExtJsDebug = true;
790  $this->enableJqueryDebug = true;
791  }
792 
793  /*****************************************************/
794  /* */
795  /* Public Getters */
796  /* */
797  /* */
798  /*****************************************************/
804  public function getTitle()
805  {
806  return $this->title;
807  }
808 
814  public function getCharSet()
815  {
816  return $this->charSet;
817  }
818 
824  public function getLanguage()
825  {
826  return $this->lang;
827  }
828 
834  public function getRenderXhtml()
835  {
836  return $this->renderXhtml;
837  }
838 
844  public function getHtmlTag()
845  {
846  return $this->htmlTag;
847  }
848 
854  public function getMetaCharsetTag()
855  {
856  return $this->metaCharsetTag;
857  }
858 
864  public function getHeadTag()
865  {
866  return $this->headTag;
867  }
868 
874  public function getFavIcon()
875  {
876  return $this->favIcon;
877  }
878 
884  public function getIconMimeType()
885  {
886  return $this->iconMimeType;
887  }
888 
894  public function getBaseUrl()
895  {
896  return $this->baseUrl;
897  }
898 
904  public function getTemplateFile()
905  {
906  return $this->templateFile;
907  }
908 
914  public function getMoveJsFromHeaderToFooter()
915  {
917  }
918 
924  public function getCompressJavascript()
925  {
927  }
928 
934  public function getCompressCss()
935  {
936  return $this->compressCss;
937  }
938 
944  public function getConcatenateFiles()
945  {
947  }
948 
954  public function getConcatenateJavascript()
955  {
957  }
958 
964  public function getConcatenateCss()
965  {
966  return $this->concatenateCss;
967  }
968 
975  {
977  }
978 
984  public function getBodyContent()
985  {
986  return $this->bodyContent;
987  }
988 
994  public function getExtJsPath()
995  {
996  return $this->extJsPath;
997  }
998 
1004  public function getInlineLanguageLabels()
1005  {
1007  }
1008 
1015  {
1017  }
1018 
1019  /*****************************************************/
1020  /* */
1021  /* Public Functions to add Data */
1022  /* */
1023  /* */
1024  /*****************************************************/
1030  public function addMetaTag($meta)
1031  {
1032  if (!in_array($meta, $this->metaTags)) {
1033  $this->metaTags[] = $meta;
1034  }
1035  }
1036 
1042  public function addInlineComment($comment)
1043  {
1044  if (!in_array($comment, $this->inlineComments)) {
1045  $this->inlineComments[] = $comment;
1046  }
1047  }
1048 
1054  public function addHeaderData($data)
1055  {
1056  if (!in_array($data, $this->headerData)) {
1057  $this->headerData[] = $data;
1058  }
1059  }
1060 
1066  public function addFooterData($data)
1067  {
1068  if (!in_array($data, $this->footerData)) {
1069  $this->footerData[] = $data;
1070  }
1071  }
1072 
1087  public function addJsLibrary($name, $file, $type = 'text/javascript', $compress = false, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1088  {
1089  if (!$type) {
1090  $type = 'text/javascript';
1091  }
1092  if (!in_array(strtolower($name), $this->jsLibs)) {
1093  $this->jsLibs[strtolower($name)] = [
1094  'file' => $file,
1095  'type' => $type,
1096  'section' => self::PART_HEADER,
1097  'compress' => $compress,
1098  'forceOnTop' => $forceOnTop,
1099  'allWrap' => $allWrap,
1100  'excludeFromConcatenation' => $excludeFromConcatenation,
1101  'splitChar' => $splitChar,
1102  'async' => $async,
1103  'integrity' => $integrity,
1104  ];
1105  }
1106  }
1107 
1122  public function addJsFooterLibrary($name, $file, $type = 'text/javascript', $compress = false, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1123  {
1124  if (!$type) {
1125  $type = 'text/javascript';
1126  }
1127  $name .= '_jsFooterLibrary';
1128  if (!in_array(strtolower($name), $this->jsLibs)) {
1129  $this->jsLibs[strtolower($name)] = [
1130  'file' => $file,
1131  'type' => $type,
1132  'section' => self::PART_FOOTER,
1133  'compress' => $compress,
1134  'forceOnTop' => $forceOnTop,
1135  'allWrap' => $allWrap,
1136  'excludeFromConcatenation' => $excludeFromConcatenation,
1137  'splitChar' => $splitChar,
1138  'async' => $async,
1139  'integrity' => $integrity,
1140  ];
1141  }
1142  }
1143 
1157  public function addJsFile($file, $type = 'text/javascript', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1158  {
1159  if (!$type) {
1160  $type = 'text/javascript';
1161  }
1162  if (!isset($this->jsFiles[$file])) {
1163  $this->jsFiles[$file] = [
1164  'file' => $file,
1165  'type' => $type,
1166  'section' => self::PART_HEADER,
1167  'compress' => $compress,
1168  'forceOnTop' => $forceOnTop,
1169  'allWrap' => $allWrap,
1170  'excludeFromConcatenation' => $excludeFromConcatenation,
1171  'splitChar' => $splitChar,
1172  'async' => $async,
1173  'integrity' => $integrity,
1174  ];
1175  }
1176  }
1177 
1191  public function addJsFooterFile($file, $type = 'text/javascript', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|', $async = false, $integrity = '')
1192  {
1193  if (!$type) {
1194  $type = 'text/javascript';
1195  }
1196  if (!isset($this->jsFiles[$file])) {
1197  $this->jsFiles[$file] = [
1198  'file' => $file,
1199  'type' => $type,
1200  'section' => self::PART_FOOTER,
1201  'compress' => $compress,
1202  'forceOnTop' => $forceOnTop,
1203  'allWrap' => $allWrap,
1204  'excludeFromConcatenation' => $excludeFromConcatenation,
1205  'splitChar' => $splitChar,
1206  'async' => $async,
1207  'integrity' => $integrity,
1208  ];
1209  }
1210  }
1211 
1220  public function addJsInlineCode($name, $block, $compress = true, $forceOnTop = false)
1221  {
1222  if (!isset($this->jsInline[$name]) && !empty($block)) {
1223  $this->jsInline[$name] = [
1224  'code' => $block . LF,
1225  'section' => self::PART_HEADER,
1226  'compress' => $compress,
1227  'forceOnTop' => $forceOnTop
1228  ];
1229  }
1230  }
1231 
1240  public function addJsFooterInlineCode($name, $block, $compress = true, $forceOnTop = false)
1241  {
1242  if (!isset($this->jsInline[$name]) && !empty($block)) {
1243  $this->jsInline[$name] = [
1244  'code' => $block . LF,
1245  'section' => self::PART_FOOTER,
1246  'compress' => $compress,
1247  'forceOnTop' => $forceOnTop
1248  ];
1249  }
1250  }
1251 
1258  public function addExtOnReadyCode($block, $forceOnTop = false)
1259  {
1260  if (!in_array($block, $this->extOnReadyCode)) {
1261  if ($forceOnTop) {
1262  array_unshift($this->extOnReadyCode, $block);
1263  } else {
1264  $this->extOnReadyCode[] = $block;
1265  }
1266  }
1267  }
1268 
1274  public function addExtDirectCode(array $filterNamespaces = [])
1275  {
1276  if ($this->extDirectCodeAdded) {
1277  return;
1278  }
1279  $this->extDirectCodeAdded = true;
1280  if (empty($filterNamespaces)) {
1281  $filterNamespaces = ['TYPO3'];
1282  }
1283 
1284  // Add language labels for ExtDirect
1286  'extDirect_timeoutHeader' => 'LLL:EXT:lang/Resources/Private/Language/locallang_misc.xlf:extDirect_timeoutHeader',
1287  'extDirect_timeoutMessage' => 'LLL:EXT:lang/Resources/Private/Language/locallang_misc.xlf:extDirect_timeoutMessage'
1288  ], true);
1289 
1290  $token = ($api = '');
1291  if (TYPO3_MODE === 'BE') {
1293  $token = $formprotection->generateToken('extDirect');
1294 
1295  // Debugger Console strings
1296  $this->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/debugger.xlf');
1297 
1298  $this->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/wizard.xlf');
1299  }
1301  $extDirect = GeneralUtility::makeInstance(\TYPO3\CMS\Core\ExtDirect\ExtDirectApi::class);
1302  $api = $extDirect->getApiPhp($filterNamespaces);
1303  if ($api) {
1304  $this->addJsInlineCode('TYPO3ExtDirectAPI', $api, false);
1305  }
1306  // Note: we need to iterate through the object, because the addProvider method
1307  // does this only with multiple arguments
1308  $this->addExtOnReadyCode('
1309  (function() {
1310  TYPO3.ExtDirectToken = "' . $token . '";
1311  for (var api in Ext.app.ExtDirectAPI) {
1312  var provider = Ext.Direct.addProvider(Ext.app.ExtDirectAPI[api]);
1313  provider.on("beforecall", function(provider, transaction, meta) {
1314  if (transaction.data) {
1315  transaction.data[transaction.data.length] = TYPO3.ExtDirectToken;
1316  } else {
1317  transaction.data = [TYPO3.ExtDirectToken];
1318  }
1319  });
1320 
1321  provider.on("call", function(provider, transaction, meta) {
1322  if (transaction.isForm) {
1323  transaction.params.securityToken = TYPO3.ExtDirectToken;
1324  }
1325  });
1326  }
1327  })();
1328 
1329  var extDirectDebug = function(message, header, group) {
1330  var DebugConsole = null;
1331 
1332  if (top && top.TYPO3 && typeof top.TYPO3.DebugConsole === "object") {
1333  DebugConsole = top.TYPO3.DebugConsole;
1334  } else if (typeof TYPO3 === "object" && typeof TYPO3.DebugConsole === "object") {
1335  DebugConsole = TYPO3.DebugConsole;
1336  }
1337 
1338  if (DebugConsole !== null) {
1339  DebugConsole.add(message, header, group);
1340  } else if (typeof console === "object") {
1341  console.log(message);
1342  } else {
1343  document.write(message);
1344  }
1345  };
1346 
1347  Ext.Direct.on("exception", function(event) {
1348  if (event.code === Ext.Direct.exceptions.TRANSPORT && !event.where) {
1349  top.TYPO3.Notification.error(
1350  TYPO3.l10n.localize("extDirect_timeoutHeader"),
1351  TYPO3.l10n.localize("extDirect_timeoutMessage")
1352  );
1353  } else {
1354  var backtrace = "";
1355  if (event.code === "parse") {
1356  extDirectDebug(
1357  "<p>" + event.xhr.responseText + "<\\/p>",
1358  event.type,
1359  "ExtDirect - Exception"
1360  );
1361  } else if (event.code === "router") {
1362  top.TYPO3.Notification.error(
1363  event.code,
1364  event.message
1365  );
1366  } else if (event.where) {
1367  backtrace = "<p style=\\"margin-top: 20px;\\">" +
1368  "<strong>Backtrace:<\\/strong><br \\/>" +
1369  event.where.replace(/#/g, "<br \\/>#") +
1370  "<\\/p>";
1371  extDirectDebug(
1372  "<p>" + event.message + "<\\/p>" + backtrace,
1373  event.method,
1374  "ExtDirect - Exception"
1375  );
1376  }
1377 
1378 
1379  }
1380  });
1381 
1382  Ext.Direct.on("event", function(event, provider) {
1383  if (typeof event.debug !== "undefined" && event.debug !== "") {
1384  extDirectDebug(event.debug, event.method, "ExtDirect - Debug");
1385  }
1386  });
1387  ', true);
1388  }
1389 
1403  public function addCssFile($file, $rel = 'stylesheet', $media = 'all', $title = '', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|')
1404  {
1405  if (!isset($this->cssFiles[$file])) {
1406  $this->cssFiles[$file] = [
1407  'file' => $file,
1408  'rel' => $rel,
1409  'media' => $media,
1410  'title' => $title,
1411  'compress' => $compress,
1412  'forceOnTop' => $forceOnTop,
1413  'allWrap' => $allWrap,
1414  'excludeFromConcatenation' => $excludeFromConcatenation,
1415  'splitChar' => $splitChar
1416  ];
1417  }
1418  }
1419 
1433  public function addCssLibrary($file, $rel = 'stylesheet', $media = 'all', $title = '', $compress = true, $forceOnTop = false, $allWrap = '', $excludeFromConcatenation = false, $splitChar = '|')
1434  {
1435  if (!isset($this->cssLibs[$file])) {
1436  $this->cssLibs[$file] = [
1437  'file' => $file,
1438  'rel' => $rel,
1439  'media' => $media,
1440  'title' => $title,
1441  'compress' => $compress,
1442  'forceOnTop' => $forceOnTop,
1443  'allWrap' => $allWrap,
1444  'excludeFromConcatenation' => $excludeFromConcatenation,
1445  'splitChar' => $splitChar
1446  ];
1447  }
1448  }
1449 
1458  public function addCssInlineBlock($name, $block, $compress = false, $forceOnTop = false)
1459  {
1460  if (!isset($this->cssInline[$name]) && !empty($block)) {
1461  $this->cssInline[$name] = [
1462  'code' => $block,
1463  'compress' => $compress,
1464  'forceOnTop' => $forceOnTop
1465  ];
1466  }
1467  }
1468 
1477  public function loadJquery($version = null, $source = null, $namespace = self::JQUERY_NAMESPACE_DEFAULT)
1478  {
1479  // Set it to the version that is shipped with the TYPO3 core
1480  if ($version === null || $version === 'latest') {
1481  $version = self::JQUERY_VERSION_LATEST;
1482  }
1483  // Check if the source is set, otherwise set it to "default"
1484  if ($source === null) {
1485  $source = 'local';
1486  }
1487  if ($source === 'local' && !in_array($version, $this->availableLocalJqueryVersions)) {
1488  throw new \UnexpectedValueException('The requested jQuery version is not available in the local filesystem.', 1341505305);
1489  }
1490  if (!preg_match('/^[a-zA-Z0-9]+$/', $namespace)) {
1491  throw new \UnexpectedValueException('The requested namespace contains non alphanumeric characters.', 1341571604);
1492  }
1493  $this->jQueryVersions[$namespace] = [
1494  'version' => $version,
1495  'source' => $source
1496  ];
1497  }
1498 
1505  public function loadRequireJs()
1506  {
1507  $this->addRequireJs = true;
1508  if (!empty($this->requireJsConfig) && !empty($this->publicRequireJsConfig)) {
1509  return;
1510  }
1511 
1513  $isDevelopment = GeneralUtility::getApplicationContext()->isDevelopment();
1514  $cacheIdentifier = 'requireJS_' . md5(implode(',', $loadedExtensions) . ($isDevelopment ? ':dev' : '') . GeneralUtility::getIndpEnv('TYPO3_REQUEST_SCRIPT'));
1516  $cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('assets');
1517  $requireJsConfig = $cache->get($cacheIdentifier);
1518 
1519  // if we did not get a configuration from the cache, compute and store it in the cache
1520  if (!isset($requireJsConfig['internal']) || !isset($requireJsConfig['public'])) {
1521  $requireJsConfig = $this->computeRequireJsConfig($isDevelopment, $loadedExtensions);
1522  $cache->set($cacheIdentifier, $requireJsConfig);
1523  }
1524 
1525  $this->requireJsConfig = $requireJsConfig['internal'];
1526  $this->publicRequireJsConfig = $requireJsConfig['public'];
1527  $this->internalRequireJsPathModuleNames = $requireJsConfig['internalNames'];
1528  }
1529 
1538  protected function computeRequireJsConfig($isDevelopment, array $loadedExtensions)
1539  {
1540  // load all paths to map to package names / namespaces
1541  $requireJsConfig = [
1542  'public' => [],
1543  'internal' => [],
1544  'internalNames' => [],
1545  ];
1546 
1547  // In order to avoid browser caching of JS files, adding a GET parameter to the files loaded via requireJS
1548  if ($isDevelopment) {
1549  $requireJsConfig['public']['urlArgs'] = 'bust=' . $GLOBALS['EXEC_TIME'];
1550  } else {
1551  $requireJsConfig['public']['urlArgs'] = 'bust=' . GeneralUtility::hmac(TYPO3_version . PATH_site);
1552  }
1553  $corePath = ExtensionManagementUtility::extPath('core', 'Resources/Public/JavaScript/Contrib/');
1554  $corePath = PathUtility::getAbsoluteWebPath($corePath);
1555  // first, load all paths for the namespaces, and configure contrib libs.
1556  $requireJsConfig['public']['paths'] = [
1557  'jquery-ui' => $corePath . 'jquery-ui',
1558  'datatables' => $corePath . 'jquery.dataTables',
1559  'matchheight' => $corePath . 'jquery.matchHeight-min',
1560  'nprogress' => $corePath . 'nprogress',
1561  'moment' => $corePath . 'moment',
1562  'cropper' => $corePath . 'cropper.min',
1563  'imagesloaded' => $corePath . 'imagesloaded.pkgd.min',
1564  'bootstrap' => $corePath . 'bootstrap/bootstrap',
1565  'twbs/bootstrap-datetimepicker' => $corePath . 'bootstrap-datetimepicker',
1566  'autosize' => $corePath . 'autosize',
1567  'taboverride' => $corePath . 'taboverride.min',
1568  'twbs/bootstrap-slider' => $corePath . 'bootstrap-slider.min',
1569  'jquery/autocomplete' => $corePath . 'jquery.autocomplete',
1570  'd3' => $corePath . 'd3/d3'
1571  ];
1572  $requireJsConfig['public']['typo3BaseUrl'] = false;
1573  $publicPackageNames = ['core', 'frontend', 'backend'];
1574  foreach ($loadedExtensions as $packageName) {
1575  $jsPath = 'EXT:' . $packageName . '/Resources/Public/JavaScript/';
1576  $absoluteJsPath = GeneralUtility::getFileAbsFileName($jsPath);
1577  $fullJsPath = PathUtility::getAbsoluteWebPath($absoluteJsPath);
1578  $fullJsPath = rtrim($fullJsPath, '/');
1579  if (!empty($fullJsPath) && file_exists($absoluteJsPath)) {
1580  $type = in_array($packageName, $publicPackageNames, true) ? 'public' : 'internal';
1581  $requireJsConfig[$type]['paths']['TYPO3/CMS/' . GeneralUtility::underscoredToUpperCamelCase($packageName)] = $fullJsPath;
1582  }
1583  }
1584  // sanitize module names in internal 'paths'
1585  $internalPathModuleNames = array_keys($requireJsConfig['internal']['paths'] ?? []);
1586  $sanitizedInternalPathModuleNames = array_map(
1587  function ($moduleName) {
1588  // trim spaces and slashes & add ending slash
1589  return trim($moduleName, ' /') . '/';
1590  },
1591  $internalPathModuleNames
1592  );
1593  $requireJsConfig['internalNames'] = array_combine(
1594  $sanitizedInternalPathModuleNames,
1595  $internalPathModuleNames
1596  );
1597 
1598  // check if additional AMD modules need to be loaded if a single AMD module is initialized
1599  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RequireJS']['postInitializationModules'])) {
1600  $this->addInlineSettingArray(
1601  'RequireJS.PostInitializationModules',
1602  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RequireJS']['postInitializationModules']
1603  );
1604  }
1605 
1606  return $requireJsConfig;
1607  }
1608 
1621  public function addRequireJsConfiguration(array $configuration)
1622  {
1623  if (TYPO3_MODE === 'BE') {
1624  // Load RequireJS in backend context at first. Doing this in FE could break the output
1625  $this->loadRequireJs();
1626  }
1627  \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($this->requireJsConfig, $configuration);
1628  }
1629 
1636  protected function getRequireJsLoader(): string
1637  {
1638  $html = '';
1639  $backendRequest = TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_BE;
1640  $backendUserLoggedIn = !empty($GLOBALS['BE_USER']->user['uid']);
1641 
1642  // no backend request - basically frontend
1643  if (!$backendRequest) {
1644  $requireJsConfig = $this->getRequireJsConfig(static::REQUIREJS_SCOPE_CONFIG);
1645  $requireJsConfig['typo3BaseUrl'] = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH') . '?eID=requirejs';
1646  // backend request, but no backend user logged in
1647  } elseif (!$backendUserLoggedIn) {
1648  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
1649  $requireJsConfig = $this->getRequireJsConfig(static::REQUIREJS_SCOPE_CONFIG);
1650  $requireJsConfig['typo3BaseUrl'] = (string)$uriBuilder->buildUriFromRoute('ajax_core_requirejs');
1651  // backend request, having backend user logged in
1652  } else {
1653  $requireJsConfig = array_replace_recursive(
1654  $this->publicRequireJsConfig,
1655  $this->requireJsConfig
1656  );
1657  }
1658 
1659  // add (probably filtered) RequireJS configuration
1660  $html .= GeneralUtility::wrapJS('var require = ' . json_encode($requireJsConfig)) . LF;
1661  // directly after that, include the require.js file
1662  $html .= '<script src="'
1663  . $this->processJsFile($this->requireJsPath . 'require.js')
1664  . '" type="text/javascript"></script>' . LF;
1665 
1666  if (!empty($requireJsConfig['typo3BaseUrl'])) {
1667  $html .= '<script src="'
1668  . $this->processJsFile(
1669  'EXT:core/Resources/Public/JavaScript/requirejs-loader.js'
1670  )
1671  . '" type="text/javascript"></script>' . LF;
1672  }
1673 
1674  return $html;
1675  }
1676 
1683  protected function filterArrayKeys(array $array, array $keys, bool $keep = true): array
1684  {
1685  return array_filter(
1686  $array,
1687  function (string $key) use ($keys, $keep) {
1688  return in_array($key, $keys, true) === $keep;
1689  },
1690  ARRAY_FILTER_USE_KEY
1691  );
1692  }
1693 
1710  public function loadRequireJsModule($mainModuleName, $callBackFunction = null)
1711  {
1712  $inlineCodeKey = $mainModuleName;
1713  // make sure requireJS is initialized
1714  $this->loadRequireJs();
1715  // move internal module path definition to public module definition
1716  // (since loading a module ends up disclosing the existence anyway)
1717  $baseModuleName = $this->findRequireJsBaseModuleName($mainModuleName);
1718  if ($baseModuleName !== null && isset($this->requireJsConfig['paths'][$baseModuleName])) {
1719  $this->publicRequireJsConfig['paths'][$baseModuleName] = $this->requireJsConfig['paths'][$baseModuleName];
1720  unset($this->requireJsConfig['paths'][$baseModuleName]);
1721  }
1722  // execute the main module, and load a possible callback function
1723  $javaScriptCode = 'require(["' . $mainModuleName . '"]';
1724  if ($callBackFunction !== null) {
1725  $inlineCodeKey .= sha1($callBackFunction);
1726  $javaScriptCode .= ', ' . $callBackFunction;
1727  }
1728  $javaScriptCode .= ');';
1729  $this->addJsInlineCode('RequireJS-Module-' . $inlineCodeKey, $javaScriptCode);
1730  }
1731 
1738  public function loadExtJS($css = true, $theme = true)
1739  {
1740  $this->addExtJS = true;
1741  $this->extJStheme = $theme;
1742  $this->extJScss = $css;
1743  }
1744 
1748  public function enableExtJsDebug()
1749  {
1750  $this->enableExtJsDebug = true;
1751  }
1752 
1759  protected function findRequireJsBaseModuleName(string $moduleName)
1760  {
1761  // trim spaces and slashes & add ending slash
1762  $sanitizedModuleName = trim($moduleName, ' /') . '/';
1763  foreach ($this->internalRequireJsPathModuleNames as $sanitizedBaseModuleName => $baseModuleName) {
1764  if (strpos($sanitizedModuleName, $sanitizedBaseModuleName) === 0) {
1765  return $baseModuleName;
1766  }
1767  }
1768  return null;
1769  }
1770 
1779  public function addInlineLanguageLabel($key, $value)
1780  {
1781  $this->inlineLanguageLabels[$key] = $value;
1782  }
1783 
1793  public function addInlineLanguageLabelArray(array $array, $parseWithLanguageService = false)
1794  {
1795  if ($parseWithLanguageService === true) {
1796  foreach ($array as $key => $value) {
1797  if (TYPO3_MODE === 'FE') {
1798  $array[$key] = $this->getTypoScriptFrontendController()->sL($value);
1799  } else {
1800  $array[$key] = $this->getLanguageService()->sL($value);
1801  }
1802  }
1803  }
1804 
1805  $this->inlineLanguageLabels = array_merge($this->inlineLanguageLabels, $array);
1806  }
1807 
1816  public function addInlineLanguageLabelFile($fileRef, $selectionPrefix = '', $stripFromSelectionName = '', $errorMode = 0)
1817  {
1818  $index = md5($fileRef . $selectionPrefix . $stripFromSelectionName);
1819  if ($fileRef && !isset($this->inlineLanguageLabelFiles[$index])) {
1820  $this->inlineLanguageLabelFiles[$index] = [
1821  'fileRef' => $fileRef,
1822  'selectionPrefix' => $selectionPrefix,
1823  'stripFromSelectionName' => $stripFromSelectionName,
1824  'errorMode' => $errorMode
1825  ];
1826  }
1827  }
1828 
1838  public function addInlineSetting($namespace, $key, $value)
1839  {
1840  if ($namespace) {
1841  if (strpos($namespace, '.')) {
1842  $parts = explode('.', $namespace);
1843  $a = &$this->inlineSettings;
1844  foreach ($parts as $part) {
1845  $a = &$a[$part];
1846  }
1847  $a[$key] = $value;
1848  } else {
1849  $this->inlineSettings[$namespace][$key] = $value;
1850  }
1851  } else {
1852  $this->inlineSettings[$key] = $value;
1853  }
1854  }
1855 
1865  public function addInlineSettingArray($namespace, array $array)
1866  {
1867  if ($namespace) {
1868  if (strpos($namespace, '.')) {
1869  $parts = explode('.', $namespace);
1870  $a = &$this->inlineSettings;
1871  foreach ($parts as $part) {
1872  $a = &$a[$part];
1873  }
1874  $a = array_merge((array)$a, $array);
1875  } else {
1876  $this->inlineSettings[$namespace] = array_merge((array)$this->inlineSettings[$namespace], $array);
1877  }
1878  } else {
1879  $this->inlineSettings = array_merge($this->inlineSettings, $array);
1880  }
1881  }
1882 
1888  public function addBodyContent($content)
1889  {
1890  $this->bodyContent .= $content;
1891  }
1892 
1893  /*****************************************************/
1894  /* */
1895  /* Render Functions */
1896  /* */
1897  /*****************************************************/
1904  public function render($part = self::PART_COMPLETE)
1905  {
1906  $this->prepareRendering();
1908  $metaTags = implode(LF, $this->metaTags);
1910  $template = $this->getTemplateForPart($part);
1911 
1912  // The page renderer needs a full reset, even when only rendering one part of the page
1913  // This means that you can only register footer files *after* the header has been already rendered.
1914  // In case you render the footer part first, header files can only be added *after* the footer has been rendered
1915  $this->reset();
1916  $templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1917  return trim($templateService->substituteMarkerArray($template, $markerArray, '###|###'));
1918  }
1919 
1927  public function renderPageWithUncachedObjects($substituteHash)
1928  {
1929  $this->prepareRendering();
1930  $markerArray = $this->getPreparedMarkerArrayForPageWithUncachedObjects($substituteHash);
1931  $template = $this->getTemplateForPart(self::PART_COMPLETE);
1932  $templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1933  return trim($templateService->substituteMarkerArray($template, $markerArray, '###|###'));
1934  }
1935 
1945  public function renderJavaScriptAndCssForProcessingOfUncachedContentObjects($cachedPageContent, $substituteHash)
1946  {
1947  $this->prepareRendering();
1949  $title = $this->title ? str_replace('|', htmlspecialchars($this->title), $this->titleTag) : '';
1950  $markerArray = [
1951  '<!-- ###TITLE' . $substituteHash . '### -->' => $title,
1952  '<!-- ###CSS_LIBS' . $substituteHash . '### -->' => $cssLibs,
1953  '<!-- ###CSS_INCLUDE' . $substituteHash . '### -->' => $cssFiles,
1954  '<!-- ###CSS_INLINE' . $substituteHash . '### -->' => $cssInline,
1955  '<!-- ###JS_INLINE' . $substituteHash . '### -->' => $jsInline,
1956  '<!-- ###JS_INCLUDE' . $substituteHash . '### -->' => $jsFiles,
1957  '<!-- ###JS_LIBS' . $substituteHash . '### -->' => $jsLibs,
1958  '<!-- ###META' . $substituteHash . '### -->' => implode(LF, $this->metaTags),
1959  '<!-- ###HEADERDATA' . $substituteHash . '### -->' => implode(LF, $this->headerData),
1960  '<!-- ###FOOTERDATA' . $substituteHash . '### -->' => implode(LF, $this->footerData),
1961  '<!-- ###JS_LIBS_FOOTER' . $substituteHash . '### -->' => $jsFooterLibs,
1962  '<!-- ###JS_INCLUDE_FOOTER' . $substituteHash . '### -->' => $jsFooterFiles,
1963  '<!-- ###JS_INLINE_FOOTER' . $substituteHash . '### -->' => $jsFooterInline
1964  ];
1965  foreach ($markerArray as $placeHolder => $content) {
1966  $cachedPageContent = str_replace($placeHolder, $content, $cachedPageContent);
1967  }
1968  $this->reset();
1969  return $cachedPageContent;
1970  }
1971 
1977  protected function prepareRendering()
1978  {
1979  if ($this->getRenderXhtml()) {
1980  $this->endingSlash = ' /';
1981  } else {
1982  $this->metaCharsetTag = str_replace(' />', '>', $this->metaCharsetTag);
1983  $this->baseUrlTag = str_replace(' />', '>', $this->baseUrlTag);
1984  $this->shortcutTag = str_replace(' />', '>', $this->shortcutTag);
1985  $this->endingSlash = '';
1986  }
1987  }
1988 
1994  protected function renderJavaScriptAndCss()
1995  {
1996  $this->executePreRenderHook();
1997  $mainJsLibs = $this->renderMainJavaScriptLibraries();
1998  if ($this->concatenateFiles || $this->concatenateJavascript || $this->concatenateCss) {
1999  // Do the file concatenation
2000  $this->doConcatenate();
2001  }
2002  if ($this->compressCss || $this->compressJavascript) {
2003  // Do the file compression
2004  $this->doCompress();
2005  }
2007  $cssLibs = $this->renderCssLibraries();
2008  $cssFiles = $this->renderCssFiles();
2009  $cssInline = $this->renderCssInline();
2011  list($jsFiles, $jsFooterFiles) = $this->renderJavaScriptFiles();
2013  $jsLibs = $mainJsLibs . $jsLibs;
2014  if ($this->moveJsFromHeaderToFooter) {
2016  $jsLibs = '';
2018  $jsFiles = '';
2020  $jsInline = '';
2021  }
2024  }
2025 
2042  {
2043  $markerArray = [
2044  'XMLPROLOG_DOCTYPE' => $this->xmlPrologAndDocType,
2045  'HTMLTAG' => $this->htmlTag,
2046  'HEADTAG' => $this->headTag,
2047  'METACHARSET' => $this->charSet ? str_replace('|', htmlspecialchars($this->charSet), $this->metaCharsetTag) : '',
2048  'INLINECOMMENT' => $this->inlineComments ? LF . LF . '<!-- ' . LF . implode(LF, $this->inlineComments) . '-->' . LF . LF : '',
2049  'BASEURL' => $this->baseUrl ? str_replace('|', $this->baseUrl, $this->baseUrlTag) : '',
2050  'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) : '',
2051  'CSS_LIBS' => $cssLibs,
2052  'CSS_INCLUDE' => $cssFiles,
2053  'CSS_INLINE' => $cssInline,
2054  'JS_INLINE' => $jsInline,
2055  'JS_INCLUDE' => $jsFiles,
2056  'JS_LIBS' => $jsLibs,
2057  'TITLE' => $this->title ? str_replace('|', htmlspecialchars($this->title), $this->titleTag) : '',
2058  'META' => $metaTags,
2059  'HEADERDATA' => $this->headerData ? implode(LF, $this->headerData) : '',
2060  'FOOTERDATA' => $this->footerData ? implode(LF, $this->footerData) : '',
2061  'JS_LIBS_FOOTER' => $jsFooterLibs,
2062  'JS_INCLUDE_FOOTER' => $jsFooterFiles,
2063  'JS_INLINE_FOOTER' => $jsFooterInline,
2064  'BODY' => $this->bodyContent
2065  ];
2066  $markerArray = array_map('trim', $markerArray);
2067  return $markerArray;
2068  }
2069 
2076  protected function getPreparedMarkerArrayForPageWithUncachedObjects($substituteHash)
2077  {
2078  $markerArray = [
2079  'XMLPROLOG_DOCTYPE' => $this->xmlPrologAndDocType,
2080  'HTMLTAG' => $this->htmlTag,
2081  'HEADTAG' => $this->headTag,
2082  'METACHARSET' => $this->charSet ? str_replace('|', htmlspecialchars($this->charSet), $this->metaCharsetTag) : '',
2083  'INLINECOMMENT' => $this->inlineComments ? LF . LF . '<!-- ' . LF . implode(LF, $this->inlineComments) . '-->' . LF . LF : '',
2084  'BASEURL' => $this->baseUrl ? str_replace('|', $this->baseUrl, $this->baseUrlTag) : '',
2085  'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) : '',
2086  'META' => '<!-- ###META' . $substituteHash . '### -->',
2087  'BODY' => $this->bodyContent,
2088  'TITLE' => '<!-- ###TITLE' . $substituteHash . '### -->',
2089  'CSS_LIBS' => '<!-- ###CSS_LIBS' . $substituteHash . '### -->',
2090  'CSS_INCLUDE' => '<!-- ###CSS_INCLUDE' . $substituteHash . '### -->',
2091  'CSS_INLINE' => '<!-- ###CSS_INLINE' . $substituteHash . '### -->',
2092  'JS_INLINE' => '<!-- ###JS_INLINE' . $substituteHash . '### -->',
2093  'JS_INCLUDE' => '<!-- ###JS_INCLUDE' . $substituteHash . '### -->',
2094  'JS_LIBS' => '<!-- ###JS_LIBS' . $substituteHash . '### -->',
2095  'HEADERDATA' => '<!-- ###HEADERDATA' . $substituteHash . '### -->',
2096  'FOOTERDATA' => '<!-- ###FOOTERDATA' . $substituteHash . '### -->',
2097  'JS_LIBS_FOOTER' => '<!-- ###JS_LIBS_FOOTER' . $substituteHash . '### -->',
2098  'JS_INCLUDE_FOOTER' => '<!-- ###JS_INCLUDE_FOOTER' . $substituteHash . '### -->',
2099  'JS_INLINE_FOOTER' => '<!-- ###JS_INLINE_FOOTER' . $substituteHash . '### -->'
2100  ];
2101  $markerArray = array_map('trim', $markerArray);
2102  return $markerArray;
2103  }
2104 
2111  protected function getTemplateForPart($part)
2112  {
2113  $templateFile = GeneralUtility::getFileAbsFileName($this->templateFile);
2114  if (is_file($templateFile)) {
2115  $template = file_get_contents($templateFile);
2116  if ($this->removeLineBreaksFromTemplate) {
2117  $template = strtr($template, [LF => '', CR => '']);
2118  }
2119  if ($part !== self::PART_COMPLETE) {
2120  $templatePart = explode('###BODY###', $template);
2121  $template = $templatePart[$part - 1];
2122  }
2123  } else {
2124  $template = '';
2125  }
2126  return $template;
2127  }
2128 
2135  protected function renderMainJavaScriptLibraries()
2136  {
2137  $out = '';
2138 
2139  // Include RequireJS
2140  if ($this->addRequireJs) {
2141  $out .= $this->getRequireJsLoader();
2142  }
2143 
2144  // Include jQuery Core for each namespace, depending on the version and source
2145  if (!empty($this->jQueryVersions)) {
2146  foreach ($this->jQueryVersions as $namespace => $jQueryVersion) {
2147  $out .= $this->renderJqueryScriptTag($jQueryVersion['version'], $jQueryVersion['source'], $namespace);
2148  }
2149  }
2150 
2151  // Include extJS
2152  if ($this->addExtJS) {
2153  // Use the base adapter all the time
2154  $out .= '<script src="' . $this->processJsFile($this->extJsPath . 'adapter/ext-base' . ($this->enableExtJsDebug ? '-debug' : '') . '.js') . '" type="text/javascript"></script>' . LF;
2155  $out .= '<script src="' . $this->processJsFile($this->extJsPath . 'ext-all' . ($this->enableExtJsDebug ? '-debug' : '') . '.js') . '" type="text/javascript"></script>' . LF;
2156  // Add extJS localization
2157  // Load standard ISO mapping and modify for use with ExtJS
2158  $localeMap = $this->locales->getIsoMapping();
2159  $localeMap[''] = 'en';
2160  $localeMap['default'] = 'en';
2161  // Greek
2162  $localeMap['gr'] = 'el_GR';
2163  // Norwegian Bokmaal
2164  $localeMap['no'] = 'no_BO';
2165  // Swedish
2166  $localeMap['se'] = 'se_SV';
2167  $extJsLang = isset($localeMap[$this->lang]) ? $localeMap[$this->lang] : $this->lang;
2168  $extJsLocaleFile = $this->extJsPath . 'locale/ext-lang-' . $extJsLang . '.js';
2169  if (file_exists(PATH_site . $extJsLocaleFile)) {
2170  $out .= '<script src="' . $this->processJsFile($extJsLocaleFile) . '" type="text/javascript" charset="utf-8"></script>' . LF;
2171  }
2172  // Remove extjs from JScodeLibArray
2173  unset($this->jsFiles[$this->extJsPath . 'ext-all.js'], $this->jsFiles[$this->extJsPath . 'ext-all-debug.js']);
2174  }
2176  if (TYPO3_MODE === 'BE') {
2177  $noBackendUserLoggedIn = empty($GLOBALS['BE_USER']->user['uid']);
2178  $this->addAjaxUrlsToInlineSettings($noBackendUserLoggedIn);
2179  }
2180  $inlineSettings = $this->inlineLanguageLabels ? 'TYPO3.lang = ' . json_encode($this->inlineLanguageLabels) . ';' : '';
2181  $inlineSettings .= $this->inlineSettings ? 'TYPO3.settings = ' . json_encode($this->inlineSettings) . ';' : '';
2182  if ($this->addExtJS) {
2183  // Set clear.gif, move it on top, add handler code
2184  $code = '';
2185  if (!empty($this->extOnReadyCode)) {
2186  foreach ($this->extOnReadyCode as $block) {
2187  $code .= $block;
2188  }
2189  }
2190  $clearGifPath = GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Public/Images/clear.gif');
2191  $clearGifPath = htmlspecialchars(PathUtility::getAbsoluteWebPath($clearGifPath));
2192  $out .= $this->inlineJavascriptWrap[0] . '
2193  Ext.ns("TYPO3");
2194  Ext.BLANK_IMAGE_URL = "' . $clearGifPath . '";
2195  Ext.SSL_SECURE_URL = "' . $clearGifPath . '";' . LF
2196  . $inlineSettings
2197  . 'Ext.onReady(function() {'
2198  . $code
2199  . ' });'
2200  . $this->inlineJavascriptWrap[1];
2201  $this->extOnReadyCode = [];
2202  // Include TYPO3.l10n object
2203  if (TYPO3_MODE === 'BE') {
2204  $out .= '<script src="' . $this->processJsFile('EXT:lang/Resources/Public/JavaScript/Typo3Lang.js') . '" type="text/javascript" charset="utf-8"></script>' . LF;
2205  }
2206  if ($this->extJScss) {
2207  if (isset($GLOBALS['TBE_STYLES']['extJS']['all'])) {
2208  $this->addCssLibrary($GLOBALS['TBE_STYLES']['extJS']['all'], 'stylesheet', 'all', '', true);
2209  } else {
2210  $this->addCssLibrary($this->extJsPath . 'resources/css/ext-all-notheme.css', 'stylesheet', 'all', '', true);
2211  }
2212  }
2213  if ($this->extJStheme) {
2214  if (isset($GLOBALS['TBE_STYLES']['extJS']['theme'])) {
2215  $this->addCssLibrary($GLOBALS['TBE_STYLES']['extJS']['theme'], 'stylesheet', 'all', '', true);
2216  } else {
2217  $this->addCssLibrary($this->extJsPath . 'resources/css/xtheme-blue.css', 'stylesheet', 'all', '', true);
2218  }
2219  }
2220  } else {
2221  // no extJS loaded, but still inline settings
2222  if ($inlineSettings !== '') {
2223  // make sure the global TYPO3 is available
2224  $inlineSettings = 'var TYPO3 = TYPO3 || {};' . CRLF . $inlineSettings;
2225  $out .= $this->inlineJavascriptWrap[0] . $inlineSettings . $this->inlineJavascriptWrap[1];
2226  // Add language module only if also jquery is guaranteed to be there
2227  if (TYPO3_MODE === 'BE' && !empty($this->jQueryVersions)) {
2228  $this->loadRequireJsModule('TYPO3/CMS/Lang/Lang');
2229  }
2230  }
2231  }
2232  return $out;
2233  }
2234 
2238  protected function loadJavaScriptLanguageStrings()
2239  {
2240  if (!empty($this->inlineLanguageLabelFiles)) {
2241  foreach ($this->inlineLanguageLabelFiles as $languageLabelFile) {
2242  $this->includeLanguageFileForInline($languageLabelFile['fileRef'], $languageLabelFile['selectionPrefix'], $languageLabelFile['stripFromSelectionName'], $languageLabelFile['errorMode']);
2243  }
2244  }
2245  $this->inlineLanguageLabelFiles = [];
2246  // Convert settings back to UTF-8 since json_encode() only works with UTF-8:
2247  if ($this->getCharSet() && $this->getCharSet() !== 'utf-8' && is_array($this->inlineSettings)) {
2248  $this->convertCharsetRecursivelyToUtf8($this->inlineSettings, $this->getCharSet());
2249  }
2250  }
2251 
2258  protected function convertCharsetRecursivelyToUtf8(&$data, string $fromCharset)
2259  {
2260  foreach ($data as $key => $value) {
2261  if (is_array($data[$key])) {
2262  $this->convertCharsetRecursivelyToUtf8($data[$key], $fromCharset);
2263  } elseif (is_string($data[$key])) {
2264  $data[$key] = mb_convert_encoding($data[$key], 'utf-8', $fromCharset);
2265  }
2266  }
2267  }
2268 
2274  protected function addAjaxUrlsToInlineSettings(bool $publicRoutesOnly = false)
2275  {
2276  $ajaxUrls = [];
2277  // Note: this method of adding Ajax URLs is @deprecated as of TYPO3 v8, and will be removed in TYPO3 v9
2278  foreach ($GLOBALS['TYPO3_CONF_VARS']['BE']['AJAX'] as $ajaxHandler => $_) {
2279  $ajaxUrls[$ajaxHandler] = BackendUtility::getAjaxUrl($ajaxHandler);
2280  }
2281 
2282  // also add the ajax-based routes
2284  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
2286  $router = GeneralUtility::makeInstance(Router::class);
2287  $routes = $router->getRoutes();
2288  foreach ($routes as $routeIdentifier => $route) {
2289  if ($publicRoutesOnly && $route->getOption('access') !== 'public') {
2290  continue;
2291  }
2292  if ($route->getOption('ajax')) {
2293  $uri = (string)$uriBuilder->buildUriFromRoute($routeIdentifier);
2294  // use the shortened value in order to use this in JavaScript
2295  $routeIdentifier = str_replace('ajax_', '', $routeIdentifier);
2296  $ajaxUrls[$routeIdentifier] = $uri;
2297  }
2298  }
2299 
2300  $this->inlineSettings['ajaxUrls'] = $ajaxUrls;
2301  }
2302 
2311  protected function renderJqueryScriptTag($version, $source, $namespace)
2312  {
2313  switch (true) {
2314  case isset($this->jQueryCdnUrls[$source]):
2315  if ($this->enableJqueryDebug) {
2316  $minifyPart = '';
2317  } else {
2318  $minifyPart = '.min';
2319  }
2320  $jQueryFileName = sprintf($this->jQueryCdnUrls[$source], $version, $minifyPart);
2321  break;
2322  case $source === 'local':
2323  $jQueryFileName = $this->jQueryPath . 'jquery-' . rawurlencode($version);
2324  if ($this->enableJqueryDebug) {
2325  $jQueryFileName .= '.js';
2326  } else {
2327  $jQueryFileName .= '.min.js';
2328  }
2329  $jQueryFileName = $this->processJsFile($jQueryFileName);
2330  break;
2331  default:
2332  $jQueryFileName = $source;
2333  }
2334  // Include the jQuery Core
2335  $scriptTag = '<script src="' . htmlspecialchars($jQueryFileName) . '" type="text/javascript"></script>' . LF;
2336  // Set the noConflict mode to be available via "TYPO3.jQuery" in all installations
2337  switch ($namespace) {
2338  case self::JQUERY_NAMESPACE_DEFAULT_NOCONFLICT:
2339  $scriptTag .= GeneralUtility::wrapJS('jQuery.noConflict();') . LF;
2340  break;
2341  case self::JQUERY_NAMESPACE_NONE:
2342  break;
2343  case self::JQUERY_NAMESPACE_DEFAULT:
2344 
2345  default:
2346  $scriptTag .= GeneralUtility::wrapJS('var TYPO3 = TYPO3 || {}; TYPO3.' . $namespace . ' = jQuery.noConflict(true); var $ = TYPO3.' . $namespace . ';') . LF;
2347  }
2348  return $scriptTag;
2349  }
2350 
2356  protected function renderCssLibraries()
2357  {
2358  $cssFiles = '';
2359  if (!empty($this->cssLibs)) {
2360  foreach ($this->cssLibs as $file => $properties) {
2361  $file = $this->getStreamlinedFileName($file);
2362  $tag = '<link rel="' . htmlspecialchars($properties['rel'])
2363  . '" type="text/css" href="' . htmlspecialchars($file)
2364  . '" media="' . htmlspecialchars($properties['media']) . '"'
2365  . ($properties['title'] ? ' title="' . htmlspecialchars($properties['title']) . '"' : '')
2366  . $this->endingSlash . '>';
2367  if ($properties['allWrap']) {
2368  $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2369  $tag = $wrapArr[0] . $tag . $wrapArr[1];
2370  }
2371  $tag .= LF;
2372  if ($properties['forceOnTop']) {
2373  $cssFiles = $tag . $cssFiles;
2374  } else {
2375  $cssFiles .= $tag;
2376  }
2377  }
2378  }
2379  return $cssFiles;
2380  }
2381 
2387  protected function renderCssFiles()
2388  {
2389  $cssFiles = '';
2390  if (!empty($this->cssFiles)) {
2391  foreach ($this->cssFiles as $file => $properties) {
2392  $file = $this->getStreamlinedFileName($file);
2393  $tag = '<link rel="' . htmlspecialchars($properties['rel'])
2394  . '" type="text/css" href="' . htmlspecialchars($file)
2395  . '" media="' . htmlspecialchars($properties['media']) . '"'
2396  . ($properties['title'] ? ' title="' . htmlspecialchars($properties['title']) . '"' : '')
2397  . $this->endingSlash . '>';
2398  if ($properties['allWrap']) {
2399  $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2400  $tag = $wrapArr[0] . $tag . $wrapArr[1];
2401  }
2402  $tag .= LF;
2403  if ($properties['forceOnTop']) {
2404  $cssFiles = $tag . $cssFiles;
2405  } else {
2406  $cssFiles .= $tag;
2407  }
2408  }
2409  }
2410  return $cssFiles;
2411  }
2412 
2418  protected function renderCssInline()
2419  {
2420  $cssInline = '';
2421  if (!empty($this->cssInline)) {
2422  foreach ($this->cssInline as $name => $properties) {
2423  $cssCode = '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF;
2424  if ($properties['forceOnTop']) {
2425  $cssInline = $cssCode . $cssInline;
2426  } else {
2427  $cssInline .= $cssCode;
2428  }
2429  }
2430  $cssInline = $this->inlineCssWrap[0] . $cssInline . $this->inlineCssWrap[1];
2431  }
2432  return $cssInline;
2433  }
2434 
2441  {
2442  $jsLibs = '';
2443  $jsFooterLibs = '';
2444  if (!empty($this->jsLibs)) {
2445  foreach ($this->jsLibs as $properties) {
2446  $properties['file'] = $this->getStreamlinedFileName($properties['file']);
2447  $async = ($properties['async']) ? ' async="async"' : '';
2448  $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '" crossorigin="anonymous"' : '';
2449  $tag = '<script src="' . htmlspecialchars($properties['file']) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $integrity . '></script>';
2450  if ($properties['allWrap']) {
2451  $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2452  $tag = $wrapArr[0] . $tag . $wrapArr[1];
2453  }
2454  $tag .= LF;
2455  if ($properties['forceOnTop']) {
2456  if ($properties['section'] === self::PART_HEADER) {
2457  $jsLibs = $tag . $jsLibs;
2458  } else {
2459  $jsFooterLibs = $tag . $jsFooterLibs;
2460  }
2461  } else {
2462  if ($properties['section'] === self::PART_HEADER) {
2463  $jsLibs .= $tag;
2464  } else {
2465  $jsFooterLibs .= $tag;
2466  }
2467  }
2468  }
2469  }
2470  if ($this->moveJsFromHeaderToFooter) {
2472  $jsLibs = '';
2473  }
2474  return [$jsLibs, $jsFooterLibs];
2475  }
2476 
2482  protected function renderJavaScriptFiles()
2483  {
2484  $jsFiles = '';
2485  $jsFooterFiles = '';
2486  if (!empty($this->jsFiles)) {
2487  foreach ($this->jsFiles as $file => $properties) {
2488  $file = $this->getStreamlinedFileName($file);
2489  $async = ($properties['async']) ? ' async="async"' : '';
2490  $integrity = ($properties['integrity']) ? ' integrity="' . htmlspecialchars($properties['integrity']) . '" crossorigin="anonymous"' : '';
2491  $tag = '<script src="' . htmlspecialchars($file) . '" type="' . htmlspecialchars($properties['type']) . '"' . $async . $integrity . '></script>';
2492  if ($properties['allWrap']) {
2493  $wrapArr = explode($properties['splitChar'] ?: '|', $properties['allWrap'], 2);
2494  $tag = $wrapArr[0] . $tag . $wrapArr[1];
2495  }
2496  $tag .= LF;
2497  if ($properties['forceOnTop']) {
2498  if ($properties['section'] === self::PART_HEADER) {
2499  $jsFiles = $tag . $jsFiles;
2500  } else {
2501  $jsFooterFiles = $tag . $jsFooterFiles;
2502  }
2503  } else {
2504  if ($properties['section'] === self::PART_HEADER) {
2505  $jsFiles .= $tag;
2506  } else {
2507  $jsFooterFiles .= $tag;
2508  }
2509  }
2510  }
2511  }
2512  if ($this->moveJsFromHeaderToFooter) {
2514  $jsFiles = '';
2515  }
2516  return [$jsFiles, $jsFooterFiles];
2517  }
2518 
2524  protected function renderInlineJavaScript()
2525  {
2526  $jsInline = '';
2527  $jsFooterInline = '';
2528  if (!empty($this->jsInline)) {
2529  foreach ($this->jsInline as $name => $properties) {
2530  $jsCode = '/*' . htmlspecialchars($name) . '*/' . LF . $properties['code'] . LF;
2531  if ($properties['forceOnTop']) {
2532  if ($properties['section'] === self::PART_HEADER) {
2533  $jsInline = $jsCode . $jsInline;
2534  } else {
2535  $jsFooterInline = $jsCode . $jsFooterInline;
2536  }
2537  } else {
2538  if ($properties['section'] === self::PART_HEADER) {
2539  $jsInline .= $jsCode;
2540  } else {
2541  $jsFooterInline .= $jsCode;
2542  }
2543  }
2544  }
2545  }
2546  if ($jsInline) {
2547  $jsInline = $this->inlineJavascriptWrap[0] . $jsInline . $this->inlineJavascriptWrap[1];
2548  }
2549  if ($jsFooterInline) {
2550  $jsFooterInline = $this->inlineJavascriptWrap[0] . $jsFooterInline . $this->inlineJavascriptWrap[1];
2551  }
2552  if ($this->moveJsFromHeaderToFooter) {
2554  $jsInline = '';
2555  }
2556  return [$jsInline, $jsFooterInline];
2557  }
2558 
2568  protected function includeLanguageFileForInline($fileRef, $selectionPrefix = '', $stripFromSelectionName = '', $errorMode = 0)
2569  {
2570  if (!isset($this->lang) || !isset($this->charSet)) {
2571  throw new \RuntimeException('Language and character encoding are not set.', 1284906026);
2572  }
2573  $labelsFromFile = [];
2574  $allLabels = $this->readLLfile($fileRef, $errorMode);
2575  if ($allLabels !== false) {
2576  // Merge language specific translations:
2577  if ($this->lang !== 'default' && isset($allLabels[$this->lang])) {
2578  $labels = array_merge($allLabels['default'], $allLabels[$this->lang]);
2579  } else {
2580  $labels = $allLabels['default'];
2581  }
2582  // Iterate through all locallang labels:
2583  foreach ($labels as $label => $value) {
2584  // If $selectionPrefix is set, only respect labels that start with $selectionPrefix
2585  if ($selectionPrefix === '' || strpos($label, $selectionPrefix) === 0) {
2586  // Remove substring $stripFromSelectionName from label
2587  $label = str_replace($stripFromSelectionName, '', $label);
2588  $labelsFromFile[$label] = $value;
2589  }
2590  }
2591  $this->inlineLanguageLabels = array_merge($this->inlineLanguageLabels, $labelsFromFile);
2592  }
2593  }
2594 
2602  protected function readLLfile($fileRef, $errorMode = 0)
2603  {
2605  $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
2606 
2607  if ($this->lang !== 'default') {
2608  $languages = array_reverse($this->languageDependencies);
2609  // At least we need to have English
2610  if (empty($languages)) {
2611  $languages[] = 'default';
2612  }
2613  } else {
2614  $languages = ['default'];
2615  }
2616 
2617  $localLanguage = [];
2618  foreach ($languages as $language) {
2619  $tempLL = $languageFactory->getParsedData($fileRef, $language, $this->charSet, $errorMode);
2620 
2621  $localLanguage['default'] = $tempLL['default'];
2622  if (!isset($localLanguage[$this->lang])) {
2623  $localLanguage[$this->lang] = $localLanguage['default'];
2624  }
2625  if ($this->lang !== 'default' && isset($tempLL[$language])) {
2626  // Merge current language labels onto labels from previous language
2627  // This way we have a labels with fall back applied
2628  \TYPO3\CMS\Core\Utility\ArrayUtility::mergeRecursiveWithOverrule($localLanguage[$this->lang], $tempLL[$language], true, false);
2629  }
2630  }
2631 
2632  return $localLanguage;
2633  }
2634 
2635  /*****************************************************/
2636  /* */
2637  /* Tools */
2638  /* */
2639  /*****************************************************/
2644  protected function doConcatenate()
2645  {
2646  $this->doConcatenateCss();
2647  $this->doConcatenateJavaScript();
2648  }
2649 
2653  protected function doConcatenateJavaScript()
2654  {
2655  if ($this->concatenateFiles || $this->concatenateJavascript) {
2656  if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsConcatenateHandler'])) {
2657  // use external concatenation routine
2658  $params = [
2659  'jsLibs' => &$this->jsLibs,
2660  'jsFiles' => &$this->jsFiles,
2661  'jsFooterFiles' => &$this->jsFooterFiles,
2662  'headerData' => &$this->headerData,
2663  'footerData' => &$this->footerData
2664  ];
2665  GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsConcatenateHandler'], $params, $this);
2666  } else {
2667  $this->jsLibs = $this->getCompressor()->concatenateJsFiles($this->jsLibs);
2668  $this->jsFiles = $this->getCompressor()->concatenateJsFiles($this->jsFiles);
2669  $this->jsFooterFiles = $this->getCompressor()->concatenateJsFiles($this->jsFooterFiles);
2670  }
2671  }
2672  }
2673 
2677  protected function doConcatenateCss()
2678  {
2679  if ($this->concatenateFiles || $this->concatenateCss) {
2680  if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssConcatenateHandler'])) {
2681  // use external concatenation routine
2682  $params = [
2683  'cssFiles' => &$this->cssFiles,
2684  'cssLibs' => &$this->cssLibs,
2685  'headerData' => &$this->headerData,
2686  'footerData' => &$this->footerData
2687  ];
2688  GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssConcatenateHandler'], $params, $this);
2689  } else {
2690  $cssOptions = [];
2691  if (TYPO3_MODE === 'BE') {
2692  $cssOptions = ['baseDirectories' => $GLOBALS['TBE_TEMPLATE']->getSkinStylesheetDirectories()];
2693  }
2694  $this->cssLibs = $this->getCompressor()->concatenateCssFiles($this->cssLibs, $cssOptions);
2695  $this->cssFiles = $this->getCompressor()->concatenateCssFiles($this->cssFiles, $cssOptions);
2696  }
2697  }
2698  }
2699 
2703  protected function doCompress()
2704  {
2705  $this->doCompressJavaScript();
2706  $this->doCompressCss();
2707  }
2708 
2712  protected function doCompressCss()
2713  {
2714  if ($this->compressCss) {
2715  if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'])) {
2716  // Use external compression routine
2717  $params = [
2718  'cssInline' => &$this->cssInline,
2719  'cssFiles' => &$this->cssFiles,
2720  'cssLibs' => &$this->cssLibs,
2721  'headerData' => &$this->headerData,
2722  'footerData' => &$this->footerData
2723  ];
2724  GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['cssCompressHandler'], $params, $this);
2725  } else {
2726  $this->cssLibs = $this->getCompressor()->compressCssFiles($this->cssLibs);
2727  $this->cssFiles = $this->getCompressor()->compressCssFiles($this->cssFiles);
2728  }
2729  }
2730  }
2731 
2735  protected function doCompressJavaScript()
2736  {
2737  if ($this->compressJavascript) {
2738  if (!empty($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler'])) {
2739  // Use external compression routine
2740  $params = [
2741  'jsInline' => &$this->jsInline,
2742  'jsFooterInline' => &$this->jsFooterInline,
2743  'jsLibs' => &$this->jsLibs,
2744  'jsFiles' => &$this->jsFiles,
2745  'jsFooterFiles' => &$this->jsFooterFiles,
2746  'headerData' => &$this->headerData,
2747  'footerData' => &$this->footerData
2748  ];
2749  GeneralUtility::callUserFunction($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['jsCompressHandler'], $params, $this);
2750  } else {
2751  // Traverse the arrays, compress files
2752  if (!empty($this->jsInline)) {
2753  foreach ($this->jsInline as $name => $properties) {
2754  if ($properties['compress']) {
2755  $error = '';
2756  $this->jsInline[$name]['code'] = GeneralUtility::minifyJavaScript($properties['code'], $error);
2757  if ($error) {
2758  $this->compressError .= 'Error with minify JS Inline Block "' . $name . '": ' . $error . LF;
2759  }
2760  }
2761  }
2762  }
2763  $this->jsLibs = $this->getCompressor()->compressJsFiles($this->jsLibs);
2764  $this->jsFiles = $this->getCompressor()->compressJsFiles($this->jsFiles);
2765  $this->jsFooterFiles = $this->getCompressor()->compressJsFiles($this->jsFooterFiles);
2766  }
2767  }
2768  }
2769 
2775  protected function getCompressor()
2776  {
2777  if ($this->compressor === null) {
2778  $this->compressor = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\ResourceCompressor::class);
2779  }
2780  return $this->compressor;
2781  }
2782 
2791  protected function processJsFile($filename)
2792  {
2793  $filename = $this->getStreamlinedFileName($filename, false);
2794  if ($this->compressJavascript) {
2795  $filename = $this->getCompressor()->compressJsFile($filename);
2796  } elseif (TYPO3_MODE === 'FE') {
2797  $filename = GeneralUtility::createVersionNumberedFilename($filename);
2798  }
2799  return $this->getAbsoluteWebPath($filename);
2800  }
2801 
2814  protected function getStreamlinedFileName($file, $prepareForOutput = true)
2815  {
2816  if (strpos($file, 'EXT:') === 0) {
2817  $file = GeneralUtility::getFileAbsFileName($file);
2818  // as the path is now absolute, make it "relative" to the current script to stay compatible
2819  $file = PathUtility::getRelativePathTo($file);
2820  $file = rtrim($file, '/');
2821  } else {
2822  $file = GeneralUtility::resolveBackPath($file);
2823  }
2824  if ($prepareForOutput) {
2826  $file = $this->getAbsoluteWebPath($file);
2827  }
2828  return $file;
2829  }
2830 
2840  protected function getAbsoluteWebPath(string $file): string
2841  {
2842  if (TYPO3_MODE === 'FE') {
2843  return $file;
2844  }
2845  return PathUtility::getAbsoluteWebPath($file);
2846  }
2847 
2853  protected function getTypoScriptFrontendController()
2854  {
2855  return $GLOBALS['TSFE'];
2856  }
2857 
2863  protected function getLanguageService()
2864  {
2865  return $GLOBALS['LANG'];
2866  }
2867 
2868  /*****************************************************/
2869  /* */
2870  /* Hooks */
2871  /* */
2872  /*****************************************************/
2876  protected function executePreRenderHook()
2877  {
2878  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-preProcess'])) {
2879  $params = [
2880  'jsLibs' => &$this->jsLibs,
2881  'jsFooterLibs' => &$this->jsFooterLibs,
2882  'jsFiles' => &$this->jsFiles,
2883  'jsFooterFiles' => &$this->jsFooterFiles,
2884  'cssLibs' => &$this->cssLibs,
2885  'cssFiles' => &$this->cssFiles,
2886  'headerData' => &$this->headerData,
2887  'footerData' => &$this->footerData,
2888  'jsInline' => &$this->jsInline,
2889  'jsFooterInline' => &$this->jsFooterInline,
2890  'cssInline' => &$this->cssInline
2891  ];
2892  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-preProcess'] as $hook) {
2893  GeneralUtility::callUserFunction($hook, $params, $this);
2894  }
2895  }
2896  }
2897 
2901  protected function executeRenderPostTransformHook()
2902  {
2903  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postTransform'])) {
2904  $params = [
2905  'jsLibs' => &$this->jsLibs,
2906  'jsFooterLibs' => &$this->jsFooterLibs,
2907  'jsFiles' => &$this->jsFiles,
2908  'jsFooterFiles' => &$this->jsFooterFiles,
2909  'cssLibs' => &$this->cssLibs,
2910  'cssFiles' => &$this->cssFiles,
2911  'headerData' => &$this->headerData,
2912  'footerData' => &$this->footerData,
2913  'jsInline' => &$this->jsInline,
2914  'jsFooterInline' => &$this->jsFooterInline,
2915  'cssInline' => &$this->cssInline
2916  ];
2917  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postTransform'] as $hook) {
2918  GeneralUtility::callUserFunction($hook, $params, $this);
2919  }
2920  }
2921  }
2922 
2937  {
2938  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'])) {
2939  $params = [
2940  'jsLibs' => &$jsLibs,
2941  'jsFiles' => &$jsFiles,
2942  'jsFooterFiles' => &$jsFooterFiles,
2943  'cssLibs' => &$cssLibs,
2944  'cssFiles' => &$cssFiles,
2945  'headerData' => &$this->headerData,
2946  'footerData' => &$this->footerData,
2947  'jsInline' => &$jsInline,
2948  'cssInline' => &$cssInline,
2949  'xmlPrologAndDocType' => &$this->xmlPrologAndDocType,
2950  'htmlTag' => &$this->htmlTag,
2951  'headTag' => &$this->headTag,
2952  'charSet' => &$this->charSet,
2953  'metaCharsetTag' => &$this->metaCharsetTag,
2954  'shortcutTag' => &$this->shortcutTag,
2955  'inlineComments' => &$this->inlineComments,
2956  'baseUrl' => &$this->baseUrl,
2957  'baseUrlTag' => &$this->baseUrlTag,
2958  'favIcon' => &$this->favIcon,
2959  'iconMimeType' => &$this->iconMimeType,
2960  'titleTag' => &$this->titleTag,
2961  'title' => &$this->title,
2962  'metaTags' => &$this->metaTags,
2963  'jsFooterInline' => &$jsFooterInline,
2964  'jsFooterLibs' => &$jsFooterLibs,
2965  'bodyContent' => &$this->bodyContent
2966  ];
2967  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'] as $hook) {
2968  GeneralUtility::callUserFunction($hook, $params, $this);
2969  }
2970  }
2971  }
2972 }
static minifyJavaScript($script, &$error='')
loadJquery($version=null, $source=null, $namespace=self::JQUERY_NAMESPACE_DEFAULT)
renderJavaScriptAndCssForProcessingOfUncachedContentObjects($cachedPageContent, $substituteHash)
convertCharsetRecursivelyToUtf8(&$data, string $fromCharset)
renderPageWithUncachedObjects($substituteHash)
addInlineSettingArray($namespace, array $array)
getStreamlinedFileName($file, $prepareForOutput=true)
getPreparedMarkerArrayForPageWithUncachedObjects($substituteHash)
renderJqueryScriptTag($version, $source, $namespace)
addJsFooterInlineCode($name, $block, $compress=true, $forceOnTop=false)
static getRelativePathTo($targetPath)
Definition: PathUtility.php:29
addJsFooterLibrary($name, $file, $type='text/javascript', $compress=false, $forceOnTop=false, $allWrap='', $excludeFromConcatenation=false, $splitChar='|', $async=false, $integrity='')
addJsFooterFile($file, $type='text/javascript', $compress=true, $forceOnTop=false, $allWrap='', $excludeFromConcatenation=false, $splitChar='|', $async=false, $integrity='')
addCssFile($file, $rel='stylesheet', $media='all', $title='', $compress=true, $forceOnTop=false, $allWrap='', $excludeFromConcatenation=false, $splitChar='|')
static callUserFunction($funcName, &$params, &$ref, $_='', $errorMode=0)
setXmlPrologAndDocType($xmlPrologAndDocType)
addInlineSetting($namespace, $key, $value)
static getAbsoluteWebPath($targetPath)
Definition: PathUtility.php:40
static hmac($input, $additionalSecret='')
static getFileAbsFileName($filename, $_=null, $_2=null)
render($part=self::PART_COMPLETE)
getRequireJsConfig(string $scope=null)
static makeInstance($className,... $constructorArguments)
getPreparedMarkerArray($jsLibs, $jsFiles, $jsFooterFiles, $cssLibs, $cssFiles, $jsInline, $cssInline, $jsFooterInline, $jsFooterLibs, $metaTags)
addCssInlineBlock($name, $block, $compress=false, $forceOnTop=false)
loadExtJS($css=true, $theme=true)
includeLanguageFileForInline($fileRef, $selectionPrefix='', $stripFromSelectionName='', $errorMode=0)
static get($classNameOrType='default',... $constructorArguments)
filterArrayKeys(array $array, array $keys, bool $keep=true)
static mergeRecursiveWithOverrule(array &$original, array $overrule, $addKeys=true, $includeEmptyValues=true, $enableUnsetFeature=true)
addJsInlineCode($name, $block, $compress=true, $forceOnTop=false)
findRequireJsBaseModuleName(string $moduleName)
addInlineLanguageLabelFile($fileRef, $selectionPrefix='', $stripFromSelectionName='', $errorMode=0)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
loadRequireJsModule($mainModuleName, $callBackFunction=null)
addInlineLanguageLabelArray(array $array, $parseWithLanguageService=false)
computeRequireJsConfig($isDevelopment, array $loadedExtensions)
addCssLibrary($file, $rel='stylesheet', $media='all', $title='', $compress=true, $forceOnTop=false, $allWrap='', $excludeFromConcatenation=false, $splitChar='|')
addExtOnReadyCode($block, $forceOnTop=false)
addJsFile($file, $type='text/javascript', $compress=true, $forceOnTop=false, $allWrap='', $excludeFromConcatenation=false, $splitChar='|', $async=false, $integrity='')
addJsLibrary($name, $file, $type='text/javascript', $compress=false, $forceOnTop=false, $allWrap='', $excludeFromConcatenation=false, $splitChar='|', $async=false, $integrity='')
executePostRenderHook(&$jsLibs, &$jsFiles, &$jsFooterFiles, &$cssLibs, &$cssFiles, &$jsInline, &$cssInline, &$jsFooterInline, &$jsFooterLibs)
addRequireJsConfiguration(array $configuration)