[BUGFIX] Removing child records in a workspace does not work
authorOliver Hader <oliver@typo3.org>
Tue, 5 Apr 2011 13:14:19 +0000 (15:14 +0200)
committerOliver Hader <oliver@typo3.org>
Tue, 5 Apr 2011 14:27:50 +0000 (16:27 +0200)
These parts are not working with IRRE in workspaces:
Removing children in TCEforms
Removing children in the list view
Reverting changes in the workspace module

Change-Id: I703d7e4cb08a3a3f3f07de0c7c63f2b827119adc
Resolved: #13941
Releases: 4.6, 4.5

typo3/sysext/version/class.tx_version_tcemain.php
typo3/sysext/version/class.tx_version_tcemain_commandmap.php

index 4c58ffb..16a497a 100644 (file)
@@ -173,7 +173,6 @@ class tx_version_tcemain {
                        // by someone else before
                if (!$recordWasDeleted) {
                        $recordWasDeleted = TRUE;
-                       $id = $record['uid'];
 
                                // For Live version, try if there is a workspace version because if so, rather "delete" that instead
                                // Look, if record is an offline version, then delete directly:
@@ -223,8 +222,21 @@ class tx_version_tcemain {
                                $tcemainObj->deleteEl($table, $id);
                        } else {
                                // Otherwise, try to delete by versioning:
+                               $copyMappingArray = $tcemainObj->copyMappingArray;
                                $tcemainObj->versionizeRecord($table, $id, 'DELETED!', TRUE);
-                               $tcemainObj->deleteL10nOverlayRecords($table, $id);
+
+                               // Determine newly created versions:
+                               // (remove placeholders are copied and modified, thus they appear in the copyMappingArray)
+                               $versionizedElements = t3lib_div::arrayDiffAssocRecursive(
+                                       $tcemainObj->copyMappingArray,
+                                       $copyMappingArray
+                               );
+                               // Delete localization overlays:
+                               foreach ($versionizedElements as $versionizedTableName => $versionizedOriginalIds) {
+                                       foreach ($versionizedOriginalIds as $versionizedOriginalId => $_) {
+                                               $tcemainObj->deleteL10nOverlayRecords($versionizedTableName, $versionizedOriginalId);
+                                       }
+                               }
                        }
                }
        }
index 80250ce..58a706e 100644 (file)
@@ -31,6 +31,7 @@
 class tx_version_tcemain_CommandMap {
        const SCOPE_WorkspacesSwap = 'SCOPE_WorkspacesSwap';
        const SCOPE_WorkspacesSetStage = 'SCOPE_WorkspacesSetStage';
+       const SCOPE_WorkspacesClear = 'SCOPE_WorkspacesClear';
 
        const KEY_ScopeErrorMessage = 'KEY_ScopeErrorMessage';
        const KEY_ScopeErrorCode = 'KEY_ScopeErrorCode';
@@ -200,6 +201,7 @@ class tx_version_tcemain_CommandMap {
        public function process() {
                $this->resolveWorkspacesSwapDependencies();
                $this->resolveWorkspacesSetStageDependencies();
+               $this->resolveWorkspacesClearDependencies();
                return $this;
        }
 
@@ -215,6 +217,7 @@ class tx_version_tcemain_CommandMap {
                $scope = self::SCOPE_WorkspacesSwap;
                $dependency = $this->getDependencyUtility($scope);
 
+                       // Traverses the cmd[] array and fetches the accordant actions:
                foreach ($this->commandMap as $table => $liveIdCollection) {
                        foreach ($liveIdCollection as $liveId => $commandCollection) {
                                foreach ($commandCollection as $command => $properties) {
@@ -274,6 +277,7 @@ class tx_version_tcemain_CommandMap {
                $scope = self::SCOPE_WorkspacesSetStage;
                $dependency = $this->getDependencyUtility($scope);
 
+                       // Traverses the cmd[] array and fetches the accordant actions:
                foreach ($this->commandMap as $table => $liveIdCollection) {
                        foreach ($liveIdCollection as $liveIdList => $commandCollection) {
                                foreach ($commandCollection as $command => $properties) {
@@ -335,6 +339,31 @@ class tx_version_tcemain_CommandMap {
        }
 
        /**
+        * Resolves workspaces related dependencies for clearing/flushing of the command map.
+        * Workspaces records that have children or (relative) parents which are versionized
+        * but not cleared/flushed with this request, are removed from the command map.
+        *
+        * @return void
+        */
+       protected function resolveWorkspacesClearDependencies() {
+               $scope = self::SCOPE_WorkspacesClear;
+               $dependency = $this->getDependencyUtility($scope);
+
+                       // Traverses the cmd[] array and fetches the accordant actions:
+               foreach ($this->commandMap as $table => $liveIdCollection) {
+                       foreach ($liveIdCollection as $liveId => $commandCollection) {
+                               foreach ($commandCollection as $command => $properties) {
+                                       if ($command === 'version' && isset($properties['action']) && ($properties['action'] === 'clearWSID' || $properties['action'] === 'flush')) {
+                                               $dependency->addElement($table, $liveId, array('properties' => $properties));
+                                       }
+                               }
+                       }
+               }
+
+               $this->applyWorkspacesDependencies($dependency, $scope);
+       }
+
+       /**
         * Explodes id-lists in the command map for staging actions.
         *
         * @throws RuntimeException
@@ -373,6 +402,8 @@ class tx_version_tcemain_CommandMap {
                $transformDependentElementsToUseLiveId = $this->getScopeData($scope, self::KEY_TransformDependentElementsToUseLiveId);
 
                $elementsToBeVersionized = $dependency->getElements();
+
+                       // Use the uid of the live record instead of the workspace record:
                if ($transformDependentElementsToUseLiveId) {
                        $elementsToBeVersionized = $this->transformDependentElementsToUseLiveId($elementsToBeVersionized);
                }
@@ -385,6 +416,8 @@ class tx_version_tcemain_CommandMap {
                                $dependentElements = $this->transformDependentElementsToUseLiveId($dependentElements);
                        }
 
+                       // Gets the difference (intersection) between elements that were submitted by the user
+                       // and the evaluation of all dependent records that should be used for this action instead:
                        $intersectingElements = array_intersect_key($dependentElements, $elementsToBeVersionized);
 
                        if (count($intersectingElements) > 0) {
@@ -440,10 +473,14 @@ class tx_version_tcemain_CommandMap {
        protected function update(t3lib_utility_Dependency_Element $intersectingElement, array $elements, $scope) {
                $orderedCommandMap = array();
 
-               $commonProperties = $this->processCallback(
-                       $this->getScopeData($scope, self::KEY_GetCommonPropertiesCallback),
-                       array($intersectingElement)
-               );
+               $commonProperties = array();
+
+               if ($this->getScopeData($scope, self::KEY_GetCommonPropertiesCallback)) {
+                       $commonProperties = $this->processCallback(
+                               $this->getScopeData($scope, self::KEY_GetCommonPropertiesCallback),
+                               array($intersectingElement)
+                       );
+               }
 
                /** @var $dependentElement t3lib_utility_Dependency_Element */
                foreach ($elements as $element) {
@@ -454,13 +491,17 @@ class tx_version_tcemain_CommandMap {
                        );
 
                        $this->remove($table, $id, 'version');
-                       $orderedCommandMap[$table][$id]['version'] = array_merge(
-                               $commonProperties,
-                               $this->processCallback(
-                                       $this->getScopeData($scope, self::KEY_GetElementPropertiesCallback),
-                                       array($element)
-                               )
-                       );
+                       $orderedCommandMap[$table][$id]['version'] = $commonProperties;
+
+                       if ($this->getScopeData($scope, self::KEY_GetElementPropertiesCallback)) {
+                               $orderedCommandMap[$table][$id]['version'] = array_merge(
+                                       $commonProperties,
+                                       $this->processCallback(
+                                               $this->getScopeData($scope, self::KEY_GetElementPropertiesCallback),
+                                               array($element)
+                                       )
+                               );
+                       }
                }
 
                // Ensure that ordered command map is on top of the command map:
@@ -536,6 +577,23 @@ class tx_version_tcemain_CommandMap {
        }
 
        /**
+        * Callback to get common properties of dependent elements for clearing.
+        *
+        * @param t3lib_utility_Dependency_Element $element
+        * @return array
+        */
+       protected function getCommonClearPropertiesCallback(t3lib_utility_Dependency_Element $element) {
+               $commonSwapProperties = array();
+
+               $elementProperties = $element->getDataValue('properties');
+               if (isset($elementProperties['action'])) {
+                       $commonSwapProperties['action'] = $elementProperties['action'];
+               }
+
+               return $commonSwapProperties;
+       }
+
+       /**
         * Callback to get common properties of dependent elements for swapping/publishing.
         *
         * @param t3lib_utility_Dependency_Element $element
@@ -591,7 +649,6 @@ class tx_version_tcemain_CommandMap {
                return $commonSetStageProperties;
        }
 
-
        /**
         * Gets an instance of the depency resolver utility.
         *
@@ -666,6 +723,58 @@ class tx_version_tcemain_CommandMap {
        }
 
        /**
+        * Callback to determine whether a new child reference shall be considered in the dependency resolver utility.
+        * Only elements that are a delete placeholder are considered.
+        *
+        * @param array $callerArguments
+        * @param array $targetArgument
+        * @param t3lib_utility_Dependency_Element $caller
+        * @param string $eventName
+        * @return string Skip response (if required)
+        */
+       public function createClearDependentElementChildReferenceCallback(array $callerArguments, array $targetArgument, t3lib_utility_Dependency_Element $caller, $eventName) {
+               $response = $this->createNewDependentElementChildReferenceCallback($callerArguments, $targetArgument, $caller, $eventName);
+
+               if (empty($response)) {
+                       /** @var $reference t3lib_utility_Dependency_Reference */
+                       $reference = $callerArguments['reference'];
+
+                       $record = $reference->getElement()->getRecord();
+                       if ($record['t3ver_state'] != 2) {
+                               $response = t3lib_utility_Dependency_Element::RESPONSE_Skip;
+                       }
+               }
+
+               return $response;
+       }
+
+       /**
+        * Callback to determine whether a new parent reference shall be considered in the dependency resolver utility.
+        * Only elements that are a delete placeholder are considered.
+        *
+        * @param array $callerArguments
+        * @param array $targetArgument
+        * @param t3lib_utility_Dependency_Element $caller
+        * @param string $eventName
+        * @return string Skip response (if required)
+        */
+       public function createClearDependentElementParentReferenceCallback(array $callerArguments, array $targetArgument, t3lib_utility_Dependency_Element $caller, $eventName) {
+               $response = $this->createNewDependentElementParentReferenceCallback($callerArguments, $targetArgument, $caller, $eventName);
+
+               if (empty($response)) {
+                       /** @var $reference t3lib_utility_Dependency_Reference */
+                       $reference = $callerArguments['reference'];
+
+                       $record = $reference->getElement()->getRecord();
+                       if ($record['t3ver_state'] != 2) {
+                               $response = t3lib_utility_Dependency_Element::RESPONSE_Skip;
+                       }
+               }
+
+               return $response;
+       }
+
+       /**
         * Callback to add additional data to new elements created in the dependency resolver utility.
         *
         * @param t3lib_utility_Dependency_Element $caller
@@ -710,28 +819,70 @@ class tx_version_tcemain_CommandMap {
         */
        protected function constructScopes() {
                $this->scopes = array(
+                       // settings for publishing and swapping:
                        self::SCOPE_WorkspacesSwap => array(
+                               // error message and error code
                                self::KEY_ScopeErrorMessage => 'Record "%s" (%s:%s) cannot be swapped or published independently, because it is related to other new or modified records.',
                                self::KEY_ScopeErrorCode => 1288283630,
+                               // callback functons used to modify the commandMap
+                               // + element properties are specific for each element
+                               // + common properties are the same for all elements
                                self::KEY_GetElementPropertiesCallback => 'getElementSwapPropertiesCallback',
                                self::KEY_GetCommonPropertiesCallback => 'getCommonSwapPropertiesCallback',
+                               // callback function used, when a new element to be checked is added
                                self::KEY_ElementConstructCallback => 'createNewDependentElementCallback',
+                               // callback function used to determine whether an element is a valid child or parent reference (e.g. IRRE)
                                self::KEY_ElementCreateChildReferenceCallback => 'createNewDependentElementChildReferenceCallback',
                                self::KEY_ElementCreateParentReferenceCallback => 'createNewDependentElementParentReferenceCallback',
+                               // callback function used to get the correct record uid to be used in the error message
                                self::KEY_PurgeWithErrorMessageGetIdCallback => 'getElementLiveIdCallback',
+                               // callback function used to fetch the correct record uid on modifying the commandMap
                                self::KEY_UpdateGetIdCallback => 'getElementLiveIdCallback',
+                               // setting whether to use the uid of the live record instead of the workspace record
                                self::KEY_TransformDependentElementsToUseLiveId => TRUE,
                        ),
+                       // settings for modifying the stage:
                        self::SCOPE_WorkspacesSetStage => array(
+                               // error message and error code
                                self::KEY_ScopeErrorMessage => 'Record "%s" (%s:%s) cannot be sent to another stage independently, because it is related to other new or modified records.',
                                self::KEY_ScopeErrorCode => 1289342524,
+                               // callback functons used to modify the commandMap
+                               // + element properties are specific for each element
+                               // + common properties are the same for all elements
                                self::KEY_GetElementPropertiesCallback => 'getElementSetStagePropertiesCallback',
                                self::KEY_GetCommonPropertiesCallback => 'getCommonSetStagePropertiesCallback',
+                               // callback function used, when a new element to be checked is added
                                self::KEY_ElementConstructCallback => NULL,
+                               // callback function used to determine whether an element is a valid child or parent reference (e.g. IRRE)
                                self::KEY_ElementCreateChildReferenceCallback => 'createNewDependentElementChildReferenceCallback',
                                self::KEY_ElementCreateParentReferenceCallback => 'createNewDependentElementParentReferenceCallback',
+                               // callback function used to get the correct record uid to be used in the error message
+                               self::KEY_PurgeWithErrorMessageGetIdCallback => 'getElementIdCallback',
+                               // callback function used to fetch the correct record uid on modifying the commandMap
+                               self::KEY_UpdateGetIdCallback => 'getElementIdCallback',
+                               // setting whether to use the uid of the live record instead of the workspace record
+                               self::KEY_TransformDependentElementsToUseLiveId => FALSE,
+                       ),
+                       // settings for clearing and flushing:
+                       self::SCOPE_WorkspacesClear => array(
+                               // error message and error code
+                               self::KEY_ScopeErrorMessage => 'Record "%s" (%s:%s) cannot be flushed independently, because it is related to other new or modified records.',
+                               self::KEY_ScopeErrorCode => 1300467990,
+                               // callback functons used to modify the commandMap
+                               // + element properties are specific for each element
+                               // + common properties are the same for all elements
+                               self::KEY_GetElementPropertiesCallback => NULL,
+                               self::KEY_GetCommonPropertiesCallback => 'getCommonClearPropertiesCallback',
+                               // callback function used, when a new element to be checked is added
+                               self::KEY_ElementConstructCallback => NULL,
+                               // callback function used to determine whether an element is a valid child or parent reference (e.g. IRRE)
+                               self::KEY_ElementCreateChildReferenceCallback => 'createClearDependentElementChildReferenceCallback',
+                               self::KEY_ElementCreateParentReferenceCallback => 'createClearDependentElementParentReferenceCallback',
+                               // callback function used to get the correct record uid to be used in the error message
                                self::KEY_PurgeWithErrorMessageGetIdCallback => 'getElementIdCallback',
+                               // callback function used to fetch the correct record uid on modifying the commandMap
                                self::KEY_UpdateGetIdCallback => 'getElementIdCallback',
+                               // setting whether to use the uid of the live record instead of the workspace record
                                self::KEY_TransformDependentElementsToUseLiveId => FALSE,
                        ),
                );