From 6a68a399a19b4d4560f6feaa867df3726da20367 Mon Sep 17 00:00:00 2001
From: Benni Mack <benni@typo3.org>
Date: Thu, 24 Sep 2020 18:14:01 +0200
Subject: [PATCH] [BUGFIX] Fix copying of moved records in workspace

When a moved content is copied onto another page,
the sorting is off due to not respecting move placeholders.

This is due to a limitation in PlainDataResolver which
cannot handle both liveIds and move placeholder IDs at
the same time.

For this to work, only the move placeholders are added,
and the respective live ids are kept.

The tests show that the previous behavior is wrong,
as the ordering should be
- Regular Element 2
- Regular Element 1
- Regular Element 3
on the newly created draft page 91.

Resolves: #92416
Releases: master, 10.4
Change-Id: I979a183d80517ab579b3889dfb672c16e396b551
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/65849
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: Claus-Peter Eberwein <claus-peter.eberwein@b13.com>
Tested-by: David Steeb <david.steeb@b13.com>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
Reviewed-by: Claus-Peter Eberwein <claus-peter.eberwein@b13.com>
Reviewed-by: David Steeb <david.steeb@b13.com>
Reviewed-by: Benni Mack <benni@typo3.org>
---
 .../core/Classes/DataHandling/DataHandler.php | 34 ++++++++++++++++---
 .../changeContentSortingAndCopyDraftPage.csv  | 21 ++++++------
 .../changeContentSortingAndCopyDraftPage.csv  | 21 ++++++------
 .../changeContentSortingAndCopyDraftPage.csv  | 13 ++++---
 4 files changed, 56 insertions(+), 33 deletions(-)

diff --git a/typo3/sysext/core/Classes/DataHandling/DataHandler.php b/typo3/sysext/core/Classes/DataHandling/DataHandler.php
index 1b4ff291be87..38333453172b 100644
--- a/typo3/sysext/core/Classes/DataHandling/DataHandler.php
+++ b/typo3/sysext/core/Classes/DataHandling/DataHandler.php
@@ -3415,6 +3415,7 @@ class DataHandler implements LoggerAwareInterface
     {
         // Copy the page itself:
         $theNewRootID = $this->copyRecord('pages', $uid, $destPid, $first);
+        $currentWorkspaceId = (int)$this->BE_USER->workspace;
         // If a new page was created upon the copy operation we will proceed with all the tables ON that page:
         if ($theNewRootID) {
             foreach ($copyTablesArray as $table) {
@@ -3435,9 +3436,15 @@ class DataHandler implements LoggerAwareInterface
                         }
                     }
                     $isTableWorkspaceEnabled = BackendUtility::isTableWorkspaceEnabled($table);
+                    if ($isTableWorkspaceEnabled) {
+                        $fields[] = 't3ver_oid';
+                        $fields[] = 't3ver_state';
+                        $fields[] = 't3ver_wsid';
+                        $fields[] = 't3ver_move_id';
+                    }
                     $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table);
                     $this->addDeleteRestriction($queryBuilder->getRestrictions()->removeAll());
-                    $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, (int)$this->BE_USER->workspace));
+                    $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(WorkspaceRestriction::class, $currentWorkspaceId));
                     $queryBuilder
                         ->select(...$fields)
                         ->from($table)
@@ -3454,11 +3461,27 @@ class DataHandler implements LoggerAwareInterface
                     try {
                         $result = $queryBuilder->execute();
                         $rows = [];
+                        $movedLiveIds = [];
+                        $movedLiveRecords = [];
                         while ($row = $result->fetch()) {
-                            $rows[$row['uid']] = $row;
+                            if ($isTableWorkspaceEnabled && (int)$row['t3ver_state'] === VersionState::MOVE_PLACEHOLDER) {
+                                $movedLiveIds[(int)$row['t3ver_move_id']] = (int)$row['uid'];
+                            }
+                            $rows[(int)$row['uid']] = $row;
                         }
                         // Resolve placeholders of workspace versions
-                        if (!empty($rows) && (int)$this->BE_USER->workspace !== 0 && $isTableWorkspaceEnabled) {
+                        if (!empty($rows) && $currentWorkspaceId > 0 && $isTableWorkspaceEnabled) {
+                            // If a record was moved within the page, the PlainDataResolver needs the move placeholder
+                            // but not the original live version, otherwise the move placeholder is not considered at all
+                            // For this reason, we find the live ids, where there was also a move placeholder in the SQL
+                            // query above in $movedLiveIds and now we removed them before handing them over to PlainDataResolver.
+                            // see changeContentSortingAndCopyDraftPage test
+                            foreach ($movedLiveIds as $liveId => $movePlaceHolderId) {
+                                if (isset($rows[$liveId])) {
+                                    $movedLiveRecords[$movePlaceHolderId] = $rows[$liveId];
+                                    unset($rows[$liveId]);
+                                }
+                            }
                             $rows = array_reverse(
                                 $this->resolveVersionedRecords(
                                     $table,
@@ -3468,6 +3491,9 @@ class DataHandler implements LoggerAwareInterface
                                 ),
                                 true
                             );
+                            foreach ($movedLiveRecords as $movePlaceHolderId => $liveRecord) {
+                                $rows[$movePlaceHolderId] = $liveRecord;
+                            }
                         }
                         if (is_array($rows)) {
                             $languageSourceMap = [];
@@ -3477,7 +3503,7 @@ class DataHandler implements LoggerAwareInterface
                                 // Skip localized records that will be processed in
                                 // copyL10nOverlayRecords() on copying the default language record
                                 $transOrigPointer = $row[$transOrigPointerField];
-                                if ($row[$languageField] > 0 && $transOrigPointer > 0 && isset($rows[$transOrigPointer])) {
+                                if ($row[$languageField] > 0 && $transOrigPointer > 0 && (isset($rows[$transOrigPointer]) || isset($movedLiveIds[$transOrigPointer]))) {
                                     continue;
                                 }
                                 // Copying each of the underlying records...
diff --git a/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/Modify/DataSet/changeContentSortingAndCopyDraftPage.csv b/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/Modify/DataSet/changeContentSortingAndCopyDraftPage.csv
index 0dd6f93d8e46..3d452e23b589 100644
--- a/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/Modify/DataSet/changeContentSortingAndCopyDraftPage.csv
+++ b/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/Modify/DataSet/changeContentSortingAndCopyDraftPage.csv
@@ -26,18 +26,17 @@
 ,325,89,448,0,2,321,301,302,1,4,0,302,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
 ,326,89,544,0,2,297,0,0,1,3,0,0,302,"[MOVE-TO PLACEHOLDER for #302, WS#1]"
 ,327,91,256,0,0,0,0,299,1,1,0,0,0,"Regular Element #3"
-# @todo: Sorting is not correct here: should be Element 2 then Element 1 then Element 3
 ,328,91,256,0,0,0,0,299,1,-1,0,327,0,"Regular Element #3"
 ,329,91,128,0,1,327,327,300,1,1,0,0,0,"[Translate to Dansk:] Regular Element #3"
 ,330,91,128,0,1,327,327,300,1,-1,0,329,0,"[Translate to Dansk:] Regular Element #3"
-,331,91,64,0,0,0,0,298,1,1,0,0,0,"Regular Element #2"
-,332,91,64,0,0,0,0,298,1,-1,0,331,0,"Regular Element #2"
-,333,91,32,0,0,0,0,297,1,1,0,0,0,"Regular Element #1"
-,334,91,32,0,0,0,0,297,1,-1,0,333,0,"Regular Element #1"
-,335,91,16,0,1,333,333,301,1,1,0,0,0,"[Translate to Dansk:] Regular Element #1"
-,336,91,16,0,1,333,333,301,1,-1,0,335,0,"[Translate to Dansk:] Regular Element #1"
-,337,91,8,0,2,333,335,302,1,1,0,0,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
-,338,91,8,0,2,333,335,302,1,-1,0,337,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
+,331,91,64,0,0,0,0,297,1,1,0,0,0,"Regular Element #1"
+,332,91,64,0,0,0,0,297,1,-1,0,331,0,"Regular Element #1"
+,333,91,32,0,1,331,331,301,1,1,0,0,0,"[Translate to Dansk:] Regular Element #1"
+,334,91,32,0,1,331,331,301,1,-1,0,333,0,"[Translate to Dansk:] Regular Element #1"
+,335,91,16,0,2,331,333,302,1,1,0,0,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
+,336,91,16,0,2,331,333,302,1,-1,0,335,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
+,337,91,8,0,0,0,0,298,1,1,0,0,0,"Regular Element #2"
+,338,91,8,0,0,0,0,298,1,-1,0,337,0,"Regular Element #2"
 "sys_refindex",,,,,,,,,,,,,,
 ,"hash","tablename","recuid","field","sorting","deleted","workspace","ref_table","ref_uid",,,,,
 ,"25426f92d44dd2ccf416108462b446e3","sys_workspace",1,"custom_stages",0,0,0,"sys_workspace_stage",1,,,,,
@@ -46,5 +45,5 @@
 ,"cd92ab3ab5798dc139f98107e7c28cb7","tt_content",323,"l18n_parent",0,0,1,"tt_content",321,,,,,
 ,"1c8013278ae997181374e3c76322e114","tt_content",325,"l18n_parent",0,0,1,"tt_content",321,,,,,
 ,"893ed65ba064539a74c6c86b2dd3a9ed","tt_content",330,"l18n_parent",0,0,1,"tt_content",327,,,,,
-,"7d48ba6e36389495b8eb362a9f8f8d78","tt_content",336,"l18n_parent",0,0,1,"tt_content",333,,,,,
-,"3f864e649c25f88940bd245f6895b5cf","tt_content",338,"l18n_parent",0,0,1,"tt_content",333,,,,,
+,"b3c908a26827758805d70fd387de5143","tt_content",334,"l18n_parent",0,0,1,"tt_content",331,,,,,
+,"01a973226431a87669a5c5ebd9e2b351","tt_content",336,"l18n_parent",0,0,1,"tt_content",331,,,,,
diff --git a/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/Publish/DataSet/changeContentSortingAndCopyDraftPage.csv b/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/Publish/DataSet/changeContentSortingAndCopyDraftPage.csv
index 5ffa7da31865..73a1e931c5d1 100644
--- a/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/Publish/DataSet/changeContentSortingAndCopyDraftPage.csv
+++ b/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/Publish/DataSet/changeContentSortingAndCopyDraftPage.csv
@@ -24,18 +24,17 @@
 ,325,89,448,0,2,321,301,302,1,4,0,302,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
 ,326,89,544,0,2,297,0,0,1,3,0,0,302,"[MOVE-TO PLACEHOLDER for #302, WS#1]"
 ,327,91,256,0,0,0,0,299,1,1,0,0,0,"Regular Element #3"
-# @todo: Sorting is not correct here: should be Element 2 then Element 1 then Element 3
 ,328,91,256,0,0,0,0,299,1,-1,0,327,0,"Regular Element #3"
 ,329,91,128,0,1,327,327,300,1,1,0,0,0,"[Translate to Dansk:] Regular Element #3"
 ,330,91,128,0,1,327,327,300,1,-1,0,329,0,"[Translate to Dansk:] Regular Element #3"
-,331,91,64,0,0,0,0,298,1,1,0,0,0,"Regular Element #2"
-,332,91,64,0,0,0,0,298,1,-1,0,331,0,"Regular Element #2"
-,333,91,32,0,0,0,0,297,1,1,0,0,0,"Regular Element #1"
-,334,91,32,0,0,0,0,297,1,-1,0,333,0,"Regular Element #1"
-,335,91,16,0,1,333,333,301,1,1,0,0,0,"[Translate to Dansk:] Regular Element #1"
-,336,91,16,0,1,333,333,301,1,-1,0,335,0,"[Translate to Dansk:] Regular Element #1"
-,337,91,8,0,2,333,335,302,1,1,0,0,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
-,338,91,8,0,2,333,335,302,1,-1,0,337,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
+,331,91,64,0,0,0,0,297,1,1,0,0,0,"Regular Element #1"
+,332,91,64,0,0,0,0,297,1,-1,0,331,0,"Regular Element #1"
+,333,91,32,0,1,331,331,301,1,1,0,0,0,"[Translate to Dansk:] Regular Element #1"
+,334,91,32,0,1,331,331,301,1,-1,0,333,0,"[Translate to Dansk:] Regular Element #1"
+,335,91,16,0,2,331,333,302,1,1,0,0,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
+,336,91,16,0,2,331,333,302,1,-1,0,335,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
+,337,91,8,0,0,0,0,298,1,1,0,0,0,"Regular Element #2"
+,338,91,8,0,0,0,0,298,1,-1,0,337,0,"Regular Element #2"
 "sys_refindex",,,,,,,,,,,,,,
 ,"hash","tablename","recuid","field","sorting","deleted","workspace","ref_table","ref_uid",,,,,
 ,"25426f92d44dd2ccf416108462b446e3","sys_workspace",1,"custom_stages",0,0,0,"sys_workspace_stage",1,,,,,
@@ -44,5 +43,5 @@
 ,"cd92ab3ab5798dc139f98107e7c28cb7","tt_content",323,"l18n_parent",0,0,1,"tt_content",321,,,,,
 ,"1c8013278ae997181374e3c76322e114","tt_content",325,"l18n_parent",0,0,1,"tt_content",321,,,,,
 ,"893ed65ba064539a74c6c86b2dd3a9ed","tt_content",330,"l18n_parent",0,0,1,"tt_content",327,,,,,
-,"7d48ba6e36389495b8eb362a9f8f8d78","tt_content",336,"l18n_parent",0,0,1,"tt_content",333,,,,,
-,"3f864e649c25f88940bd245f6895b5cf","tt_content",338,"l18n_parent",0,0,1,"tt_content",333,,,,,
+,"b3c908a26827758805d70fd387de5143","tt_content",334,"l18n_parent",0,0,1,"tt_content",331,,,,,
+,"01a973226431a87669a5c5ebd9e2b351","tt_content",336,"l18n_parent",0,0,1,"tt_content",331,,,,,
diff --git a/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/PublishAll/DataSet/changeContentSortingAndCopyDraftPage.csv b/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/PublishAll/DataSet/changeContentSortingAndCopyDraftPage.csv
index 07a002bf6c31..a7a1963b4f21 100644
--- a/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/PublishAll/DataSet/changeContentSortingAndCopyDraftPage.csv
+++ b/typo3/sysext/workspaces/Tests/Functional/DataHandling/Regular/PublishAll/DataSet/changeContentSortingAndCopyDraftPage.csv
@@ -18,20 +18,19 @@
 ,311,90,512,0,1,0,310,310,0,0,0,0,0,"[Translate to Dansk:] Regular Element #10"
 ,312,90,768,0,2,0,311,311,0,0,0,0,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #10"
 ,320,89,512,0,0,0,0,298,2,2,0,298,0,"Regular Element #2"
-# @todo: Sorting is not correct here: should be Element 2 then Element 1 then Element 3
 ,327,91,256,0,0,0,0,299,0,0,0,0,0,"Regular Element #3"
 ,329,91,128,0,1,327,327,300,0,0,0,0,0,"[Translate to Dansk:] Regular Element #3"
-,331,91,64,0,0,0,0,298,0,0,0,0,0,"Regular Element #2"
-,333,91,32,0,0,0,0,297,0,0,0,0,0,"Regular Element #1"
-,335,91,16,0,1,333,333,301,0,0,0,0,0,"[Translate to Dansk:] Regular Element #1"
-,337,91,8,0,2,333,335,302,0,0,0,0,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
+,331,91,64,0,0,0,0,297,0,0,0,0,0,"Regular Element #1"
+,333,91,32,0,1,331,331,301,0,0,0,0,0,"[Translate to Dansk:] Regular Element #1"
+,335,91,16,0,2,331,333,302,0,0,0,0,0,"[Translate to Deutsch:] [Translate to Dansk:] Regular Element #1"
+,337,91,8,0,0,0,0,298,0,0,0,0,0,"Regular Element #2"
 "sys_refindex",,,,,,,,,,,,,,
 ,"hash","tablename","recuid","field","sorting","deleted","workspace","ref_table","ref_uid",,,,,
 ,"25426f92d44dd2ccf416108462b446e3","sys_workspace",1,"custom_stages",0,0,0,"sys_workspace_stage",1,,,,,
 ,"01a3ce8c4e3b2bb1aa439dc29081f996","sys_workspace_stage",1,"responsible_persons",0,0,0,"be_users",3,,,,,
 ,"1b70a8e25c22645f7a49a79f57f3cf3f","sys_category",31,"parent",0,0,0,"sys_category",28,,,,,
 ,"691bd3ea92e56cae98fbcd3320abab16","tt_content",329,"l18n_parent",0,0,0,"tt_content",327,,,,,
-,"29336c6601631206b91ab727e3afd3ab","tt_content",335,"l18n_parent",0,0,0,"tt_content",333,,,,,
-,"06ef094e3198da6562845ccf7264c617","tt_content",337,"l18n_parent",0,0,0,"tt_content",333,,,,,
 ,"eb360eb09940bbb656509c7f6fda9b05","tt_content",301,"l18n_parent",0,0,0,"tt_content",297,,,,,
 ,"b6e55a3d99888c9a7007226ad685306c","tt_content",302,"l18n_parent",0,0,0,"tt_content",297,,,,,
+,"5ff17d2f286e6ca001fdfb2dea3a6fb5","tt_content",333,"l18n_parent",0,0,0,"tt_content",331,,,,,
+,"9922c9ce8e620250d1ca9d1dab1a2bff","tt_content",335,"l18n_parent",0,0,0,"tt_content",331,,,,,
-- 
GitLab