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