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