[BUGFIX] Always use default language for uniqueInSite 22/58722/5
authorBenni Mack <benni@typo3.org>
Fri, 26 Oct 2018 16:55:33 +0000 (18:55 +0200)
committerBenni Mack <benni@typo3.org>
Mon, 29 Oct 2018 09:58:12 +0000 (10:58 +0100)
SlugHelper fetches records of the same language, and takes the
pageId of the _current_ language record (uid), which is wrong.
For records, it must be 'pid', for pages it must be 'uid', but for
translated pages it must be 'pages.l10n_parent'.

Resolves: #86625
Releases: master
Change-Id: I7af6f0438f5a90169505069c557792d4e7b08b7c
Reviewed-on: https://review.typo3.org/58722
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
typo3/sysext/core/Classes/DataHandling/Model/RecordState.php
typo3/sysext/core/Classes/DataHandling/SlugHelper.php

index e7a0935..beeff35 100644 (file)
@@ -145,12 +145,18 @@ class RecordState
     }
 
     /**
-     * Resolve identifier of node used as aggregate. For translated pages
-     * that would result in the `uid` of the outer-most language parent page.
+     * Resolves node identifier (`pid`) of current subject. For translated pages
+     * that would result in the `uid` of the outer-most language parent page
+     * otherwise it's the `pid` of the current subject.
+     *
+     * Example:
+     * + pages: uid: 10, pid: 5, sys_language_uid: 0, l10n_parent: 0  -> returns 5
+     * + pages: uid: 11, pid: 5, sys_language_uid: 1, l10n_parent: 10 -> returns 10
+     * + other: uid: 12, pid: 10 -> returns 10
      *
      * @return string
      */
-    public function resolveAggregateNodeIdentifier(): string
+    public function resolveNodeIdentifier(): string
     {
         if ($this->subject->isNode()
             && $this->context->getLanguageId() > 0
@@ -158,7 +164,33 @@ class RecordState
         ) {
             return $this->languageLink->getHead()->getSubject()->getIdentifier();
         }
+        return $this->node->getIdentifier();
+    }
 
+    /**
+     * Resolves node identifier used as aggregate for current subject. For translated
+     * pages that would result in the `uid` of the outer-most language parent page,
+     * for pages it's the identifier of the current subject, otherwise it's
+     * the `pid` of the current subject.
+     *
+     * Example:
+     * + pages: uid: 10, pid: 5, sys_language_uid: 0, l10n_parent: 0  -> returns 10
+     * + pages: uid: 11, pid: 5, sys_language_uid: 1, l10n_parent: 10 -> returns 10
+     * + other: uid: 12, pid: 10 -> returns 10
+     *
+     * @return string
+     */
+    public function resolveNodeAggregateIdentifier(): string
+    {
+        if ($this->subject->isNode()
+            && $this->context->getLanguageId() > 0
+            && $this->languageLink !== null
+        ) {
+            return $this->languageLink->getHead()->getSubject()->getIdentifier();
+        }
+        if ($this->subject->isNode()) {
+            return $this->subject->getIdentifier();
+        }
         return $this->node->getIdentifier();
     }
 }
index 86cba5b..364852f 100644 (file)
@@ -22,6 +22,7 @@ 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\DataHandling\Model\RecordState;
+use TYPO3\CMS\Core\DataHandling\Model\RecordStateFactory;
 use TYPO3\CMS\Core\Exception\SiteNotFoundException;
 use TYPO3\CMS\Core\Routing\SiteMatcher;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
@@ -213,7 +214,7 @@ class SlugHelper
      */
     public function isUniqueInPid(string $slug, RecordState $state): bool
     {
-        $pageId = (int)$state->resolveAggregateNodeIdentifier();
+        $pageId = (int)$state->resolveNodeIdentifier();
         $recordId = $state->getSubject()->getIdentifier();
         $languageId = $state->getContext()->getLanguageId();
 
@@ -245,7 +246,7 @@ class SlugHelper
      */
     public function isUniqueInSite(string $slug, RecordState $state): bool
     {
-        $pageId = (int)$state->resolveAggregateNodeIdentifier();
+        $pageId = (int)$state->resolveNodeIdentifier();
         $recordId = $state->getSubject()->getIdentifier();
         $languageId = $state->getContext()->getLanguageId();
 
@@ -273,7 +274,10 @@ class SlugHelper
         $siteOfCurrentRecord = $siteMatcher->matchByPageId($pageId);
         foreach ($records as $record) {
             try {
-                $siteOfExistingRecord = $siteMatcher->matchByPageId((int)$record['uid']);
+                $recordState = RecordStateFactory::forName($this->tableName)->fromArray($record);
+                $siteOfExistingRecord = $siteMatcher->matchByPageId(
+                    (int)$recordState->resolveNodeAggregateIdentifier()
+                );
             } catch (SiteNotFoundException $exception) {
                 // In case not site is found, the record is not
                 // organized in any site or pseudo-site
@@ -350,6 +354,14 @@ class SlugHelper
         if ($this->workspaceEnabled) {
             $fieldNames[] = 't3ver_state';
         }
+        $languageFieldName = $GLOBALS['TCA'][$this->tableName]['ctrl']['languageField'] ?? null;
+        if (is_string($languageFieldName)) {
+            $fieldNames[] = $languageFieldName;
+        }
+        $languageParentFieldName = $GLOBALS['TCA'][$this->tableName]['ctrl']['transOrigPointerField'] ?? null;
+        if (is_string($languageParentFieldName)) {
+            $fieldNames[] = $languageParentFieldName;
+        }
 
         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->tableName);
         $queryBuilder->getRestrictions()
@@ -392,15 +404,15 @@ class SlugHelper
      */
     protected function applyLanguageConstraint(QueryBuilder $queryBuilder, int $languageId)
     {
-        $languageField = $GLOBALS['TCA'][$this->tableName]['ctrl']['languageField'] ?? null;
-        if (!is_string($languageField)) {
+        $languageFieldName = $GLOBALS['TCA'][$this->tableName]['ctrl']['languageField'] ?? null;
+        if (!is_string($languageFieldName)) {
             return;
         }
 
         // Only check records of the given language
         $queryBuilder->andWhere(
             $queryBuilder->expr()->eq(
-                $languageField,
+                $languageFieldName,
                 $queryBuilder->createNamedParameter($languageId, \PDO::PARAM_INT)
             )
         );