diff --git a/Classes/Configuration/AutomaticConfigurator.php b/Classes/Configuration/AutomaticConfigurator.php index f5d1a92a..33259fbd 100644 --- a/Classes/Configuration/AutomaticConfigurator.php +++ b/Classes/Configuration/AutomaticConfigurator.php @@ -177,6 +177,7 @@ protected function getTemplate() { 'emptyUrlReturnValue' => GeneralUtility::getIndpEnv('TYPO3_SITE_PATH') ), 'pagePath' => array( + 'dontResolveShortcuts' => TRUE ), 'fileName' => array( 'defaultToHTMLsuffixOnPrev' => 0, diff --git a/Classes/Configuration/ConfigurationReader.php b/Classes/Configuration/ConfigurationReader.php index 5220bdda..4efa4199 100644 --- a/Classes/Configuration/ConfigurationReader.php +++ b/Classes/Configuration/ConfigurationReader.php @@ -92,6 +92,7 @@ class ConfigurationReader { 'init/defaultLanguageUid' => 0, 'init/emptySegmentValue' => '', 'pagePath/spaceCharacter' => '-', // undocumented & deprecated! + 'pagePath/dontResolveShortcuts' => TRUE ); /** diff --git a/Classes/Encoder/UrlEncoder.php b/Classes/Encoder/UrlEncoder.php index ac4d5957..8fb38c6f 100644 --- a/Classes/Encoder/UrlEncoder.php +++ b/Classes/Encoder/UrlEncoder.php @@ -563,6 +563,10 @@ protected function createPathComponentThroughOverride() { protected function createPathComponentUsingRootline() { $this->logger->debug('Starting path generation'); + if ($this->configuration->get('pagePath/dontResolveShortcuts') === FALSE) { + $this->urlParameters['id'] = $this->resolveShortcut($this->urlParameters['id']); + } + $mountPointParameter = ''; if (isset($this->urlParameters['MP'])) { $mountPointParameter = $this->urlParameters['MP']; @@ -668,6 +672,34 @@ protected function createPathComponentUsingRootline() { $this->logger->debug('Finished path generation'); } + /** + * Resolve the potential shortcut pageId + * Returns the original uid, if the page is not a shortcut. + * It uses the TypoScriptFrontendController to resolve the page shortcut. + * This is why the method could throw a RuntimeException, when shortcuts are nested 20 or more recursions. + * + * @param int $pageId The pageid of a potential shortcut to resolve + * + * @return int + */ + protected function resolveShortcut($pageId) { + $page = $this->pageRepository->getPage($pageId); + if ((int)$page['doktype'] === PageRepository::DOKTYPE_SHORTCUT) { + try{ + $page = $this->tsfe->getPageShortcut($page['shortcut'], $page['shortcut_mode'], $pageId); + $pageId = $page['uid']; + } catch (\RuntimeException $ex) { + if ($ex->getCode() === 1294587212) { + $this->logger->warning('Maximum iteration of shortcuts reached on pageId #' . $pageId . '!' + . 'The resulting path could be wrong!'); + } + } catch (\TYPO3\CMS\Core\Error\Http\PageNotFoundException $ex) { + $this->logger->warning('The target page of the shortcut(#' . $pageId . ') could not be found!'); + } + } + return $pageId; + } + /** * Encodes fixed postVars. * diff --git a/Tests/Fixtures/realurl.xml b/Tests/Fixtures/realurl.xml index 0e4a9f10..9573158f 100644 --- a/Tests/Fixtures/realurl.xml +++ b/Tests/Fixtures/realurl.xml @@ -16,6 +16,15 @@ Page Tree: - 3: page3 - 4: page4 (tx_realurl_exclude=1) - 7: subpage7-without-parent + - 10: shortcut10-to-firstsubpage + - 11: subpage11 + - 12: shortcut12-to-parentpage + - 13: shortcut13-to-subpage15 + - 14: subpage14 + - 15: subpage15 + - 16: shortcut16-to-page8 + - 17: shortcut17-to-page3 + - 18: shortcut18-to-page16 --> 1 @@ -116,4 +125,115 @@ Page Tree: 0 0 + + 10 + 1 + 4 + + 1 + 0 + shortcut10-to-firstsubpage + + + 0 + 0 + + + 11 + 10 + 1 + 0 + subpage11 + + + 0 + 0 + + + 12 + 1 + 4 + + 3 + 0 + shortcut12-to-parentpage + + + 0 + 0 + + + 13 + 1 + 4 + 15 + 0 + 0 + shortcut13-to-subpage15 + + + 0 + 0 + + + 14 + 13 + 0 + 0 + subpage14 + + + 0 + 0 + + + 15 + 13 + 0 + 0 + subpage15 + + + 0 + 0 + + + 16 + 13 + 4 + 8 + 0 + 0 + shortcut16-to-page8 + + + 0 + 0 + + + 17 + 1 + 4 + 3 + 0 + 0 + shortcut17-to-page3 + + + 0 + 0 + + + 18 + 1 + 4 + 16 + 0 + 0 + shortcut18-to-page16 + + + 0 + 0 + \ No newline at end of file diff --git a/Tests/Functional/Encoder/UrlEncoderTest.php b/Tests/Functional/Encoder/UrlEncoderTest.php index 0ac9d855..6fa8d10b 100644 --- a/Tests/Functional/Encoder/UrlEncoderTest.php +++ b/Tests/Functional/Encoder/UrlEncoderTest.php @@ -187,6 +187,84 @@ public function testRegisterDisablesEncoding() { $this->assertEquals('index.php?id=2', $parameters['LD']['totalURL'], 'tx_realurl_enable=0 TSFE register does not disable encoding'); } + /** + * Test shortcut encoding with disabled shortcut resolving + * + * @test + */ + public function testDontResolveShortcuts() { + $parameters = $this->getParametersForPage(10); + $encoder = GeneralUtility::makeInstance(UrlEncoder::class); + $encoder->encodeUrl($parameters); + $this->assertEquals('page10/', $parameters['LD']['totalURL'], 'Encode shortcut with pagePath/dontResolveShortcuts="TRUE"'); + } + + /** + * Test shortcut encoding of shortcut type "First subpage" with enabled shortcut resolving + * + * @test + */ + public function testEncodeShortcutFirstSubpage() { + $this->enableShortcutResolving(); + $parameters = $this->getParametersForPage(10); + $encoder = GeneralUtility::makeInstance(UrlEncoder::class); + $encoder->encodeUrl($parameters); + $this->assertEquals('subpage11/', $parameters['LD']['totalURL'], 'Shortcut to first subpage failed'); + } + + /** + * Test shortcut encoding of shortcut type "Parent page" with enabled shortcut resolving + * + * @test + */ + public function testEncodeShortcutParentPage() { + $this->enableShortcutResolving(); + $parameters = $this->getParametersForPage(12); + $encoder = GeneralUtility::makeInstance(UrlEncoder::class); + $encoder->encodeUrl($parameters); + $this->assertEquals('/', $parameters['LD']['totalURL'], 'Shortcut to parent page failed'); + } + + /** + * Test various shortcut encodings with enabled shortcut resolving + * + * @test + */ + public function testEncodeShortcut() { + $this->enableShortcutResolving(); + $parameters = $this->getParametersForPage(13); + $encoder = GeneralUtility::makeInstance(UrlEncoder::class); + $encoder->encodeUrl($parameters); + $this->assertEquals('shortcut13/subpage15/', $parameters['LD']['totalURL'], 'Normal shortcut to second subpage failed'); + + $parameters = $this->getParametersForPage(16); + $encoder = GeneralUtility::makeInstance(UrlEncoder::class); + $encoder->encodeUrl($parameters); + $this->assertEquals('page2/subpage8/', $parameters['LD']['totalURL'], 'Shortcut from a shortcut child to a page in another branch of the pagetree failed'); + + $parameters = $this->getParametersForPage(17); + $encoder = GeneralUtility::makeInstance(UrlEncoder::class); + $encoder->encodeUrl($parameters); + $this->assertEquals('page3/', $parameters['LD']['totalURL'], 'Shortcut to neighboring page failed'); + + $parameters = $this->getParametersForPage(18); + $encoder = GeneralUtility::makeInstance(UrlEncoder::class); + $encoder->encodeUrl($parameters); + $this->assertEquals('page2/subpage8/', $parameters['LD']['totalURL'], 'Chained shortcut failed'); + } + + /** + * Enables the shortcut resolving + */ + protected function enableShortcutResolving() { + // adjust the configuration to enable shortcut handling + $realUrlConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['realurl']); + $realUrlConf['_DEFAULT']['pagePath'] = [ + 'dontResolveShortcuts' => FALSE + ]; + $GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['realurl'] = serialize($realUrlConf); + } + /** * @return TypoScriptFrontendController */