From eb4936469039f513420c751f11f9ac50f01a60af Mon Sep 17 00:00:00 2001 From: Benni Mack <benni@typo3.org> Date: Thu, 7 Dec 2017 21:06:25 +0100 Subject: [PATCH] [TASK] Move Page Title generation to TSFE The static method "PageGenerator::generatePageTitle()" only operated on TSFE and is moved within the main controller, which can be retriggered multiple times as before. The same goes for TemplateService->printTitle(). Additionally, the method isAllowedLinkVarValue() is moved to TSFE as well. Resolves: #83254 Releases: master Change-Id: If519963e33a57c21ac5cc575e4395444ab50450d Reviewed-on: https://review.typo3.org/54973 Reviewed-by: Susanne Moog <susanne.moog@typo3.org> Tested-by: Susanne Moog <susanne.moog@typo3.org> Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Frank Naegler <frank.naegler@typo3.org> Tested-by: Frank Naegler <frank.naegler@typo3.org> --- .../Classes/TypoScript/TemplateService.php | 2 + ...254-MovedPageGenerationMethodsIntoTSFE.rst | 39 ++++++ .../TypoScriptFrontendController.php | 126 +++++++++++++++++- .../frontend/Classes/Page/PageGenerator.php | 46 ++----- .../Tests/Unit/Page/PageGeneratorTest.php | 11 ++ .../Php/MethodCallMatcher.php | 7 + .../Php/MethodCallStaticMatcher.php | 14 ++ 7 files changed, 202 insertions(+), 43 deletions(-) create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Deprecation-83254-MovedPageGenerationMethodsIntoTSFE.rst diff --git a/typo3/sysext/core/Classes/TypoScript/TemplateService.php b/typo3/sysext/core/Classes/TypoScript/TemplateService.php index b36bb97ee9d7..47b2de335c01 100644 --- a/typo3/sysext/core/Classes/TypoScript/TemplateService.php +++ b/typo3/sysext/core/Classes/TypoScript/TemplateService.php @@ -1407,9 +1407,11 @@ class TemplateService * @param string $pageTitleSeparator an alternative to the ": " as the separator between site title and page title * @return string The page title on the form "[sitetitle]: [input-title]". Not htmlspecialchar()'ed. * @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::tempPageCacheContent(), \TYPO3\CMS\Frontend\Page\PageGenerator::renderContentWithHeader() + * @deprecated since TYPO3 v9, will be removed in TYPO3 v10, use $TSFE->generatePageTitle() instead. */ public function printTitle($pageTitle, $noTitle = false, $showTitleFirst = false, $pageTitleSeparator = '') { + trigger_error('This method will be removed in TYPO3 v10. Title tag generation has been moved into TSFE itself, re-implement this method if you need to, otherwise use TSFE->generatePageTitle() for full usage.', E_USER_DEPRECATED); $siteTitle = trim($this->setup['sitetitle']); $pageTitle = $noTitle ? '' : $pageTitle; if ($showTitleFirst) { diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-83254-MovedPageGenerationMethodsIntoTSFE.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-83254-MovedPageGenerationMethodsIntoTSFE.rst new file mode 100644 index 000000000000..31ca386a203b --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-83254-MovedPageGenerationMethodsIntoTSFE.rst @@ -0,0 +1,39 @@ +.. include:: ../../Includes.txt + +============================================================= +Deprecation: #83254 - Moved page generation methods into TSFE +============================================================= + +See :issue:`83254` + +Description +=========== + +The following methods have been marked as deprecated + +* :php:`TYPO3\CMS\Frontend\Page\PageGenerator::isAllowedLinkVarValue()` +* :php:`TYPO3\CMS\Frontend\Page\PageGenerator::generatePageTitle()` +* :php:`TYPO3\CMS\Core\TypoScript\TemplateService->printTitle()` + +As their functionality has been moved into TypoScriptFrontendController. + + +Impact +====== + +Calling any of the PHP methods above will trigger a deprecation warning. + + +Affected Installations +====================== + +Any installation with a third-party extension directly accessing these methods. + + +Migration +========= + +For the generation of the page title tag, the method +:php:`TypoScriptFrontendController->generatePageTitle()` should be used instead. + +.. index:: Frontend, PHP-API, FullyScanned \ No newline at end of file diff --git a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php index 8c33e933d0e1..fb83d984dd90 100644 --- a/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php +++ b/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php @@ -2816,7 +2816,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface } if (!is_array($value)) { $temp = rawurlencode($value); - if ($test !== '' && !PageGenerator::isAllowedLinkVarValue($temp, $test)) { + if ($test !== '' && !$this->isAllowedLinkVarValue($temp, $test)) { // Error: This value was not allowed for this key continue; } @@ -2855,6 +2855,49 @@ class TypoScriptFrontendController implements LoggerAwareInterface return str_replace($tempCommaReplacementString, ',', $string); } + /** + * 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. + * + * @param string $haystack The string in which to find $needle + * @param string $needle The string to find in $haystack + * @return bool Returns TRUE if $needle matches or is found in $haystack + */ + protected function isAllowedLinkVarValue(string $haystack, string $needle): bool + { + $isAllowed = false; + // Integer + if ($needle === 'int' || $needle === 'integer') { + if (MathUtility::canBeInterpretedAsInteger($haystack)) { + $isAllowed = true; + } + } elseif (preg_match('/^\\/.+\\/[imsxeADSUXu]*$/', $needle)) { + // Regular expression, only "//" is allowed as delimiter + if (@preg_match($needle, $haystack)) { + $isAllowed = true; + } + } elseif (strstr($needle, '-')) { + // Range + if (MathUtility::canBeInterpretedAsInteger($haystack)) { + $range = explode('-', $needle); + if ($range[0] <= $haystack && $range[1] >= $haystack) { + $isAllowed = true; + } + } + } elseif (strstr($needle, '|')) { + // List + // Trim the input + $haystack = str_replace(' ', '', $haystack); + if (strstr('|' . $needle . '|', '|' . $haystack . '|')) { + $isAllowed = true; + } + } elseif ((string)$needle === (string)$haystack) { + // String comparison + $isAllowed = true; + } + return $isAllowed; + } + /** * Redirect to target page if the current page is an overlaid mountpoint. * @@ -2931,7 +2974,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface $this->tempContent = false; if (!$this->no_cache) { $seconds = 30; - $title = htmlspecialchars($this->tmpl->printTitle($this->page['title'])); + $title = htmlspecialchars($this->printTitle($this->page['title'])); $request_uri = htmlspecialchars(GeneralUtility::getIndpEnv('REQUEST_URI')); $stdMsg = ' <strong>Page is being generated.</strong><br /> @@ -3298,11 +3341,82 @@ class TypoScriptFrontendController implements LoggerAwareInterface } /** - * Generate the page title again as TSFE->altPageTitle might have been modified by an inc script + * Generate the page title, can be called multiple times, + * as $this->altPageTitle might have been modified by an uncached plugin etc. + * + * @return string the generated page title + */ + public function generatePageTitle(): string + { + $pageTitleSeparator = ''; + + // Check for a custom pageTitleSeparator, and perform stdWrap on it + if (isset($this->config['config']['pageTitleSeparator']) && $this->config['config']['pageTitleSeparator'] !== '') { + $pageTitleSeparator = $this->config['config']['pageTitleSeparator']; + + if (isset($this->config['config']['pageTitleSeparator.']) && is_array($this->config['config']['pageTitleSeparator.'])) { + $pageTitleSeparator = $this->cObj->stdWrap($pageTitleSeparator, $this->config['config']['pageTitleSeparator.']); + } else { + $pageTitleSeparator .= ' '; + } + } + + $pageTitle = $this->altPageTitle ?: $this->page['title'] ?? ''; + $titleTagContent = $this->printTitle( + $pageTitle, + (bool)$this->config['config']['noPageTitle'], + (bool)$this->config['config']['pageTitleFirst'], + $pageTitleSeparator + ); + if ($this->config['config']['titleTagFunction']) { + $titleTagContent = $this->cObj->callUserFunction( + $this->config['config']['titleTagFunction'], + [], + $titleTagContent + ); + } + // stdWrap around the title tag + if (isset($this->config['config']['pageTitle.']) && is_array($this->config['config']['pageTitle.'])) { + $titleTagContent = $this->cObj->stdWrap($titleTagContent, $this->config['config']['pageTitle.']); + } + + // config.noPageTitle = 2 - means do not render the page title + if ($this->config['config']['noPageTitle'] === 2) { + $titleTagContent = ''; + } + if ($titleTagContent !== '') { + $this->pageRenderer->setTitle($titleTagContent); + } + return (string)$titleTagContent; + } + + /** + * Compiles the content for the page <title> tag. + * + * @param string $pageTitle The input title string, typically the "title" field of a page's record. + * @param bool $noTitle If set, then only the site title is outputted (from $this->setup['sitetitle']) + * @param bool $showTitleFirst If set, then "sitetitle" and $title is swapped + * @param string $pageTitleSeparator an alternative to the ": " as the separator between site title and page title + * @return string The page title on the form "[sitetitle]: [input-title]". Not htmlspecialchar()'ed. + * @see tempPageCacheContent(), generatePageTitle() */ - protected function regeneratePageTitle() + protected function printTitle(string $pageTitle, bool $noTitle = false, bool $showTitleFirst = false, string $pageTitleSeparator = ''): string { - PageGenerator::generatePageTitle(); + $siteTitle = trim($this->tmpl->setup['sitetitle'] ?? ''); + $pageTitle = $noTitle ? '' : $pageTitle; + if ($showTitleFirst) { + $temp = $siteTitle; + $siteTitle = $pageTitle; + $pageTitle = $temp; + } + // only show a separator if there are both site title and page title + if ($pageTitle === '' || $siteTitle === '') { + $pageTitleSeparator = ''; + } elseif (empty($pageTitleSeparator)) { + // use the default separator if non given + $pageTitleSeparator = ': '; + } + return $siteTitle . $pageTitleSeparator . $pageTitle; } /** @@ -3329,7 +3443,7 @@ class TypoScriptFrontendController implements LoggerAwareInterface $this->recursivelyReplaceIntPlaceholdersInContent(); $this->getTimeTracker()->push('Substitute header section'); $this->INTincScript_loadJSCode(); - $this->regeneratePageTitle(); + $this->generatePageTitle(); $this->content = str_replace( [ diff --git a/typo3/sysext/frontend/Classes/Page/PageGenerator.php b/typo3/sysext/frontend/Classes/Page/PageGenerator.php index 2bb76ab1e791..9c9fd538b653 100644 --- a/typo3/sysext/frontend/Classes/Page/PageGenerator.php +++ b/typo3/sysext/frontend/Classes/Page/PageGenerator.php @@ -34,6 +34,7 @@ class PageGenerator /** * Do not render title tag * Typoscript setting: [config][noPageTitle] + * @deprecated will not be used anymore, and will be removed in TYPO3 v10. */ const NO_PAGE_TITLE = 2; @@ -517,7 +518,7 @@ class PageGenerator if (is_array($tsfe->pSetup['footerData.'])) { $pageRenderer->addFooterData($tsfe->cObj->cObjGet($tsfe->pSetup['footerData.'], 'footerData.')); } - static::generatePageTitle(); + $tsfe->generatePageTitle(); static::generateMetaTagHtml( isset($tsfe->pSetup['meta.']) ? $tsfe->pSetup['meta.'] : [], @@ -796,9 +797,12 @@ class PageGenerator * @param string $haystack The string in which to find $needle * @param string $needle The string to find in $haystack * @return bool Returns TRUE if $needle matches or is found in $haystack + * + * @deprecated since TYPO3 v9, will be removed in TYPO3 v10, is now called within TSFE itself, if needed outside the regular calculations, reimplement the method on your own. */ public static function isAllowedLinkVarValue($haystack, $needle) { + trigger_error('The method will be removed in TYPO3 v10.0, if needed outside of linkVar calculation, re-implement the method in your own extension', E_USER_DEPRECATED); $OK = false; // Integer if ($needle === 'int' || $needle === 'integer') { @@ -837,45 +841,13 @@ class PageGenerator * Takes the settings [config][noPageTitle], [config][pageTitleFirst], [config][titleTagFunction] * [config][pageTitleSeparator] and [config][noPageTitle] into account. * Furthermore $GLOBALS[TSFE]->altPageTitle is observed. + * + * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0, as TSFE->generatePageTitle() should be used instead. */ public static function generatePageTitle() { - /** @var TypoScriptFrontendController $tsfe */ - $tsfe = $GLOBALS['TSFE']; - - $pageTitleSeparator = ''; - - // check for a custom pageTitleSeparator, and perform stdWrap on it - if (isset($tsfe->config['config']['pageTitleSeparator']) && $tsfe->config['config']['pageTitleSeparator'] !== '') { - $pageTitleSeparator = $tsfe->config['config']['pageTitleSeparator']; - - if (isset($tsfe->config['config']['pageTitleSeparator.']) && is_array($tsfe->config['config']['pageTitleSeparator.'])) { - $pageTitleSeparator = $tsfe->cObj->stdWrap($pageTitleSeparator, $tsfe->config['config']['pageTitleSeparator.']); - } else { - $pageTitleSeparator .= ' '; - } - } - - $titleTagContent = $tsfe->tmpl->printTitle( - $tsfe->altPageTitle ?: $tsfe->page['title'], - $tsfe->config['config']['noPageTitle'], - $tsfe->config['config']['pageTitleFirst'], - $pageTitleSeparator - ); - if ($tsfe->config['config']['titleTagFunction']) { - $titleTagContent = $tsfe->cObj->callUserFunction( - $tsfe->config['config']['titleTagFunction'], - [], - $titleTagContent - ); - } - // stdWrap around the title tag - if (isset($tsfe->config['config']['pageTitle.']) && is_array($tsfe->config['config']['pageTitle.'])) { - $titleTagContent = $tsfe->cObj->stdWrap($titleTagContent, $tsfe->config['config']['pageTitle.']); - } - if ($titleTagContent !== '' && (int)$tsfe->config['config']['noPageTitle'] !== self::NO_PAGE_TITLE) { - static::getPageRenderer()->setTitle($titleTagContent); - } + trigger_error('This method will be removed in TYPO3 v10.0. Use $TSFE->generatePageTitle() instead.', E_USER_DEPRECATED); + $GLOBALS['TSFE']->generatePageTitle(); } /** diff --git a/typo3/sysext/frontend/Tests/Unit/Page/PageGeneratorTest.php b/typo3/sysext/frontend/Tests/Unit/Page/PageGeneratorTest.php index 378b11d87657..9d3605d69112 100644 --- a/typo3/sysext/frontend/Tests/Unit/Page/PageGeneratorTest.php +++ b/typo3/sysext/frontend/Tests/Unit/Page/PageGeneratorTest.php @@ -179,6 +179,8 @@ class PageGeneratorTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult); $tmpl = $this->prophesize(TemplateService::class); $tsfe = $this->prophesize(TypoScriptFrontendController::class); + $tsfe->generatePageTitle()->willReturn(''); + $tsfe->INTincScript_loadJSCode()->shouldBeCalled(); $tsfe->cObj = $cObj->reveal(); $tsfe->tmpl = $tmpl->reveal(); $tsfe->page = [ @@ -212,8 +214,11 @@ class PageGeneratorTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult); $tmpl = $this->prophesize(TemplateService::class); $tsfe = $this->prophesize(TypoScriptFrontendController::class); + $tsfe->generatePageTitle()->willReturn(''); + $tsfe->INTincScript_loadJSCode()->shouldBeCalled(); $tsfe->cObj = $cObj->reveal(); $tsfe->tmpl = $tmpl->reveal(); + $tsfe->config['config'] = []; $tsfe->page = [ 'title' => '' ]; @@ -246,8 +251,11 @@ class PageGeneratorTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult); $tmpl = $this->prophesize(TemplateService::class); $tsfe = $this->prophesize(TypoScriptFrontendController::class); + $tsfe->generatePageTitle()->willReturn(''); + $tsfe->INTincScript_loadJSCode()->shouldBeCalled(); $tsfe->cObj = $cObj->reveal(); $tsfe->tmpl = $tmpl->reveal(); + $tsfe->config['config'] = []; $tsfe->page = [ 'title' => '' ]; @@ -334,8 +342,11 @@ class PageGeneratorTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase $cObj->stdWrap(Argument::cetera())->willReturn($stdWrapResult); $tmpl = $this->prophesize(TemplateService::class); $tsfe = $this->prophesize(TypoScriptFrontendController::class); + $tsfe->generatePageTitle()->willReturn(''); + $tsfe->INTincScript_loadJSCode()->shouldBeCalled(); $tsfe->cObj = $cObj->reveal(); $tsfe->tmpl = $tmpl->reveal(); + $tsfe->config['config'] = []; $tsfe->page = [ 'title' => '' ]; diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php index 8e5988fc5df5..16eee6442340 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php @@ -1528,4 +1528,11 @@ return [ 'Breaking-83256-RemovedLockFilePathFunctionality.rst', ], ], + 'TYPO3\CMS\Core\TypoScript\TemplateService->printTitle' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-83254-MovedPageGenerationMethodsIntoTSFE.rst', + ], + ], ]; diff --git a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php index 49b793bf0972..ed9ddba20425 100644 --- a/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php +++ b/typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallStaticMatcher.php @@ -533,4 +533,18 @@ return [ 'Deprecation-83118-DeleteClauseMethods.rst', ], ], + 'TYPO3\CMS\Frontend\Page\PageGenerator::generatePageTitle' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-83254-MovedPageGenerationMethodsIntoTSFE.rst', + ], + ], + 'TYPO3\CMS\Frontend\Page\PageGenerator::isAllowedLinkVarValue' => [ + 'numberOfMandatoryArguments' => 0, + 'maximumNumberOfArguments' => 0, + 'restFiles' => [ + 'Deprecation-83254-MovedPageGenerationMethodsIntoTSFE.rst', + ], + ], ]; -- GitLab