[BUGFIX] MM references are not transformed to versioned entities 38/49338/10
authorOliver Hader <oliver@typo3.org>
Wed, 3 Aug 2016 14:03:19 +0000 (16:03 +0200)
committerSusanne Moog <susanne.moog@typo3.org>
Thu, 20 Oct 2016 14:49:36 +0000 (16:49 +0200)
Scenario:
* within a workspace
* using a MM intermediate table for relations
* having versioned entities on both sides of the relation to be defined
* however, the live uids of the entities are submitted to the data handler

Problem:
* MM relation is created with the live uids on one side

Solution:
* convert submitted relation uids to accordant version uids in workspace

Resolves: #77375
Releases: master, 7.6
Change-Id: Id099845258d3e2820ac9c369acc0339689b768a9
Reviewed-on: https://review.typo3.org/49338
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Reviewed-by: Stefan Neufeind <typo3.neufeind@speedpartner.de>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Susanne Moog <susanne.moog@typo3.org>
Tested-by: Susanne Moog <susanne.moog@typo3.org>
typo3/sysext/core/Classes/DataHandling/DataHandler.php
typo3/sysext/core/Classes/DataHandling/PlainDataResolver.php
typo3/sysext/core/Classes/Database/RelationHandler.php
typo3/sysext/workspaces/Tests/Functional/DataHandling/ManyToMany/Modify/DataSet/createCategoryWContentNAddRelation.csv
typo3/sysext/workspaces/Tests/Functional/DataHandling/ManyToMany/Modify/DataSet/createContentWCategoryNAddRelation.csv
typo3/sysext/workspaces/Tests/Functional/DataHandling/ManyToMany/Publish/DataSet/createCategoryWContentNAddRelation.csv
typo3/sysext/workspaces/Tests/Functional/DataHandling/ManyToMany/Publish/DataSet/createContentWCategoryNAddRelation.csv
typo3/sysext/workspaces/Tests/Functional/DataHandling/ManyToMany/PublishAll/DataSet/createCategoryWContentNAddRelation.csv
typo3/sysext/workspaces/Tests/Functional/DataHandling/ManyToMany/PublishAll/DataSet/createContentWCategoryNAddRelation.csv

index 51ea6f6..cbf9347 100644 (file)
@@ -2857,6 +2857,9 @@ class DataHandler
         $dbAnalysis->registerNonTableValues = !empty($tcaFieldConf['allowNonIdValues']);
         $dbAnalysis->start($newRelations, $tables, '', 0, $currentTable, $tcaFieldConf);
         if ($tcaFieldConf['MM']) {
+            // convert submitted items to use version ids instead of live ids
+            // (only required for MM relations in a workspace context)
+            $dbAnalysis->convertItemArray();
             if ($status == 'update') {
                 /** @var $oldRelations_dbAnalysis RelationHandler */
                 $oldRelations_dbAnalysis = $this->createRelationHandlerInstance();
index cbd749e..ff1ffcd 100644 (file)
@@ -94,30 +94,36 @@ class PlainDataResolver
      * Sets whether live IDs shall be kept in the final result set.
      *
      * @param bool $keepLiveIds
+     * @return PlainDataResolver
      */
     public function setKeepLiveIds($keepLiveIds)
     {
         $this->keepLiveIds = (bool)$keepLiveIds;
+        return $this;
     }
 
     /**
      * Sets whether delete placeholders shall be kept in the final result set.
      *
      * @param bool $keepDeletePlaceholder
+     * @return PlainDataResolver
      */
     public function setKeepDeletePlaceholder($keepDeletePlaceholder)
     {
         $this->keepDeletePlaceholder = (bool)$keepDeletePlaceholder;
+        return $this;
     }
 
     /**
      * Sets whether move placeholders shall be kept in case they cannot be substituted.
      *
      * @param bool $keepMovePlaceholder
+     * @return PlainDataResolver
      */
     public function setKeepMovePlaceholder($keepMovePlaceholder)
     {
         $this->keepMovePlaceholder = (bool)$keepMovePlaceholder;
+        return $this;
     }
 
     /**
@@ -129,9 +135,15 @@ class PlainDataResolver
             return $this->resolvedIds;
         }
 
-        $ids = $this->processVersionOverlays($this->liveIds);
-        $ids = $this->processSorting($ids);
-        $ids = $this->applyLiveIds($ids);
+        $ids = $this->reindex(
+            $this->processVersionOverlays($this->liveIds)
+        );
+        $ids = $this->reindex(
+            $this->processSorting($ids)
+        );
+        $ids = $this->reindex(
+            $this->applyLiveIds($ids)
+        );
 
         $this->resolvedIds = $ids;
         return $this->resolvedIds;
@@ -142,14 +154,17 @@ class PlainDataResolver
      *
      * @param int[] $ids
      * @return int[]
+     * @internal
      */
-    protected function processVersionOverlays(array $ids)
+    public function processVersionOverlays(array $ids)
     {
         if (empty($this->workspaceId) || !$this->isWorkspaceEnabled() || empty($ids)) {
             return $ids;
         }
 
-        $ids = $this->processVersionMovePlaceholders($ids);
+        $ids = $this->reindex(
+            $this->processVersionMovePlaceholders($ids)
+        );
         $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
             ->getQueryBuilderForTable($this->tableName);
 
@@ -178,7 +193,6 @@ class PlainDataResolver
                 }
             }
         }
-        $ids = $this->reindex($ids);
 
         return $ids;
     }
@@ -188,8 +202,9 @@ class PlainDataResolver
      *
      * @param int[] $ids
      * @return int[]
+     * @internal
      */
-    protected function processVersionMovePlaceholders(array $ids)
+    public function processVersionMovePlaceholders(array $ids)
     {
         // Early return on insufficient data-set
         if (empty($this->workspaceId) || !$this->isWorkspaceEnabled() || empty($ids)) {
@@ -225,8 +240,6 @@ class PlainDataResolver
             }
         }
 
-        $ids = $this->reindex($ids);
-
         return $ids;
     }
 
@@ -236,8 +249,9 @@ class PlainDataResolver
      *
      * @param int[] $ids
      * @return int[]
+     * @internal
      */
-    protected function processSorting(array $ids)
+    public function processSorting(array $ids)
     {
         // Early return on missing sorting statement or insufficient data-set
         if (empty($this->sortingStatement) || count($ids) < 2) {
@@ -264,7 +278,7 @@ class PlainDataResolver
 
         $sortedIds = $queryBuilder->execute()->fetchAll();
 
-        return $this->reindex(array_column($sortedIds, 'uid'));
+        return array_column($sortedIds, 'uid');
     }
 
     /**
@@ -274,8 +288,9 @@ class PlainDataResolver
      *
      * @param int[] $ids
      * @return int[]
+     * @internal
      */
-    protected function applyLiveIds(array $ids)
+    public function applyLiveIds(array $ids)
     {
         if (!$this->keepLiveIds || !$this->isWorkspaceEnabled() || empty($ids)) {
             return $ids;
@@ -306,7 +321,7 @@ class PlainDataResolver
             }
         }
 
-        return $this->reindex($ids);
+        return $ids;
     }
 
     /**
index d011311..6382d2e 100644 (file)
@@ -1263,6 +1263,59 @@ class RelationHandler
     }
 
     /**
+     * Converts elements in the local item array to use version ids instead of
+     * live ids, if possible. The most common use case is, to call that prior
+     * to processing with MM relations in a workspace context. For tha special
+     * case, ids on both side of the MM relation must use version ids if
+     * available.
+     *
+     * @return bool Whether items have been converted
+     */
+    public function convertItemArray()
+    {
+        $hasBeenConverted = false;
+
+        // conversion is only required in a workspace context
+        // (the case that version ids are submitted in a live context are rare)
+        if ($this->getWorkspaceId() === 0) {
+            return $hasBeenConverted;
+        }
+
+        foreach ($this->tableArray as $tableName => $ids) {
+            if (empty($ids) || !BackendUtility::isTableWorkspaceEnabled($tableName)) {
+                continue;
+            }
+
+            // convert live ids to version ids if available
+            $convertedIds = $this->getResolver($tableName, $ids)
+                ->setKeepDeletePlaceholder(false)
+                ->setKeepMovePlaceholder(false)
+                ->processVersionOverlays($ids);
+            foreach ($this->itemArray as $index => $item) {
+                if ($item['table'] !== $tableName) {
+                    continue;
+                }
+                $currentItemId = $item['id'];
+                if (
+                    !isset($convertedIds[$currentItemId])
+                    || $currentItemId === $convertedIds[$currentItemId]
+                ) {
+                    continue;
+                }
+                // adjust local item to use resolved version id
+                $this->itemArray[$index]['id'] = $convertedIds[$currentItemId];
+                $hasBeenConverted = true;
+            }
+            // update per-table reference for ids
+            if ($hasBeenConverted) {
+                $this->tableArray[$tableName] = array_values($convertedIds);
+            }
+        }
+
+        return $hasBeenConverted;
+    }
+
+    /**
      * @param NULL|int $workspaceId
      * @return bool Whether items have been purged
      */
index 1b91e16..715924a 100644 (file)
@@ -12,7 +12,7 @@ sys_category_record_mm
 ,29,297,tt_content,0,2,categories
 ,29,298,tt_content,0,1,categories
 ,30,298,tt_content,0,2,categories
-,33,299,tt_content,1,0,categories
+,33,300,tt_content,1,0,categories
 tt_content
 ,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,header,image,categories
 ,297,89,256,0,0,0,0,0,0,0,0,"Regular Element #1",0,2
index 05b425c..7ac5f5d 100644 (file)
@@ -12,7 +12,7 @@ sys_category_record_mm
 ,29,297,tt_content,0,2,categories
 ,29,298,tt_content,0,1,categories
 ,30,298,tt_content,0,2,categories
-,32,300,tt_content,0,1,categories
+,33,300,tt_content,0,1,categories
 tt_content
 ,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,header,image,categories
 ,297,89,256,0,0,0,0,0,0,0,0,"Regular Element #1",0,2
index 66fb416..603364d 100644 (file)
@@ -11,7 +11,7 @@ sys_category_record_mm
 ,29,297,tt_content,0,2,categories
 ,29,298,tt_content,0,1,categories
 ,30,298,tt_content,0,2,categories
-,32,300,tt_content,1,0,categories
+,32,299,tt_content,1,0,categories
 tt_content
 ,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,header,image,categories
 ,297,89,256,0,0,0,0,0,0,0,0,"Regular Element #1",0,2
index af8bf3a..a31db17 100644 (file)
@@ -11,7 +11,7 @@ sys_category_record_mm
 ,29,297,tt_content,0,2,categories
 ,29,298,tt_content,0,1,categories
 ,30,298,tt_content,0,2,categories
-,33,299,tt_content,0,1,categories
+,32,299,tt_content,0,1,categories
 tt_content
 ,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,header,image,categories
 ,297,89,256,0,0,0,0,0,0,0,0,"Regular Element #1",0,2
index 66fb416..603364d 100644 (file)
@@ -11,7 +11,7 @@ sys_category_record_mm
 ,29,297,tt_content,0,2,categories
 ,29,298,tt_content,0,1,categories
 ,30,298,tt_content,0,2,categories
-,32,300,tt_content,1,0,categories
+,32,299,tt_content,1,0,categories
 tt_content
 ,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,header,image,categories
 ,297,89,256,0,0,0,0,0,0,0,0,"Regular Element #1",0,2
index af8bf3a..a31db17 100644 (file)
@@ -11,7 +11,7 @@ sys_category_record_mm
 ,29,297,tt_content,0,2,categories
 ,29,298,tt_content,0,1,categories
 ,30,298,tt_content,0,2,categories
-,33,299,tt_content,0,1,categories
+,32,299,tt_content,0,1,categories
 tt_content
 ,uid,pid,sorting,deleted,sys_language_uid,l18n_parent,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,header,image,categories
 ,297,89,256,0,0,0,0,0,0,0,0,"Regular Element #1",0,2