[!!!][TASK] Remove $TYPO3_CONF_VARS['FE']['pageOverlayFields']
[Packages/TYPO3.CMS.git] / typo3 / sysext / frontend / Classes / Page / PageGenerator.php
1 <?php
2 namespace 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
17 use TYPO3\CMS\Core\Page\PageRenderer;
18 use TYPO3\CMS\Core\TimeTracker\TimeTracker;
19 use TYPO3\CMS\Core\Type\File\ImageInfo;
20 use TYPO3\CMS\Core\Utility\GeneralUtility;
21 use TYPO3\CMS\Core\Utility\MathUtility;
22 use TYPO3\CMS\Core\Utility\PathUtility;
23 use TYPO3\CMS\Extbase\Service\TypoScriptService;
24 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
25 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
26
27 /**
28 * Class for starting TypoScript page generation
29 *
30 * The class is not instantiated as an objects but called directly with the "::" operator.
31 * eg: \TYPO3\CMS\Frontend\Page\PageGenerator::pagegenInit()
32 */
33 class PageGenerator
34 {
35 /**
36 * Do not render title tag
37 * Typoscript setting: [config][noPageTitle]
38 */
39 const NO_PAGE_TITLE = 2;
40
41 /**
42 * Setting some vars in TSFE, primarily based on TypoScript config settings.
43 *
44 * @deprecated since TYPO3 v8, will be removed in TYPO3 v9
45 * @return void
46 */
47 public static function pagegenInit()
48 {
49 GeneralUtility::logDeprecatedFunction();
50 /** @var TypoScriptFrontendController $tsfe */
51 $tsfe = $GLOBALS['TSFE'];
52 if ($tsfe->page['content_from_pid'] > 0) {
53 // make REAL copy of TSFE object - not reference!
54 $temp_copy_TSFE = clone $tsfe;
55 // Set ->id to the content_from_pid value - we are going to evaluate this pid as was it a given id for a page-display!
56 $temp_copy_TSFE->id = $tsfe->page['content_from_pid'];
57 $temp_copy_TSFE->MP = '';
58 $temp_copy_TSFE->getPageAndRootlineWithDomain($tsfe->config['config']['content_from_pid_allowOutsideDomain'] ? 0 : $tsfe->domainStartPage);
59 $tsfe->contentPid = (int)$temp_copy_TSFE->id;
60 unset($temp_copy_TSFE);
61 }
62 if ($tsfe->config['config']['MP_defaults']) {
63 $temp_parts = GeneralUtility::trimExplode('|', $tsfe->config['config']['MP_defaults'], true);
64 foreach ($temp_parts as $temp_p) {
65 list($temp_idP, $temp_MPp) = explode(':', $temp_p, 2);
66 $temp_ids = GeneralUtility::intExplode(',', $temp_idP);
67 foreach ($temp_ids as $temp_id) {
68 $tsfe->MP_defaults[$temp_id] = $temp_MPp;
69 }
70 }
71 }
72 // Global vars...
73 $tsfe->indexedDocTitle = $tsfe->page['title'];
74 $tsfe->debug = !empty($tsfe->config['config']['debug']);
75 // Base url:
76 if (isset($tsfe->config['config']['baseURL'])) {
77 $tsfe->baseUrl = $tsfe->config['config']['baseURL'];
78 }
79 // Internal and External target defaults
80 $tsfe->intTarget = '' . $tsfe->config['config']['intTarget'];
81 $tsfe->extTarget = '' . $tsfe->config['config']['extTarget'];
82 $tsfe->fileTarget = '' . $tsfe->config['config']['fileTarget'];
83 if ($tsfe->config['config']['spamProtectEmailAddresses'] === 'ascii') {
84 $tsfe->spamProtectEmailAddresses = 'ascii';
85 } else {
86 $tsfe->spamProtectEmailAddresses = MathUtility::forceIntegerInRange($tsfe->config['config']['spamProtectEmailAddresses'], -10, 10, 0);
87 }
88 // calculate the absolute path prefix
89 if (!empty($tsfe->config['config']['absRefPrefix'])) {
90 $absRefPrefix = trim($tsfe->config['config']['absRefPrefix']);
91 if ($absRefPrefix === 'auto') {
92 $tsfe->absRefPrefix = GeneralUtility::getIndpEnv('TYPO3_SITE_PATH');
93 } else {
94 $tsfe->absRefPrefix = $absRefPrefix;
95 }
96 } else {
97 $tsfe->absRefPrefix = '';
98 }
99 if ($tsfe->type && $tsfe->config['config']['frameReloadIfNotInFrameset']) {
100 GeneralUtility::deprecationLog(
101 'frameReloadIfNotInFrameset has been marked as deprecated since TYPO3 v8, ' .
102 'and will be removed in TYPO3 v9.'
103 );
104 $tdlLD = $tsfe->tmpl->linkData($tsfe->page, '_top', $tsfe->no_cache, '');
105 $tsfe->additionalJavaScript['JSCode'] .= 'if(!parent.' . trim($tsfe->sPre) . ' && !parent.view_frame) top.location.href="' . $tsfe->baseUrlWrap($tdlLD['totalURL']) . '"';
106 }
107 $tsfe->compensateFieldWidth = '' . $tsfe->config['config']['compensateFieldWidth'];
108 $tsfe->lockFilePath = '' . $tsfe->config['config']['lockFilePath'];
109 $tsfe->lockFilePath = $tsfe->lockFilePath ?: $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'];
110 if (isset($tsfe->config['config']['noScaleUp'])) {
111 GeneralUtility::deprecationLog('The TypoScript property "config.noScaleUp" is deprecated since TYPO3 v8 and will be removed in TYPO3 v9. Please use the global TYPO3 configuration setting "GFX/processor_allowUpscaling" instead.');
112 }
113 $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_allowUpscaling'] = (bool)(isset($tsfe->config['config']['noScaleUp']) ? !$tsfe->config['config']['noScaleUp'] : $GLOBALS['TYPO3_CONF_VARS']['GFX']['processor_allowUpscaling']);
114 $tsfe->ATagParams = trim($tsfe->config['config']['ATagParams']) ? ' ' . trim($tsfe->config['config']['ATagParams']) : '';
115 if ($tsfe->config['config']['setJS_mouseOver']) {
116 $tsfe->setJS('mouseOver');
117 }
118 if ($tsfe->config['config']['setJS_openPic']) {
119 $tsfe->setJS('openPic');
120 }
121 static::initializeSearchWordDataInTsfe();
122 // linkVars
123 $tsfe->calculateLinkVars();
124 // dtdAllowsFrames indicates whether to use the target attribute in links
125 $tsfe->dtdAllowsFrames = false;
126 if ($tsfe->config['config']['doctype']) {
127 if (in_array(
128 (string)$tsfe->config['config']['doctype'],
129 ['xhtml_trans', 'xhtml_frames', 'xhtml_basic', 'html5'],
130 true)
131 ) {
132 $tsfe->dtdAllowsFrames = true;
133 }
134 } else {
135 $tsfe->dtdAllowsFrames = true;
136 }
137 // Setting XHTML-doctype from doctype
138 if (!$tsfe->config['config']['xhtmlDoctype']) {
139 $tsfe->config['config']['xhtmlDoctype'] = $tsfe->config['config']['doctype'];
140 }
141 if ($tsfe->config['config']['xhtmlDoctype']) {
142 $tsfe->xhtmlDoctype = $tsfe->config['config']['xhtmlDoctype'];
143 // Checking XHTML-docytpe
144 switch ((string)$tsfe->config['config']['xhtmlDoctype']) {
145 case 'xhtml_trans':
146 case 'xhtml_strict':
147 $tsfe->xhtmlVersion = 100;
148 break;
149 case 'xhtml_frames':
150 GeneralUtility::deprecationLog(
151 'xhtmlDoctype = xhtml_frames and doctype = xhtml_frames have been marked as deprecated since TYPO3 v8, ' .
152 'and will be removed in TYPO3 v9.'
153 );
154 $tsfe->xhtmlVersion = 100;
155 break;
156 case 'xhtml_basic':
157 $tsfe->xhtmlVersion = 105;
158 break;
159 case 'xhtml_11':
160 case 'xhtml+rdfa_10':
161 $tsfe->xhtmlVersion = 110;
162 break;
163 default:
164 static::getPageRenderer()->setRenderXhtml(false);
165 $tsfe->xhtmlDoctype = '';
166 $tsfe->xhtmlVersion = 0;
167 }
168 } else {
169 static::getPageRenderer()->setRenderXhtml(false);
170 }
171 }
172
173 /**
174 * Rendering the page content
175 *
176 * @return void
177 */
178 public static function renderContent()
179 {
180 /** @var TypoScriptFrontendController $tsfe */
181 $tsfe = $GLOBALS['TSFE'];
182
183 /** @var TimeTracker $timeTracker */
184 $timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
185
186 // PAGE CONTENT
187 $timeTracker->incStackPointer();
188 $timeTracker->push($tsfe->sPre, 'PAGE');
189 $pageContent = $tsfe->cObj->cObjGet($tsfe->pSetup);
190 if ($tsfe->pSetup['wrap']) {
191 $pageContent = $tsfe->cObj->wrap($pageContent, $tsfe->pSetup['wrap']);
192 }
193 if ($tsfe->pSetup['stdWrap.']) {
194 $pageContent = $tsfe->cObj->stdWrap($pageContent, $tsfe->pSetup['stdWrap.']);
195 }
196 // PAGE HEADER (after content - maybe JS is inserted!
197 // if 'disableAllHeaderCode' is set, all the header-code is discarded!
198 if ($tsfe->config['config']['disableAllHeaderCode']) {
199 $tsfe->content = $pageContent;
200 } else {
201 self::renderContentWithHeader($pageContent);
202 }
203 $timeTracker->pull($timeTracker->LR ? $tsfe->content : '');
204 $timeTracker->decStackPointer();
205 }
206
207 /**
208 * Rendering normal HTML-page with header by wrapping the generated content ($pageContent) in body-tags and setting the header accordingly.
209 *
210 * @param string $pageContent The page content which TypoScript objects has generated
211 * @return void
212 */
213 public static function renderContentWithHeader($pageContent)
214 {
215 /** @var TypoScriptFrontendController $tsfe */
216 $tsfe = $GLOBALS['TSFE'];
217
218 /** @var TimeTracker $timeTracker */
219 $timeTracker = GeneralUtility::makeInstance(TimeTracker::class);
220
221 $pageRenderer = static::getPageRenderer();
222 if ($tsfe->config['config']['moveJsFromHeaderToFooter']) {
223 $pageRenderer->enableMoveJsFromHeaderToFooter();
224 }
225 if ($tsfe->config['config']['pageRendererTemplateFile']) {
226 $file = $tsfe->tmpl->getFileName($tsfe->config['config']['pageRendererTemplateFile']);
227 if ($file) {
228 $pageRenderer->setTemplateFile($file);
229 }
230 }
231 $headerComment = $tsfe->config['config']['headerComment'];
232 if (trim($headerComment)) {
233 $pageRenderer->addInlineComment(TAB . str_replace(LF, (LF . TAB), trim($headerComment)) . LF);
234 }
235 // Setting charset:
236 $theCharset = $tsfe->metaCharset;
237 // Reset the content variables:
238 $tsfe->content = '';
239 $htmlTagAttributes = [];
240 $htmlLang = $tsfe->config['config']['htmlTag_langKey'] ?: ($tsfe->sys_language_isocode ?: 'en');
241 // Set content direction: (More info: http://www.tau.ac.il/~danon/Hebrew/HTML_and_Hebrew.html)
242 if ($tsfe->config['config']['htmlTag_dir']) {
243 $htmlTagAttributes['dir'] = htmlspecialchars($tsfe->config['config']['htmlTag_dir']);
244 }
245 // Setting document type:
246 $docTypeParts = [];
247 $xmlDocument = true;
248 // Part 1: XML prologue
249 switch ((string)$tsfe->config['config']['xmlprologue']) {
250 case 'none':
251 $xmlDocument = false;
252 break;
253 case 'xml_10':
254 $docTypeParts[] = '<?xml version="1.0" encoding="' . $theCharset . '"?>';
255 break;
256 case 'xml_11':
257 $docTypeParts[] = '<?xml version="1.1" encoding="' . $theCharset . '"?>';
258 break;
259 case '':
260 if ($tsfe->xhtmlVersion) {
261 $docTypeParts[] = '<?xml version="1.0" encoding="' . $theCharset . '"?>';
262 } else {
263 $xmlDocument = false;
264 }
265 break;
266 default:
267 $docTypeParts[] = $tsfe->config['config']['xmlprologue'];
268 }
269 // Part 2: DTD
270 $doctype = $tsfe->config['config']['doctype'];
271 if ($doctype) {
272 switch ($doctype) {
273 case 'xhtml_trans':
274 $docTypeParts[] = '<!DOCTYPE html
275 PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
276 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
277 break;
278 case 'xhtml_strict':
279 $docTypeParts[] = '<!DOCTYPE html
280 PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
281 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
282 break;
283 case 'xhtml_frames':
284 $docTypeParts[] = '<!DOCTYPE html
285 PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
286 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">';
287 break;
288 case 'xhtml_basic':
289 $docTypeParts[] = '<!DOCTYPE html
290 PUBLIC "-//W3C//DTD XHTML Basic 1.0//EN"
291 "http://www.w3.org/TR/xhtml-basic/xhtml-basic10.dtd">';
292 break;
293 case 'xhtml_11':
294 $docTypeParts[] = '<!DOCTYPE html
295 PUBLIC "-//W3C//DTD XHTML 1.1//EN"
296 "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
297 break;
298 case 'xhtml+rdfa_10':
299 $docTypeParts[] = '<!DOCTYPE html
300 PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN"
301 "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd">';
302 break;
303 case 'html5':
304 $docTypeParts[] = '<!DOCTYPE html>';
305 if ($xmlDocument) {
306 $pageRenderer->setMetaCharsetTag('<meta charset="|" />');
307 } else {
308 $pageRenderer->setMetaCharsetTag('<meta charset="|">');
309 }
310 break;
311 case 'none':
312 break;
313 default:
314 $docTypeParts[] = $doctype;
315 }
316 } else {
317 $docTypeParts[] = '<!DOCTYPE html>';
318 if ($xmlDocument) {
319 $pageRenderer->setMetaCharsetTag('<meta charset="|" />');
320 } else {
321 $pageRenderer->setMetaCharsetTag('<meta charset="|">');
322 }
323 }
324 if ($tsfe->xhtmlVersion) {
325 $htmlTagAttributes['xml:lang'] = $htmlLang;
326 }
327 if ($tsfe->xhtmlVersion < 110 || $doctype === 'html5') {
328 $htmlTagAttributes['lang'] = $htmlLang;
329 }
330 if ($tsfe->xhtmlVersion || $doctype === 'html5' && $xmlDocument) {
331 // We add this to HTML5 to achieve a slightly better backwards compatibility
332 $htmlTagAttributes['xmlns'] = 'http://www.w3.org/1999/xhtml';
333 if (is_array($tsfe->config['config']['namespaces.'])) {
334 foreach ($tsfe->config['config']['namespaces.'] as $prefix => $uri) {
335 // $uri gets htmlspecialchared later
336 $htmlTagAttributes['xmlns:' . htmlspecialchars($prefix)] = $uri;
337 }
338 }
339 }
340 // Swap XML and doctype order around (for MSIE / Opera standards compliance)
341 if ($tsfe->config['config']['doctypeSwitch']) {
342 $docTypeParts = array_reverse($docTypeParts);
343 }
344 // Adding doctype parts:
345 if (!empty($docTypeParts)) {
346 $pageRenderer->setXmlPrologAndDocType(implode(LF, $docTypeParts));
347 }
348 // Begin header section:
349 if ($tsfe->config['config']['htmlTag_setParams'] !== 'none') {
350 $_attr = $tsfe->config['config']['htmlTag_setParams'] ? $tsfe->config['config']['htmlTag_setParams'] : GeneralUtility::implodeAttributes($htmlTagAttributes);
351 } else {
352 $_attr = '';
353 }
354 $htmlTag = '<html' . ($_attr ? ' ' . $_attr : '') . '>';
355 if (isset($tsfe->config['config']['htmlTag_stdWrap.'])) {
356 $htmlTag = $tsfe->cObj->stdWrap($htmlTag, $tsfe->config['config']['htmlTag_stdWrap.']);
357 }
358 $pageRenderer->setHtmlTag($htmlTag);
359 // Head tag:
360 $headTag = $tsfe->pSetup['headTag'] ?: '<head>';
361 if (isset($tsfe->pSetup['headTag.'])) {
362 $headTag = $tsfe->cObj->stdWrap($headTag, $tsfe->pSetup['headTag.']);
363 }
364 $pageRenderer->setHeadTag($headTag);
365 // Setting charset meta tag:
366 $pageRenderer->setCharSet($theCharset);
367 $pageRenderer->addInlineComment(' This website is powered by TYPO3 - inspiring people to share!
368 TYPO3 is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.
369 TYPO3 is copyright ' . TYPO3_copyright_year . ' of Kasper Skaarhoj. Extensions are copyright of their respective owners.
370 Information and contribution at ' . TYPO3_URL_GENERAL . '
371 ');
372 if ($tsfe->baseUrl) {
373 $pageRenderer->setBaseUrl($tsfe->baseUrl);
374 }
375 if ($tsfe->pSetup['shortcutIcon']) {
376 $favIcon = ltrim($tsfe->tmpl->getFileName($tsfe->pSetup['shortcutIcon']), '/');
377 $iconFileInfo = GeneralUtility::makeInstance(ImageInfo::class, PATH_site . $favIcon);
378 if ($iconFileInfo->isFile()) {
379 $iconMimeType = $iconFileInfo->getMimeType();
380 if ($iconMimeType) {
381 $iconMimeType = ' type="' . $iconMimeType . '"';
382 $pageRenderer->setIconMimeType($iconMimeType);
383 }
384 $pageRenderer->setFavIcon(PathUtility::getAbsoluteWebPath($tsfe->absRefPrefix . $favIcon));
385 }
386 }
387 // Including CSS files
388 if (is_array($tsfe->tmpl->setup['plugin.'])) {
389 $stylesFromPlugins = '';
390 foreach ($tsfe->tmpl->setup['plugin.'] as $key => $iCSScode) {
391 if (is_array($iCSScode)) {
392 if ($iCSScode['_CSS_DEFAULT_STYLE'] && empty($tsfe->config['config']['removeDefaultCss'])) {
393 if (isset($iCSScode['_CSS_DEFAULT_STYLE.'])) {
394 $cssDefaultStyle = $tsfe->cObj->stdWrap($iCSScode['_CSS_DEFAULT_STYLE'], $iCSScode['_CSS_DEFAULT_STYLE.']);
395 } else {
396 $cssDefaultStyle = $iCSScode['_CSS_DEFAULT_STYLE'];
397 }
398 $stylesFromPlugins .= '/* default styles for extension "' . substr($key, 0, -1) . '" */' . LF . $cssDefaultStyle . LF;
399 }
400 if ($iCSScode['_CSS_PAGE_STYLE'] && empty($tsfe->config['config']['removePageCss'])) {
401 $cssPageStyle = implode(LF, $iCSScode['_CSS_PAGE_STYLE']);
402 if (isset($iCSScode['_CSS_PAGE_STYLE.'])) {
403 $cssPageStyle = $tsfe->cObj->stdWrap($cssPageStyle, $iCSScode['_CSS_PAGE_STYLE.']);
404 }
405 $cssPageStyle = '/* specific page styles for extension "' . substr($key, 0, -1) . '" */' . LF . $cssPageStyle;
406 self::addCssToPageRenderer($cssPageStyle, true, 'InlinePageCss');
407 }
408 }
409 }
410 if (!empty($stylesFromPlugins)) {
411 self::addCssToPageRenderer($stylesFromPlugins, false, 'InlineDefaultCss');
412 }
413 }
414 if ($tsfe->pSetup['stylesheet']) {
415 $ss = $tsfe->tmpl->getFileName($tsfe->pSetup['stylesheet']);
416 if ($ss) {
417 $pageRenderer->addCssFile($ss);
418 }
419 }
420 /**********************************************************************/
421 /* config.includeCSS / config.includeCSSLibs
422 /**********************************************************************/
423 if (is_array($tsfe->pSetup['includeCSS.'])) {
424 foreach ($tsfe->pSetup['includeCSS.'] as $key => $CSSfile) {
425 if (!is_array($CSSfile)) {
426 $cssFileConfig = &$tsfe->pSetup['includeCSS.'][$key . '.'];
427 if (isset($cssFileConfig['if.']) && !$tsfe->cObj->checkIf($cssFileConfig['if.'])) {
428 continue;
429 }
430 $ss = $cssFileConfig['external'] ? $CSSfile : $tsfe->tmpl->getFileName($CSSfile);
431 if ($ss) {
432 if ($cssFileConfig['import']) {
433 if (!$cssFileConfig['external'] && $ss[0] !== '/') {
434 // To fix MSIE 6 that cannot handle these as relative paths (according to Ben v Ende)
435 $ss = GeneralUtility::dirname(GeneralUtility::getIndpEnv('SCRIPT_NAME')) . '/' . $ss;
436 }
437 $pageRenderer->addCssInlineBlock('import_' . $key, '@import url("' . htmlspecialchars($ss) . '") ' . htmlspecialchars($cssFileConfig['media']) . ';', empty($cssFileConfig['disableCompression']), (bool)$cssFileConfig['forceOnTop']);
438 } else {
439 $pageRenderer->addCssFile(
440 $ss,
441 $cssFileConfig['alternate'] ? 'alternate stylesheet' : 'stylesheet',
442 $cssFileConfig['media'] ?: 'all',
443 $cssFileConfig['title'] ?: '',
444 empty($cssFileConfig['disableCompression']),
445 (bool)$cssFileConfig['forceOnTop'],
446 $cssFileConfig['allWrap'],
447 (bool)$cssFileConfig['excludeFromConcatenation'],
448 $cssFileConfig['allWrap.']['splitChar']
449 );
450 unset($cssFileConfig);
451 }
452 }
453 }
454 }
455 }
456 if (is_array($tsfe->pSetup['includeCSSLibs.'])) {
457 foreach ($tsfe->pSetup['includeCSSLibs.'] as $key => $CSSfile) {
458 if (!is_array($CSSfile)) {
459 $cssFileConfig = &$tsfe->pSetup['includeCSSLibs.'][$key . '.'];
460 if (isset($cssFileConfig['if.']) && !$tsfe->cObj->checkIf($cssFileConfig['if.'])) {
461 continue;
462 }
463 $ss = $cssFileConfig['external'] ? $CSSfile : $tsfe->tmpl->getFileName($CSSfile);
464 if ($ss) {
465 if ($cssFileConfig['import']) {
466 if (!$cssFileConfig['external'] && $ss[0] !== '/') {
467 // To fix MSIE 6 that cannot handle these as relative paths (according to Ben v Ende)
468 $ss = GeneralUtility::dirname(GeneralUtility::getIndpEnv('SCRIPT_NAME')) . '/' . $ss;
469 }
470 $pageRenderer->addCssInlineBlock('import_' . $key, '@import url("' . htmlspecialchars($ss) . '") ' . htmlspecialchars($cssFileConfig['media']) . ';', empty($cssFileConfig['disableCompression']), (bool)$cssFileConfig['forceOnTop']);
471 } else {
472 $pageRenderer->addCssLibrary(
473 $ss,
474 $cssFileConfig['alternate'] ? 'alternate stylesheet' : 'stylesheet',
475 $cssFileConfig['media'] ?: 'all',
476 $cssFileConfig['title'] ?: '',
477 empty($cssFileConfig['disableCompression']),
478 (bool)$cssFileConfig['forceOnTop'],
479 $cssFileConfig['allWrap'],
480 (bool)$cssFileConfig['excludeFromConcatenation'],
481 $cssFileConfig['allWrap.']['splitChar']
482 );
483 unset($cssFileConfig);
484 }
485 }
486 }
487 }
488 }
489
490 // Stylesheets
491 $style = '';
492 if ($tsfe->pSetup['insertClassesFromRTE']) {
493 $tsfe->logDeprecatedTyposcript(
494 'page.insertClassesFromRTE',
495 'Loading CSS classes from the RTE directly is discouraged in TYPO3 v8, as CSS classes should be '
496 . 'defined in CSS/LESS/SASS files instead, ensuring to load only what is necessary for a page, and '
497 . 'speeding up page rendering ("above the fold"). Additionally CSS should be defined in CSS files or '
498 . 'TypoScript and not via magic of pageTSconfig, overlaid by userTSconfig.'
499 );
500 $pageTSConfig = $tsfe->getPagesTSconfig();
501 $RTEclasses = $pageTSConfig['RTE.']['classes.'];
502 if (is_array($RTEclasses)) {
503 foreach ($RTEclasses as $RTEclassName => $RTEvalueArray) {
504 if ($RTEvalueArray['value']) {
505 $style .= '
506 .' . substr($RTEclassName, 0, -1) . ' {' . $RTEvalueArray['value'] . '}';
507 }
508 }
509 }
510 if ($tsfe->pSetup['insertClassesFromRTE.']['add_mainStyleOverrideDefs'] && is_array($pageTSConfig['RTE.']['default.']['mainStyleOverride_add.'])) {
511 $mSOa_tList = GeneralUtility::trimExplode(',', strtoupper($tsfe->pSetup['insertClassesFromRTE.']['add_mainStyleOverrideDefs']), true);
512 foreach ($pageTSConfig['RTE.']['default.']['mainStyleOverride_add.'] as $mSOa_key => $mSOa_value) {
513 if (!is_array($mSOa_value) && (in_array('*', $mSOa_tList) || in_array($mSOa_key, $mSOa_tList))) {
514 $style .= '
515 ' . $mSOa_key . ' {' . $mSOa_value . '}';
516 }
517 }
518 }
519 }
520 // Setting body tag margins in CSS:
521 if (isset($tsfe->pSetup['bodyTagMargins']) && $tsfe->pSetup['bodyTagMargins.']['useCSS']) {
522 $margins = (int)$tsfe->pSetup['bodyTagMargins'];
523 $style .= '
524 BODY {margin: ' . $margins . 'px ' . $margins . 'px ' . $margins . 'px ' . $margins . 'px;}';
525 }
526 // CSS_inlineStyle from TS
527 $style .= trim($tsfe->pSetup['CSS_inlineStyle']);
528 $style .= $tsfe->cObj->cObjGet($tsfe->pSetup['cssInline.'], 'cssInline.');
529 if (trim($style)) {
530 self::addCssToPageRenderer($style, true, 'additionalTSFEInlineStyle');
531 }
532 // Javascript Libraries
533 if (is_array($tsfe->pSetup['javascriptLibs.'])) {
534 // Include jQuery into the page renderer
535 if (!empty($tsfe->pSetup['javascriptLibs.']['jQuery'])) {
536 $jQueryTS = $tsfe->pSetup['javascriptLibs.']['jQuery.'];
537 // Check if version / source is set, if not set variable to "NULL" to use the default of the page renderer
538 $version = isset($jQueryTS['version']) ? $jQueryTS['version'] : null;
539 $source = isset($jQueryTS['source']) ? $jQueryTS['source'] : null;
540 // When "noConflict" is not set or "1" enable the default jQuery noConflict mode, otherwise disable the namespace
541 if (!isset($jQueryTS['noConflict']) || !empty($jQueryTS['noConflict'])) {
542 // Set namespace to the "noConflict.namespace" value if "noConflict.namespace" has a value
543 if (!empty($jQueryTS['noConflict.']['namespace'])) {
544 $namespace = $jQueryTS['noConflict.']['namespace'];
545 } else {
546 $namespace = PageRenderer::JQUERY_NAMESPACE_DEFAULT_NOCONFLICT;
547 }
548 } else {
549 $namespace = PageRenderer::JQUERY_NAMESPACE_NONE;
550 }
551 $pageRenderer->loadJquery($version, $source, $namespace);
552 }
553 }
554 // JavaScript library files
555 if (is_array($tsfe->pSetup['includeJSLibs.'])) {
556 foreach ($tsfe->pSetup['includeJSLibs.'] as $key => $JSfile) {
557 if (!is_array($JSfile)) {
558 if (isset($tsfe->pSetup['includeJSLibs.'][$key . '.']['if.']) && !$tsfe->cObj->checkIf($tsfe->pSetup['includeJSLibs.'][$key . '.']['if.'])) {
559 continue;
560 }
561 $ss = $tsfe->pSetup['includeJSLibs.'][$key . '.']['external'] ? $JSfile : $tsfe->tmpl->getFileName($JSfile);
562 if ($ss) {
563 $jsFileConfig = &$tsfe->pSetup['includeJSLibs.'][$key . '.'];
564 $type = $jsFileConfig['type'];
565 if (!$type) {
566 $type = 'text/javascript';
567 }
568
569 $pageRenderer->addJsLibrary(
570 $key,
571 $ss,
572 $type,
573 empty($jsFileConfig['disableCompression']),
574 (bool)$jsFileConfig['forceOnTop'],
575 $jsFileConfig['allWrap'],
576 (bool)$jsFileConfig['excludeFromConcatenation'],
577 $jsFileConfig['allWrap.']['splitChar'],
578 (bool)$jsFileConfig['async'],
579 $jsFileConfig['integrity']
580 );
581 unset($jsFileConfig);
582 }
583 }
584 }
585 }
586 if (is_array($tsfe->pSetup['includeJSFooterlibs.'])) {
587 foreach ($tsfe->pSetup['includeJSFooterlibs.'] as $key => $JSfile) {
588 if (!is_array($JSfile)) {
589 if (isset($tsfe->pSetup['includeJSFooterlibs.'][$key . '.']['if.']) && !$tsfe->cObj->checkIf($tsfe->pSetup['includeJSFooterlibs.'][$key . '.']['if.'])) {
590 continue;
591 }
592 $ss = $tsfe->pSetup['includeJSFooterlibs.'][$key . '.']['external'] ? $JSfile : $tsfe->tmpl->getFileName($JSfile);
593 if ($ss) {
594 $jsFileConfig = &$tsfe->pSetup['includeJSFooterlibs.'][$key . '.'];
595 $type = $jsFileConfig['type'];
596 if (!$type) {
597 $type = 'text/javascript';
598 }
599 $pageRenderer->addJsFooterLibrary(
600 $key,
601 $ss,
602 $type,
603 empty($jsFileConfig['disableCompression']),
604 (bool)$jsFileConfig['forceOnTop'],
605 $jsFileConfig['allWrap'],
606 (bool)$jsFileConfig['excludeFromConcatenation'],
607 $jsFileConfig['allWrap.']['splitChar'],
608 (bool)$jsFileConfig['async'],
609 $jsFileConfig['integrity']
610 );
611 unset($jsFileConfig);
612 }
613 }
614 }
615 }
616 // JavaScript files
617 if (is_array($tsfe->pSetup['includeJS.'])) {
618 foreach ($tsfe->pSetup['includeJS.'] as $key => $JSfile) {
619 if (!is_array($JSfile)) {
620 if (isset($tsfe->pSetup['includeJS.'][$key . '.']['if.']) && !$tsfe->cObj->checkIf($tsfe->pSetup['includeJS.'][$key . '.']['if.'])) {
621 continue;
622 }
623 $ss = $tsfe->pSetup['includeJS.'][$key . '.']['external'] ? $JSfile : $tsfe->tmpl->getFileName($JSfile);
624 if ($ss) {
625 $jsConfig = &$tsfe->pSetup['includeJS.'][$key . '.'];
626 $type = $jsConfig['type'];
627 if (!$type) {
628 $type = 'text/javascript';
629 }
630 $pageRenderer->addJsFile(
631 $ss,
632 $type,
633 empty($jsConfig['disableCompression']),
634 (bool)$jsConfig['forceOnTop'],
635 $jsConfig['allWrap'],
636 (bool)$jsConfig['excludeFromConcatenation'],
637 $jsConfig['allWrap.']['splitChar'],
638 (bool)$jsConfig['async'],
639 $jsConfig['integrity']
640 );
641 unset($jsConfig);
642 }
643 }
644 }
645 }
646 if (is_array($tsfe->pSetup['includeJSFooter.'])) {
647 foreach ($tsfe->pSetup['includeJSFooter.'] as $key => $JSfile) {
648 if (!is_array($JSfile)) {
649 if (isset($tsfe->pSetup['includeJSFooter.'][$key . '.']['if.']) && !$tsfe->cObj->checkIf($tsfe->pSetup['includeJSFooter.'][$key . '.']['if.'])) {
650 continue;
651 }
652 $ss = $tsfe->pSetup['includeJSFooter.'][$key . '.']['external'] ? $JSfile : $tsfe->tmpl->getFileName($JSfile);
653 if ($ss) {
654 $jsConfig = &$tsfe->pSetup['includeJSFooter.'][$key . '.'];
655 $type = $jsConfig['type'];
656 if (!$type) {
657 $type = 'text/javascript';
658 }
659 $pageRenderer->addJsFooterFile(
660 $ss,
661 $type,
662 empty($jsConfig['disableCompression']),
663 (bool)$jsConfig['forceOnTop'],
664 $jsConfig['allWrap'],
665 (bool)$jsConfig['excludeFromConcatenation'],
666 $jsConfig['allWrap.']['splitChar'],
667 (bool)$jsConfig['async'],
668 $jsConfig['integrity']
669 );
670 unset($jsConfig);
671 }
672 }
673 }
674 }
675 // Headerdata
676 if (is_array($tsfe->pSetup['headerData.'])) {
677 $pageRenderer->addHeaderData($tsfe->cObj->cObjGet($tsfe->pSetup['headerData.'], 'headerData.'));
678 }
679 // Footerdata
680 if (is_array($tsfe->pSetup['footerData.'])) {
681 $pageRenderer->addFooterData($tsfe->cObj->cObjGet($tsfe->pSetup['footerData.'], 'footerData.'));
682 }
683 static::generatePageTitle();
684
685 $metaTagsHtml = static::generateMetaTagHtml(
686 isset($tsfe->pSetup['meta.']) ? $tsfe->pSetup['meta.'] : [],
687 $tsfe->xhtmlVersion,
688 $tsfe->cObj
689 );
690 foreach ($metaTagsHtml as $metaTag) {
691 $pageRenderer->addMetaTag($metaTag);
692 }
693
694 unset($tsfe->additionalHeaderData['JSCode']);
695 if (is_array($tsfe->config['INTincScript'])) {
696 $tsfe->additionalHeaderData['JSCode'] = $tsfe->JSCode;
697 // Storing the JSCode vars...
698 $tsfe->config['INTincScript_ext']['divKey'] = $tsfe->uniqueHash();
699 $tsfe->config['INTincScript_ext']['additionalHeaderData'] = $tsfe->additionalHeaderData;
700 // Storing the header-data array
701 $tsfe->config['INTincScript_ext']['additionalFooterData'] = $tsfe->additionalFooterData;
702 // Storing the footer-data array
703 $tsfe->config['INTincScript_ext']['additionalJavaScript'] = $tsfe->additionalJavaScript;
704 // Storing the JS-data array
705 $tsfe->config['INTincScript_ext']['additionalCSS'] = $tsfe->additionalCSS;
706 // Storing the Style-data array
707 $tsfe->additionalHeaderData = ['<!--HD_' . $tsfe->config['INTincScript_ext']['divKey'] . '-->'];
708 // Clearing the array
709 $tsfe->additionalFooterData = ['<!--FD_' . $tsfe->config['INTincScript_ext']['divKey'] . '-->'];
710 // Clearing the array
711 $tsfe->divSection .= '<!--TDS_' . $tsfe->config['INTincScript_ext']['divKey'] . '-->';
712 } else {
713 $tsfe->INTincScript_loadJSCode();
714 }
715 $scriptJsCode = '';
716
717 if ($tsfe->spamProtectEmailAddresses && $tsfe->spamProtectEmailAddresses !== 'ascii') {
718 $scriptJsCode = '
719 // decrypt helper function
720 function decryptCharcode(n,start,end,offset) {
721 n = n + offset;
722 if (offset > 0 && n > end) {
723 n = start + (n - end - 1);
724 } else if (offset < 0 && n < start) {
725 n = end - (start - n - 1);
726 }
727 return String.fromCharCode(n);
728 }
729 // decrypt string
730 function decryptString(enc,offset) {
731 var dec = "";
732 var len = enc.length;
733 for(var i=0; i < len; i++) {
734 var n = enc.charCodeAt(i);
735 if (n >= 0x2B && n <= 0x3A) {
736 dec += decryptCharcode(n,0x2B,0x3A,offset); // 0-9 . , - + / :
737 } else if (n >= 0x40 && n <= 0x5A) {
738 dec += decryptCharcode(n,0x40,0x5A,offset); // A-Z @
739 } else if (n >= 0x61 && n <= 0x7A) {
740 dec += decryptCharcode(n,0x61,0x7A,offset); // a-z
741 } else {
742 dec += enc.charAt(i);
743 }
744 }
745 return dec;
746 }
747 // decrypt spam-protected emails
748 function linkTo_UnCryptMailto(s) {
749 location.href = decryptString(s,' . $tsfe->spamProtectEmailAddresses * -1 . ');
750 }
751 ';
752 }
753 // Add inline JS
754 $inlineJS = '';
755 // defined in php
756 if (is_array($tsfe->inlineJS)) {
757 foreach ($tsfe->inlineJS as $key => $val) {
758 if (!is_array($val)) {
759 $inlineJS .= LF . $val . LF;
760 }
761 }
762 }
763 // defined in TS with page.inlineJS
764 // Javascript inline code
765 $inline = $tsfe->cObj->cObjGet($tsfe->pSetup['jsInline.'], 'jsInline.');
766 if ($inline) {
767 $inlineJS .= LF . $inline . LF;
768 }
769 // Javascript inline code for Footer
770 $inlineFooterJs = $tsfe->cObj->cObjGet($tsfe->pSetup['jsFooterInline.'], 'jsFooterInline.');
771 // Should minify?
772 if ($tsfe->config['config']['compressJs']) {
773 $pageRenderer->enableCompressJavascript();
774 $minifyErrorScript = ($minifyErrorInline = '');
775 $scriptJsCode = GeneralUtility::minifyJavaScript($scriptJsCode, $minifyErrorScript);
776 if ($minifyErrorScript) {
777 $timeTracker->setTSlogMessage($minifyErrorScript, 3);
778 }
779 if ($inlineJS) {
780 $inlineJS = GeneralUtility::minifyJavaScript($inlineJS, $minifyErrorInline);
781 if ($minifyErrorInline) {
782 $timeTracker->setTSlogMessage($minifyErrorInline, 3);
783 }
784 }
785 if ($inlineFooterJs) {
786 $inlineFooterJs = GeneralUtility::minifyJavaScript($inlineFooterJs, $minifyErrorInline);
787 if ($minifyErrorInline) {
788 $timeTracker->setTSlogMessage($minifyErrorInline, 3);
789 }
790 }
791 }
792 if (!$tsfe->config['config']['removeDefaultJS']) {
793 // include default and inlineJS
794 if ($scriptJsCode) {
795 $pageRenderer->addJsInlineCode('_scriptCode', $scriptJsCode, $tsfe->config['config']['compressJs']);
796 }
797 if ($inlineJS) {
798 $pageRenderer->addJsInlineCode('TS_inlineJS', $inlineJS, $tsfe->config['config']['compressJs']);
799 }
800 if ($inlineFooterJs) {
801 $pageRenderer->addJsFooterInlineCode('TS_inlineFooter', $inlineFooterJs, $tsfe->config['config']['compressJs']);
802 }
803 } elseif ($tsfe->config['config']['removeDefaultJS'] === 'external') {
804 /*
805 * This keeps inlineJS from *_INT Objects from being moved to external files.
806 * At this point in frontend rendering *_INT Objects only have placeholders instead
807 * of actual content so moving these placeholders to external files would
808 * a) break the JS file (syntax errors due to the placeholders)
809 * b) the needed JS would never get included to the page
810 * Therefore inlineJS from *_INT Objects must not be moved to external files but
811 * kept internal.
812 */
813 $inlineJSint = '';
814 self::stripIntObjectPlaceholder($inlineJS, $inlineJSint);
815 if ($inlineJSint) {
816 $pageRenderer->addJsInlineCode('TS_inlineJSint', $inlineJSint, $tsfe->config['config']['compressJs']);
817 }
818 if (trim($scriptJsCode . $inlineJS)) {
819 $pageRenderer->addJsFile(self::inline2TempFile($scriptJsCode . $inlineJS, 'js'), 'text/javascript', $tsfe->config['config']['compressJs']);
820 }
821 if ($inlineFooterJs) {
822 $inlineFooterJSint = '';
823 self::stripIntObjectPlaceholder($inlineFooterJs, $inlineFooterJSint);
824 if ($inlineFooterJSint) {
825 $pageRenderer->addJsFooterInlineCode('TS_inlineFooterJSint', $inlineFooterJSint, $tsfe->config['config']['compressJs']);
826 }
827 $pageRenderer->addJsFooterFile(self::inline2TempFile($inlineFooterJs, 'js'), 'text/javascript', $tsfe->config['config']['compressJs']);
828 }
829 } else {
830 // Include only inlineJS
831 if ($inlineJS) {
832 $pageRenderer->addJsInlineCode('TS_inlineJS', $inlineJS, $tsfe->config['config']['compressJs']);
833 }
834 if ($inlineFooterJs) {
835 $pageRenderer->addJsFooterInlineCode('TS_inlineFooter', $inlineFooterJs, $tsfe->config['config']['compressJs']);
836 }
837 }
838 if (is_array($tsfe->pSetup['inlineLanguageLabelFiles.'])) {
839 foreach ($tsfe->pSetup['inlineLanguageLabelFiles.'] as $key => $languageFile) {
840 if (is_array($languageFile)) {
841 continue;
842 }
843 $languageFileConfig = &$tsfe->pSetup['inlineLanguageLabelFiles.'][$key . '.'];
844 if (isset($languageFileConfig['if.']) && !$tsfe->cObj->checkIf($languageFileConfig['if.'])) {
845 continue;
846 }
847 $pageRenderer->addInlineLanguageLabelFile(
848 $languageFile,
849 $languageFileConfig['selectionPrefix'] ?: '',
850 $languageFileConfig['stripFromSelectionName'] ?: '',
851 $languageFileConfig['errorMode'] ? (int)$languageFileConfig['errorMode'] : 0
852 );
853 }
854 }
855 if (is_array($tsfe->pSetup['inlineSettings.'])) {
856 $pageRenderer->addInlineSettingArray('TS', $tsfe->pSetup['inlineSettings.']);
857 }
858 // Compression and concatenate settings
859 if ($tsfe->config['config']['compressCss']) {
860 $pageRenderer->enableCompressCss();
861 }
862 if ($tsfe->config['config']['compressJs']) {
863 $pageRenderer->enableCompressJavascript();
864 }
865 if ($tsfe->config['config']['concatenateCss']) {
866 $pageRenderer->enableConcatenateCss();
867 }
868 if ($tsfe->config['config']['concatenateJs']) {
869 $pageRenderer->enableConcatenateJavascript();
870 }
871 // Backward compatibility for old configuration
872 if ($tsfe->config['config']['concatenateJsAndCss']) {
873 $pageRenderer->enableConcatenateFiles();
874 }
875 // Add header data block
876 if ($tsfe->additionalHeaderData) {
877 $pageRenderer->addHeaderData(implode(LF, $tsfe->additionalHeaderData));
878 }
879 // Add footer data block
880 if ($tsfe->additionalFooterData) {
881 $pageRenderer->addFooterData(implode(LF, $tsfe->additionalFooterData));
882 }
883 // Header complete, now add content
884 if ($tsfe->pSetup['frameSet.']) {
885 GeneralUtility::deprecationLog(
886 'frameSet, FRAME and FRAMESET have been marked as deprecated since TYPO3 v8 ' .
887 'and will be removed in TYPO3 v9.'
888 );
889 $fs = GeneralUtility::makeInstance(FramesetRenderer::class);
890 $pageRenderer->addBodyContent($fs->make($tsfe->pSetup['frameSet.']));
891 $pageRenderer->addBodyContent(LF . '<noframes>' . LF);
892 }
893 // Bodytag:
894 if ($tsfe->config['config']['disableBodyTag']) {
895 $bodyTag = '';
896 } else {
897 $defBT = $tsfe->pSetup['bodyTagCObject'] ? $tsfe->cObj->cObjGetSingle($tsfe->pSetup['bodyTagCObject'], $tsfe->pSetup['bodyTagCObject.'], 'bodyTagCObject') : '<body>';
898 $bodyTag = $tsfe->pSetup['bodyTag'] ? $tsfe->pSetup['bodyTag'] : $defBT;
899 if (isset($tsfe->pSetup['bodyTagMargins'])) {
900 $margins = (int)$tsfe->pSetup['bodyTagMargins'];
901 if ($tsfe->pSetup['bodyTagMargins.']['useCSS']) {
902 } else {
903 $bodyTag = preg_replace('/>$/', '', trim($bodyTag)) . ' leftmargin="' . $margins . '" topmargin="' . $margins . '" marginwidth="' . $margins . '" marginheight="' . $margins . '">';
904 }
905 }
906 if (trim($tsfe->pSetup['bodyTagAdd'])) {
907 $bodyTag = preg_replace('/>$/', '', trim($bodyTag)) . ' ' . trim($tsfe->pSetup['bodyTagAdd']) . '>';
908 }
909 }
910 $pageRenderer->addBodyContent(LF . $bodyTag);
911 // Div-sections
912 if ($tsfe->divSection) {
913 $pageRenderer->addBodyContent(LF . $tsfe->divSection);
914 }
915 // Page content
916 $pageRenderer->addBodyContent(LF . $pageContent);
917 if (!empty($tsfe->config['INTincScript']) && is_array($tsfe->config['INTincScript'])) {
918 // Store the serialized pageRenderer in configuration
919 $tsfe->config['INTincScript_ext']['pageRenderer'] = serialize($pageRenderer);
920 // Render complete page, keep placeholders for JavaScript and CSS
921 $tsfe->content = $pageRenderer->renderPageWithUncachedObjects($tsfe->config['INTincScript_ext']['divKey']);
922 } else {
923 // Render complete page
924 $tsfe->content = $pageRenderer->render();
925 }
926 // Ending page
927 if ($tsfe->pSetup['frameSet.']) {
928 $tsfe->content .= LF . '</noframes>';
929 }
930 }
931
932 /*************************
933 *
934 * Helper functions
935 * Remember: Calls internally must still be done on the non-instantiated class: PageGenerator::inline2TempFile()
936 *
937 *************************/
938 /**
939 * Searches for placeholder created from *_INT cObjects, removes them from
940 * $searchString and merges them to $intObjects
941 *
942 * @param string $searchString The String which should be cleaned from int-object markers
943 * @param string $intObjects The String the found int-placeholders are moved to (for further processing)
944 */
945 protected static function stripIntObjectPlaceholder(&$searchString, &$intObjects)
946 {
947 $tempArray = [];
948 preg_match_all('/\\<\\!--INT_SCRIPT.[a-z0-9]*--\\>/', $searchString, $tempArray);
949 $searchString = preg_replace('/\\<\\!--INT_SCRIPT.[a-z0-9]*--\\>/', '', $searchString);
950 $intObjects = implode('', $tempArray[0]);
951 }
952
953 /**
954 * Writes string to a temporary file named after the md5-hash of the string
955 *
956 * @param string $str CSS styles / JavaScript to write to file.
957 * @param string $ext Extension: "css" or "js
958 * @return string <script> or <link> tag for the file.
959 */
960 public static function inline2TempFile($str, $ext)
961 {
962 // Create filename / tags:
963 $script = '';
964 switch ($ext) {
965 case 'js':
966 $script = 'typo3temp/assets/js/' . GeneralUtility::shortMD5($str) . '.js';
967 break;
968 case 'css':
969 $script = 'typo3temp/assets/css/' . GeneralUtility::shortMD5($str) . '.css';
970 break;
971 }
972 // Write file:
973 if ($script) {
974 if (!@is_file(PATH_site . $script)) {
975 GeneralUtility::writeFileToTypo3tempDir(PATH_site . $script, $str);
976 }
977 }
978 return $script;
979 }
980
981 /**
982 * 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.
983 *
984 * @param string $haystack The string in which to find $needle
985 * @param string $needle The string to find in $haystack
986 * @return bool Returns TRUE if $needle matches or is found in $haystack
987 */
988 public static function isAllowedLinkVarValue($haystack, $needle)
989 {
990 $OK = false;
991 // Integer
992 if ($needle === 'int' || $needle === 'integer') {
993 if (MathUtility::canBeInterpretedAsInteger($haystack)) {
994 $OK = true;
995 }
996 } elseif (preg_match('/^\\/.+\\/[imsxeADSUXu]*$/', $needle)) {
997 // Regular expression, only "//" is allowed as delimiter
998 if (@preg_match($needle, $haystack)) {
999 $OK = true;
1000 }
1001 } elseif (strstr($needle, '-')) {
1002 // Range
1003 if (MathUtility::canBeInterpretedAsInteger($haystack)) {
1004 $range = explode('-', $needle);
1005 if ($range[0] <= $haystack && $range[1] >= $haystack) {
1006 $OK = true;
1007 }
1008 }
1009 } elseif (strstr($needle, '|')) {
1010 // List
1011 // Trim the input
1012 $haystack = str_replace(' ', '', $haystack);
1013 if (strstr('|' . $needle . '|', '|' . $haystack . '|')) {
1014 $OK = true;
1015 }
1016 } elseif ((string)$needle === (string)$haystack) {
1017 // String comparison
1018 $OK = true;
1019 }
1020 return $OK;
1021 }
1022
1023 /**
1024 * Generate title for page.
1025 * Takes the settings [config][noPageTitle], [config][pageTitleFirst], [config][titleTagFunction]
1026 * [config][pageTitleSeparator] and [config][noPageTitle] into account.
1027 * Furthermore $GLOBALS[TSFE]->altPageTitle is observed.
1028 *
1029 * @return void
1030 */
1031 public static function generatePageTitle()
1032 {
1033 /** @var TypoScriptFrontendController $tsfe */
1034 $tsfe = $GLOBALS['TSFE'];
1035
1036 $pageTitleSeparator = '';
1037
1038 // check for a custom pageTitleSeparator, and perform stdWrap on it
1039 if (isset($tsfe->config['config']['pageTitleSeparator']) && $tsfe->config['config']['pageTitleSeparator'] !== '') {
1040 $pageTitleSeparator = $tsfe->config['config']['pageTitleSeparator'];
1041
1042 if (isset($tsfe->config['config']['pageTitleSeparator.']) && is_array($tsfe->config['config']['pageTitleSeparator.'])) {
1043 $pageTitleSeparator = $tsfe->cObj->stdWrap($pageTitleSeparator, $tsfe->config['config']['pageTitleSeparator.']);
1044 } else {
1045 $pageTitleSeparator .= ' ';
1046 }
1047 }
1048
1049 $titleTagContent = $tsfe->tmpl->printTitle(
1050 $tsfe->altPageTitle ?: $tsfe->page['title'],
1051 $tsfe->config['config']['noPageTitle'],
1052 $tsfe->config['config']['pageTitleFirst'],
1053 $pageTitleSeparator
1054 );
1055 if ($tsfe->config['config']['titleTagFunction']) {
1056 $titleTagContent = $tsfe->cObj->callUserFunction(
1057 $tsfe->config['config']['titleTagFunction'],
1058 [],
1059 $titleTagContent
1060 );
1061 }
1062 // stdWrap around the title tag
1063 if (isset($tsfe->config['config']['pageTitle.']) && is_array($tsfe->config['config']['pageTitle.'])) {
1064 $titleTagContent = $tsfe->cObj->stdWrap($titleTagContent, $tsfe->config['config']['pageTitle.']);
1065 }
1066 if ($titleTagContent !== '' && (int)$tsfe->config['config']['noPageTitle'] !== self::NO_PAGE_TITLE) {
1067 static::getPageRenderer()->setTitle($titleTagContent);
1068 }
1069 }
1070
1071 /**
1072 * Generate meta tags from meta tag TypoScript
1073 *
1074 * @param array $metaTagTypoScript TypoScript configuration for meta tags (e.g. $GLOBALS['TSFE']->pSetup['meta.'])
1075 * @param bool $xhtml Whether xhtml tag-style should be used. (e.g. pass $GLOBALS['TSFE']->xhtmlVersion here)
1076 * @param ContentObjectRenderer $cObj
1077 * @return array Array of HTML meta tags
1078 */
1079 protected static function generateMetaTagHtml(array $metaTagTypoScript, $xhtml, ContentObjectRenderer $cObj)
1080 {
1081 // Add ending slash only to documents rendered as xhtml
1082 $endingSlash = $xhtml ? ' /' : '';
1083
1084 $metaTags = [
1085 '<meta name="generator" content="TYPO3 CMS"' . $endingSlash . '>'
1086 ];
1087
1088 /** @var TypoScriptService $typoScriptService */
1089 $typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
1090 $conf = $typoScriptService->convertTypoScriptArrayToPlainArray($metaTagTypoScript);
1091 foreach ($conf as $key => $properties) {
1092 if (is_array($properties)) {
1093 $nodeValue = isset($properties['_typoScriptNodeValue']) ? $properties['_typoScriptNodeValue'] : '';
1094 $value = trim($cObj->stdWrap($nodeValue, $metaTagTypoScript[$key . '.']));
1095 if ($value === '' && !empty($properties['value'])) {
1096 $value = $properties['value'];
1097 }
1098 } else {
1099 $value = $properties;
1100 }
1101
1102 $attribute = 'name';
1103 if ((is_array($properties) && !empty($properties['httpEquivalent'])) || strtolower($key) === 'refresh') {
1104 $attribute = 'http-equiv';
1105 }
1106 if (is_array($properties) && !empty($properties['attribute'])) {
1107 $attribute = $properties['attribute'];
1108 }
1109
1110 if (!is_array($value)) {
1111 $value = (array)$value;
1112 }
1113 foreach ($value as $subValue) {
1114 if (trim($subValue) !== '') {
1115 $metaTags[] = '<meta ' . $attribute . '="' . $key . '" content="' . htmlspecialchars($subValue) . '"' . $endingSlash . '>';
1116 }
1117 }
1118 }
1119 return $metaTags;
1120 }
1121
1122 /**
1123 * Fills the sWordList property and builds the regular expression in TSFE that can be used to split
1124 * strings by the submitted search words.
1125 *
1126 * @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::sWordList
1127 * @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::sWordRegEx
1128 */
1129 protected static function initializeSearchWordDataInTsfe()
1130 {
1131 /** @var TypoScriptFrontendController $tsfe */
1132 $tsfe = $GLOBALS['TSFE'];
1133
1134 $tsfe->sWordRegEx = '';
1135 $tsfe->sWordList = GeneralUtility::_GP('sword_list');
1136 if (is_array($tsfe->sWordList)) {
1137 $space = !empty($tsfe->config['config']['sword_standAlone']) ? '[[:space:]]' : '';
1138 foreach ($tsfe->sWordList as $val) {
1139 if (trim($val) !== '') {
1140 $tsfe->sWordRegEx .= $space . preg_quote($val, '/') . $space . '|';
1141 }
1142 }
1143 $tsfe->sWordRegEx = rtrim($tsfe->sWordRegEx, '|');
1144 }
1145 }
1146
1147 /**
1148 * @return PageRenderer
1149 */
1150 protected static function getPageRenderer()
1151 {
1152 return GeneralUtility::makeInstance(PageRenderer::class);
1153 }
1154
1155 /**
1156 * Adds inline CSS code, by respecting the inlineStyle2TempFile option
1157 *
1158 * @param string $cssStyles the inline CSS styling
1159 * @param bool $excludeFromConcatenation option to see if it should be concatenated
1160 * @param string $inlineBlockName the block name to add it
1161 */
1162 protected static function addCssToPageRenderer($cssStyles, $excludeFromConcatenation = false, $inlineBlockName = 'TSFEinlineStyle')
1163 {
1164 if (empty($GLOBALS['TSFE']->config['config']['inlineStyle2TempFile'])) {
1165 self::getPageRenderer()->addCssInlineBlock($inlineBlockName, $cssStyles, !empty($GLOBALS['TSFE']->config['config']['compressCss']));
1166 } else {
1167 self::getPageRenderer()->addCssFile(
1168 self::inline2TempFile($cssStyles, 'css'),
1169 'stylesheet',
1170 'all',
1171 '',
1172 (bool)$GLOBALS['TSFE']->config['config']['compressCss'],
1173 false,
1174 '',
1175 $excludeFromConcatenation
1176 );
1177 }
1178 }
1179 }