24 use TYPO3\CMS\Core\Package\PackageManager;
206 protected $metaCharsetTag =
'<meta http-equiv="Content-Type" content="text/html; charset=|" />';
231 protected $shortcutTag =
'<link rel="shortcut icon" href="%1$s"%2$s />';
270 protected $requireJsPath =
'EXT:core/Resources/Public/JavaScript/Contrib/';
278 protected $jQueryPath =
'EXT:core/Resources/Public/JavaScript/Contrib/jquery/';
317 'google' =>
'https://ajax.googleapis.com/ajax/libs/jquery/%1$s/jquery%2$s.js',
318 'msn' =>
'https://ajax.aspnetcdn.com/ajax/jQuery/jquery-%1$s%2$s.js',
319 'jquery' =>
'https://code.jquery.com/jquery-%1$s%2$s.js',
320 'cloudflare' =>
'https://cdnjs.cloudflare.com/ajax/libs/jquery/%1$s/jquery%2$s.js'
399 protected static $cache =
null;
407 $this->locales = GeneralUtility::makeInstance(\
TYPO3\CMS\Core\Localization\Locales::class);
411 $this->inlineJavascriptWrap = [
412 '<script type="text/javascript">' . LF .
'/*<![CDATA[*/' . LF,
413 '/*]]>*/' . LF .
'</script>' . LF
415 $this->inlineCssWrap = [
416 '<style type="text/css">' . LF .
'/*<![CDATA[*/' . LF .
'<!-- ' . LF,
417 '-->' . LF .
'/*]]>*/' . LF .
'</style>' . LF
420 $this->metaTagRegistry = GeneralUtility::makeInstance(MetaTagManagerRegistry::class);
421 $this->
setMetaTag(
'name',
'generator',
'TYPO3 CMS');
430 GeneralUtility::setSingletonInstance(get_class($this->metaTagRegistry), $this->metaTagRegistry);
446 $this->templateFile =
'EXT:core/Resources/Private/Templates/PageRenderer.html';
448 $this->jsFooterFiles = [];
449 $this->jsInline = [];
450 $this->jsFooterInline = [];
452 $this->cssFiles = [];
453 $this->cssInline = [];
454 $this->metaTags = [];
455 $this->inlineComments = [];
456 $this->headerData = [];
457 $this->footerData = [];
458 $this->extOnReadyCode = [];
459 $this->jQueryVersions = [];
485 $this->renderXhtml = $enable;
516 $this->languageDependencies = [];
519 if (in_array($this->lang, $this->locales->getLocales())) {
521 foreach ($this->locales->getLocaleDependencies($this->lang) as $language) {
522 $this->languageDependencies[] = $language;
594 $this->templateFile = $file;
604 $this->bodyContent = $content;
614 $this->requireJsPath = $path;
624 if ($scope === static::REQUIREJS_SCOPE_CONFIG) {
625 return array_replace_recursive(
626 $this->publicRequireJsConfig,
628 $this->requireJsConfig,
629 [
'shim',
'paths',
'packages'],
635 if ($scope === static::REQUIREJS_SCOPE_RESOLVE) {
637 $this->requireJsConfig,
638 [
'shim',
'paths',
'packages'],
656 $this->moveJsFromHeaderToFooter =
true;
664 $this->moveJsFromHeaderToFooter =
false;
672 $this->compressJavascript =
true;
680 $this->compressJavascript =
false;
688 $this->compressCss =
true;
696 $this->compressCss =
false;
705 trigger_error(
'This method will be removed in TYPO3 v10.0. Use concatenateCss() and concatenateJavascript() instead.', E_USER_DEPRECATED);
706 $this->concatenateFiles =
true;
715 trigger_error(
'This method will be removed in TYPO3 v10.0. Use concatenateCss() and concatenateJavascript() instead.', E_USER_DEPRECATED);
716 $this->concatenateFiles =
false;
724 $this->concatenateJavascript =
true;
732 $this->concatenateJavascript =
false;
740 $this->concatenateCss =
true;
748 $this->concatenateCss =
false;
756 $this->removeLineBreaksFromTemplate =
true;
764 $this->removeLineBreaksFromTemplate =
false;
773 $this->compressJavascript =
false;
774 $this->compressCss =
false;
775 $this->concatenateCss =
false;
776 $this->concatenateJavascript =
false;
777 $this->removeLineBreaksFromTemplate =
false;
778 $this->enableJqueryDebug =
true;
935 trigger_error(
'This method will be removed in TYPO3 v10.0. Use concatenateCss() and concatenateJavascript() instead.', E_USER_DEPRECATED);
1012 trigger_error(
'Method pageRenderer->addMetaTag will be removed with TYPO3 v10.0. Use pageRenderer->setMetaTag instead.', E_USER_DEPRECATED);
1013 if (!in_array($meta, $this->metaTags)) {
1014 $this->metaTags[] = $meta;
1028 public function setMetaTag(
string $type,
string $name,
string $content, array $subProperties = [], $replace =
true)
1031 $type = strtolower($type);
1032 $name = strtolower($name);
1033 if (!in_array($type, [
'property',
'name',
'http-equiv'],
true)) {
1034 throw new \InvalidArgumentException(
1035 'When setting a meta tag the only types allowed are property, name or http-equiv. "' . $type .
'" given.',
1039 $manager = $this->metaTagRegistry->getManagerForProperty($name);
1040 $manager->addProperty($name, $content, $subProperties, $replace, $type);
1051 public function getMetaTag(
string $type,
string $name): array
1054 $type = strtolower($type);
1055 $name = strtolower($name);
1057 $manager = $this->metaTagRegistry->getManagerForProperty($name);
1058 $propertyContent = $manager->getProperty($name, $type);
1060 if (!empty($propertyContent[0])) {
1064 'content' => $propertyContent[0][
'content']
1079 $type = strtolower($type);
1080 $name = strtolower($name);
1082 $manager = $this->metaTagRegistry->getManagerForProperty($name);
1083 $manager->removeProperty($name, $type);
1093 if (!in_array($comment, $this->inlineComments)) {
1094 $this->inlineComments[] = $comment;
1105 if (!in_array($data, $this->headerData)) {
1106 $this->headerData[] = $data;
1117 if (!in_array($data, $this->footerData)) {
1118 $this->footerData[] = $data;
1138 public function addJsLibrary($name, $file, $type =
'text/javascript', $compress =
false, $forceOnTop =
false, $allWrap =
'', $excludeFromConcatenation =
false, $splitChar =
'|', $async =
false, $integrity =
'', $defer =
false, $crossorigin =
'')
1141 $type =
'text/javascript';
1143 if (!in_array(strtolower($name), $this->jsLibs)) {
1144 $this->jsLibs[strtolower($name)] = [
1148 'compress' => $compress,
1149 'forceOnTop' => $forceOnTop,
1150 'allWrap' => $allWrap,
1151 'excludeFromConcatenation' => $excludeFromConcatenation,
1152 'splitChar' => $splitChar,
1154 'integrity' => $integrity,
1156 'crossorigin' => $crossorigin,
1177 public function addJsFooterLibrary($name, $file, $type =
'text/javascript', $compress =
false, $forceOnTop =
false, $allWrap =
'', $excludeFromConcatenation =
false, $splitChar =
'|', $async =
false, $integrity =
'', $defer =
false, $crossorigin =
'')
1180 $type =
'text/javascript';
1182 $name .=
'_jsFooterLibrary';
1183 if (!in_array(strtolower($name), $this->jsLibs)) {
1184 $this->jsLibs[strtolower($name)] = [
1188 'compress' => $compress,
1189 'forceOnTop' => $forceOnTop,
1190 'allWrap' => $allWrap,
1191 'excludeFromConcatenation' => $excludeFromConcatenation,
1192 'splitChar' => $splitChar,
1194 'integrity' => $integrity,
1196 'crossorigin' => $crossorigin,
1216 public function addJsFile($file, $type =
'text/javascript', $compress =
true, $forceOnTop =
false, $allWrap =
'', $excludeFromConcatenation =
false, $splitChar =
'|', $async =
false, $integrity =
'', $defer =
false, $crossorigin =
'')
1219 $type =
'text/javascript';
1221 if (!isset($this->jsFiles[$file])) {
1222 $this->jsFiles[$file] = [
1226 'compress' => $compress,
1227 'forceOnTop' => $forceOnTop,
1228 'allWrap' => $allWrap,
1229 'excludeFromConcatenation' => $excludeFromConcatenation,
1230 'splitChar' => $splitChar,
1232 'integrity' => $integrity,
1234 'crossorigin' => $crossorigin,
1254 public function addJsFooterFile($file, $type =
'text/javascript', $compress =
true, $forceOnTop =
false, $allWrap =
'', $excludeFromConcatenation =
false, $splitChar =
'|', $async =
false, $integrity =
'', $defer =
false, $crossorigin =
'')
1257 $type =
'text/javascript';
1259 if (!isset($this->jsFiles[$file])) {
1260 $this->jsFiles[$file] = [
1264 'compress' => $compress,
1265 'forceOnTop' => $forceOnTop,
1266 'allWrap' => $allWrap,
1267 'excludeFromConcatenation' => $excludeFromConcatenation,
1268 'splitChar' => $splitChar,
1270 'integrity' => $integrity,
1272 'crossorigin' => $crossorigin,
1285 public function addJsInlineCode($name, $block, $compress =
true, $forceOnTop =
false)
1287 if (!isset($this->jsInline[$name]) && !empty($block)) {
1288 $this->jsInline[$name] = [
1289 'code' => $block . LF,
1291 'compress' => $compress,
1292 'forceOnTop' => $forceOnTop
1307 if (!isset($this->jsInline[$name]) && !empty($block)) {
1308 $this->jsInline[$name] = [
1309 'code' => $block . LF,
1311 'compress' => $compress,
1312 'forceOnTop' => $forceOnTop
1331 public function addCssFile($file, $rel =
'stylesheet', $media =
'all',
$title =
'', $compress =
true, $forceOnTop =
false, $allWrap =
'', $excludeFromConcatenation =
false, $splitChar =
'|', $inline =
false)
1333 if (!isset($this->cssFiles[$file])) {
1334 $this->cssFiles[$file] = [
1339 'compress' => $compress,
1340 'forceOnTop' => $forceOnTop,
1341 'allWrap' => $allWrap,
1342 'excludeFromConcatenation' => $excludeFromConcatenation,
1343 'splitChar' => $splitChar,
1363 public function addCssLibrary($file, $rel =
'stylesheet', $media =
'all',
$title =
'', $compress =
true, $forceOnTop =
false, $allWrap =
'', $excludeFromConcatenation =
false, $splitChar =
'|', $inline =
false)
1365 if (!isset($this->cssLibs[$file])) {
1366 $this->cssLibs[$file] = [
1371 'compress' => $compress,
1372 'forceOnTop' => $forceOnTop,
1373 'allWrap' => $allWrap,
1374 'excludeFromConcatenation' => $excludeFromConcatenation,
1375 'splitChar' => $splitChar,
1389 public function addCssInlineBlock($name, $block, $compress =
false, $forceOnTop =
false)
1391 if (!isset($this->cssInline[$name]) && !empty($block)) {
1392 $this->cssInline[$name] = [
1394 'compress' => $compress,
1395 'forceOnTop' => $forceOnTop
1410 public function loadJquery($version =
null, $source =
null, $namespace = self::JQUERY_NAMESPACE_NONE,
bool $isCoreCall =
false)
1413 trigger_error(
'PageRenderer->loadJquery() will be removed in TYPO3 v10.0. Use a package manager for frontend or custom jQuery files instead.', E_USER_DEPRECATED);
1416 if ($version ===
null || $version ===
'latest') {
1420 if ($source ===
null) {
1423 if ($source ===
'local' && !in_array($version, $this->availableLocalJqueryVersions)) {
1424 throw new \UnexpectedValueException(
'The requested jQuery version is not available in the local filesystem.', 1341505305);
1426 if (!preg_match(
'/^[a-zA-Z0-9]+$/', $namespace)) {
1427 throw new \UnexpectedValueException(
'The requested namespace contains non alphanumeric characters.', 1341571604);
1429 $this->jQueryVersions[$namespace] = [
1430 'version' => $version,
1443 $this->addRequireJs =
true;
1444 if (!empty($this->requireJsConfig) && !empty($this->publicRequireJsConfig)) {
1448 $packages = GeneralUtility::makeInstance(PackageManager::class)->getActivePackages();
1449 $isDevelopment = GeneralUtility::getApplicationContext()->isDevelopment();
1450 $cacheIdentifier =
'requireJS_' . md5(implode(
',', array_keys($packages)) . ($isDevelopment ?
':dev' :
'') . GeneralUtility::getIndpEnv(
'TYPO3_REQUEST_SCRIPT'));
1452 $cache = static::$cache ?? GeneralUtility::makeInstance(CacheManager::class)->getCache(
'assets');
1480 'internalNames' => [],
1483 $corePath = $packages[
'core']->getPackagePath() .
'Resources/Public/JavaScript/Contrib/';
1487 'jquery' => $corePath .
'/jquery/jquery',
1488 'jquery-ui' => $corePath .
'jquery-ui',
1489 'datatables' => $corePath .
'jquery.dataTables',
1490 'nprogress' => $corePath .
'nprogress',
1491 'moment' => $corePath .
'moment',
1492 'cropper' => $corePath .
'cropper.min',
1493 'imagesloaded' => $corePath .
'imagesloaded.pkgd.min',
1494 'bootstrap' => $corePath .
'bootstrap/bootstrap',
1495 'twbs/bootstrap-datetimepicker' => $corePath .
'bootstrap-datetimepicker',
1496 'autosize' => $corePath .
'autosize',
1497 'taboverride' => $corePath .
'taboverride.min',
1498 'twbs/bootstrap-slider' => $corePath .
'bootstrap-slider.min',
1499 'jquery/autocomplete' => $corePath .
'jquery.autocomplete',
1500 'd3' => $corePath .
'd3/d3',
1501 'intersection-observer' => $corePath .
'/intersection-observer',
1505 $publicPackageNames = [
'core',
'frontend',
'backend'];
1506 $requireJsExtensionVersions = [];
1507 foreach ($packages as $packageName => $package) {
1508 $absoluteJsPath = $package->getPackagePath() .
'Resources/Public/JavaScript/';
1510 $fullJsPath = rtrim($fullJsPath,
'/');
1511 if (!empty($fullJsPath) && file_exists($absoluteJsPath)) {
1512 $type = in_array($packageName, $publicPackageNames,
true) ?
'public' :
'internal';
1513 $requireJsConfig[$type][
'paths'][
'TYPO3/CMS/' . GeneralUtility::underscoredToUpperCamelCase($packageName)] = $fullJsPath;
1514 $requireJsExtensionVersions[] = $package->getPackageKey() .
':' . $package->getPackageMetadata()->getVersion();
1518 $internalPathModuleNames = array_keys(
$requireJsConfig[
'internal'][
'paths'] ?? []);
1519 $sanitizedInternalPathModuleNames = array_map(
1520 function ($moduleName) {
1522 return trim($moduleName,
' /') .
'/';
1524 $internalPathModuleNames
1527 $sanitizedInternalPathModuleNames,
1528 $internalPathModuleNames
1532 if ($isDevelopment) {
1541 if (is_array(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
'RequireJS'][
'postInitializationModules'] ??
false)) {
1543 'RequireJS.PostInitializationModules',
1544 $GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
'RequireJS'][
'postInitializationModules']
1565 if (TYPO3_MODE ===
'BE') {
1569 $this->requireJsConfig = array_merge_recursive($this->requireJsConfig, $configuration);
1581 $backendRequest = TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_BE;
1582 $backendUserLoggedIn = !empty(
$GLOBALS[
'BE_USER']->user[
'uid']);
1585 if (!$backendRequest) {
1587 $requireJsConfig[
'typo3BaseUrl'] = GeneralUtility::getIndpEnv(
'TYPO3_SITE_PATH') .
'?eID=requirejs';
1589 } elseif (!$backendUserLoggedIn) {
1590 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
1592 $requireJsConfig[
'typo3BaseUrl'] = (string)$uriBuilder->buildUriFromRoute(
'ajax_core_requirejs');
1596 $this->publicRequireJsConfig,
1597 $this->requireJsConfig
1602 $html .= GeneralUtility::wrapJS(
'var require = ' . json_encode(
$requireJsConfig)) . LF;
1604 $html .=
'<script src="'
1606 .
'" type="text/javascript"></script>' . LF;
1609 $html .=
'<script src="'
1611 'EXT:core/Resources/Public/JavaScript/requirejs-loader.js'
1613 .
'" type="text/javascript"></script>' . LF;
1625 protected function filterArrayKeys(array $array, array $keys,
bool $keep =
true): array
1627 return array_filter(
1629 function (
string $key) use ($keys, $keep) {
1630 return in_array($key, $keys,
true) === $keep;
1632 ARRAY_FILTER_USE_KEY
1654 $inlineCodeKey = $mainModuleName;
1660 if ($baseModuleName !==
null && isset($this->requireJsConfig[
'paths'][$baseModuleName])) {
1661 $this->publicRequireJsConfig[
'paths'][$baseModuleName] = $this->requireJsConfig[
'paths'][$baseModuleName];
1662 unset($this->requireJsConfig[
'paths'][$baseModuleName]);
1665 $javaScriptCode =
'require([' . GeneralUtility::quoteJSvalue($mainModuleName) .
']';
1666 if ($callBackFunction !==
null) {
1667 $inlineCodeKey .= sha1($callBackFunction);
1668 $javaScriptCode .=
', ' . $callBackFunction;
1670 $javaScriptCode .=
');';
1671 $this->
addJsInlineCode(
'RequireJS-Module-' . $inlineCodeKey, $javaScriptCode);
1683 $sanitizedModuleName = trim($moduleName,
' /') .
'/';
1684 foreach ($this->internalRequireJsPathModuleNames as $sanitizedBaseModuleName => $baseModuleName) {
1685 if (strpos($sanitizedModuleName, $sanitizedBaseModuleName) === 0) {
1686 return $baseModuleName;
1701 $this->inlineLanguageLabels[$key] = $value;
1714 if ($parseWithLanguageService ===
true) {
1715 trigger_error(
'PageRenderer::addInlineLanguageLabelArray() second method argument set to true will not be supported anymore in TYPO3 v10.0.', E_USER_DEPRECATED);
1716 foreach ($array as $key => $value) {
1717 if (TYPO3_MODE ===
'FE') {
1723 } elseif ($parseWithLanguageService !==
null) {
1724 trigger_error(
'PageRenderer::addInlineLanguageLabelArray() does not need a second method argument anymore, and will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
1727 $this->inlineLanguageLabels = array_merge($this->inlineLanguageLabels, $array);
1739 $index = md5($fileRef . $selectionPrefix . $stripFromSelectionName);
1740 if ($fileRef && !isset($this->inlineLanguageLabelFiles[$index])) {
1741 $this->inlineLanguageLabelFiles[$index] = [
1742 'fileRef' => $fileRef,
1743 'selectionPrefix' => $selectionPrefix,
1744 'stripFromSelectionName' => $stripFromSelectionName
1760 if (strpos($namespace,
'.')) {
1761 $parts = explode(
'.', $namespace);
1763 foreach ($parts as $part) {
1768 $this->inlineSettings[$namespace][$key] = $value;
1771 $this->inlineSettings[$key] = $value;
1786 if (strpos($namespace,
'.')) {
1787 $parts = explode(
'.', $namespace);
1789 foreach ($parts as $part) {
1792 $a = array_merge((array)$a, $array);
1794 $this->inlineSettings[$namespace] = array_merge((array)$this->inlineSettings[$namespace], $array);
1797 $this->inlineSettings = array_merge($this->inlineSettings, $array);
1808 $this->bodyContent .= $content;
1822 public function render($part = self::PART_COMPLETE)
1834 $templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1835 return trim($templateService->substituteMarkerArray($template, $markerArray,
'###|###'));
1846 $metaTagManagers = $this->metaTagRegistry->getAllManagers();
1848 foreach ($metaTagManagers as $manager => $managerObject) {
1849 $properties = $managerObject->renderAllProperties();
1850 if (!empty($properties)) {
1869 $templateService = GeneralUtility::makeInstance(MarkerBasedTemplateService::class);
1870 return trim($templateService->substituteMarkerArray($template, $markerArray,
'###|###'));
1886 $title = $this->title ? str_replace(
'|', htmlspecialchars($this->title), $this->titleTag) :
'';
1888 '<!-- ###TITLE' . $substituteHash .
'### -->' =>
$title,
1889 '<!-- ###CSS_LIBS' . $substituteHash .
'### -->' =>
$cssLibs,
1890 '<!-- ###CSS_INCLUDE' . $substituteHash .
'### -->' =>
$cssFiles,
1891 '<!-- ###CSS_INLINE' . $substituteHash .
'### -->' =>
$cssInline,
1892 '<!-- ###JS_INLINE' . $substituteHash .
'### -->' =>
$jsInline,
1893 '<!-- ###JS_INCLUDE' . $substituteHash .
'### -->' =>
$jsFiles,
1894 '<!-- ###JS_LIBS' . $substituteHash .
'### -->' =>
$jsLibs,
1895 '<!-- ###META' . $substituteHash .
'### -->' => implode(LF, array_merge($this->metaTags, $this->
renderMetaTagsFromAPI())),
1896 '<!-- ###HEADERDATA' . $substituteHash .
'### -->' => implode(LF, $this->headerData),
1897 '<!-- ###FOOTERDATA' . $substituteHash .
'### -->' => implode(LF, $this->footerData),
1898 '<!-- ###JS_LIBS_FOOTER' . $substituteHash .
'### -->' =>
$jsFooterLibs,
1899 '<!-- ###JS_INCLUDE_FOOTER' . $substituteHash .
'### -->' =>
$jsFooterFiles,
1900 '<!-- ###JS_INLINE_FOOTER' . $substituteHash .
'### -->' =>
$jsFooterInline
1902 foreach ($markerArray as $placeHolder => $content) {
1903 $cachedPageContent = str_replace($placeHolder, $content, $cachedPageContent);
1906 return $cachedPageContent;
1917 $this->endingSlash =
' /';
1919 $this->metaCharsetTag = str_replace(
' />',
'>', $this->metaCharsetTag);
1920 $this->baseUrlTag = str_replace(
' />',
'>', $this->baseUrlTag);
1921 $this->shortcutTag = str_replace(
' />',
'>', $this->shortcutTag);
1922 $this->endingSlash =
'';
1935 if ($this->concatenateFiles || $this->concatenateJavascript || $this->concatenateCss) {
1939 if ($this->compressCss || $this->compressJavascript) {
1951 if ($this->moveJsFromHeaderToFooter) {
1984 'METACHARSET' => $this->charSet ? str_replace(
'|', htmlspecialchars($this->charSet), $this->metaCharsetTag) :
'',
1985 'INLINECOMMENT' => $this->inlineComments ? LF . LF .
'<!-- ' . LF . implode(LF, $this->inlineComments) .
'-->' . LF . LF :
'',
1986 'BASEURL' => $this->baseUrl ? str_replace(
'|', $this->baseUrl, $this->baseUrlTag) :
'',
1987 'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) :
'',
1994 'TITLE' => $this->title ? str_replace(
'|', htmlspecialchars($this->title), $this->titleTag) :
'',
1996 'HEADERDATA' => $this->headerData ? implode(LF, $this->headerData) :
'',
1997 'FOOTERDATA' => $this->footerData ? implode(LF, $this->footerData) :
'',
2001 'BODY' => $this->bodyContent
2003 $markerArray = array_map(
'trim', $markerArray);
2004 return $markerArray;
2019 'METACHARSET' => $this->charSet ? str_replace(
'|', htmlspecialchars($this->charSet), $this->metaCharsetTag) :
'',
2020 'INLINECOMMENT' => $this->inlineComments ? LF . LF .
'<!-- ' . LF . implode(LF, $this->inlineComments) .
'-->' . LF . LF :
'',
2021 'BASEURL' => $this->baseUrl ? str_replace(
'|', $this->baseUrl, $this->baseUrlTag) :
'',
2022 'SHORTCUT' => $this->favIcon ? sprintf($this->shortcutTag, htmlspecialchars($this->favIcon), $this->iconMimeType) :
'',
2023 'META' =>
'<!-- ###META' . $substituteHash .
'### -->',
2025 'TITLE' =>
'<!-- ###TITLE' . $substituteHash .
'### -->',
2026 'CSS_LIBS' =>
'<!-- ###CSS_LIBS' . $substituteHash .
'### -->',
2027 'CSS_INCLUDE' =>
'<!-- ###CSS_INCLUDE' . $substituteHash .
'### -->',
2028 'CSS_INLINE' =>
'<!-- ###CSS_INLINE' . $substituteHash .
'### -->',
2029 'JS_INLINE' =>
'<!-- ###JS_INLINE' . $substituteHash .
'### -->',
2030 'JS_INCLUDE' =>
'<!-- ###JS_INCLUDE' . $substituteHash .
'### -->',
2031 'JS_LIBS' =>
'<!-- ###JS_LIBS' . $substituteHash .
'### -->',
2032 'HEADERDATA' =>
'<!-- ###HEADERDATA' . $substituteHash .
'### -->',
2033 'FOOTERDATA' =>
'<!-- ###FOOTERDATA' . $substituteHash .
'### -->',
2034 'JS_LIBS_FOOTER' =>
'<!-- ###JS_LIBS_FOOTER' . $substituteHash .
'### -->',
2035 'JS_INCLUDE_FOOTER' =>
'<!-- ###JS_INCLUDE_FOOTER' . $substituteHash .
'### -->',
2036 'JS_INLINE_FOOTER' =>
'<!-- ###JS_INLINE_FOOTER' . $substituteHash .
'### -->'
2038 $markerArray = array_map(
'trim', $markerArray);
2039 return $markerArray;
2050 $templateFile = GeneralUtility::getFileAbsFileName($this->templateFile);
2053 if ($this->removeLineBreaksFromTemplate) {
2054 $template = strtr($template, [LF =>
'', CR =>
'']);
2056 if ($part !== self::PART_COMPLETE) {
2057 $templatePart = explode(
'###BODY###', $template);
2058 $template = $templatePart[$part - 1];
2077 if ($this->addRequireJs) {
2082 if (!empty($this->jQueryVersions)) {
2083 foreach ($this->jQueryVersions as $namespace => $jQueryVersion) {
2089 if (TYPO3_MODE ===
'BE') {
2090 $noBackendUserLoggedIn = empty(
$GLOBALS[
'BE_USER']->user[
'uid']);
2095 if (!empty($languageLabels)) {
2098 $inlineSettings .= $this->inlineSettings ?
'TYPO3.settings = ' . json_encode($this->inlineSettings) .
';' :
'';
2103 $out .= $this->inlineJavascriptWrap[0] .
$inlineSettings . $this->inlineJavascriptWrap[1];
2116 if (empty($this->inlineLanguageLabels)) {
2121 foreach ($this->inlineLanguageLabels as $key => $translationUnit) {
2122 if (is_array($translationUnit)) {
2123 $translationUnit = current($translationUnit);
2124 $labels[$key] = $translationUnit[
'target'] ?? $translationUnit[
'source'];
2126 $labels[$key] = $translationUnit;
2138 if (!empty($this->inlineLanguageLabelFiles)) {
2139 foreach ($this->inlineLanguageLabelFiles as $languageLabelFile) {
2140 $this->
includeLanguageFileForInline($languageLabelFile[
'fileRef'], $languageLabelFile[
'selectionPrefix'], $languageLabelFile[
'stripFromSelectionName']);
2143 $this->inlineLanguageLabelFiles = [];
2158 foreach ($data as $key => $value) {
2159 if (is_array($data[$key])) {
2161 } elseif (is_string($data[$key])) {
2162 $data[$key] = mb_convert_encoding($data[$key],
'utf-8', $fromCharset);
2177 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
2179 $router = GeneralUtility::makeInstance(Router::class);
2180 $routes = $router->getRoutes();
2181 foreach ($routes as $routeIdentifier => $route) {
2182 if ($publicRoutesOnly && $route->getOption(
'access') !==
'public') {
2185 if ($route->getOption(
'ajax')) {
2186 $uri = (string)$uriBuilder->buildUriFromRoute($routeIdentifier);
2188 $routeIdentifier = str_replace(
'ajax_',
'', $routeIdentifier);
2189 $ajaxUrls[$routeIdentifier] = $uri;
2193 $this->inlineSettings[
'ajaxUrls'] = $ajaxUrls;
2207 case isset($this->jQueryCdnUrls[$source]):
2208 if ($this->enableJqueryDebug) {
2211 $minifyPart =
'.min';
2213 $jQueryFileName = sprintf($this->jQueryCdnUrls[$source], $version, $minifyPart);
2215 case $source ===
'local':
2216 $jQueryFileName = $this->jQueryPath .
'jquery';
2217 if ($this->enableJqueryDebug) {
2218 $jQueryFileName .=
'.js';
2220 $jQueryFileName .=
'.min.js';
2225 $jQueryFileName = $source;
2227 $scriptTag =
'<script src="' . htmlspecialchars($jQueryFileName) .
'" type="text/javascript"></script>' . LF;
2229 if ($namespace !== self::JQUERY_NAMESPACE_NONE) {
2230 $scriptTag .= GeneralUtility::wrapJS(
'jQuery.noConflict();') . LF;
2243 if (!empty($this->cssLibs)) {
2244 foreach ($this->cssLibs as $file => $properties) {
2246 if ($properties[
'forceOnTop']) {
2264 if (!empty($this->cssFiles)) {
2265 foreach ($this->cssFiles as $file => $properties) {
2267 if ($properties[
'forceOnTop']) {
2284 private function createCssTag(array $properties,
string $file): string
2286 if ($properties[
'inline'] && @is_file($file)) {
2290 $tag =
'<link rel="' . htmlspecialchars($properties[
'rel'])
2291 .
'" type="text/css" href="' . htmlspecialchars($href)
2292 .
'" media="' . htmlspecialchars($properties[
'media']) .
'"'
2293 . ($properties[
'title'] ?
' title="' . htmlspecialchars($properties[
'title']) .
'"' :
'')
2294 . $this->endingSlash .
'>';
2296 if ($properties[
'allWrap']) {
2297 $wrapArr = explode($properties[
'splitChar'] ?:
'|', $properties[
'allWrap'], 2);
2298 $tag = $wrapArr[0] . $tag . $wrapArr[1];
2313 if (!empty($this->cssInline)) {
2314 foreach ($this->cssInline as $name => $properties) {
2315 $cssCode =
'/*' . htmlspecialchars($name) .
'*/' . LF . $properties[
'code'] . LF;
2316 if ($properties[
'forceOnTop']) {
2336 if (!empty($this->jsLibs)) {
2337 foreach ($this->jsLibs as $properties) {
2339 $async = $properties[
'async'] ?
' async="async"' :
'';
2340 $defer = $properties[
'defer'] ?
' defer="defer"' :
'';
2341 $integrity = $properties[
'integrity'] ?
' integrity="' . htmlspecialchars($properties[
'integrity']) .
'"' :
'';
2342 $crossorigin = $properties[
'crossorigin'] ?
' crossorigin="' . htmlspecialchars($properties[
'crossorigin']) .
'"' :
'';
2343 $tag =
'<script src="' . htmlspecialchars($properties[
'file']) .
'" type="' . htmlspecialchars($properties[
'type']) .
'"' . $async . $defer . $integrity . $crossorigin .
'></script>';
2344 if ($properties[
'allWrap']) {
2345 $wrapArr = explode($properties[
'splitChar'] ?:
'|', $properties[
'allWrap'], 2);
2346 $tag = $wrapArr[0] . $tag . $wrapArr[1];
2349 if ($properties[
'forceOnTop']) {
2350 if ($properties[
'section'] === self::PART_HEADER) {
2356 if ($properties[
'section'] === self::PART_HEADER) {
2364 if ($this->moveJsFromHeaderToFooter) {
2380 if (!empty($this->jsFiles)) {
2381 foreach ($this->jsFiles as $file => $properties) {
2383 $async = $properties[
'async'] ?
' async="async"' :
'';
2384 $defer = $properties[
'defer'] ?
' defer="defer"' :
'';
2385 $integrity = $properties[
'integrity'] ?
' integrity="' . htmlspecialchars($properties[
'integrity']) .
'"' :
'';
2386 $crossorigin = $properties[
'crossorigin'] ?
' crossorigin="' . htmlspecialchars($properties[
'crossorigin']) .
'"' :
'';
2387 $tag =
'<script src="' . htmlspecialchars($file) .
'" type="' . htmlspecialchars($properties[
'type']) .
'"' . $async . $defer . $integrity . $crossorigin .
'></script>';
2388 if ($properties[
'allWrap']) {
2389 $wrapArr = explode($properties[
'splitChar'] ?:
'|', $properties[
'allWrap'], 2);
2390 $tag = $wrapArr[0] . $tag . $wrapArr[1];
2393 if ($properties[
'forceOnTop']) {
2394 if ($properties[
'section'] === self::PART_HEADER) {
2400 if ($properties[
'section'] === self::PART_HEADER) {
2408 if ($this->moveJsFromHeaderToFooter) {
2424 if (!empty($this->jsInline)) {
2425 foreach ($this->jsInline as $name => $properties) {
2426 $jsCode =
'/*' . htmlspecialchars($name) .
'*/' . LF . $properties[
'code'] . LF;
2427 if ($properties[
'forceOnTop']) {
2428 if ($properties[
'section'] === self::PART_HEADER) {
2434 if ($properties[
'section'] === self::PART_HEADER) {
2448 if ($this->moveJsFromHeaderToFooter) {
2465 if (!isset($this->lang) || !isset($this->charSet)) {
2466 throw new \RuntimeException(
'Language and character encoding are not set.', 1284906026);
2468 $labelsFromFile = [];
2470 if ($allLabels !==
false) {
2472 if ($this->lang !==
'default' && isset($allLabels[$this->lang])) {
2473 $labels = array_merge($allLabels[
'default'], $allLabels[$this->lang]);
2475 $labels = $allLabels[
'default'];
2478 foreach ($labels as $label => $value) {
2480 if ($selectionPrefix ===
'' || strpos($label, $selectionPrefix) === 0) {
2482 $label = str_replace($stripFromSelectionName,
'', $label);
2483 $labelsFromFile[$label] = $value;
2486 $this->inlineLanguageLabels = array_merge($this->inlineLanguageLabels, $labelsFromFile);
2499 $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
2501 if ($this->lang !==
'default') {
2502 $languages = array_reverse($this->languageDependencies);
2504 if (empty($languages)) {
2505 $languages[] =
'default';
2508 $languages = [
'default'];
2511 $localLanguage = [];
2512 foreach ($languages as $language) {
2513 $tempLL = $languageFactory->getParsedData($fileRef, $language);
2515 $localLanguage[
'default'] = $tempLL[
'default'];
2516 if (!isset($localLanguage[$this->lang])) {
2517 $localLanguage[
$this->lang] = $localLanguage[
'default'];
2519 if ($this->lang !==
'default' && isset($tempLL[$language])) {
2526 return $localLanguage;
2549 if ($this->concatenateFiles || $this->concatenateJavascript) {
2550 if (!empty(
$GLOBALS[
'TYPO3_CONF_VARS'][TYPO3_MODE][
'jsConcatenateHandler'])) {
2559 GeneralUtility::callUserFunction(
$GLOBALS[
'TYPO3_CONF_VARS'][TYPO3_MODE][
'jsConcatenateHandler'], $params, $this);
2561 $this->jsLibs = $this->
getCompressor()->concatenateJsFiles($this->jsLibs);
2562 $this->jsFiles = $this->
getCompressor()->concatenateJsFiles($this->jsFiles);
2563 $this->jsFooterFiles = $this->
getCompressor()->concatenateJsFiles($this->jsFooterFiles);
2573 if ($this->concatenateFiles || $this->concatenateCss) {
2574 if (!empty(
$GLOBALS[
'TYPO3_CONF_VARS'][TYPO3_MODE][
'cssConcatenateHandler'])) {
2582 GeneralUtility::callUserFunction(
$GLOBALS[
'TYPO3_CONF_VARS'][TYPO3_MODE][
'cssConcatenateHandler'], $params, $this);
2585 if (TYPO3_MODE ===
'BE') {
2586 $cssOptions = [
'baseDirectories' =>
$GLOBALS[
'TBE_TEMPLATE']->getSkinStylesheetDirectories()];
2588 $this->cssLibs = $this->
getCompressor()->concatenateCssFiles($this->cssLibs, $cssOptions);
2589 $this->cssFiles = $this->
getCompressor()->concatenateCssFiles($this->cssFiles, $cssOptions);
2608 if ($this->compressCss) {
2609 if (!empty(
$GLOBALS[
'TYPO3_CONF_VARS'][TYPO3_MODE][
'cssCompressHandler'])) {
2618 GeneralUtility::callUserFunction(
$GLOBALS[
'TYPO3_CONF_VARS'][TYPO3_MODE][
'cssCompressHandler'], $params, $this);
2620 $this->cssLibs = $this->
getCompressor()->compressCssFiles($this->cssLibs);
2621 $this->cssFiles = $this->
getCompressor()->compressCssFiles($this->cssFiles);
2631 if ($this->compressJavascript) {
2632 if (!empty(
$GLOBALS[
'TYPO3_CONF_VARS'][TYPO3_MODE][
'jsCompressHandler'])) {
2643 GeneralUtility::callUserFunction(
$GLOBALS[
'TYPO3_CONF_VARS'][TYPO3_MODE][
'jsCompressHandler'], $params, $this);
2646 if (!empty($this->jsInline)) {
2647 foreach ($this->jsInline as $name => $properties) {
2648 if ($properties[
'compress']) {
2650 $this->jsInline[$name][
'code'] = GeneralUtility::minifyJavaScript($properties[
'code'], $error);
2652 $this->compressError .=
'Error with minify JS Inline Block "' . $name .
'": ' . $error . LF;
2657 $this->jsLibs = $this->
getCompressor()->compressJsFiles($this->jsLibs);
2658 $this->jsFiles = $this->
getCompressor()->compressJsFiles($this->jsFiles);
2659 $this->jsFooterFiles = $this->
getCompressor()->compressJsFiles($this->jsFooterFiles);
2671 if ($this->compressor ===
null) {
2672 $this->compressor = GeneralUtility::makeInstance(\
TYPO3\CMS\Core\Resource\ResourceCompressor::class);
2688 if ($this->compressJavascript) {
2690 } elseif (TYPO3_MODE ===
'FE') {
2691 $filename = GeneralUtility::createVersionNumberedFilename($filename);
2710 if (strpos($file,
'EXT:') === 0) {
2711 $file = GeneralUtility::getFileAbsFileName($file);
2714 $file = rtrim($file,
'/');
2716 $file = GeneralUtility::resolveBackPath($file);
2718 if ($prepareForOutput) {
2719 $file = GeneralUtility::createVersionNumberedFilename($file);
2736 if (TYPO3_MODE ===
'FE') {
2772 $hooks =
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_pagerenderer.php'][
'render-preProcess'] ??
false;
2789 foreach ($hooks as $hook) {
2790 GeneralUtility::callUserFunction($hook, $params, $this);
2799 $hooks =
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_pagerenderer.php'][
'render-postTransform'] ??
false;
2816 foreach ($hooks as $hook) {
2817 GeneralUtility::callUserFunction($hook, $params, $this);
2836 $hooks =
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_pagerenderer.php'][
'render-postProcess'] ??
false;
2868 foreach ($hooks as $hook) {
2869 GeneralUtility::callUserFunction($hook, $params, $this);
2884 return '<style type="text/css"'
2885 .
' media="' . htmlspecialchars($properties[
'media']) .
'"'
2886 . ($properties[
'title'] ?
' title="' . htmlspecialchars($properties[
'title']) .
'"' :
'')
2888 .
'/*<![CDATA[*/' . LF .
'<!-- ' . LF
2890 .
'-->' . LF .
'/*]]>*/' . LF .
'</style>' . LF;