From dbe425f91a81a10eda4d0fd7d26dd7ea2cfb368c Mon Sep 17 00:00:00 2001
From: Oliver Hader <oliver@typo3.org>
Date: Thu, 29 Jun 2017 18:16:27 +0200
Subject: [PATCH] [BUGFIX] Copying workspace version record fails

Copying workspace version records fails in Doctrine DBAL exception due to
using computed properties directly in the database - which do not exist.
This misbehavior has been introduced in issue #79515 which switched to
use BackendUtility:workspaceOL() without further sanitization.

Resolves: #81747
Releases: master, 8.7
Change-Id: Iabc67f9a7375d4246289e50205aac6dc8c750259
Reviewed-on: https://review.typo3.org/53357
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
---
 .../Classes/Utility/BackendUtility.php        | 46 ++++++++++++++++++-
 .../Tests/Unit/Utility/BackendUtilityTest.php | 32 +++++++++++++
 .../core/Classes/DataHandling/DataHandler.php |  1 +
 3 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/typo3/sysext/backend/Classes/Utility/BackendUtility.php b/typo3/sysext/backend/Classes/Utility/BackendUtility.php
index 6914a61081ef..da685056c684 100644
--- a/typo3/sysext/backend/Classes/Utility/BackendUtility.php
+++ b/typo3/sysext/backend/Classes/Utility/BackendUtility.php
@@ -180,6 +180,39 @@ class BackendUtility
         return $row;
     }
 
+    /**
+     * Purges computed properties starting with underscore character ('_').
+     *
+     * @param array $record
+     * @return array
+     */
+    public static function purgeComputedPropertiesFromRecord(array $record): array
+    {
+        return array_filter(
+            $record,
+            function (string $propertyName): bool {
+                return $propertyName[0] !== '_';
+            },
+            ARRAY_FILTER_USE_KEY
+        );
+    }
+
+    /**
+     * Purges computed property names starting with underscore character ('_').
+     *
+     * @param array $propertyNames
+     * @return array
+     */
+    public static function purgeComputedPropertyNames(array $propertyNames): array
+    {
+        return array_filter(
+            $propertyNames,
+            function (string $propertyName): bool {
+                return $propertyName[0] !== '_';
+            }
+        );
+    }
+
     /**
      * Makes an backwards explode on the $str and returns an array with ($table, $uid).
      * Example: tt_content_45 => array('tt_content', 45)
@@ -3910,7 +3943,12 @@ class BackendUtility
                 $orig_pid = $row['pid'];
                 $movePldSwap = self::movePlhOL($table, $row);
             }
-            $wsAlt = self::getWorkspaceVersionOfRecord($wsid, $table, $row['uid'], implode(',', array_keys($row)));
+            $wsAlt = self::getWorkspaceVersionOfRecord(
+                $wsid,
+                $table,
+                $row['uid'],
+                implode(',', static::purgeComputedPropertyNames(array_keys($row)))
+            );
             // If version was found, swap the default record with that one.
             if (is_array($wsAlt)) {
                 // Check if this is in move-state:
@@ -3978,7 +4016,11 @@ class BackendUtility
             }
             // Find pointed-to record.
             if ($versionState->equals(VersionState::MOVE_PLACEHOLDER) && $moveID) {
-                if ($origRow = self::getRecord($table, $moveID, implode(',', array_keys($row)))) {
+                if ($origRow = self::getRecord(
+                    $table,
+                    $moveID,
+                    implode(',', static::purgeComputedPropertyNames(array_keys($row)))
+                )) {
                     $row = $origRow;
                     return true;
                 }
diff --git a/typo3/sysext/backend/Tests/Unit/Utility/BackendUtilityTest.php b/typo3/sysext/backend/Tests/Unit/Utility/BackendUtilityTest.php
index 9a6de40e2cd1..12a700f85160 100644
--- a/typo3/sysext/backend/Tests/Unit/Utility/BackendUtilityTest.php
+++ b/typo3/sysext/backend/Tests/Unit/Utility/BackendUtilityTest.php
@@ -1176,4 +1176,36 @@ class BackendUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
         $return = BackendUtility::getTCAtypes($table, $rec, $useFieldNameAsKey);
         $this->assertSame($expected, $return);
     }
+
+    /**
+     * @test
+     */
+    public function purgeComputedPropertyNamesRemovesPropertiesStartingWithUnderscore()
+    {
+        $propertyNames = [
+            'uid',
+            'pid',
+            '_ORIG_PID'
+        ];
+        $computedPropertyNames = BackendUtility::purgeComputedPropertyNames($propertyNames);
+        self::assertSame(['uid', 'pid'], $computedPropertyNames);
+    }
+
+    /**
+     * @test
+     */
+    public function purgeComputedPropertiesFromRecordRemovesPropertiesStartingWithUnderscore()
+    {
+        $record = [
+            'uid'       => 1,
+            'pid'       => 2,
+            '_ORIG_PID' => 1
+        ];
+        $expected = [
+            'uid' => 1,
+            'pid' => 2
+        ];
+        $computedProperties = BackendUtility::purgeComputedPropertiesFromRecord($record);
+        self::assertSame($expected, $computedProperties);
+    }
 }
diff --git a/typo3/sysext/core/Classes/DataHandling/DataHandler.php b/typo3/sysext/core/Classes/DataHandling/DataHandler.php
index 1254355e748e..99f052587239 100644
--- a/typo3/sysext/core/Classes/DataHandling/DataHandler.php
+++ b/typo3/sysext/core/Classes/DataHandling/DataHandler.php
@@ -3395,6 +3395,7 @@ class DataHandler
         $data = [];
         $nonFields = array_unique(GeneralUtility::trimExplode(',', 'uid,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,t3ver_oid,t3ver_wsid,t3ver_id,t3ver_label,t3ver_state,t3ver_count,t3ver_stage,t3ver_tstamp,' . $excludeFields, true));
         BackendUtility::workspaceOL($table, $row, -99, false);
+        $row = BackendUtility::purgeComputedPropertiesFromRecord($row);
 
         // Initializing:
         $theNewID = StringUtility::getUniqueId('NEW');
-- 
GitLab