[BUGFIX] FAL relations duplicated when saving in workspaces 13/47313/2
authorAndreas Wolf <andreas.wolf@typo3.org>
Thu, 22 Oct 2015 17:40:11 +0000 (19:40 +0200)
committerAndreas Wolf <andreas.wolf@typo3.org>
Sun, 5 Jun 2016 15:14:43 +0000 (17:14 +0200)
In a workspace, when a record is saved which contains an IRRE-managed
relation in a flexform field, the relation is duplicated. The reason is
that the workspace information is lost along the way when processing the
flexform content.

This commit introduces an additional method parameter to the relevant
flexform processing callbacks.

Additionally, this fixes an error in the Flexform XML fixture, which
(apparently) made the test go into an endless loop.

Work time for this patch was sponsored by Matthias Krappitz (aemka.de),
the time for developing the test was sponsored by me.

Releases: master, 7.6, 6.2
Resolves: #70962
Change-Id: If6d71cf966408fcf976365fc01e6a2529bf1902d
(cherry picked from commit a0cd456491740ae3adee1206d193f2f67b6677b1)
Reviewed-on: https://review.typo3.org/47313
Reviewed-by: Andreas Wolf <andreas.wolf@typo3.org>
Tested-by: Andreas Wolf <andreas.wolf@typo3.org>
typo3/sysext/core/Classes/DataHandling/DataHandler.php
typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/ActionTestCase.php [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/DataSet/LiveDefaultElements.csv [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/DataSet/LiveDefaultPages.csv [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/Fixtures/fileadmin/typo3-logo.png [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/Fixtures/Extensions/irre_tutorial/Configuration/FlexForms/tt_content_flexform.xml

index cfeec42..cdee8dd 100644 (file)
@@ -3025,17 +3025,18 @@ class DataHandler
      * @param array $dataStructArray Data structure for the form (might be sheets or not). Only values in the data array which has a configuration in the data structure will be processed.
      * @param array $pParams A set of parameters to pass through for the calling of the evaluation functions
      * @param string $callBackFunc Optional call back function, see checkValue_flex_procInData_travDS()  DEPRECATED, use \TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools instead for traversal!
+     * @param array $workspaceOptions
      * @return array The modified 'data' part.
      * @see checkValue_flex_procInData_travDS()
      */
-    public function checkValue_flex_procInData($dataPart, $dataPart_current, $uploadedFiles, $dataStructArray, $pParams, $callBackFunc = '')
+    public function checkValue_flex_procInData($dataPart, $dataPart_current, $uploadedFiles, $dataStructArray, $pParams, $callBackFunc = '', array $workspaceOptions = array())
     {
         if (is_array($dataPart)) {
             foreach ($dataPart as $sKey => $sheetDef) {
                 list($dataStruct, $actualSheet) = GeneralUtility::resolveSheetDefInDS($dataStructArray, $sKey);
                 if (is_array($dataStruct) && $actualSheet == $sKey && is_array($sheetDef)) {
                     foreach ($sheetDef as $lKey => $lData) {
-                        $this->checkValue_flex_procInData_travDS($dataPart[$sKey][$lKey], $dataPart_current[$sKey][$lKey], $uploadedFiles[$sKey][$lKey], $dataStruct['ROOT']['el'], $pParams, $callBackFunc, $sKey . '/' . $lKey . '/');
+                        $this->checkValue_flex_procInData_travDS($dataPart[$sKey][$lKey], $dataPart_current[$sKey][$lKey], $uploadedFiles[$sKey][$lKey], $dataStruct['ROOT']['el'], $pParams, $callBackFunc, $sKey . '/' . $lKey . '/', $workspaceOptions);
                     }
                 }
             }
@@ -3054,10 +3055,11 @@ class DataHandler
      * @param array $pParams A set of parameters to pass through for the calling of the evaluation functions / call back function
      * @param string $callBackFunc Call back function, default is checkValue_SW(). If $this->callBackObj is set to an object, the callback function in that object is called instead.
      * @param string $structurePath
+     * @param array $workspaceOptions
      * @return void
      * @see checkValue_flex_procInData()
      */
-    public function checkValue_flex_procInData_travDS(&$dataValues, $dataValues_current, $uploadedFiles, $DSelements, $pParams, $callBackFunc, $structurePath)
+    public function checkValue_flex_procInData_travDS(&$dataValues, $dataValues_current, $uploadedFiles, $DSelements, $pParams, $callBackFunc, $structurePath, array $workspaceOptions = array())
     {
         if (!is_array($DSelements)) {
             return;
@@ -3086,7 +3088,7 @@ class DataHandler
                             continue;
                         }
 
-                        $this->checkValue_flex_procInData_travDS($dataValues[$key]['el'][$ik][$theKey]['el'], is_array($dataValues_current[$key]['el'][$ik]) ? $dataValues_current[$key]['el'][$ik][$theKey]['el'] : array(), $uploadedFiles[$key]['el'][$ik][$theKey]['el'], $DSelements[$key]['el'][$theKey]['el'], $pParams, $callBackFunc, $structurePath . $key . '/el/' . $ik . '/' . $theKey . '/el/');
+                        $this->checkValue_flex_procInData_travDS($dataValues[$key]['el'][$ik][$theKey]['el'], is_array($dataValues_current[$key]['el'][$ik]) ? $dataValues_current[$key]['el'][$ik][$theKey]['el'] : array(), $uploadedFiles[$key]['el'][$ik][$theKey]['el'], $DSelements[$key]['el'][$theKey]['el'], $pParams, $callBackFunc, $structurePath . $key . '/el/' . $ik . '/' . $theKey . '/el/', $workspaceOptions);
                         // If element is added dynamically in the flexform of TCEforms, we map the ID-string to the next numerical index we can have in that particular section of elements:
                         // The fact that the order changes is not important since order is controlled by a separately submitted index.
                         if (substr($ik, 0, 3) == 'ID-') {
@@ -3103,7 +3105,7 @@ class DataHandler
                     if (!isset($dataValues[$key]['el'])) {
                         $dataValues[$key]['el'] = array();
                     }
-                    $this->checkValue_flex_procInData_travDS($dataValues[$key]['el'], $dataValues_current[$key]['el'], $uploadedFiles[$key]['el'], $DSelements[$key]['el'], $pParams, $callBackFunc, $structurePath . $key . '/el/');
+                    $this->checkValue_flex_procInData_travDS($dataValues[$key]['el'], $dataValues_current[$key]['el'], $uploadedFiles[$key]['el'], $DSelements[$key]['el'], $pParams, $callBackFunc, $structurePath . $key . '/el/', $workspaceOptions);
                 }
             } else {
                 if (!is_array($dsConf['TCEforms']['config']) || !is_array($dataValues[$key])) {
@@ -3113,9 +3115,9 @@ class DataHandler
                 foreach ($dataValues[$key] as $vKey => $data) {
                     if ($callBackFunc) {
                         if (is_object($this->callBackObj)) {
-                            $res = $this->callBackObj->{$callBackFunc}($pParams, $dsConf['TCEforms']['config'], $dataValues[$key][$vKey], $dataValues_current[$key][$vKey], $uploadedFiles[$key][$vKey], $structurePath . $key . '/' . $vKey . '/');
+                            $res = $this->callBackObj->{$callBackFunc}($pParams, $dsConf['TCEforms']['config'], $dataValues[$key][$vKey], $dataValues_current[$key][$vKey], $uploadedFiles[$key][$vKey], $structurePath . $key . '/' . $vKey . '/', $workspaceOptions);
                         } else {
-                            $res = $this->{$callBackFunc}($pParams, $dsConf['TCEforms']['config'], $dataValues[$key][$vKey], $dataValues_current[$key][$vKey], $uploadedFiles[$key][$vKey], $structurePath . $key . '/' . $vKey . '/');
+                            $res = $this->{$callBackFunc}($pParams, $dsConf['TCEforms']['config'], $dataValues[$key][$vKey], $dataValues_current[$key][$vKey], $uploadedFiles[$key][$vKey], $structurePath . $key . '/' . $vKey . '/', $workspaceOptions);
                         }
                     } else {
                         // Default
@@ -3827,7 +3829,7 @@ class DataHandler
             $currentValueArray = GeneralUtility::xml2array($value);
             // Traversing the XML structure, processing files:
             if (is_array($currentValueArray)) {
-                $currentValueArray['data'] = $this->checkValue_flex_procInData($currentValueArray['data'], array(), array(), $dataStructArray, array($table, $uid, $field, $realDestPid), 'copyRecord_flexFormCallBack');
+                $currentValueArray['data'] = $this->checkValue_flex_procInData($currentValueArray['data'], array(), array(), $dataStructArray, array($table, $uid, $field, $realDestPid), 'copyRecord_flexFormCallBack', $workspaceOptions);
                 // Setting value as an array! -> which means the input will be processed according to the 'flex' type when the new copy is created.
                 $value = $currentValueArray;
             }
@@ -3986,12 +3988,14 @@ class DataHandler
      * @param array $pParams Array of parameters in num-indexes: table, uid, field
      * @param array $dsConf TCA field configuration (from Data Structure XML)
      * @param string $dataValue The value of the flexForm field
-     * @param string $dataValue_ext1 Not used.
-     * @param string $dataValue_ext2 Not used.
+     * @param string $_1 Not used.
+     * @param string $_2 Not used.
+     * @param string $_3 Not used.
+     * @param array $workspaceOptions
      * @return array Result array with key "value" containing the value of the processing.
      * @see copyRecord(), checkValue_flex_procInData_travDS()
      */
-    public function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)
+    public function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $_1, $_2, $_3, $workspaceOptions)
     {
         // Extract parameters:
         list($table, $uid, $field, $realDestPid) = $pParams;
@@ -3999,7 +4003,7 @@ class DataHandler
         $dataValue = $this->copyRecord_procFilesRefs($dsConf, $uid, $dataValue);
         // If references are set for this field, set flag so they can be corrected later (in ->remapListedDBRecords())
         if (($this->isReferenceField($dsConf) || $this->getInlineFieldType($dsConf) !== false) && (string)$dataValue !== '') {
-            $dataValue = $this->copyRecord_procBasedOnFieldType($table, $uid, $field, $dataValue, array(), $dsConf, $realDestPid);
+            $dataValue = $this->copyRecord_procBasedOnFieldType($table, $uid, $field, $dataValue, array(), $dsConf, $realDestPid, 0, $workspaceOptions);
             $this->registerDBList[$table][$uid][$field] = 'FlexForm_reference';
         }
         // Return
diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/ActionTestCase.php b/typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/ActionTestCase.php
new file mode 100644 (file)
index 0000000..f068760
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+namespace TYPO3\CMS\Core\Tests\Functional\DataHandling\FlexformIrre;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+
+class ActionTestCase extends \TYPO3\CMS\Core\Tests\Functional\DataHandling\AbstractDataHandlerActionTestCase
+{
+
+    /**
+     * @var array
+     */
+    protected $pathsToLinkInTestInstance = array(
+        'typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/Fixtures/fileadmin' => 'fileadmin/fixture',
+    );
+
+    /**
+     * @var string
+     */
+    protected $scenarioDataSetDirectory = 'typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/DataSet/';
+
+    protected function setUp()
+    {
+        $this->testExtensionsToLoad[] = 'typo3/sysext/version/';
+        $this->testExtensionsToLoad[] = 'typo3/sysext/workspaces/';
+
+        parent::setUp();
+        $this->importScenarioDataSet('LiveDefaultPages');
+        $this->importScenarioDataSet('LiveDefaultElements');
+    }
+
+    /**
+     * @test
+     */
+    public function newVersionOfFileRelationInFlexformFieldIsCreatedOnSave()
+    {
+        $this->backendUser->workspace = 1;
+        $GLOBALS['BE_USER']->workspace = 1;
+        $this->getActionService()->modifyRecords(1, [
+            //'sys_file_reference' => ['uid' => 10, 'hidden' => 0],
+            'tt_content' => ['uid' => 100, 'header' => 'Content #1 (WS)']
+        ]);
+
+        // there should be one relation in the live WS and one in the draft WS pointing to the file field.
+        $this->assertEquals(2, $this->getDatabaseConnection()->exec_SELECTcountRows('uid', 'sys_file_reference', 'uid_local = 20'));
+    }
+
+}
diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/DataSet/LiveDefaultElements.csv b/typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/DataSet/LiveDefaultElements.csv
new file mode 100644 (file)
index 0000000..3729b2b
--- /dev/null
@@ -0,0 +1,16 @@
+"sys_language"
+,"uid","pid","hidden","title","flag"
+,1,0,0,"Deutsch","de"
+"sys_workspace"
+,"uid","pid","title","db_mountpoints"
+,1,0,"Test WS","88"
+tt_content
+,uid,pid,CType,sorting,deleted,sys_language_uid,l18n_parent,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,header,"tx_irretutorial_flexform"
+,100,1,"",256,0,0,0,0,0,0,0,0,"Content #1","<?xml version='1.0' encoding='utf-8' standalone='yes' ?><T3FlexForms><data><sheet index='sDEF'><language index='lDEF'><field index='files'><value index='vDEF'>1</value></field></language></sheet></data></T3FlexForms>"
+# we assume the auto-generated storage 1 to exist here
+sys_file
+,uid,pid,storage,identifier,name
+,20,0,1,"/typo3-logo.png","typo3-logo.png"
+sys_file_reference
+,uid,pid,uid_local,uid_foreign,tablenames,fieldname,sorting,sorting_foreign,table_local,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id
+,1,1,20,100,"tt_content","files",1,1,"sys_file",0,0,0,0,0
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/DataSet/LiveDefaultPages.csv b/typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/DataSet/LiveDefaultPages.csv
new file mode 100644 (file)
index 0000000..ab9ec6d
--- /dev/null
@@ -0,0 +1,4 @@
+pages
+,uid,pid,sorting,deleted,t3ver_wsid,t3ver_state,t3ver_stage,t3ver_oid,t3ver_move_id,title
+,1,0,256,0,0,0,0,0,0,DataHandlerTest
+,2,1,256,0,0,0,0,0,0,Second Test page
diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/Fixtures/fileadmin/typo3-logo.png b/typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/Fixtures/fileadmin/typo3-logo.png
new file mode 100644 (file)
index 0000000..335c15e
Binary files /dev/null and b/typo3/sysext/core/Tests/Functional/DataHandling/FlexformIrre/Fixtures/fileadmin/typo3-logo.png differ
index 08bf9f8..af0c076 100644 (file)
@@ -1,31 +1,65 @@
 <T3DataStructure>
-       <ROOT>
-               <type>array</type>
-               <el>
-                       <hotels>
-                               <TCEforms>
-                                       <label>LLL:EXT:irre_tutorial/Resources/Private/Language/locallang_db.xml:tt_content.tx_irretutorial_flexform.hotels</label>
-                                       <config>
-                                               <type>inline</type>
-                                               <foreign_table>tx_irretutorial_1nff_hotel</foreign_table>
-                                               <foreign_field>parentid</foreign_field>
-                                               <foreign_table_field>parenttable</foreign_table_field>
-                                               <foreign_match_fields type="array">
-                                                       <parentidentifier>flexform.hotels</parentidentifier>
-                                               </foreign_match_fields>
-                                               <maxitems>10</maxitems>
-                                               <appearance type="array">
-                                                       <showSynchronizationLink>1</showSynchronizationLink>
-                                                       <showAllLocalizationLink>1</showAllLocalizationLink>
-                                                       <showPossibleLocalizationRecords>1</showPossibleLocalizationRecords>
-                                                       <showRemovedLocalizationRecords>1</showRemovedLocalizationRecords>
-                                               </appearance>
-                                               <behaviour type="array">
-                                                       <localizationMode>select</localizationMode>
-                                               </behaviour>
-                                       </config>
-                               </TCEforms>
-                       </hotels>
-               </el>
-       </ROOT>
+       <sheets>
+               <sDEF>
+                       <ROOT>
+                               <type>array</type>
+                               <el>
+                                       <hotels>
+                                               <TCEforms>
+                                                       <label>
+                                                               LLL:EXT:irre_tutorial/Resources/Private/Language/locallang_db.xml:tt_content.tx_irretutorial_flexform.hotels
+                                                       </label>
+                                                       <config>
+                                                               <type>inline</type>
+                                                               <foreign_table>tx_irretutorial_1nff_hotel</foreign_table>
+                                                               <foreign_field>parentid</foreign_field>
+                                                               <foreign_table_field>parenttable</foreign_table_field>
+                                                               <foreign_match_fields type="array">
+                                                                       <parentidentifier>flexform.hotels</parentidentifier>
+                                                               </foreign_match_fields>
+                                                               <maxitems>10</maxitems>
+                                                               <appearance type="array">
+                                                                       <showSynchronizationLink>1</showSynchronizationLink>
+                                                                       <showAllLocalizationLink>1</showAllLocalizationLink>
+                                                                       <showPossibleLocalizationRecords>1</showPossibleLocalizationRecords>
+                                                                       <showRemovedLocalizationRecords>1</showRemovedLocalizationRecords>
+                                                               </appearance>
+                                                               <behaviour type="array">
+                                                                       <localizationMode>select</localizationMode>
+                                                               </behaviour>
+                                                       </config>
+                                               </TCEforms>
+                                       </hotels>
+                                       <files>
+                                               <TCEforms>
+                                                       <label>Files</label>
+                                                       <config>
+                                                               <type>inline</type>
+                                                               <foreign_table>sys_file_reference</foreign_table>
+                                                               <foreign_field>uid_foreign</foreign_field>
+                                                               <foreign_sortby>sorting_foreign</foreign_sortby>
+                                                               <foreign_table_field>tablenames</foreign_table_field>
+                                                               <foreign_match_fields>
+                                                                       <fieldname>files</fieldname>
+                                                               </foreign_match_fields>
+                                                               <foreign_label>uid_local</foreign_label>
+                                                               <foreign_selector>uid_local</foreign_selector>
+                                                               <maxitems>10</maxitems>
+                                                               <appearance type="array">
+                                                                       <showSynchronizationLink>1</showSynchronizationLink>
+                                                                       <showAllLocalizationLink>1</showAllLocalizationLink>
+                                                                       <showPossibleLocalizationRecords>1</showPossibleLocalizationRecords>
+                                                                       <showRemovedLocalizationRecords>1</showRemovedLocalizationRecords>
+                                                               </appearance>
+                                                               <behaviour type="array">
+                                                                       <localizationMode>select</localizationMode>
+                                                                       <localizeChildrenAtParentLocalization>1</localizeChildrenAtParentLocalization>
+                                                               </behaviour>
+                                                       </config>
+                                               </TCEforms>
+                                       </files>
+                               </el>
+                       </ROOT>
+               </sDEF>
+       </sheets>
 </T3DataStructure>