1<?php 2namespace TYPO3\CMS\Frontend\Page; 3 4/* 5 * This file is part of the TYPO3 CMS project. 6 * 7 * It is free software; you can redistribute it and/or modify it under 8 * the terms of the GNU General Public License, either version 2 9 * of the License, or any later version. 10 * 11 * For the full copyright and license information, please read the 12 * LICENSE.txt file that was distributed with this source code. 13 * 14 * The TYPO3 project - inspiring people to share! 15 */ 16 17use Psr\Http\Message\ServerRequestInterface; 18use TYPO3\CMS\Core\Core\Environment; 19use TYPO3\CMS\Core\Page\PageRenderer; 20use TYPO3\CMS\Core\Site\Entity\SiteLanguage; 21use TYPO3\CMS\Core\TimeTracker\TimeTracker; 22use TYPO3\CMS\Core\Type\File\ImageInfo; 23use TYPO3\CMS\Core\TypoScript\TypoScriptService; 24use TYPO3\CMS\Core\Utility\GeneralUtility; 25use TYPO3\CMS\Core\Utility\MathUtility; 26use TYPO3\CMS\Core\Utility\PathUtility; 27use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; 28use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; 29use TYPO3\CMS\Frontend\Resource\FilePathSanitizer; 30 31/** 32 * Class for starting TypoScript page generation 33 * 34 * The class is not instantiated as an objects but called directly with the "::" operator. 35 * @deprecated this class will be removed in TYPO3 v10.0, as all functionality has been built in RequestHandler already. 36 */ 37class PageGenerator 38{ 39 /** 40 * Do not render title tag 41 * Typoscript setting: [config][noPageTitle] 42 * @deprecated will not be used anymore, and will be removed in TYPO3 v10.0. 43 */ 44 const NO_PAGE_TITLE = 2; 45 46 /** 47 * Rendering the page content 48 * @deprecated since TYPO3 v9.4 will be removed in TYPO3 v10.0. This functionality is now within RequestHandler. 49 */ 50 public static function renderContent() 51 { 52 trigger_error('PageGenerator::renderContent() will be removed in TYPO3 v10.0. This logic is now built in TYPO3s Frontend RequestHandler.', E_USER_DEPRECATED); 53 /** @var TypoScriptFrontendController $tsfe */ 54 $tsfe = $GLOBALS['TSFE']; 55 56 /** @var TimeTracker $timeTracker */ 57 $timeTracker = GeneralUtility::makeInstance(TimeTracker::class); 58 59 // PAGE CONTENT 60 $timeTracker->incStackPointer(); 61 $timeTracker->push($tsfe->sPre, 'PAGE'); 62 $pageContent = $tsfe->cObj->cObjGet($tsfe->pSetup); 63 if ($tsfe->pSetup['wrap']) { 64 $pageContent = $tsfe->cObj->wrap($pageContent, $tsfe->pSetup['wrap']); 65 } 66 if ($tsfe->pSetup['stdWrap.']) { 67 $pageContent = $tsfe->cObj->stdWrap($pageContent, $tsfe->pSetup['stdWrap.']); 68 } 69 // PAGE HEADER (after content - maybe JS is inserted! 70 // if 'disableAllHeaderCode' is set, all the header-code is discarded! 71 if ($tsfe->config['config']['disableAllHeaderCode']) { 72 $tsfe->content = $pageContent; 73 } else { 74 self::renderContentWithHeader($pageContent); 75 } 76 $timeTracker->pull($timeTracker->LR ? $tsfe->content : ''); 77 $timeTracker->decStackPointer(); 78 } 79 80 /** 81 * Rendering normal HTML-page with header by wrapping the generated content ($pageContent) in body-tags and setting the header accordingly. 82 * 83 * @param string $pageContent The page content which TypoScript objects has generated 84 * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0. This functionality is now within TYPO3's Frontend Request Handler. 85 */ 86 public static function renderContentWithHeader($pageContent) 87 { 88 trigger_error('PageGenerator::renderContentWithHeader() will be removed in TYPO3 v10.0. This logic is now built in TYPO3s Frontend RequestHandler.', E_USER_DEPRECATED); 89 /** @var TypoScriptFrontendController $tsfe */ 90 $tsfe = $GLOBALS['TSFE']; 91 92 /** @var TimeTracker $timeTracker */ 93 $timeTracker = GeneralUtility::makeInstance(TimeTracker::class); 94 95 $pageRenderer = static::getPageRenderer(); 96 if ($tsfe->config['config']['moveJsFromHeaderToFooter'] ?? false) { 97 $pageRenderer->enableMoveJsFromHeaderToFooter(); 98 } 99 if ($tsfe->config['config']['pageRendererTemplateFile'] ?? false) { 100 try { 101 $file = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize($tsfe->config['config']['pageRendererTemplateFile']); 102 $pageRenderer->setTemplateFile($file); 103 } catch (\TYPO3\CMS\Core\Resource\Exception $e) { 104 // do nothing 105 } 106 } 107 $headerComment = $tsfe->config['config']['headerComment'] ?? null; 108 if (trim($headerComment)) { 109 $pageRenderer->addInlineComment("\t" . str_replace(LF, LF . "\t", trim($headerComment)) . LF); 110 } 111 // Setting charset: 112 $theCharset = $tsfe->metaCharset; 113 // Reset the content variables: 114 $tsfe->content = ''; 115 $htmlTagAttributes = []; 116 $htmlLang = $tsfe->config['config']['htmlTag_langKey'] ?? ($tsfe->sys_language_isocode ?: 'en'); 117 // Set content direction 118 // More info: http://www.tau.ac.il/~danon/Hebrew/HTML_and_Hebrew.html) 119 $direction = $tsfe->config['config']['htmlTag_dir'] ?? null; 120 if (self::getCurrentSiteLanguage()) { 121 $direction = self::getCurrentSiteLanguage()->getDirection(); 122 $htmlLang = self::getCurrentSiteLanguage()->getTwoLetterIsoCode(); 123 } 124 if ($direction) { 125 $htmlTagAttributes['dir'] = htmlspecialchars($direction); 126 } 127 // Setting document type: 128 $docTypeParts = []; 129 $xmlDocument = true; 130 // Part 1: XML prologue 131 switch ((string)($tsfe->config['config']['xmlprologue'] ?? '')) { 132 case 'none': 133 $xmlDocument = false; 134 break; 135 case 'xml_10': 136 $docTypeParts[] = '<?xml version="1.0" encoding="' . $theCharset . '"?>'; 137 break; 138 case 'xml_11': 139 $docTypeParts[] = '<?xml version="1.1" encoding="' . $theCharset . '"?>'; 140 break; 141 case '': 142 if ($tsfe->xhtmlVersion) { 143 $docTypeParts[] = '<?xml version="1.0" encoding="' . $theCharset . '"?>'; 144 } else { 145 $xmlDocument = false; 146 } 147 break; 148 default: 149 $docTypeParts[] = $tsfe->config['config']['xmlprologue']; 150 } 151 // Part 2: DTD 152 $doctype = $tsfe->config['config']['doctype'] ?? null; 153 if ($doctype) { 154 switch ($doctype) { 155 case 'xhtml_trans': 156 $docTypeParts[] = '<!DOCTYPE html 157 PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 158 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'; 159 break; 160 case 'xhtml_strict': 161 $docTypeParts[] = '<!DOCTYPE html 162 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 163 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'; 164 break; 165 case 'xhtml_basic': 166 $docTypeParts[] = '<!DOCTYPE html 167 PUBLIC "-//W3C//DTD XHTML Basic 1.0//EN" 168 "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">'; 169 break; 170 case 'xhtml_11': 171 $docTypeParts[] = '<!DOCTYPE html 172 PUBLIC "-//W3C//DTD XHTML 1.1//EN" 173 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'; 174 break; 175 case 'xhtml+rdfa_10': 176 $docTypeParts[] = '<!DOCTYPE html 177 PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" 178 "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">'; 179 break; 180 case 'html5': 181 $docTypeParts[] = '<!DOCTYPE html>'; 182 if ($xmlDocument) { 183 $pageRenderer->setMetaCharsetTag('<meta charset="|" />'); 184 } else { 185 $pageRenderer->setMetaCharsetTag('<meta charset="|">'); 186 } 187 break; 188 case 'none': 189 break; 190 default: 191 $docTypeParts[] = $doctype; 192 } 193 } else { 194 $docTypeParts[] = '<!DOCTYPE html>'; 195 if ($xmlDocument) { 196 $pageRenderer->setMetaCharsetTag('<meta charset="|" />'); 197 } else { 198 $pageRenderer->setMetaCharsetTag('<meta charset="|">'); 199 } 200 } 201 if ($tsfe->xhtmlVersion) { 202 $htmlTagAttributes['xml:lang'] = $htmlLang; 203 } 204 if ($tsfe->xhtmlVersion < 110 || $doctype === 'html5') { 205 $htmlTagAttributes['lang'] = $htmlLang; 206 } 207 if ($tsfe->xhtmlVersion || $doctype === 'html5' && $xmlDocument) { 208 // We add this to HTML5 to achieve a slightly better backwards compatibility 209 $htmlTagAttributes['xmlns'] = 'http://www.w3.org/1999/xhtml'; 210 if (is_array($tsfe->config['config']['namespaces.'])) { 211 foreach ($tsfe->config['config']['namespaces.'] as $prefix => $uri) { 212 // $uri gets htmlspecialchared later 213 $htmlTagAttributes['xmlns:' . htmlspecialchars($prefix)] = $uri; 214 } 215 } 216 } 217 // Swap XML and doctype order around (for MSIE / Opera standards compliance) 218 if ($tsfe->config['config']['doctypeSwitch'] ?? false) { 219 $docTypeParts = array_reverse($docTypeParts); 220 } 221 // Adding doctype parts: 222 if (!empty($docTypeParts)) { 223 $pageRenderer->setXmlPrologAndDocType(implode(LF, $docTypeParts)); 224 } 225 // Begin header section: 226 if (is_array($tsfe->config['config']['htmlTag.']['attributes.'] ?? null)) { 227 $_attr = ''; 228 foreach ($tsfe->config['config']['htmlTag.']['attributes.'] as $attributeName => $value) { 229 $_attr .= ' ' . htmlspecialchars($attributeName) . ($value !== '' ? '="' . htmlspecialchars((string)$value) . '"' : ''); 230 // If e.g. "htmlTag.attributes.dir" is set, make sure it is not added again with "implodeAttributes()" 231 if (isset($htmlTagAttributes[$attributeName])) { 232 unset($htmlTagAttributes[$attributeName]); 233 } 234 } 235 $_attr = GeneralUtility::implodeAttributes($htmlTagAttributes) . $_attr; 236 } elseif (($tsfe->config['config']['htmlTag_setParams'] ?? '') === 'none') { 237 $_attr = ''; 238 } elseif (isset($tsfe->config['config']['htmlTag_setParams'])) { 239 $_attr = $tsfe->config['config']['htmlTag_setParams']; 240 } else { 241 $_attr = GeneralUtility::implodeAttributes($htmlTagAttributes); 242 } 243 $htmlTag = '<html' . ($_attr ? ' ' . $_attr : '') . '>'; 244 if (isset($tsfe->config['config']['htmlTag_stdWrap.'])) { 245 $htmlTag = $tsfe->cObj->stdWrap($htmlTag, $tsfe->config['config']['htmlTag_stdWrap.']); 246 } 247 $pageRenderer->setHtmlTag($htmlTag); 248 // Head tag: 249 $headTag = $tsfe->pSetup['headTag'] ?? '<head>'; 250 if (isset($tsfe->pSetup['headTag.'])) { 251 $headTag = $tsfe->cObj->stdWrap($headTag, $tsfe->pSetup['headTag.']); 252 } 253 $pageRenderer->setHeadTag($headTag); 254 // Setting charset meta tag: 255 $pageRenderer->setCharSet($theCharset); 256 $pageRenderer->addInlineComment(' This website is powered by TYPO3 - inspiring people to share! 257 TYPO3 is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL. 258 TYPO3 is copyright ' . TYPO3_copyright_year . ' of Kasper Skaarhoj. Extensions are copyright of their respective owners. 259 Information and contribution at ' . TYPO3_URL_GENERAL . ' 260'); 261 if ($tsfe->baseUrl) { 262 $pageRenderer->setBaseUrl($tsfe->baseUrl); 263 } 264 if ($tsfe->pSetup['shortcutIcon'] ?? false) { 265 try { 266 $favIcon = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize($tsfe->pSetup['shortcutIcon']); 267 $iconFileInfo = GeneralUtility::makeInstance(ImageInfo::class, Environment::getPublicPath() . '/' . $favIcon); 268 if ($iconFileInfo->isFile()) { 269 $iconMimeType = $iconFileInfo->getMimeType(); 270 if ($iconMimeType) { 271 $iconMimeType = ' type="' . $iconMimeType . '"'; 272 $pageRenderer->setIconMimeType($iconMimeType); 273 } 274 $pageRenderer->setFavIcon(PathUtility::getAbsoluteWebPath($tsfe->absRefPrefix . $favIcon)); 275 } 276 } catch (\TYPO3\CMS\Core\Resource\Exception $e) { 277 // do nothing 278 } 279 } 280 // Including CSS files 281 if (isset($tsfe->tmpl->setup['plugin.']) && is_array($tsfe->tmpl->setup['plugin.'])) { 282 $stylesFromPlugins = ''; 283 foreach ($tsfe->tmpl->setup['plugin.'] as $key => $iCSScode) { 284 if (is_array($iCSScode)) { 285 if ($iCSScode['_CSS_DEFAULT_STYLE'] && empty($tsfe->config['config']['removeDefaultCss'])) { 286 if (isset($iCSScode['_CSS_DEFAULT_STYLE.'])) { 287 $cssDefaultStyle = $tsfe->cObj->stdWrap($iCSScode['_CSS_DEFAULT_STYLE'], $iCSScode['_CSS_DEFAULT_STYLE.']); 288 } else { 289 $cssDefaultStyle = $iCSScode['_CSS_DEFAULT_STYLE']; 290 } 291 $stylesFromPlugins .= '/* default styles for extension "' . substr($key, 0, -1) . '" */' . LF . $cssDefaultStyle . LF; 292 } 293 if ($iCSScode['_CSS_PAGE_STYLE'] && empty($tsfe->config['config']['removePageCss'])) { 294 $cssPageStyle = implode(LF, $iCSScode['_CSS_PAGE_STYLE']); 295 if (isset($iCSScode['_CSS_PAGE_STYLE.'])) { 296 $cssPageStyle = $tsfe->cObj->stdWrap($cssPageStyle, $iCSScode['_CSS_PAGE_STYLE.']); 297 } 298 $cssPageStyle = '/* specific page styles for extension "' . substr($key, 0, -1) . '" */' . LF . $cssPageStyle; 299 self::addCssToPageRenderer($cssPageStyle, true, 'InlinePageCss'); 300 } 301 } 302 } 303 if (!empty($stylesFromPlugins)) { 304 self::addCssToPageRenderer($stylesFromPlugins, false, 'InlineDefaultCss'); 305 } 306 } 307 /**********************************************************************/ 308 /* config.includeCSS / config.includeCSSLibs 309 /**********************************************************************/ 310 if (isset($tsfe->pSetup['includeCSS.']) && is_array($tsfe->pSetup['includeCSS.'])) { 311 foreach ($tsfe->pSetup['includeCSS.'] as $key => $CSSfile) { 312 if (!is_array($CSSfile)) { 313 $cssFileConfig = &$tsfe->pSetup['includeCSS.'][$key . '.']; 314 if (isset($cssFileConfig['if.']) && !$tsfe->cObj->checkIf($cssFileConfig['if.'])) { 315 continue; 316 } 317 if ($cssFileConfig['external']) { 318 $ss = $CSSfile; 319 } else { 320 try { 321 $ss = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize($CSSfile); 322 } catch (\TYPO3\CMS\Core\Resource\Exception $e) { 323 $ss = null; 324 } 325 } 326 if ($ss) { 327 if ($cssFileConfig['import']) { 328 if (!$cssFileConfig['external'] && $ss[0] !== '/') { 329 // To fix MSIE 6 that cannot handle these as relative paths (according to Ben v Ende) 330 $ss = GeneralUtility::dirname(GeneralUtility::getIndpEnv('SCRIPT_NAME')) . '/' . $ss; 331 } 332 $pageRenderer->addCssInlineBlock('import_' . $key, '@import url("' . htmlspecialchars($ss) . '") ' . htmlspecialchars($cssFileConfig['media']) . ';', empty($cssFileConfig['disableCompression']), (bool)$cssFileConfig['forceOnTop']); 333 } else { 334 $pageRenderer->addCssFile( 335 $ss, 336 $cssFileConfig['alternate'] ? 'alternate stylesheet' : 'stylesheet', 337 $cssFileConfig['media'] ?: 'all', 338 $cssFileConfig['title'] ?: '', 339 $cssFileConfig['external'] ? false : empty($cssFileConfig['disableCompression']), 340 (bool)$cssFileConfig['forceOnTop'], 341 $cssFileConfig['allWrap'], 342 (bool)$cssFileConfig['excludeFromConcatenation'] || (bool)$cssFileConfig['inline'], 343 $cssFileConfig['allWrap.']['splitChar'], 344 $cssFileConfig['inline'] 345 ); 346 unset($cssFileConfig); 347 } 348 } 349 } 350 } 351 } 352 if (isset($tsfe->pSetup['includeCSSLibs.']) && is_array($tsfe->pSetup['includeCSSLibs.'])) { 353 foreach ($tsfe->pSetup['includeCSSLibs.'] as $key => $CSSfile) { 354 if (!is_array($CSSfile)) { 355 $cssFileConfig = &$tsfe->pSetup['includeCSSLibs.'][$key . '.']; 356 if (isset($cssFileConfig['if.']) && !$tsfe->cObj->checkIf($cssFileConfig['if.'])) { 357 continue; 358 } 359 if ($cssFileConfig['external']) { 360 $ss = $CSSfile; 361 } else { 362 try { 363 $ss = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize($CSSfile); 364 } catch (\TYPO3\CMS\Core\Resource\Exception $e) { 365 $ss = null; 366 } 367 } 368 if ($ss) { 369 if ($cssFileConfig['import']) { 370 if (!$cssFileConfig['external'] && $ss[0] !== '/') { 371 // To fix MSIE 6 that cannot handle these as relative paths (according to Ben v Ende) 372 $ss = GeneralUtility::dirname(GeneralUtility::getIndpEnv('SCRIPT_NAME')) . '/' . $ss; 373 } 374 $pageRenderer->addCssInlineBlock('import_' . $key, '@import url("' . htmlspecialchars($ss) . '") ' . htmlspecialchars($cssFileConfig['media']) . ';', empty($cssFileConfig['disableCompression']), (bool)$cssFileConfig['forceOnTop']); 375 } else { 376 $pageRenderer->addCssLibrary( 377 $ss, 378 $cssFileConfig['alternate'] ? 'alternate stylesheet' : 'stylesheet', 379 $cssFileConfig['media'] ?: 'all', 380 $cssFileConfig['title'] ?: '', 381 $cssFileConfig['external'] ? false : empty($cssFileConfig['disableCompression']), 382 (bool)$cssFileConfig['forceOnTop'], 383 $cssFileConfig['allWrap'], 384 (bool)$cssFileConfig['excludeFromConcatenation'] || (bool)$cssFileConfig['inline'], 385 $cssFileConfig['allWrap.']['splitChar'], 386 $cssFileConfig['inline'] 387 ); 388 unset($cssFileConfig); 389 } 390 } 391 } 392 } 393 } 394 395 // CSS_inlineStyle from TS 396 $style = trim($tsfe->pSetup['CSS_inlineStyle'] ?? ''); 397 $style .= $tsfe->cObj->cObjGet($tsfe->pSetup['cssInline.'] ?? null, 'cssInline.'); 398 if (trim($style)) { 399 self::addCssToPageRenderer($style, true, 'additionalTSFEInlineStyle'); 400 } 401 // Javascript Libraries 402 if (isset($tsfe->pSetup['javascriptLibs.']) && is_array($tsfe->pSetup['javascriptLibs.'])) { 403 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, the setting page.javascriptLibs has been deprecated and will be removed in TYPO3 v10.0. 404 trigger_error('The setting page.javascriptLibs will be removed in TYPO3 v10.0.', E_USER_DEPRECATED); 405 406 // Include jQuery into the page renderer 407 if (!empty($tsfe->pSetup['javascriptLibs.']['jQuery'])) { 408 // @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, the setting page.javascriptLibs.jQuery has been deprecated and will be removed in TYPO3 v10.0. 409 trigger_error('The setting page.javascriptLibs.jQuery will be removed in TYPO3 v10.0.', E_USER_DEPRECATED); 410 411 $jQueryTS = $tsfe->pSetup['javascriptLibs.']['jQuery.']; 412 // Check if version / source is set, if not set variable to "NULL" to use the default of the page renderer 413 $version = $jQueryTS['version'] ?? null; 414 $source = $jQueryTS['source'] ?? null; 415 // When "noConflict" is not set or "1" enable the default jQuery noConflict mode, otherwise disable the namespace 416 if (!isset($jQueryTS['noConflict']) || !empty($jQueryTS['noConflict'])) { 417 $namespace = 'noConflict'; 418 } else { 419 $namespace = PageRenderer::JQUERY_NAMESPACE_NONE; 420 } 421 $pageRenderer->loadJquery($version, $source, $namespace, true); 422 } 423 } 424 // JavaScript library files 425 if (isset($tsfe->pSetup['includeJSLibs.']) && is_array($tsfe->pSetup['includeJSLibs.'])) { 426 foreach ($tsfe->pSetup['includeJSLibs.'] as $key => $JSfile) { 427 if (!is_array($JSfile)) { 428 if (isset($tsfe->pSetup['includeJSLibs.'][$key . '.']['if.']) && !$tsfe->cObj->checkIf($tsfe->pSetup['includeJSLibs.'][$key . '.']['if.'])) { 429 continue; 430 } 431 if ($tsfe->pSetup['includeJSLibs.'][$key . '.']['external']) { 432 $ss = $JSfile; 433 } else { 434 try { 435 $ss = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize($JSfile); 436 } catch (\TYPO3\CMS\Core\Resource\Exception $e) { 437 $ss = null; 438 } 439 } 440 if ($ss) { 441 $jsFileConfig = &$tsfe->pSetup['includeJSLibs.'][$key . '.']; 442 $type = $jsFileConfig['type']; 443 if (!$type) { 444 $type = 'text/javascript'; 445 } 446 $crossorigin = $jsFileConfig['crossorigin']; 447 if (!$crossorigin && $jsFileConfig['integrity'] && $jsFileConfig['external']) { 448 $crossorigin = 'anonymous'; 449 } 450 $pageRenderer->addJsLibrary( 451 $key, 452 $ss, 453 $type, 454 $jsFileConfig['external'] ? false : empty($jsFileConfig['disableCompression']), 455 (bool)$jsFileConfig['forceOnTop'], 456 $jsFileConfig['allWrap'], 457 (bool)$jsFileConfig['excludeFromConcatenation'], 458 $jsFileConfig['allWrap.']['splitChar'], 459 (bool)$jsFileConfig['async'], 460 $jsFileConfig['integrity'], 461 (bool)$jsFileConfig['defer'], 462 $crossorigin 463 ); 464 unset($jsFileConfig); 465 } 466 } 467 } 468 } 469 if (isset($tsfe->pSetup['includeJSFooterlibs.']) && is_array($tsfe->pSetup['includeJSFooterlibs.'])) { 470 foreach ($tsfe->pSetup['includeJSFooterlibs.'] as $key => $JSfile) { 471 if (!is_array($JSfile)) { 472 if (isset($tsfe->pSetup['includeJSFooterlibs.'][$key . '.']['if.']) && !$tsfe->cObj->checkIf($tsfe->pSetup['includeJSFooterlibs.'][$key . '.']['if.'])) { 473 continue; 474 } 475 if ($tsfe->pSetup['includeJSFooterlibs.'][$key . '.']['external']) { 476 $ss = $JSfile; 477 } else { 478 try { 479 $ss = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize($JSfile); 480 } catch (\TYPO3\CMS\Core\Resource\Exception $e) { 481 $ss = null; 482 } 483 } 484 if ($ss) { 485 $jsFileConfig = &$tsfe->pSetup['includeJSFooterlibs.'][$key . '.']; 486 $type = $jsFileConfig['type']; 487 if (!$type) { 488 $type = 'text/javascript'; 489 } 490 $crossorigin = $jsFileConfig['crossorigin']; 491 if (!$crossorigin && $jsFileConfig['integrity'] && $jsFileConfig['external']) { 492 $crossorigin = 'anonymous'; 493 } 494 $pageRenderer->addJsFooterLibrary( 495 $key, 496 $ss, 497 $type, 498 $jsFileConfig['external'] ? false : empty($jsFileConfig['disableCompression']), 499 (bool)$jsFileConfig['forceOnTop'], 500 $jsFileConfig['allWrap'], 501 (bool)$jsFileConfig['excludeFromConcatenation'], 502 $jsFileConfig['allWrap.']['splitChar'], 503 (bool)$jsFileConfig['async'], 504 $jsFileConfig['integrity'], 505 (bool)$jsFileConfig['defer'], 506 $crossorigin 507 ); 508 unset($jsFileConfig); 509 } 510 } 511 } 512 } 513 // JavaScript files 514 if (isset($tsfe->pSetup['includeJS.']) && is_array($tsfe->pSetup['includeJS.'])) { 515 foreach ($tsfe->pSetup['includeJS.'] as $key => $JSfile) { 516 if (!is_array($JSfile)) { 517 if (isset($tsfe->pSetup['includeJS.'][$key . '.']['if.']) && !$tsfe->cObj->checkIf($tsfe->pSetup['includeJS.'][$key . '.']['if.'])) { 518 continue; 519 } 520 if ($tsfe->pSetup['includeJS.'][$key . '.']['external']) { 521 $ss = $JSfile; 522 } else { 523 try { 524 $ss = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize($JSfile); 525 } catch (\TYPO3\CMS\Core\Resource\Exception $e) { 526 $ss = null; 527 } 528 } 529 if ($ss) { 530 $jsConfig = &$tsfe->pSetup['includeJS.'][$key . '.']; 531 $type = $jsConfig['type']; 532 if (!$type) { 533 $type = 'text/javascript'; 534 } 535 $crossorigin = $jsConfig['crossorigin']; 536 if (!$crossorigin && $jsConfig['integrity'] && $jsConfig['external']) { 537 $crossorigin = 'anonymous'; 538 } 539 $pageRenderer->addJsFile( 540 $ss, 541 $type, 542 $jsConfig['external'] ? false : empty($jsConfig['disableCompression']), 543 (bool)$jsConfig['forceOnTop'], 544 $jsConfig['allWrap'], 545 (bool)$jsConfig['excludeFromConcatenation'], 546 $jsConfig['allWrap.']['splitChar'], 547 (bool)$jsConfig['async'], 548 $jsConfig['integrity'], 549 (bool)$jsConfig['defer'], 550 $crossorigin 551 ); 552 unset($jsConfig); 553 } 554 } 555 } 556 } 557 if (isset($tsfe->pSetup['includeJSFooter.']) && is_array($tsfe->pSetup['includeJSFooter.'])) { 558 foreach ($tsfe->pSetup['includeJSFooter.'] as $key => $JSfile) { 559 if (!is_array($JSfile)) { 560 if (isset($tsfe->pSetup['includeJSFooter.'][$key . '.']['if.']) && !$tsfe->cObj->checkIf($tsfe->pSetup['includeJSFooter.'][$key . '.']['if.'])) { 561 continue; 562 } 563 if ($tsfe->pSetup['includeJSFooter.'][$key . '.']['external']) { 564 $ss = $JSfile; 565 } else { 566 try { 567 $ss = GeneralUtility::makeInstance(FilePathSanitizer::class)->sanitize($JSfile); 568 } catch (\TYPO3\CMS\Core\Resource\Exception $e) { 569 $ss = null; 570 } 571 } 572 if ($ss) { 573 $jsConfig = &$tsfe->pSetup['includeJSFooter.'][$key . '.']; 574 $type = $jsConfig['type']; 575 if (!$type) { 576 $type = 'text/javascript'; 577 } 578 $crossorigin = $jsConfig['crossorigin']; 579 if (!$crossorigin && $jsConfig['integrity'] && $jsConfig['external']) { 580 $crossorigin = 'anonymous'; 581 } 582 $pageRenderer->addJsFooterFile( 583 $ss, 584 $type, 585 $jsConfig['external'] ? false : empty($jsConfig['disableCompression']), 586 (bool)$jsConfig['forceOnTop'], 587 $jsConfig['allWrap'], 588 (bool)$jsConfig['excludeFromConcatenation'], 589 $jsConfig['allWrap.']['splitChar'], 590 (bool)$jsConfig['async'], 591 $jsConfig['integrity'], 592 (bool)$jsConfig['defer'], 593 $crossorigin 594 ); 595 unset($jsConfig); 596 } 597 } 598 } 599 } 600 // Headerdata 601 if (isset($tsfe->pSetup['headerData.']) && is_array($tsfe->pSetup['headerData.'])) { 602 $pageRenderer->addHeaderData($tsfe->cObj->cObjGet($tsfe->pSetup['headerData.'], 'headerData.')); 603 } 604 // Footerdata 605 if (isset($tsfe->pSetup['footerData.']) && is_array($tsfe->pSetup['footerData.'])) { 606 $pageRenderer->addFooterData($tsfe->cObj->cObjGet($tsfe->pSetup['footerData.'], 'footerData.')); 607 } 608 $tsfe->generatePageTitle(); 609 610 // @internal hook for EXT:seo, will be gone soon, do not use it in your own extensions 611 $_params = ['page' => $tsfe->page]; 612 $_ref = ''; 613 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['TYPO3\CMS\Frontend\Page\PageGenerator']['generateMetaTags'] ?? [] as $_funcRef) { 614 GeneralUtility::callUserFunction($_funcRef, $_params, $_ref); 615 } 616 617 static::generateMetaTagHtml( 618 $tsfe->pSetup['meta.'] ?? [], 619 $tsfe->cObj 620 ); 621 622 unset($tsfe->additionalHeaderData['JSCode']); 623 if (isset($tsfe->config['INTincScript']) && is_array($tsfe->config['INTincScript'])) { 624 $tsfe->additionalHeaderData['JSCode'] = $tsfe->JSCode; 625 // Storing the JSCode vars... 626 $tsfe->config['INTincScript_ext']['divKey'] = $tsfe->uniqueHash(); 627 $tsfe->config['INTincScript_ext']['additionalHeaderData'] = $tsfe->additionalHeaderData; 628 // Storing the header-data array 629 $tsfe->config['INTincScript_ext']['additionalFooterData'] = $tsfe->additionalFooterData; 630 // Storing the footer-data array 631 $tsfe->config['INTincScript_ext']['additionalJavaScript'] = $tsfe->additionalJavaScript; 632 // Storing the JS-data array 633 $tsfe->config['INTincScript_ext']['additionalCSS'] = $tsfe->additionalCSS; 634 // Storing the Style-data array 635 $tsfe->additionalHeaderData = ['<!--HD_' . $tsfe->config['INTincScript_ext']['divKey'] . '-->']; 636 // Clearing the array 637 $tsfe->additionalFooterData = ['<!--FD_' . $tsfe->config['INTincScript_ext']['divKey'] . '-->']; 638 // Clearing the array 639 $tsfe->divSection .= '<!--TDS_' . $tsfe->config['INTincScript_ext']['divKey'] . '-->'; 640 } else { 641 $tsfe->INTincScript_loadJSCode(); 642 } 643 $scriptJsCode = ''; 644 645 if ($tsfe->spamProtectEmailAddresses && $tsfe->spamProtectEmailAddresses !== 'ascii') { 646 $scriptJsCode = ' 647 // decrypt helper function 648 function decryptCharcode(n,start,end,offset) { 649 n = n + offset; 650 if (offset > 0 && n > end) { 651 n = start + (n - end - 1); 652 } else if (offset < 0 && n < start) { 653 n = end - (start - n - 1); 654 } 655 return String.fromCharCode(n); 656 } 657 // decrypt string 658 function decryptString(enc,offset) { 659 var dec = ""; 660 var len = enc.length; 661 for(var i=0; i < len; i++) { 662 var n = enc.charCodeAt(i); 663 if (n >= 0x2B && n <= 0x3A) { 664 dec += decryptCharcode(n,0x2B,0x3A,offset); // 0-9 . , - + / : 665 } else if (n >= 0x40 && n <= 0x5A) { 666 dec += decryptCharcode(n,0x40,0x5A,offset); // A-Z @ 667 } else if (n >= 0x61 && n <= 0x7A) { 668 dec += decryptCharcode(n,0x61,0x7A,offset); // a-z 669 } else { 670 dec += enc.charAt(i); 671 } 672 } 673 return dec; 674 } 675 // decrypt spam-protected emails 676 function linkTo_UnCryptMailto(s) { 677 location.href = decryptString(s,' . $tsfe->spamProtectEmailAddresses * -1 . '); 678 } 679 '; 680 } 681 // Add inline JS 682 $inlineJS = ''; 683 // defined in php 684 if (is_array($tsfe->inlineJS)) { 685 foreach ($tsfe->inlineJS as $key => $val) { 686 if (!is_array($val)) { 687 $inlineJS .= LF . $val . LF; 688 } 689 } 690 } 691 // defined in TS with page.inlineJS 692 // Javascript inline code 693 $inline = $tsfe->cObj->cObjGet($tsfe->pSetup['jsInline.'] ?? null, 'jsInline.'); 694 if ($inline) { 695 $inlineJS .= LF . $inline . LF; 696 } 697 // Javascript inline code for Footer 698 $inlineFooterJs = $tsfe->cObj->cObjGet($tsfe->pSetup['jsFooterInline.'] ?? null, 'jsFooterInline.'); 699 // Should minify? 700 if ($tsfe->config['config']['compressJs'] ?? false) { 701 $pageRenderer->enableCompressJavascript(); 702 $minifyErrorScript = ($minifyErrorInline = ''); 703 $scriptJsCode = GeneralUtility::minifyJavaScript($scriptJsCode, $minifyErrorScript); 704 if ($minifyErrorScript) { 705 $timeTracker->setTSlogMessage($minifyErrorScript, 3); 706 } 707 if ($inlineJS) { 708 $inlineJS = GeneralUtility::minifyJavaScript($inlineJS, $minifyErrorInline); 709 if ($minifyErrorInline) { 710 $timeTracker->setTSlogMessage($minifyErrorInline, 3); 711 } 712 } 713 if ($inlineFooterJs) { 714 $inlineFooterJs = GeneralUtility::minifyJavaScript($inlineFooterJs, $minifyErrorInline); 715 if ($minifyErrorInline) { 716 $timeTracker->setTSlogMessage($minifyErrorInline, 3); 717 } 718 } 719 } 720 if (!isset($tsfe->config['config']['removeDefaultJS']) || !$tsfe->config['config']['removeDefaultJS']) { 721 // include default and inlineJS 722 if ($scriptJsCode) { 723 $pageRenderer->addJsInlineCode('_scriptCode', $scriptJsCode, $tsfe->config['config']['compressJs']); 724 } 725 if ($inlineJS) { 726 $pageRenderer->addJsInlineCode('TS_inlineJS', $inlineJS, $tsfe->config['config']['compressJs']); 727 } 728 if ($inlineFooterJs) { 729 $pageRenderer->addJsFooterInlineCode('TS_inlineFooter', $inlineFooterJs, $tsfe->config['config']['compressJs']); 730 } 731 } elseif ($tsfe->config['config']['removeDefaultJS'] === 'external') { 732 /* 733 * This keeps inlineJS from *_INT Objects from being moved to external files. 734 * At this point in frontend rendering *_INT Objects only have placeholders instead 735 * of actual content so moving these placeholders to external files would 736 * a) break the JS file (syntax errors due to the placeholders) 737 * b) the needed JS would never get included to the page 738 * Therefore inlineJS from *_INT Objects must not be moved to external files but 739 * kept internal. 740 */ 741 $inlineJSint = ''; 742 self::stripIntObjectPlaceholder($inlineJS, $inlineJSint); 743 if ($inlineJSint) { 744 $pageRenderer->addJsInlineCode('TS_inlineJSint', $inlineJSint, $tsfe->config['config']['compressJs']); 745 } 746 if (trim($scriptJsCode . $inlineJS)) { 747 $pageRenderer->addJsFile(self::inline2TempFile($scriptJsCode . $inlineJS, 'js'), 'text/javascript', $tsfe->config['config']['compressJs']); 748 } 749 if ($inlineFooterJs) { 750 $inlineFooterJSint = ''; 751 self::stripIntObjectPlaceholder($inlineFooterJs, $inlineFooterJSint); 752 if ($inlineFooterJSint) { 753 $pageRenderer->addJsFooterInlineCode('TS_inlineFooterJSint', $inlineFooterJSint, $tsfe->config['config']['compressJs']); 754 } 755 $pageRenderer->addJsFooterFile(self::inline2TempFile($inlineFooterJs, 'js'), 'text/javascript', $tsfe->config['config']['compressJs']); 756 } 757 } else { 758 // Include only inlineJS 759 if ($inlineJS) { 760 $pageRenderer->addJsInlineCode('TS_inlineJS', $inlineJS, $tsfe->config['config']['compressJs']); 761 } 762 if ($inlineFooterJs) { 763 $pageRenderer->addJsFooterInlineCode('TS_inlineFooter', $inlineFooterJs, $tsfe->config['config']['compressJs']); 764 } 765 } 766 if (isset($tsfe->pSetup['inlineLanguageLabelFiles.']) && is_array($tsfe->pSetup['inlineLanguageLabelFiles.'])) { 767 foreach ($tsfe->pSetup['inlineLanguageLabelFiles.'] as $key => $languageFile) { 768 if (is_array($languageFile)) { 769 continue; 770 } 771 $languageFileConfig = &$tsfe->pSetup['inlineLanguageLabelFiles.'][$key . '.']; 772 if (isset($languageFileConfig['if.']) && !$tsfe->cObj->checkIf($languageFileConfig['if.'])) { 773 continue; 774 } 775 $pageRenderer->addInlineLanguageLabelFile( 776 $languageFile, 777 $languageFileConfig['selectionPrefix'] ?: '', 778 $languageFileConfig['stripFromSelectionName'] ?: '' 779 ); 780 } 781 } 782 if (isset($tsfe->pSetup['inlineSettings.']) && is_array($tsfe->pSetup['inlineSettings.'])) { 783 $pageRenderer->addInlineSettingArray('TS', $tsfe->pSetup['inlineSettings.']); 784 } 785 // Compression and concatenate settings 786 if ($tsfe->config['config']['compressCss'] ?? false) { 787 $pageRenderer->enableCompressCss(); 788 } 789 if ($tsfe->config['config']['compressJs'] ?? false) { 790 $pageRenderer->enableCompressJavascript(); 791 } 792 if ($tsfe->config['config']['concatenateCss'] ?? false) { 793 $pageRenderer->enableConcatenateCss(); 794 } 795 if ($tsfe->config['config']['concatenateJs'] ?? false) { 796 $pageRenderer->enableConcatenateJavascript(); 797 } 798 // Backward compatibility for old configuration 799 // @deprecated - remove this option in TYPO3 v10.0. 800 if ($tsfe->config['config']['concatenateJsAndCss'] ?? false) { 801 trigger_error('Setting config.concatenateJsAndCss is deprecated in favor of config.concatenateJs and config.concatenateCss, and will have no effect anymore in TYPO3 v10.0.', E_USER_DEPRECATED); 802 $pageRenderer->enableConcatenateCss(); 803 $pageRenderer->enableConcatenateJavascript(); 804 } 805 // Add header data block 806 if ($tsfe->additionalHeaderData) { 807 $pageRenderer->addHeaderData(implode(LF, $tsfe->additionalHeaderData)); 808 } 809 // Add footer data block 810 if ($tsfe->additionalFooterData) { 811 $pageRenderer->addFooterData(implode(LF, $tsfe->additionalFooterData)); 812 } 813 // Header complete, now add content 814 // Bodytag: 815 if ($tsfe->config['config']['disableBodyTag'] ?? false) { 816 $bodyTag = ''; 817 } else { 818 $defBT = (isset($tsfe->pSetup['bodyTagCObject']) && $tsfe->pSetup['bodyTagCObject']) 819 ? $tsfe->cObj->cObjGetSingle($tsfe->pSetup['bodyTagCObject'], $tsfe->pSetup['bodyTagCObject.'], 'bodyTagCObject') 820 : '<body>'; 821 $bodyTag = (isset($tsfe->pSetup['bodyTag']) && $tsfe->pSetup['bodyTag']) 822 ? $tsfe->pSetup['bodyTag'] 823 : $defBT; 824 if (trim($tsfe->pSetup['bodyTagAdd'] ?? '')) { 825 $bodyTag = preg_replace('/>$/', '', trim($bodyTag)) . ' ' . trim($tsfe->pSetup['bodyTagAdd']) . '>'; 826 } 827 } 828 $pageRenderer->addBodyContent(LF . $bodyTag); 829 // Div-sections 830 if ($tsfe->divSection) { 831 $pageRenderer->addBodyContent(LF . $tsfe->divSection); 832 } 833 // Page content 834 $pageRenderer->addBodyContent(LF . $pageContent); 835 if (!empty($tsfe->config['INTincScript']) && is_array($tsfe->config['INTincScript'])) { 836 // Store the serialized pageRenderer in configuration 837 $tsfe->config['INTincScript_ext']['pageRenderer'] = serialize($pageRenderer); 838 // Render complete page, keep placeholders for JavaScript and CSS 839 $tsfe->content = $pageRenderer->renderPageWithUncachedObjects($tsfe->config['INTincScript_ext']['divKey']); 840 } else { 841 // Render complete page 842 $tsfe->content = $pageRenderer->render(); 843 } 844 } 845 846 /************************* 847 * 848 * Helper functions 849 * Remember: Calls internally must still be done on the non-instantiated class: PageGenerator::inline2TempFile() 850 * 851 *************************/ 852 /** 853 * Searches for placeholder created from *_INT cObjects, removes them from 854 * $searchString and merges them to $intObjects 855 * 856 * @param string $searchString The String which should be cleaned from int-object markers 857 * @param string $intObjects The String the found int-placeholders are moved to (for further processing) 858 */ 859 protected static function stripIntObjectPlaceholder(&$searchString, &$intObjects) 860 { 861 $tempArray = []; 862 preg_match_all('/\\<\\!--INT_SCRIPT.[a-z0-9]*--\\>/', $searchString, $tempArray); 863 $searchString = preg_replace('/\\<\\!--INT_SCRIPT.[a-z0-9]*--\\>/', '', $searchString); 864 $intObjects = implode('', $tempArray[0]); 865 } 866 867 /** 868 * Writes string to a temporary file named after the md5-hash of the string 869 * 870 * @param string $str CSS styles / JavaScript to write to file. 871 * @param string $ext Extension: "css" or "js 872 * @return string <script> or <link> tag for the file. 873 * @deprecated since TYPO3 v9.4, will be removed in TYPO3 v10.0. This functionality is now within TYPO3's Frontend Request Handler. 874 */ 875 public static function inline2TempFile($str, $ext) 876 { 877 trigger_error('PageGenerator::inline2TempFile() will be removed in TYPO3 v10.0. This logic is now built in TYPO3s Frontend RequestHandler.', E_USER_DEPRECATED); 878 // Create filename / tags: 879 $script = ''; 880 switch ($ext) { 881 case 'js': 882 $script = 'typo3temp/assets/js/' . GeneralUtility::shortMD5($str) . '.js'; 883 break; 884 case 'css': 885 $script = 'typo3temp/assets/css/' . GeneralUtility::shortMD5($str) . '.css'; 886 break; 887 } 888 // Write file 889 if ($script && !@is_file(Environment::getPublicPath() . '/' . $script)) { 890 GeneralUtility::writeFileToTypo3tempDir(Environment::getPublicPath() . '/' . $script, $str); 891 } 892 return $script; 893 } 894 895 /** 896 * Checks if the value defined in "config.linkVars" contains an allowed value. Otherwise, return FALSE which means the value will not be added to any links. 897 * 898 * @param string $haystack The string in which to find $needle 899 * @param string $needle The string to find in $haystack 900 * @return bool Returns TRUE if $needle matches or is found in $haystack 901 * 902 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, is now called within TSFE itself, if needed outside the regular calculations, reimplement the method on your own. 903 */ 904 public static function isAllowedLinkVarValue($haystack, $needle) 905 { 906 trigger_error('The method will be removed in TYPO3 v10.0, if needed outside of linkVar calculation, re-implement the method in your own extension.', E_USER_DEPRECATED); 907 $OK = false; 908 // Integer 909 if ($needle === 'int' || $needle === 'integer') { 910 if (MathUtility::canBeInterpretedAsInteger($haystack)) { 911 $OK = true; 912 } 913 } elseif (preg_match('/^\\/.+\\/[imsxeADSUXu]*$/', $needle)) { 914 // Regular expression, only "//" is allowed as delimiter 915 if (@preg_match($needle, $haystack)) { 916 $OK = true; 917 } 918 } elseif (strstr($needle, '-')) { 919 // Range 920 if (MathUtility::canBeInterpretedAsInteger($haystack)) { 921 $range = explode('-', $needle); 922 if ($range[0] <= $haystack && $range[1] >= $haystack) { 923 $OK = true; 924 } 925 } 926 } elseif (strstr($needle, '|')) { 927 // List 928 // Trim the input 929 $haystack = str_replace(' ', '', $haystack); 930 if (strstr('|' . $needle . '|', '|' . $haystack . '|')) { 931 $OK = true; 932 } 933 } elseif ((string)$needle === (string)$haystack) { 934 // String comparison 935 $OK = true; 936 } 937 return $OK; 938 } 939 940 /** 941 * Generate title for page. 942 * Takes the settings [config][noPageTitle], [config][pageTitleFirst], [config][titleTagFunction] 943 * [config][pageTitleSeparator] and [config][noPageTitle] into account. 944 * Furthermore $GLOBALS[TSFE]->altPageTitle is observed. 945 * 946 * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, as TSFE->generatePageTitle() should be used instead. 947 */ 948 public static function generatePageTitle() 949 { 950 trigger_error('This method will be removed in TYPO3 v10.0. Use $TSFE->generatePageTitle() instead.', E_USER_DEPRECATED); 951 $GLOBALS['TSFE']->generatePageTitle(); 952 } 953 954 /** 955 * Generate meta tags from meta tag TypoScript 956 * 957 * @param array $metaTagTypoScript TypoScript configuration for meta tags (e.g. $GLOBALS['TSFE']->pSetup['meta.']) 958 * @param ContentObjectRenderer $cObj 959 */ 960 protected static function generateMetaTagHtml(array $metaTagTypoScript, ContentObjectRenderer $cObj) 961 { 962 $pageRenderer = static::getPageRenderer(); 963 964 /** @var TypoScriptService $typoScriptService */ 965 $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class); 966 $conf = $typoScriptService->convertTypoScriptArrayToPlainArray($metaTagTypoScript); 967 foreach ($conf as $key => $properties) { 968 $replace = false; 969 if (is_array($properties)) { 970 $nodeValue = $properties['_typoScriptNodeValue'] ?? ''; 971 $value = trim($cObj->stdWrap($nodeValue, $metaTagTypoScript[$key . '.'])); 972 if ($value === '' && !empty($properties['value'])) { 973 $value = $properties['value']; 974 $replace = false; 975 } 976 } else { 977 $value = $properties; 978 } 979 980 $attribute = 'name'; 981 if ((is_array($properties) && !empty($properties['httpEquivalent'])) || strtolower($key) === 'refresh') { 982 $attribute = 'http-equiv'; 983 } 984 if (is_array($properties) && !empty($properties['attribute'])) { 985 $attribute = $properties['attribute']; 986 } 987 if (is_array($properties) && !empty($properties['replace'])) { 988 $replace = true; 989 } 990 991 if (!is_array($value)) { 992 $value = (array)$value; 993 } 994 foreach ($value as $subValue) { 995 if (trim($subValue) !== '') { 996 $pageRenderer->setMetaTag($attribute, $key, $subValue, [], $replace); 997 } 998 } 999 } 1000 } 1001 1002 /** 1003 * @return PageRenderer 1004 */ 1005 protected static function getPageRenderer() 1006 { 1007 return GeneralUtility::makeInstance(PageRenderer::class); 1008 } 1009 1010 /** 1011 * Adds inline CSS code, by respecting the inlineStyle2TempFile option 1012 * 1013 * @param string $cssStyles the inline CSS styling 1014 * @param bool $excludeFromConcatenation option to see if it should be concatenated 1015 * @param string $inlineBlockName the block name to add it 1016 */ 1017 protected static function addCssToPageRenderer($cssStyles, $excludeFromConcatenation = false, $inlineBlockName = 'TSFEinlineStyle') 1018 { 1019 if (empty($GLOBALS['TSFE']->config['config']['inlineStyle2TempFile'])) { 1020 self::getPageRenderer()->addCssInlineBlock($inlineBlockName, $cssStyles, !empty($GLOBALS['TSFE']->config['config']['compressCss'])); 1021 } else { 1022 self::getPageRenderer()->addCssFile( 1023 self::inline2TempFile($cssStyles, 'css'), 1024 'stylesheet', 1025 'all', 1026 '', 1027 (bool)$GLOBALS['TSFE']->config['config']['compressCss'], 1028 false, 1029 '', 1030 $excludeFromConcatenation 1031 ); 1032 } 1033 } 1034 1035 /** 1036 * Returns the currently configured "site language" if a site is configured (= resolved) in the current request. 1037 * 1038 * @internal 1039 */ 1040 protected static function getCurrentSiteLanguage(): ?SiteLanguage 1041 { 1042 if (isset($GLOBALS['TYPO3_REQUEST']) 1043 && $GLOBALS['TYPO3_REQUEST'] instanceof ServerRequestInterface 1044 && $GLOBALS['TYPO3_REQUEST']->getAttribute('language') instanceof SiteLanguage) { 1045 return $GLOBALS['TYPO3_REQUEST']->getAttribute('language'); 1046 } 1047 return null; 1048 } 1049} 1050