[BUGFIX] Resolve version move placeholder in RootlineUtility 78/58178/5
authorOliver Hader <oliver@typo3.org>
Tue, 4 Sep 2018 10:04:24 +0000 (12:04 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Tue, 4 Sep 2018 10:56:17 +0000 (12:56 +0200)
In order to correctly work with rootlines (e.g. when resolving
according sites and pseudo-sites for URL resolving and generation)
possible version move placeholders have to be resolved.

Resolves: #86137
Releases: master
Change-Id: Ief9841f7592018292c034c03c45343363cf79248
Reviewed-on: https://review.typo3.org/58178
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: Christian Kuhn <lolli@schwarzbu.ch>
Tested-by: Christian Kuhn <lolli@schwarzbu.ch>
typo3/sysext/core/Classes/Utility/RootlineUtility.php
typo3/sysext/core/Tests/Unit/Utility/RootlineUtilityTest.php

index 139da58..5d85a6c 100644 (file)
@@ -15,8 +15,10 @@ namespace TYPO3\CMS\Core\Utility;
  */
 
 use Doctrine\DBAL\DBALException;
+use Doctrine\DBAL\FetchMode;
 use TYPO3\CMS\Core\Context\Context;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Database\Query\QueryBuilder;
 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
 use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
 use TYPO3\CMS\Core\Exception\Page\BrokenRootLineException;
@@ -24,6 +26,7 @@ use TYPO3\CMS\Core\Exception\Page\CircularRootLineException;
 use TYPO3\CMS\Core\Exception\Page\MountPointsDisabledException;
 use TYPO3\CMS\Core\Exception\Page\PageNotFoundException;
 use TYPO3\CMS\Core\Exception\Page\PagePropertyRelationNotFoundException;
+use TYPO3\CMS\Core\Versioning\VersionState;
 use TYPO3\CMS\Frontend\Page\PageRepository;
 
 /**
@@ -129,7 +132,6 @@ class RootlineUtility
      */
     public function __construct($uid, $mountPointParameter = '', $context = null)
     {
-        $this->pageUid = (int)$uid;
         $this->mountPointParameter = trim($mountPointParameter);
         if ($context instanceof PageRepository) {
             trigger_error('Calling RootlineUtility with PageRepository as third parameter will be unsupported with TYPO3 v10.0. Use a Context object directly', E_USER_DEPRECATED);
@@ -151,6 +153,12 @@ class RootlineUtility
             }
             $this->parseMountPointParameter();
         }
+
+        $this->pageUid = $this->resolvePageId(
+            (int)$uid,
+            (int)$this->workspaceUid
+        );
+
         if (self::$cache === null) {
             self::$cache = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Cache\CacheManager::class)->getCache('cache_rootline');
         }
@@ -474,4 +482,109 @@ class RootlineUtility
             $this->parsedMountPointParameters[$mountedPageUid] = $mountPageUid;
         }
     }
+
+    /**
+     * @param int $pageId
+     * @param int $workspaceId
+     * @return int
+     */
+    protected function resolvePageId(int $pageId, int $workspaceId): int
+    {
+        if ($pageId === 0 || $workspaceId === 0) {
+            return $pageId;
+        }
+
+        $page = $this->resolvePageRecord(
+            $pageId,
+            ['uid', 't3ver_oid', 't3ver_state']
+        );
+        if (!VersionState::cast($page['t3ver_state'])
+            ->equals(VersionState::MOVE_POINTER)) {
+            return $pageId;
+        }
+
+        $movePlaceholder = $this->resolveMovePlaceHolder(
+            (int)$page['t3ver_oid'],
+            ['uid'],
+            $workspaceId
+        );
+        if (empty($movePlaceholder['uid'])) {
+            return $pageId;
+        }
+
+        return (int)$movePlaceholder['uid'];
+    }
+
+    /**
+     * @param int $pageId
+     * @param array $fieldNames
+     * @return array|null
+     */
+    protected function resolvePageRecord(int $pageId, array $fieldNames): ?array
+    {
+        $queryBuilder = $this->createQueryBuilder('pages');
+        $queryBuilder->getRestrictions()->removeAll()
+            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
+        $statement = $queryBuilder
+            ->from('pages')
+            ->select(...$fieldNames)
+            ->where(
+                $queryBuilder->expr()->eq(
+                    'uid',
+                    $queryBuilder->createNamedParameter($pageId, \PDO::PARAM_INT)
+                )
+            )
+            ->setMaxResults(1)
+            ->execute();
+
+        $record = $statement->fetch(FetchMode::ASSOCIATIVE);
+        return $record ?: null;
+    }
+
+    /**
+     * @param int $liveId
+     * @param array $fieldNames
+     * @param int $workspaceId
+     * @return array|null
+     */
+    protected function resolveMovePlaceHolder(int $liveId, array $fieldNames, int $workspaceId): ?array
+    {
+        $queryBuilder = $this->createQueryBuilder('pages');
+        $queryBuilder->getRestrictions()->removeAll()
+            ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
+
+        $statement = $queryBuilder
+            ->from('pages')
+            ->select(...$fieldNames)
+            ->setMaxResults(1)
+            ->where(
+                $queryBuilder->expr()->eq(
+                    't3ver_wsid',
+                    $queryBuilder->createNamedParameter($workspaceId, \PDO::PARAM_INT)
+                ),
+                $queryBuilder->expr()->eq(
+                    't3ver_state',
+                    $queryBuilder->createNamedParameter(VersionState::MOVE_PLACEHOLDER, \PDO::PARAM_INT)
+                ),
+                $queryBuilder->expr()->eq(
+                    't3ver_move_id',
+                    $queryBuilder->createNamedParameter($liveId, \PDO::PARAM_INT)
+                )
+            )
+            ->execute();
+
+        $record = $statement->fetch(FetchMode::ASSOCIATIVE);
+        return $record ?: null;
+    }
+
+    /**
+     * @param string $tableName
+     * @return QueryBuilder
+     */
+    protected function createQueryBuilder(string $tableName): QueryBuilder
+    {
+        return GeneralUtility::makeInstance(ConnectionPool::class)
+            ->getQueryBuilderForTable($tableName);
+    }
 }
index 9658cf3..2151d99 100644 (file)
@@ -51,9 +51,11 @@ class RootlineUtilityTest extends UnitTestCase
 
         $this->subject = $this->getAccessibleMock(
             RootlineUtility::class,
-            ['enrichWithRelationFields'],
+            ['enrichWithRelationFields', 'resolvePageId'],
             [1, '', new Context()]
         );
+
+        $this->subject->expects(static::any())->method('resolvePageId')->willReturnArgument(0);
     }
 
     /**
@@ -305,6 +307,8 @@ class RootlineUtilityTest extends UnitTestCase
      */
     public function getCacheIdentifierContainsAllContextParameters(): void
     {
+        $this->subject->expects(static::any())->method('resolvePageId')->willReturn(42);
+
         $context = new Context();
         $context->setAspect('workspace', new WorkspaceAspect(15));
         $context->setAspect('language', new LanguageAspect(8, 8, LanguageAspect::OVERLAYS_OFF));
@@ -324,6 +328,8 @@ class RootlineUtilityTest extends UnitTestCase
      */
     public function getCacheIdentifierReturnsValidIdentifierWithCommasInMountPointParameter(): void
     {
+        $this->subject->expects(static::any())->method('resolvePageId')->willReturn(42);
+
         /** @var AbstractFrontend $cacheFrontendMock */
         $cacheFrontendMock = $this->getMockForAbstractClass(
             AbstractFrontend::class,