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
*/