Fixed bug #14050: CleanUp - CGL format of t3lib files - t3lib_positionmap
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_tcemain.php
index 7a56f5b..2a23844 100644 (file)
@@ -2,7 +2,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 1999-2009 Kasper Skaarhoj (kasperYYYY@typo3.com)
+*  (c) 1999-2010 Kasper Skårhøj (kasperYYYY@typo3.com)
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -28,9 +28,9 @@
  * Contains the TYPO3 Core Engine
  *
  * $Id$
- * Revised for TYPO3 3.9 October 2005 by Kasper Skaarhoj
+ * Revised for TYPO3 3.9 October 2005 by Kasper Skårhøj
  *
- * @author     Kasper Skaarhoj <kasperYYYY@typo3.com>
+ * @author     Kasper Skårhøj <kasperYYYY@typo3.com>
  */
 /**
  * [CLASS/FUNCTION INDEX of SCRIPT]
  *
  * tce_db.php for further comments and SYNTAX! Also see document 'TYPO3 Core API' for details.
  *
- * @author     Kasper Skaarhoj <kasperYYYY@typo3.com>
+ * @author     Kasper Skårhøj <kasperYYYY@typo3.com>
  * @package TYPO3
  * @subpackage t3lib
  */
@@ -255,7 +255,6 @@ class t3lib_TCEmain {
        var $bypassAccessCheckForRecords = FALSE;       // Boolean: If true, access check, check for deleted etc. for records is bypassed. YOU MUST KNOW what you are doing if you use this feature!
 
        var $copyWhichTables = '*';                             // String. Comma-list. This list of tables decides which tables will be copied. If empty then none will. If '*' then all will (that the user has permission to of course)
-       var $generalComment = '';                               // General comment, eg. for staging in workspaces.
 
        var $copyTree = 0;                                              // Integer. If 0 then branch is NOT copied. If 1 then pages on the 1st level is copied. If 2 then pages on the second level is copied ... and so on
 
@@ -282,7 +281,6 @@ class t3lib_TCEmain {
        var $copiedFileMap = Array();                           // A map between input file name and final destination for files being attached to records.
        var $RTEmagic_copyIndex = Array();                      // Contains [table][id][field] of fiels where RTEmagic images was copied. Holds old filename as key and new filename as value.
        var $errorLog = Array();                                        // Errors are collected in this variable.
-       var $accumulateForNotifEmail = Array();         // For accumulating information about workspace stages raised on elements so a single mail is sent as notification.
 
 
 
@@ -339,6 +337,9 @@ class t3lib_TCEmain {
        var $copyMappingArray = Array();                        // Used by the copy action to track the ids of new pages so subpages are correctly inserted! THIS is internally cleared for each executed copy operation! DO NOT USE THIS FROM OUTSIDE! Read from copyMappingArray_merged instead which is accumulating this information.
        var $remapStack = array();                                      // array used for remapping uids and values at the end of process_datamap
        var $remapStackRecords = array();                       // array used for remapping uids and values at the end of process_datamap (e.g. $remapStackRecords[<table>][<uid>] = <index in $remapStack>)
+       protected $remapStackChildIds = array();        // array used for checking whether new children need to be remapped
+       protected $remapStackActions = array();         // array used for executing addition actions after remapping happened (sett processRemapStack())
+       protected $remapStackRefIndex = array();        // array used for executing post-processing on the reference index
        var $updateRefIndexStack = array();                     // array used for additional calls to $this->updateRefIndex
        var $callFromImpExp = false;                            // tells, that this TCEmain was called from tx_impext - this variable is set by tx_impexp
        var $newIndexMap = array();                                     // Array for new flexform index mapping
@@ -376,7 +377,7 @@ class t3lib_TCEmain {
         * @param       object          An alternative userobject you can set instead of the default, which is $GLOBALS['BE_USER']
         * @return      void
         */
-       function start($data,$cmd,$altUserObject='')    {
+       public function start($data, $cmd, $altUserObject = '') {
 
                        // Initializing BE_USER
                $this->BE_USER = is_object($altUserObject) ? $altUserObject : $GLOBALS['BE_USER'];
@@ -384,7 +385,7 @@ class t3lib_TCEmain {
                $this->username = $this->BE_USER->user['username'];
                $this->admin = $this->BE_USER->user['admin'];
 
-               if ($GLOBALS['BE_USER']->uc['recursiveDelete'])    {
+               if ($this->BE_USER->uc['recursiveDelete']) {
                        $this->deleteTree = 1;
                }
 
@@ -616,7 +617,7 @@ class t3lib_TCEmain {
         *
         * @return      void
         */
-       function process_datamap() {
+       public function process_datamap() {
                global $TCA, $TYPO3_CONF_VARS;
                        // Keep versionized(!) relations here locally:
                $registerDBList = array();
@@ -692,7 +693,9 @@ class t3lib_TCEmain {
                                                                if (strstr($pid_value,'NEW'))   {       // If a NEW... id
                                                                        if (substr($pid_value,0,1)=='-') {$negFlag=-1;$pid_value=substr($pid_value,1);} else {$negFlag=1;}
                                                                        if (isset($this->substNEWwithIDs[$pid_value]))  {       // Trying to find the correct numerical value as it should be mapped by earlier processing of another new record.
-                                                                               $old_pid_value = $pid_value;
+                                                                               if ($negFlag === 1) {
+                                                                                       $old_pid_value = $this->substNEWwithIDs[$pid_value];
+                                                                               }
                                                                                $pid_value=intval($negFlag*$this->substNEWwithIDs[$pid_value]);
                                                                        } else {$OK = 0;}       // If not found in the substArray we must stop the process...
                                                                } elseif ($pid_value>=0 && $this->BE_USER->workspace!==0 && $TCA[$table]['ctrl']['versioning_followPages'])     {       // PID points to page, the workspace is an offline space and the table follows page during versioning: This means we must check if the PID page has a version in the workspace with swapmode set to 0 (zero = page+content) and if so, change the pid to the uid of that version.
@@ -757,7 +760,7 @@ class t3lib_TCEmain {
                                                        $status = 'new';                                                // Yes new record, change $record_status to 'insert'
                                                } else {        // Nope... $id is a number
                                                        $fieldArray = array();
-                                                       $recordAccess = $this->checkRecordUpdateAccess($table,$id);
+                                                       $recordAccess = $this->checkRecordUpdateAccess($table, $id, $incomingFieldArray, $hookObjectsArr);
                                                        if (!$recordAccess)             {
                                                                $propArr = $this->getRecordProperties($table,$id);
                                                                $this->log($table,$id,2,0,1,"Attempt to modify record '%s' (%s) without permission. Or non-existing page.",2,array($propArr['header'],$table.':'.$id),$propArr['event_pid']);
@@ -911,7 +914,13 @@ class t3lib_TCEmain        {
                                                                                        }
                                                                                        $phShadowId = $this->insertDB($table,$id,$fieldArray,TRUE,0,TRUE);      // When inserted, $this->substNEWwithIDs[$id] will be changed to the uid of THIS version and so the interface will pick it up just nice!
                                                                                        if ($phShadowId)        {
-                                                                                               $this->placeholderShadowing($table,$phShadowId);
+                                                                                                       // Processes fields of the placeholder record:
+                                                                                               $this->triggerRemapAction(
+                                                                                                       $table,
+                                                                                                       $id,
+                                                                                                       array($this, 'placeholderShadowing'),
+                                                                                                       array($table, $phShadowId)
+                                                                                               );
                                                                                                        // Hold auto-versionized ids of placeholders:
                                                                                                $this->autoVersionIdMap[$table][$this->substNEWwithIDs[$id]] = $phShadowId;
                                                                                        }
@@ -1187,7 +1196,7 @@ class t3lib_TCEmain       {
                                        $eFileMarker = $eFile['markerField']&&trim($mixedRec[$eFile['markerField']]) ? trim($mixedRec[$eFile['markerField']]) : '###TYPO3_STATICFILE_EDIT###';
                                        $insertContent = str_replace($eFileMarker,'',$mixedRec[$eFile['contentField']]);        // must replace the marker if present in content!
 
-                                       $SW_fileNewContent = $parseHTML->substituteSubpart($SW_fileContent, $eFileMarker, chr(10).$insertContent.chr(10), 1, 1);
+                                       $SW_fileNewContent = $parseHTML->substituteSubpart($SW_fileContent, $eFileMarker, LF.$insertContent.LF, 1, 1);
                                        t3lib_div::writeFile($eFile['editFile'],$SW_fileNewContent);
 
                                                // Write status:
@@ -1248,7 +1257,7 @@ class t3lib_TCEmain       {
                $recFID = $table.':'.$id.':'.$field;
 
                        // Processing special case of field pages.doktype
-               if ($table=='pages' && $field=='doktype')       {
+               if (($table === 'pages' || $table === 'pages_language_overlay') && $field === 'doktype') {
                                // If the user may not use this specific doktype, we issue a warning
                        if (! ($this->admin || t3lib_div::inList($this->BE_USER->groupData['pagetypes_select'],$value)))        {
                                $propArr = $this->getRecordProperties($table,$id);
@@ -1524,7 +1533,7 @@ class t3lib_TCEmain       {
                                        );
                                break;
                                case 'db':
-                                       $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'group', $table);
+                                       $valueArray = $this->checkValue_group_select_processDBdata($valueArray, $tcaFieldConf, $id, $status, 'group', $table, $field);
                                break;
                        }
                }
@@ -1533,15 +1542,16 @@ class t3lib_TCEmain     {
                                // check, if there is a NEW... id in the value, that should be substituded later
                        if (strpos($value, 'NEW') !== false) {
                                $this->remapStackRecords[$table][$id] = array('remapStackIndex' => count($this->remapStack));
+                               $this->addNewValuesToRemapStackChildIds($valueArray);
                                $this->remapStack[] = array(
                                        'func' => 'checkValue_group_select_processDBdata',
-                                       'args' => array($valueArray,$tcaFieldConf,$id,$status,'select',$table),
+                                       'args' => array($valueArray, $tcaFieldConf, $id, $status, 'select', $table, $field),
                                        'pos' => array('valueArray' => 0, 'tcaFieldConf' => 1, 'id' => 2, 'table' => 5),
                                        'field' => $field
                                );
                                $unsetResult = true;
                        } else {
-                               $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'select', $table);
+                               $valueArray = $this->checkValue_group_select_processDBdata($valueArray, $tcaFieldConf, $id, $status, 'select', $table, $field);
                        }
                }
 
@@ -1637,6 +1647,7 @@ class t3lib_TCEmain       {
                                        } else {
                                                $theFileValues=t3lib_div::trimExplode(',',$curValue,1);
                                        }
+                                       $currentFilesForHistory = implode(',', $theFileValues);
 
                                                // DELETE files: If existing files were found, traverse those and register files for deletion which has been removed:
                                        if (count($theFileValues))      {
@@ -1690,11 +1701,25 @@ class t3lib_TCEmain     {
                                                                                // If we have a unique destination filename, then write the file:
                                                                        if ($theDestFile)       {
                                                                                t3lib_div::upload_copy_move($theFile,$theDestFile);
+
+                                                                                       // Hook for post-processing the upload action
+                                                                               if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processUpload'])) {
+                                                                                       foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processUpload'] as $classRef) {
+                                                                                               $hookObject = t3lib_div::getUserObj($classRef);
+
+                                                                                               if (!($hookObject instanceof t3lib_TCEmain_processUploadHook)) {
+                                                                                                       throw new UnexpectedValueException('$hookObject must implement interface t3lib_TCEmain_processUploadHook', 1279962349);
+                                                                                               }
+
+                                                                                               $hookObject->processUpload_postProcessAction($theDestFile, $this);
+                                                                                       }
+                                                                               }
+
                                                                                $this->copiedFileMap[$theFile] = $theDestFile;
                                                                                clearstatcache();
                                                                                if (!@is_file($theDestFile))    $this->log($table,$id,5,0,1,"Copying file '%s' failed!: The destination path (%s) may be write protected. Please make it write enabled!. (%s)",16,array($theFile, dirname($theDestFile), $recFID),$propArr['event_pid']);
                                                                        } else $this->log($table,$id,5,0,1,"Copying file '%s' failed!: No destination file (%s) possible!. (%s)",11,array($theFile, $theDestFile, $recFID),$propArr['event_pid']);
-                                                               } else $this->log($table,$id,5,0,1,"Fileextension '%s' not allowed. (%s)",12,array($fI['fileext'], $recFID),$propArr['event_pid']);
+                                                               } else $this->log($table,$id,5,0,1,"File extension '%s' not allowed. (%s)",12,array($fI['fileext'], $recFID),$propArr['event_pid']);
                                                        } else $this->log($table,$id,5,0,1,"Filesize (%s) of file '%s' exceeds limit (%s). (%s)",13,array(t3lib_div::formatSize($fileSize),$theFile,t3lib_div::formatSize($maxSize*1024),$recFID),$propArr['event_pid']);
                                                } else $this->log($table,$id,5,0,1,'The destination (%s) or the source file (%s) does not exist. (%s)',14,array($dest, $theFile, $recFID),$propArr['event_pid']);
 
@@ -1721,6 +1746,15 @@ class t3lib_TCEmain      {
                                }
                                if ($status=='update')  {
                                        $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,0);
+                                       $newFiles = implode(',', $dbAnalysis->getValueArray());
+                                       list(,,$recFieldName) = explode(':', $recFID);
+                                       if ($currentFilesForHistory != $newFiles) {
+                                               $this->mmHistoryRecords[$currentTable . ':' . $id]['oldRecord'][$recFieldName] = $currentFilesForHistory;
+                                               $this->mmHistoryRecords[$currentTable . ':' . $id]['newRecord'][$recFieldName] = $newFiles;
+                                       } else {
+                                               $this->mmHistoryRecords[$currentTable . ':' . $id]['oldRecord'][$currentField] = '';
+                                               $this->mmHistoryRecords[$currentTable . ':' . $id]['newRecord'][$currentField] = '';
+                                       }
                                } else {
                                        $this->dbAnalysisStore[] = array($dbAnalysis, $tcaFieldConf['MM'], $id, 0);     // This will be traversed later to execute the actions
                                }
@@ -1769,7 +1803,7 @@ class t3lib_TCEmain       {
                                                                                        clearstatcache();
                                                                                        if (!@is_file($theDestFile)) $this->log($table, $id, 5, 0, 1, "Copying file '%s' failed!: The destination path (%s) may be write protected. Please make it write enabled!. (%s)", 16, array($theFile, dirname($theDestFile), $recFID), $propArr['event_pid']);
                                                                                } else $this->log($table, $id, 5, 0, 1, "Copying file '%s' failed!: No destination file (%s) possible!. (%s)", 11, array($theFile, $theDestFile, $recFID), $propArr['event_pid']);
-                                                                       } else $this->log($table, $id, 5, 0, 1, "Fileextension '%s' not allowed. (%s)", 12, array($fI['fileext'], $recFID), $propArr['event_pid']);
+                                                                       } else $this->log($table, $id, 5, 0, 1, "File extension '%s' not allowed. (%s)", 12, array($fI['fileext'], $recFID), $propArr['event_pid']);
                                                                } else $this->log($table, $id, 5, 0, 1, "Filesize (%s) of file '%s' exceeds limit (%s). (%s)", 13, array(t3lib_div::formatSize($fileSize), $theFile,t3lib_div::formatSize($maxSize * 1024),$recFID), $propArr['event_pid']);
 
                                                                        // If the destination file was created, we will set the new filename in the value array, otherwise unset the entry in the value array!
@@ -1933,6 +1967,7 @@ class t3lib_TCEmain       {
                        // We need to decide whether we use the stack or can save the relation directly.
                if(strpos($value, 'NEW') !== false || !t3lib_div::testInt($id)) {
                        $this->remapStackRecords[$table][$id] = array('remapStackIndex' => count($this->remapStack));
+                       $this->addNewValuesToRemapStackChildIds($valueArray);
                        $this->remapStack[] = array(
                                'func' => 'checkValue_inline_processDBdata',
                                'args' => array($valueArray, $tcaFieldConf, $id, $status, $table, $field),
@@ -2175,20 +2210,35 @@ class t3lib_TCEmain     {
         * @param       string          Status string ('update' or 'new')
         * @param       string          The type, either 'select', 'group' or 'inline'
         * @param       string          Table name, needs to be passed to t3lib_loadDBGroup
+        * @param       string          field name, needs to be set for writing to sys_history
         * @return      array           Modified value array
         */
-       function checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,$type,$currentTable)       {
+       function checkValue_group_select_processDBdata($valueArray, $tcaFieldConf, $id, $status, $type, $currentTable, $currentField) {
                $tables = $type=='group'?$tcaFieldConf['allowed']:$tcaFieldConf['foreign_table'].','.$tcaFieldConf['neg_foreign_table'];
                $prep = $type=='group'?$tcaFieldConf['prepend_tname']:$tcaFieldConf['neg_foreign_table'];
+               $newRelations = implode(',', $valueArray);
 
                $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
                /* @var $dbAnalysis t3lib_loadDBGroup */
                $dbAnalysis->registerNonTableValues=$tcaFieldConf['allowNonIdValues'] ? 1 : 0;
-               $dbAnalysis->start(implode(',',$valueArray),$tables, '', 0, $currentTable, $tcaFieldConf);
+               $dbAnalysis->start($newRelations, $tables, '', 0, $currentTable, $tcaFieldConf);
 
                if ($tcaFieldConf['MM'])        {
                        if ($status=='update')  {
+                               $oldRelations_dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
+                               /* @var $oldRelations_dbAnalysis t3lib_loadDBGroup */
+                               $oldRelations_dbAnalysis->registerNonTableValues=$tcaFieldConf['allowNonIdValues'] ? 1 : 0;
+                                       // db analysis with $id will initialize with the existing relations
+                               $oldRelations_dbAnalysis->start('', $tables, $tcaFieldConf['MM'], $id, $currentTable, $tcaFieldConf);
+                               $oldRelations = implode(',', $oldRelations_dbAnalysis->getValueArray());
                                $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,$prep);
+                               if ($oldRelations != $newRelations) {
+                                       $this->mmHistoryRecords[$currentTable . ':' . $id]['oldRecord'][$currentField] = $oldRelations;
+                                       $this->mmHistoryRecords[$currentTable . ':' . $id]['newRecord'][$currentField] = $newRelations;
+                               } else {
+                                       $this->mmHistoryRecords[$currentTable . ':' . $id]['oldRecord'][$currentField] = '';
+                                       $this->mmHistoryRecords[$currentTable . ':' . $id]['newRecord'][$currentField] = '';
+                               }
                        } else {
                                $this->dbAnalysisStore[] = array($dbAnalysis,$tcaFieldConf['MM'],$id,$prep,$currentTable);      // This will be traversed later to execute the actions
                        }
@@ -2471,6 +2521,11 @@ class t3lib_TCEmain      {
                                // update record in intermediate table (sorting & pointer uid to parent record)
                        $dbAnalysis->writeForeignField($tcaFieldConf, $id, 0, $skipSorting);
                        $newValue = ($keepTranslation ? 0 : $dbAnalysis->countItems(false));
+                       // IRRE with MM relation:
+               } else if ($this->getInlineFieldType($tcaFieldConf) == 'mm') {
+                               // in order to fully support all the MM stuff, directly call checkValue_group_select_processDBdata instead of repeating the needed code here
+                       $valueArray = $this->checkValue_group_select_processDBdata($valueArray, $tcaFieldConf, $id, $status, 'select', $table, $field);
+                       $newValue = ($keepTranslation ? 0 : $valueArray[0]);
                        // IRRE with comma separated values:
                } else {
                        $valueArray = $dbAnalysis->getValueArray();
@@ -2513,7 +2568,7 @@ class t3lib_TCEmain       {
         *
         * @return      void
         */
-       function process_cmdmap() {
+       public function process_cmdmap() {
                global $TCA, $TYPO3_CONF_VARS;
 
                        // Editing frozen:
@@ -2526,12 +2581,13 @@ class t3lib_TCEmain     {
                $hookObjectsArr = array();
                if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'])) {
                        foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'] as $classRef) {
-                               $hookObjectsArr[] = t3lib_div::getUserObj($classRef);
+                               $hookObj = t3lib_div::getUserObj($classRef);
+                               if (method_exists($hookObj, 'processCmdmap_beforeStart')) {
+                                       $hookObj->processCmdmap_beforeStart($this);
+                               }
+                               $hookObjectsArr[] = $hookObj;
                        }
                }
-#debug($this->cmdmap);
-
-               $this->accumulateForNotifEmail = array();       // Reset notification array
 
                        // Traverse command map:
                foreach (array_keys($this->cmdmap) as $table) {
@@ -2541,7 +2597,7 @@ class t3lib_TCEmain       {
                        if (!$modifyAccessList) {
                                $id = 0;
                                $this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table));
-                       }       // FIXME: $id not set here (Comment added by Sebastian Kurfuerst)
+                       }       // FIXME: $id not set here (Comment added by Sebastian Kurfürst)
 
                                // Check basic permissions and circumstances:
                        if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->cmdmap[$table]) && $modifyAccessList)       {
@@ -2555,135 +2611,78 @@ class t3lib_TCEmain    {
                                                $command = key($incomingCmdArray);
                                                $value = current($incomingCmdArray);
 
-                                               foreach($hookObjectsArr as $hookObj) {
+                                               foreach ($hookObjectsArr as $hookObj) {
                                                        if (method_exists($hookObj, 'processCmdmap_preProcess')) {
                                                                $hookObj->processCmdmap_preProcess($command, $table, $id, $value, $this);
                                                        }
                                                }
 
                                                        // Init copyMapping array:
-                                               $this->copyMappingArray = Array();              // Must clear this array before call from here to those functions: Contains mapping information between new and old id numbers.
-
-                                                       // Branch, based on command
-                                               switch ($command)       {
-                                                       case 'move':
-                                                               $this->moveRecord($table,$id,$value);
-                                                       break;
-                                                       case 'copy':
-                                                               if ($table === 'pages') {
-                                                                       $this->copyPages($id,$value);
-                                                               } else {
-                                                                       $this->copyRecord($table,$id,$value,1);
-                                                               }
-                                                       break;
-                                                       case 'localize':
-                                                               $this->localize($table,$id,$value);
-                                                       break;
-                                                       case 'inlineLocalizeSynchronize':
-                                                               $this->inlineLocalizeSynchronize($table, $id, $value);
-                                                       break;
-                                                       case 'version':
-                                                               switch ((string)$value['action'])       {
-                                                                       case 'new':
-                                                                               $versionizeTree = t3lib_div::intInRange(!isset($value['treeLevels'])?-1:$value['treeLevels'],-1,100);
-                                                                               if ($table == 'pages' && $versionizeTree>=0)    {
-                                                                                       $this->versionizePages($id,$value['label'],$versionizeTree);
-                                                                               } else {
-                                                                                       $this->versionizeRecord($table,$id,$value['label']);
-                                                                               }
-                                                                       break;
-                                                                       case 'swap':
-                                                                               $swapMode = $GLOBALS['BE_USER']->getTSConfigVal('options.workspaces.swapMode');
-                                                                               $elementList = array();
-                                                                               if ($swapMode == 'any' || ($swapMode == 'page' && $table == 'pages')) {
-                                                                                       // check if we are allowed to do synchronios publish. We must have a single element in the cmdmap to be allowed
-                                                                                       if (count($this->cmdmap) == 1 && count($this->cmdmap[$table]) == 1) {
-                                                                                               $elementList = $this->findPageElementsForVersionSwap($table, $id, $value['swapWith']);
-                                                                                       }
-                                                                               }
-                                                                               if (count($elementList) == 0) {
-                                                                                       $elementList[$table][] = array($id, $value['swapWith']);
-                                                                               }
-                                                                               foreach ($elementList as $tbl => $idList) {
-                                                                                       foreach ($idList as $idKey => $idSet) {
-                                                                                               $this->version_swap($tbl,$idSet[0],$idSet[1],$value['swapIntoWS']);
-                                                                                       }
-                                                                               }
-                                                                       break;
-                                                                       case 'clearWSID':
-                                                                               $this->version_clearWSID($table,$id);
-                                                                       break;
-                                                                       case 'flush':
-                                                                               $this->version_clearWSID($table,$id,TRUE);
-                                                                       break;
-                                                                       case 'setStage':
-                                                                               $elementList = array();
-                                                                               $idList = $elementList[$table] = t3lib_div::trimExplode(',',$id,1);
-                                                                               $setStageMode = $GLOBALS['BE_USER']->getTSConfigVal('options.workspaces.changeStageMode');
-                                                                               if ($setStageMode == 'any' || $setStageMode == 'page') {
-                                                                                       if (count($idList) == 1) {
-                                                                                               $rec = t3lib_BEfunc::getRecord($table, $idList[0], 't3ver_wsid');
-                                                                                               $workspaceId = $rec['t3ver_wsid'];
-                                                                                       }
-                                                                                       else {
-                                                                                               $workspaceId = $GLOBALS['BE_USER']->workspace;
-                                                                                       }
-                                                                                       if ($table !== 'pages') {
-                                                                                               if ($setStageMode == 'any') {
-                                                                                                       // (1) Find page to change stage and (2) find other elements from the same ws to change stage
-                                                                                                       $pageIdList = array();
-                                                                                                       $this->findPageIdsForVersionStateChange($table, $idList, $workspaceId, $pageIdList, $elementList);
-                                                                                                       $this->findPageElementsForVersionStageChange($pageIdList, $workspaceId, $elementList);
-                                                                                               }
-                                                                                       }
-                                                                                       else {
-                                                                                               // Find all elements from the same ws to change stage
-                                                                                               $this->findRealPageIds($idList);
-                                                                                               $this->findPageElementsForVersionStageChange($idList, $workspaceId, $elementList);
-                                                                                       }
-                                                                               }
+                                                       // Must clear this array before call from here to those functions:
+                                                       // Contains mapping information between new and old id numbers.
+                                               $this->copyMappingArray = array();
+
+                                                       // process the command
+                                               $commandIsProcessed = FALSE;
+                                               foreach ($hookObjectsArr as $hookObj) {
+                                                       if (method_exists($hookObj, 'processCmdmap')) {
+                                                               $hookObj->processCmdmap($command, $table, $id, $value, $commandIsProcessed, $this);
+                                                       }
+                                               }
 
-                                                                               foreach ($elementList as $tbl => $elementIdList) {
-                                                                                       foreach($elementIdList as $elementId)   {
-                                                                                               $this->version_setStage($tbl,$elementId,$value['stageId'],$value['comment']?$value['comment']:$this->generalComment, TRUE);
-                                                                                       }
-                                                                               }
-                                                                       break;
-                                                               }
-                                                       break;
-                                                       case 'delete':
-                                                               $this->deleteAction($table, $id);
-                                                       break;
-                                                       case 'undelete':
-                                                               $this->undeleteRecord($table, $id);
-                                                       break;
+                                               // only execute default commands if a hook hasn't been processed the command already
+                                               if (!$commandIsProcessed) {
+                                                               // Branch, based on command
+                                                       switch ($command) {
+                                                               case 'move':
+                                                                       $this->moveRecord($table,$id,$value);
+                                                               break;
+                                                               case 'copy':
+                                                                       if ($table === 'pages') {
+                                                                               $this->copyPages($id,$value);
+                                                                       } else {
+                                                                               $this->copyRecord($table,$id,$value,1);
+                                                                       }
+                                                               break;
+                                                               case 'localize':
+                                                                       $this->localize($table,$id,$value);
+                                                               break;
+                                                               case 'inlineLocalizeSynchronize':
+                                                                       $this->inlineLocalizeSynchronize($table, $id, $value);
+                                                               break;
+                                                               case 'delete':
+                                                                       $this->deleteAction($table, $id);
+                                                               break;
+                                                               case 'undelete':
+                                                                       $this->undeleteRecord($table, $id);
+                                                               break;
+                                                       }
                                                }
 
-                                               foreach($hookObjectsArr as $hookObj) {
+                                               foreach ($hookObjectsArr as $hookObj) {
                                                        if (method_exists($hookObj, 'processCmdmap_postProcess')) {
                                                                $hookObj->processCmdmap_postProcess($command, $table, $id, $value, $this);
                                                        }
                                                }
 
                                                        // Merging the copy-array info together for remapping purposes.
-                                               $this->copyMappingArray_merged= t3lib_div::array_merge_recursive_overrule($this->copyMappingArray_merged,$this->copyMappingArray);
+                                               $this->copyMappingArray_merged = t3lib_div::array_merge_recursive_overrule($this->copyMappingArray_merged, $this->copyMappingArray);
                                        }
                                }
                        }
                }
 
-                       // Finally, before exit, check if there are ID references to remap. This might be the case if versioning or copying has taken place!
+                       // Finally, before exit, check if there are ID references to remap.
+                       // This might be the case if versioning or copying has taken place!
                $this->remapListedDBRecords();
+               $this->processRemapStack();
 
-
-                       // Empty accumulation array:
-               foreach($this->accumulateForNotifEmail as $notifItem)   {
-                       $this->notifyStageChange($notifItem['shared'][0],$notifItem['shared'][1],implode(', ',$notifItem['elements']),0,$notifItem['shared'][2]);
+               foreach ($hookObjectsArr as $hookObj) {
+                       if (method_exists($hookObj, 'processCmdmap_afterFinish')) {
+                               $hookObj->processCmdmap_afterFinish($this);
+                       }
                }
 
-               $this->accumulateForNotifEmail = array();       // Reset notification array
-
-#              die("REMOVE ME");
        }
 
 
@@ -2733,99 +2732,115 @@ class t3lib_TCEmain    {
                                // Now, the $uid is the actual record we will copy while $origUid is the record we asked to get copied - but that could be a live version.
 */
                        if ($this->doesRecordExist($table,$uid,'show')) {               // This checks if the record can be selected which is all that a copy action requires.
-                               $data = Array();
-
-                               $nonFields = array_unique(t3lib_div::trimExplode(',','uid,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,t3ver_oid,t3ver_wsid,t3ver_id,t3ver_label,t3ver_state,t3ver_swapmode,t3ver_count,t3ver_stage,t3ver_tstamp,'.$excludeFields,1));
-
-                               // $row = $this->recordInfo($table,$uid,'*');
-                               $row = t3lib_BEfunc::getRecordWSOL($table,$uid);        // So it copies (and localized) content from workspace...
-                               if (is_array($row))     {
-
-                                               // Initializing:
-                                       $theNewID = uniqid('NEW');
-                                       $enableField = isset($TCA[$table]['ctrl']['enablecolumns']) ? $TCA[$table]['ctrl']['enablecolumns']['disabled'] : '';
-                                       $headerField = $TCA[$table]['ctrl']['label'];
-
-                                               // Getting default data:
-                                       $defaultData = $this->newFieldArray($table);
-
-                                               // Getting "copy-after" fields if applicable:
-                                       $copyAfterFields = $destPid<0 ? $this->fixCopyAfterDuplFields($table,$uid,abs($destPid),0) : array();
-
-                                               // Page TSconfig related:
-                                       $tscPID = t3lib_BEfunc::getTSconfig_pidValue($table,$uid,$destPid);     // NOT using t3lib_BEfunc::getTSCpid() because we need the real pid - not the ID of a page, if the input is a page...
-                                       $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
-                                       $tE = $this->getTableEntries($table,$TSConfig);
-
-                                               // Traverse ALL fields of the selected record:
-                                       foreach($row as $field => $value)       {
-                                               if (!in_array($field,$nonFields))       {
-
-                                                               // Get TCA configuration for the field:
-                                                       $conf = $TCA[$table]['columns'][$field]['config'];
+                               $fullLanguageCheckNeeded = ($table != 'pages');
+                               if (($language > 0 && $this->BE_USER->checkLanguageAccess($language) ) ||
+                                               $this->BE_USER->recordEditAccessInternals(
+                                                       $table, $uid, false, false, $fullLanguageCheckNeeded
+                                               )
+                                       ) { //Used to check language and general editing rights
+                                       $data = Array();
+
+                                       $nonFields = array_unique(t3lib_div::trimExplode(',','uid,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,t3ver_oid,t3ver_wsid,t3ver_id,t3ver_label,t3ver_state,t3ver_swapmode,t3ver_count,t3ver_stage,t3ver_tstamp,'.$excludeFields,1));
+
+                                       // $row = $this->recordInfo($table,$uid,'*');
+                                       $row = t3lib_BEfunc::getRecordWSOL($table,$uid);        // So it copies (and localized) content from workspace...
+                                       if (is_array($row))     {
 
-                                                               // Preparation/Processing of the value:
-                                                       if ($field=='pid')      {       // "pid" is hardcoded of course:
-                                                               $value = $destPid;
-                                                       } elseif (isset($overrideValues[$field]))       {       // Override value...
-                                                               $value = $overrideValues[$field];
-                                                       } elseif (isset($copyAfterFields[$field]))      {       // Copy-after value if available:
-                                                               $value = $copyAfterFields[$field];
-                                                       } elseif ($TCA[$table]['ctrl']['setToDefaultOnCopy'] && t3lib_div::inList($TCA[$table]['ctrl']['setToDefaultOnCopy'],$field))   {       // Revert to default for some fields:
-                                                               $value = $defaultData[$field];
-                                                       } else {
-                                                                       // Hide at copy may override:
-                                                               if ($first && $field==$enableField && $TCA[$table]['ctrl']['hideAtCopy'] && !$this->neverHideAtCopy && !$tE['disableHideAtCopy'])       {
-                                                                       $value=1;
-                                                               }
-                                                                       // Prepend label on copy:
-                                                               if ($first && $field==$headerField && $TCA[$table]['ctrl']['prependAtCopy'] && !$tE['disablePrependAtCopy'])    {
-                                                                       $value = $this->getCopyHeader($table,$this->resolvePid($table,$destPid),$field,$this->clearPrefixFromValue($table,$value),0);
+                                                       // Initializing:
+                                               $theNewID = uniqid('NEW');
+                                               $enableField = isset($TCA[$table]['ctrl']['enablecolumns']) ? $TCA[$table]['ctrl']['enablecolumns']['disabled'] : '';
+                                               $headerField = $TCA[$table]['ctrl']['label'];
+
+                                                       // Getting default data:
+                                               $defaultData = $this->newFieldArray($table);
+
+                                                       // Getting "copy-after" fields if applicable:
+                                               $copyAfterFields = $destPid<0 ? $this->fixCopyAfterDuplFields($table,$uid,abs($destPid),0) : array();
+
+                                                       // Page TSconfig related:
+                                               $tscPID = t3lib_BEfunc::getTSconfig_pidValue($table,$uid,$destPid);     // NOT using t3lib_BEfunc::getTSCpid() because we need the real pid - not the ID of a page, if the input is a page...
+                                               $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
+                                               $tE = $this->getTableEntries($table,$TSConfig);
+
+                                                       // Traverse ALL fields of the selected record:
+                                               foreach($row as $field => $value)       {
+                                                       if (!in_array($field,$nonFields))       {
+
+                                                                       // Get TCA configuration for the field:
+                                                               $conf = $TCA[$table]['columns'][$field]['config'];
+
+                                                                       // Preparation/Processing of the value:
+                                                               if ($field=='pid')      {       // "pid" is hardcoded of course:
+                                                                       $value = $destPid;
+                                                               } elseif (isset($overrideValues[$field]))       {       // Override value...
+                                                                       $value = $overrideValues[$field];
+                                                               } elseif (isset($copyAfterFields[$field]))      {       // Copy-after value if available:
+                                                                       $value = $copyAfterFields[$field];
+                                                               } elseif ($TCA[$table]['ctrl']['setToDefaultOnCopy'] && t3lib_div::inList($TCA[$table]['ctrl']['setToDefaultOnCopy'],$field))   {       // Revert to default for some fields:
+                                                                       $value = $defaultData[$field];
+                                                               } else {
+                                                                               // Hide at copy may override:
+                                                                       if ($first && $field == $enableField && $TCA[$table]['ctrl']['hideAtCopy'] && !$this->neverHideAtCopy && !$tE['disableHideAtCopy']) {
+                                                                               $value = 1;
+                                                                       }
+                                                                               // Prepend label on copy:
+                                                                       if ($first && $field == $headerField && $TCA[$table]['ctrl']['prependAtCopy'] && !$tE['disablePrependAtCopy']) {
+                                                                               $value = $this->getCopyHeader($table,$this->resolvePid($table,$destPid),$field,$this->clearPrefixFromValue($table,$value),0);
+                                                                       }
+                                                                               // Processing based on the TCA config field type (files, references, flexforms...)
+                                                                       $value = $this->copyRecord_procBasedOnFieldType($table, $uid, $field, $value, $row, $conf, $tscPID, $language);
                                                                }
-                                                                       // Processing based on the TCA config field type (files, references, flexforms...)
-                                                               $value = $this->copyRecord_procBasedOnFieldType($table, $uid, $field, $value, $row, $conf, $tscPID, $language);
-                                                       }
 
-                                                               // Add value to array.
-                                                       $data[$table][$theNewID][$field] = $value;
+                                                                       // Add value to array.
+                                                               $data[$table][$theNewID][$field] = $value;
+                                                       }
+                                               }
+                                                       // Overriding values:
+                                               if ($TCA[$table]['ctrl']['editlock']) {
+                                                       $data[$table][$theNewID][$TCA[$table]['ctrl']['editlock']] = 0;
                                                }
-                                       }
-
-                                               // Overriding values:
-                                       if ($TCA[$table]['ctrl']['editlock'])   {
-                                               $data[$table][$theNewID][$TCA[$table]['ctrl']['editlock']] = 0;
-                                       }
-
-                                               // Setting original UID:
-                                       if ($TCA[$table]['ctrl']['origUid'])    {
-                                               $data[$table][$theNewID][$TCA[$table]['ctrl']['origUid']] = $uid;
-                                       }
 
-                                               // Do the copy by simply submitting the array through TCEmain:
-                                       $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain');
-                                       /* @var $copyTCE t3lib_TCEmain  */
-                                       $copyTCE->stripslashes_values = 0;
-                                       $copyTCE->copyTree = $this->copyTree;
-                                       $copyTCE->cachedTSconfig = $this->cachedTSconfig;       // Copy forth the cached TSconfig
-                                       $copyTCE->dontProcessTransformations=1;         // Transformations should NOT be carried out during copy
+                                                       // Setting original UID:
+                                               if ($TCA[$table]['ctrl']['origUid']) {
+                                                       $data[$table][$theNewID][$TCA[$table]['ctrl']['origUid']] = $uid;
+                                               }
 
-                                       $copyTCE->start($data,'',$this->BE_USER);
-                                       $copyTCE->process_datamap();
+                                                       // Do the copy by simply submitting the array through TCEmain:
+                                               $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain');
+                                               /* @var $copyTCE t3lib_TCEmain  */
+                                               $copyTCE->stripslashes_values = 0;
+                                               $copyTCE->copyTree = $this->copyTree;
+                                               $copyTCE->cachedTSconfig = $this->cachedTSconfig;       // Copy forth the cached TSconfig
+                                               $copyTCE->dontProcessTransformations=1;         // Transformations should NOT be carried out during copy
+
+                                               $copyTCE->start($data,'',$this->BE_USER);
+                                               $copyTCE->process_datamap();
+
+                                                       // Getting the new UID:
+                                               $theNewSQLID = $copyTCE->substNEWwithIDs[$theNewID];
+                                               if ($theNewSQLID) {
+                                                       $this->copyRecord_fixRTEmagicImages($table, t3lib_BEfunc::wsMapId($table, $theNewSQLID));
+                                                       $this->copyMappingArray[$table][$origUid] = $theNewSQLID;
+                                                               // Keep automatically versionized record information:
+                                                       if (isset($copyTCE->autoVersionIdMap[$table][$theNewSQLID])) {
+                                                               $this->autoVersionIdMap[$table][$theNewSQLID] = $copyTCE->autoVersionIdMap[$table][$theNewSQLID];
+                                                       }
+                                               }
 
-                                               // Getting the new UID:
-                                       $theNewSQLID = $copyTCE->substNEWwithIDs[$theNewID];
-                                       if ($theNewSQLID)       {
-                                               $this->copyRecord_fixRTEmagicImages($table,t3lib_BEfunc::wsMapId($table,$theNewSQLID));
-                                               $this->copyMappingArray[$table][$origUid] = $theNewSQLID;
-                                       }
+                                                       // Copy back the cached TSconfig
+                                               $this->cachedTSconfig = $copyTCE->cachedTSconfig;
+                                               $this->errorLog = array_merge($this->errorLog, $copyTCE->errorLog);
+                                               unset($copyTCE);
 
-                                               // Copy back the cached TSconfig
-                                       $this->cachedTSconfig = $copyTCE->cachedTSconfig;
-                                       $this->errorLog = array_merge($this->errorLog,$copyTCE->errorLog);
-                                       unset($copyTCE);
+                                               if($language == 0) {
+                                                               //repointing the new translation records to the parent record we just created
+                                                       $overrideValues[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] = $theNewSQLID;
+                                                       $this->copyL10nOverlayRecords($table, $uid, $destPid<0 ? $tscPID : $destPid , $first, $overrideValues, $excludeFields);
+                                               }
 
-                                       return $theNewSQLID;
-                               } else $this->log($table,$uid,3,0,1,'Attempt to copy record that did not exist!');
+                                               return $theNewSQLID;
+                                       } else $this->log($table,$uid,3,0,1,'Attempt to copy record that did not exist!');
+                               }  else $this->log($table,$uid,3,0,1,'Attempt to copy record without having permissions to do so. ['.$this->BE_USER->errorMsg.'].');
                        } else $this->log($table,$uid,3,0,1,'Attempt to copy record without permission');
                }
        }
@@ -2984,36 +2999,6 @@ class t3lib_TCEmain      {
        }
 
        /**
-        * Copies all records from tables in $copyTablesArray from page with $old_pid to page with $new_pid
-        * Uses raw-copy for the operation (meant for versioning!)
-        *
-        * @param       integer         Current page id.
-        * @param       integer         New page id
-        * @param       array           Array of tables from which to copy
-        * @return      void
-        * @see versionizePages()
-        */
-       function rawCopyPageContent($old_pid,$new_pid,$copyTablesArray) {
-               global $TCA;
-
-               if ($new_pid)   {
-                       foreach($copyTablesArray as $table)     {
-                               if ($table && is_array($TCA[$table]) && $table!='pages')        {       // all records under the page is copied.
-                                       $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($old_pid).$this->deleteClause($table));
-                                       while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))     {
-                                                       // Check, if this record has already been copied by a parent record as relation:
-                                               if (!$this->copyMappingArray[$table][$row['uid']]) {
-                                                               // Copying each of the underlying records (method RAW)
-                                                       $this->copyRecord_raw($table,$row['uid'],$new_pid);
-                                               }
-                                       }
-                                       $GLOBALS['TYPO3_DB']->sql_free_result($mres);
-                               }
-                       }
-               }
-       }
-
-       /**
         * Inserts a record in the database, passing TCA configuration values through checkValue() but otherwise does NOTHING and checks nothing regarding permissions.
         * Passes the "version" parameter to insertDB() so the copy will look like a new version in the log - should probably be changed or modified a bit for more broad usage...
         *
@@ -3136,8 +3121,8 @@ class t3lib_TCEmain       {
 
                                        // Walk through the items, copy them and remember the new id:
                                foreach ($dbAnalysis->itemArray as $k => $v) {
-                                               // If language is set, this isn't a copy action but a localization of our parent/ancestor:
-                                       if ($language>0) {
+                                               // If language is set and differs from original record, this isn't a copy action but a localization of our parent/ancestor:
+                                       if ($language > 0 && t3lib_BEfunc::isTableLocalizable($table) && $language != $row[$TCA[$table]['ctrl']['languageField']]) {
                                                        // If children should be localized when the parent gets localized the first time, just do it:
                                                if ($localizationMode!=false && isset($conf['behaviour']['localizeChildrenAtParentLocalization']) && $conf['behaviour']['localizeChildrenAtParentLocalization']) {
                                                        $newId = $this->localize($v['table'], $v['id'], $language);
@@ -3146,8 +3131,17 @@ class t3lib_TCEmain      {
                                        } else {
                                                if (!t3lib_div::testInt($realDestPid)) {
                                                        $newId = $this->copyRecord($v['table'], $v['id'], -$v['id']);
-                                               } elseif ($realDestPid == -1) {
-                                                       $newId = $this->versionizeRecord($v['table'], $v['id'], 'Auto-created for WS #'.$this->BE_USER->workspace);
+                                               } elseif ($realDestPid == -1 && t3lib_BEfunc::isTableWorkspaceEnabled($v['table'])) {
+                                                       $workspaceVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord(
+                                                               $this->BE_USER->workspace, $v['table'], $v['id'], 'uid'
+                                                       );
+                                                               // If workspace version does not exist, create a new one:
+                                                       if ($workspaceVersion === FALSE) {
+                                                               $newId = $this->versionizeRecord($v['table'], $v['id'], 'Auto-created for WS #' . $this->BE_USER->workspace);
+                                                               // If workspace version already exists, use it:
+                                                       } else {
+                                                               $newId = $workspaceVersion['uid'];
+                                                       }
                                                } else {
                                                        $newId = $this->copyRecord_raw($v['table'], $v['id'], $realDestPid);
                                                }
@@ -3368,7 +3362,32 @@ class t3lib_TCEmain      {
 
 
 
+       /**
+        * Find l10n-overlay records and perform the requested move action for these records.
+        *
+        * @param       string          $table: Record Table
+        * @param       string          $uid: Record UID
+        * @param       string          $destPid: Position to move to
+        * @return      void
+        */
+       function copyL10nOverlayRecords($table, $uid, $destPid, $first=0, $overrideValues=array(), $excludeFields='') {
+                       //there's no need to perform this for page-records
+               if (!t3lib_BEfunc::isTableLocalizable($table) || !empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) || !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) {
+                       return;
+               }
 
+               $where = '';
+               if (isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
+                       $where = ' AND t3ver_oid=0';
+               }
+
+               $l10nRecords = t3lib_BEfunc::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], $uid, $where);
+               if (is_array($l10nRecords)) {
+                       foreach ($l10nRecords as $record) {
+                               $this->copyRecord($table, $record['uid'], $destPid, $first, $overrideValues, $excludeFields, $record[$GLOBALS['TCA'][$table]['ctrl']['languageField']]);
+                       }
+               }
+       }
 
 
 
@@ -3393,11 +3412,15 @@ class t3lib_TCEmain     {
         * @return      void
         */
        function moveRecord($table,$uid,$destPid)       {
-               global $TCA;
+               global $TCA, $TYPO3_CONF_VARS;
 
                if ($TCA[$table])       {
 
-                               // In case the record to be moved turns out to be an offline version, we have to find the live version and work on that one (this case happens for pages with "branch" versioning type)
+                               // In case the record to be moved turns out to be an offline version,
+                               // we have to find the live version and work on that one (this case
+                               // happens for pages with "branch" versioning type)
+                               // note: as "branch" versioning is deprecated since TYPO3 4.2, this
+                               // functionality will be removed in TYPO3 4.7 (note by benni: a hook could replace this)
                        if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid'))      {
                                $uid = $lookForLiveVersion['uid'];
                        }
@@ -3425,59 +3448,31 @@ class t3lib_TCEmain     {
                        }
 
                                // Checking if there is anything else disallowing moving the record by checking if editing is allowed
-                       $mayEditAccess = $this->BE_USER->recordEditAccessInternals($table,$uid);
+                       $fullLanguageCheckNeeded = ($table != 'pages');
+                       $mayEditAccess = $this->BE_USER->recordEditAccessInternals($table, $uid, false, false, $fullLanguageCheckNeeded);
 
                                // If moving is allowed, begin the processing:
                        if ($mayEditAccess)     {
                                if ($mayMoveAccess)     {
                                        if ($mayInsertAccess)   {
 
-                                               if ($this->BE_USER->workspace!==0)      {       // Draft workspace...:
-                                                               // Get workspace version of the source record, if any:
-                                                       $WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid');
+                                               $recordWasMoved = FALSE;
 
-                                                               // If no version exists and versioningWS is in version 2, a new placeholder is made automatically:
-                                                       if (!$WSversion['uid'] && (int)$TCA[$table]['ctrl']['versioningWS']>=2 && (int)$moveRec['t3ver_state']!=3)      {
-                                                               $this->versionizeRecord($table,$uid,'Placeholder version for moving record');
-                                                               $WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid');       // Will not create new versions in live workspace though...
-                                                       }
-
-                                                               // Check workspace permissions:
-                                                       $workspaceAccessBlocked = array();
-                                                       $recIsNewVersion = (int)$moveRec['t3ver_state']>0;      // Element was in "New/Deleted/Moved" so it can be moved...
-                                                       $destRes = $this->BE_USER->workspaceAllowLiveRecordsInPID($resolvedPid,$table);
-                                                       $canMoveRecord = $recIsNewVersion || (int)$TCA[$table]['ctrl']['versioningWS']>=2;
-
-                                                               // Workspace source check:
-                                                       if (!$recIsNewVersion)  {
-                                                               if ($errorCode = $this->BE_USER->workspaceCannotEditRecord($table, $WSversion['uid'] ? $WSversion['uid'] : $uid))       {
-                                                                       $workspaceAccessBlocked['src1']='Record could not be edited in workspace: '.$errorCode.' ';
-                                                               } else {
-                                                                       if (!$canMoveRecord && $this->BE_USER->workspaceAllowLiveRecordsInPID($moveRec['pid'],$table)<=0)       {
-                                                                               $workspaceAccessBlocked['src2']='Could not remove record from table "'.$table.'" from its page "'.$moveRec['pid'].'" ';
-                                                                       }
+                                                       // move the record via a hook, used e.g. for versioning
+                                               if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'])) {
+                                                       foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'] as $classRef) {
+                                                               $hookObj = t3lib_div::getUserObj($classRef);
+                                                               if (method_exists($hookObj, 'moveRecord')) {
+                                                                       $hookObj->moveRecord($table, $uid, $destPid, $propArr, $moveRec, $resolvedPid, $recordWasMoved, $this);
                                                                }
                                                        }
+                                               }
 
-                                                               // Workspace destination check:
-                                                       if (!($destRes>0 || ($canMoveRecord && !$destRes)))     {       // All records can be inserted if $destRes is greater than zero. Only new versions can be inserted if $destRes is false. NO RECORDS can be inserted if $destRes is negative which indicates a stage not allowed for use. If "versioningWS" is version 2, moving can take place of versions.
-                                                               $workspaceAccessBlocked['dest1']='Could not insert record from table "'.$table.'" in destination PID "'.$resolvedPid.'" ';
-                                                       } elseif ($destRes==1 && $WSversion['uid'])     {
-                                                               $workspaceAccessBlocked['dest2']='Could not insert other versions in destination PID ';
-                                                       }
-
-                                                       if (!count($workspaceAccessBlocked))    {
-                                                               if ($WSversion['uid'] && !$recIsNewVersion && (int)$TCA[$table]['ctrl']['versioningWS']>=2)     { // If the move operation is done on a versioned record, which is NOT new/deletd placeholder and versioningWS is in version 2, then...
-                                                                       $this->moveRecord_wsPlaceholders($table,$uid,$destPid,$WSversion['uid']);
-                                                               } else {
-                                                                       $this->moveRecord_raw($table,$uid,$destPid);
-                                                               }
-                                                       } else {
-                                                               $this->newlog("Move attempt failed due to workspace restrictions: ".implode(' // ',$workspaceAccessBlocked),1);
-                                                       }
-                                               } else {        // Live workspace - move it!
-                                                       $this->moveRecord_raw($table,$uid,$destPid);
+                                                       // move the record if a hook hasn't moved it yet
+                                               if (!$recordWasMoved) {
+                                                       $this->moveRecord_raw($table, $uid, $destPid);
                                                }
+
                                        } else {
                                                $this->log($table,$uid,4,0,1,"Attempt to move record '%s' (%s) without having permissions to insert.",14,array($propArr['header'],$table.':'.$uid),$propArr['event_pid']);
                                        }
@@ -3490,69 +3485,6 @@ class t3lib_TCEmain      {
                }
        }
 
-       /**
-        * Creates a move placeholder for workspaces.
-        * USE ONLY INTERNALLY
-        * Moving placeholder: Can be done because the system sees it as a placeholder for NEW elements like t3ver_state=1
-        * Moving original: Will either create the placeholder if it doesn't exist or move existing placeholder in workspace.
-        *
-        * @param       string          Table name to move
-        * @param       integer         Record uid to move (online record)
-        * @param       integer         Position to move to: $destPid: >=0 then it points to a page-id on which to insert the record (as the first element). <0 then it points to a uid from its own table after which to insert it (works if
-        * @param       integer         UID of offline version of online record
-        * @return      void
-        * @see moveRecord()
-        */
-       function moveRecord_wsPlaceholders($table,$uid,$destPid,$wsUid) {
-               global $TCA;
-
-               if ($plh = t3lib_BEfunc::getMovePlaceholder($table,$uid,'uid')) {
-                               // If already a placeholder exists, move it:
-                       $this->moveRecord_raw($table,$plh['uid'],$destPid);
-               } else {
-                               // First, we create a placeholder record in the Live workspace that represents the position to where the record is eventually moved to.
-                       $newVersion_placeholderFieldArray = array();
-                       if ($TCA[$table]['ctrl']['crdate'])     {
-                               $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
-                       }
-                       if ($TCA[$table]['ctrl']['cruser_id'])  {
-                               $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['cruser_id']] = $this->userid;
-                       }
-                       if ($TCA[$table]['ctrl']['tstamp'] && count($fieldArray))       {
-                               $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
-                       }
-
-                       if ($table == 'pages') {
-                                       // Copy page access settings from original page to placeholder
-                               $perms_clause = $this->BE_USER->getPagePermsClause(1);
-                               $access = t3lib_BEfunc::readPageAccess($uid, $perms_clause);
-
-                               $newVersion_placeholderFieldArray['perms_userid']    = $access['perms_userid'];
-                               $newVersion_placeholderFieldArray['perms_groupid']   = $access['perms_groupid'];
-                               $newVersion_placeholderFieldArray['perms_user']      = $access['perms_user'];
-                               $newVersion_placeholderFieldArray['perms_group']     = $access['perms_group'];
-                               $newVersion_placeholderFieldArray['perms_everybody'] = $access['perms_everybody'];
-                       }
-
-                       $newVersion_placeholderFieldArray['t3ver_label'] = 'MOVE-TO PLACEHOLDER for #'.$uid;
-                       $newVersion_placeholderFieldArray['t3ver_move_id'] = $uid;
-                       $newVersion_placeholderFieldArray['t3ver_state'] = 3;   // Setting placeholder state value for temporary record
-                       $newVersion_placeholderFieldArray['t3ver_wsid'] = $this->BE_USER->workspace;    // Setting workspace - only so display of place holders can filter out those from other workspaces.
-                       $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['label']] = '[MOVE-TO PLACEHOLDER for #'.$uid.', WS#'.$this->BE_USER->workspace.']';
-
-                       $newVersion_placeholderFieldArray['pid'] = 0;   // Initially, create at root level.
-                       $id = 'NEW_MOVE_PLH';
-                       $this->insertDB($table,$id,$newVersion_placeholderFieldArray,FALSE);    // Saving placeholder as 'original'
-
-                               // Move the new placeholder from temporary root-level to location:
-                       $this->moveRecord_raw($table,$this->substNEWwithIDs[$id],$destPid);
-
-                               // Move the workspace-version of the original to be the version of the move-to-placeholder:
-                       $updateFields = array();
-                       $updateFields['t3ver_state'] = 4;       // Setting placeholder state value for version (so it can know it is currently a new version...)
-                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($wsUid), $updateFields);
-               }
-       }
 
        /**
         * Moves a record without checking security of any sort.
@@ -3609,6 +3541,8 @@ class t3lib_TCEmain       {
                                $this->moveRecord_procFields($table,$uid,$destPid);
                                        // Create query for update:
                                $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
+                                       // check for the localizations of that element
+                               $this->moveL10nOverlayRecords($table, $uid, $destPid);
 
                                        // Call post processing hooks:
                                foreach($hookObjectsArr as $hookObj) {
@@ -3652,6 +3586,8 @@ class t3lib_TCEmain       {
                                                $this->moveRecord_procFields($table,$uid,$destPid);
                                                        // Create query for update:
                                                $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
+                                                       // check for the localizations of that element
+                                               $this->moveL10nOverlayRecords($table, $uid, $destPid);
 
                                                        // Call post processing hooks:
                                                foreach($hookObjectsArr as $hookObj) {
@@ -3721,11 +3657,11 @@ class t3lib_TCEmain     {
         * @param       array           $conf: TCA configuration of current field
         * @return      void
         */
-       function moveRecord_procBasedOnFieldType($table,$uid,$destPid,$field,$value,$conf) {
+       function moveRecord_procBasedOnFieldType($table, $uid, $destPid, $field, $value, $conf) {
                $moveTable = '';
                $moveIds = array();
 
-               if ($conf['type'] == 'inline')  {
+               if ($conf['type'] == 'inline') {
                        $foreign_table = $conf['foreign_table'];
                        $moveChildrenWithParent = (!isset($conf['behaviour']['disableMovingChildrenWithParent']) || !$conf['behaviour']['disableMovingChildrenWithParent']);
 
@@ -3733,18 +3669,49 @@ class t3lib_TCEmain     {
                                $inlineType = $this->getInlineFieldType($conf);
                                if ($inlineType == 'list' || $inlineType == 'field') {
                                        $moveTable = $foreign_table;
+                                       if ($table == 'pages') {
+                                                       // If the inline elements are related to a page record,
+                                                       // make sure they reside at that page and not at its parent
+                                               $destPid = $uid;
+                                       }
                                        $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
                                        $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
                                }
                        }
                }
 
-                       // move the records
+                       // Move the records
                if (isset($dbAnalysis)) {
                                // Moving records to a positive destination will insert each
                                // record at the beginning, thus the order is reversed here:
                        foreach (array_reverse($dbAnalysis->itemArray) as $v) {
-                               $this->moveRecord($v['table'],$v['id'],$destPid);
+                               $this->moveRecord($v['table'], $v['id'], $destPid);
+                       }
+               }
+       }
+
+       /**
+        * Find l10n-overlay records and perform the requested move action for these records.
+        *
+        * @param       string          $table: Record Table
+        * @param       string          $uid: Record UID
+        * @param       string          $destPid: Position to move to
+        * @return      void
+        */
+       function moveL10nOverlayRecords($table, $uid, $destPid) {
+                       //there's no need to perform this for page-records or not localizeable tables
+               if (!t3lib_BEfunc::isTableLocalizable($table) || !empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) || !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) {
+                       return;
+               }
+
+               $where = '';
+               if (isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
+                       $where = ' AND t3ver_oid=0';
+               }
+               $l10nRecords = t3lib_BEfunc::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], $uid, $where);
+               if (is_array($l10nRecords)) {
+                       foreach ($l10nRecords as $record) {
+                               $this->moveRecord($table, $record['uid'], $destPid);
                        }
                }
        }
@@ -3819,6 +3786,16 @@ class t3lib_TCEmain      {
 
                                                                                                // Execute the copy:
                                                                                        $newId = $this->copyRecord($table, $uid, -$uid, 1, $overrideValues, implode(',', $excludeFields), $language);
+                                                                                       $autoVersionNewId = $this->getAutoVersionId($table, $newId);
+                                                                                       if (is_null($autoVersionNewId) === FALSE) {
+                                                                                               $this->triggerRemapAction(
+                                                                                                       $table,
+                                                                                                       $newId,
+                                                                                                       array($this, 'placeholderShadowing'),
+                                                                                                       array($table, $autoVersionNewId),
+                                                                                                       TRUE
+                                                                                               );
+                                                                                       }
                                                                                } else {
 
                                                                                                // Create new record:
@@ -3913,10 +3890,12 @@ class t3lib_TCEmain     {
                                                if (t3lib_div::testInt($type) && isset($elementsOriginal[$type])) {
                                                        $item = $elementsOriginal[$type];
                                                        $item['id'] = $this->localize($item['table'], $item['id'], $language);
+                                                       $item['id'] = $this->overlayAutoVersionId($item['table'], $item['id']);
                                                        $dbAnalysisCurrent->itemArray[] = $item;
                                                } elseif (t3lib_div::inList('localize,synchronize', $type)) {
                                                        foreach ($elementsOriginal as $originalId => $item) {
                                                                $item['id'] = $this->localize($item['table'], $item['id'], $language);
+                                                               $item['id'] = $this->overlayAutoVersionId($item['table'], $item['id']);
                                                                $dbAnalysisCurrent->itemArray[] = $item;
                                                        }
                                                }
@@ -3971,49 +3950,26 @@ class t3lib_TCEmain     {
         * @return      void
         */
        function deleteAction($table, $id)      {
-               global $TCA;
+               global $TYPO3_CONF_VARS;
 
-               $delRec = t3lib_BEfunc::getRecord($table, $id);
+               $recordToDelete = t3lib_BEfunc::getRecord($table, $id);
 
-               if (is_array($delRec))  {       // Record asked to be deleted was found:
+                       // Record asked to be deleted was found:
+               if (is_array($recordToDelete)) {
+                       $recordWasDeleted = FALSE;
 
-                               // For Live version, try if there is a workspace version because if so, rather "delete" that instead
-                       if ($delRec['pid']!=-1) {       // Look, if record is an offline version, then delete directly:
-                               if ($wsVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $id))     {
-                                       $delRec = $wsVersion;
-                                       $id = $delRec['uid'];
+                       if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'])) {
+                               foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'] as $classRef) {
+                                       $hookObj = t3lib_div::getUserObj($classRef);
+                                       if (method_exists($hookObj, 'processCmdmap_deleteAction')) {
+                                               $hookObj->processCmdmap_deleteAction($table, $id, $recordToDelete, $recordWasDeleted, $this);
+                                       }
                                }
                        }
 
-                       if ($delRec['pid']==-1) {       // Look, if record is an offline version, then delete directly:
-                               if ($TCA[$table]['ctrl']['versioningWS'])       {
-                                       if ($this->BE_USER->workspace==0 || (int)$delRec['t3ver_wsid']==$this->BE_USER->workspace)      {       // In Live workspace, delete any. In other workspaces there must be match.
-                                               $liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'uid,t3ver_state');
-
-                                               if ($delRec['t3ver_wsid']==0 || (int)$liveRec['t3ver_state']<=0)        {       // Delete those in WS 0 + if their live records state was not "Placeholder".
-                                                       $this->deleteEl($table, $id);
-                                               } else {        // If live record was placeholder (new/deleted), rather clear it from workspace (because it clears both version and placeholder).
-                                                       $this->version_clearWSID($table,$id);
-                                               }
-                                       } else $this->newlog('Tried to delete record from another workspace',1);
-                               } else $this->newlog('Versioning not enabled for record with PID = -1!',2);
-                       } elseif ($res = $this->BE_USER->workspaceAllowLiveRecordsInPID($delRec['pid'], $table))        {       // Look, if record is "online" or in a versionized branch, then delete directly.
-                               if ($res>0)     {
-                                       $this->deleteEl($table, $id);
-                               } else $this->newlog('Stage of root point did not allow for deletion',1);
-                       } elseif ((int)$delRec['t3ver_state']===3) {    // Placeholders for moving operations are deletable directly.
-
-                                       // Get record which its a placeholder for and reset the t3ver_state of that:
-                               if ($wsRec = t3lib_BEfunc::getWorkspaceVersionOfRecord($delRec['t3ver_wsid'], $table, $delRec['t3ver_move_id'], 'uid')) {
-                                               // Clear the state flag of the workspace version of the record
-                                       $updateFields = array();
-                                       $updateFields['t3ver_state'] = 0;       // Setting placeholder state value for version (so it can know it is currently a new version...)
-                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($wsRec['uid']), $updateFields);
-                               }
+                               // delete the record if a hook hasn't deleted it yet
+                       if (!$recordWasDeleted) {
                                $this->deleteEl($table, $id);
-                       } else {
-                               // Otherwise, try to delete by versioning:
-                               $this->versionizeRecord($table,$id,'DELETED!',TRUE);
                        }
                }
        }
@@ -4089,7 +4045,7 @@ class t3lib_TCEmain       {
                global $TCA;
 
                        // Checking if there is anything else disallowing deleting the record by checking if editing is allowed
-               $mayEditAccess = $this->BE_USER->recordEditAccessInternals($table, $uid, FALSE, $undeleteRecord);
+               $mayEditAccess = $this->BE_USER->recordEditAccessInternals($table, $uid, FALSE, $undeleteRecord, TRUE);
 
                $uid = intval($uid);
                if ($TCA[$table] && $uid)       {
@@ -4119,6 +4075,11 @@ class t3lib_TCEmain      {
                                                        // before (un-)deleting this record, check for child records or references
                                                $this->deleteRecord_procFields($table, $uid, $undeleteRecord);
                                                $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
+
+                                                        // delete all l10n records aswell, impossible during undelete because it might bring too many records back to life
+                                               if (!$undeleteRecord) {
+                                                       $this->deleteL10nOverlayRecords($table, $uid);
+                                               }
                                        } else {
 
                                                        // Fetches all fields with flexforms and look for files to delete:
@@ -4150,6 +4111,8 @@ class t3lib_TCEmain       {
 
                                                        // Delete the hard way...:
                                                $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($uid));
+
+                                               $this->deleteL10nOverlayRecords($table, $uid);
                                        }
 
                                        $state = $undeleteRecord ? 1 : 3;       // 1 means insert, 3 means delete
@@ -4214,9 +4177,9 @@ class t3lib_TCEmain       {
                        foreach($files as $dat) {
                                if (@is_file($dat['ID_absFile']))       {
                                        unlink ($dat['ID_absFile']);
-#echo 'DELETE FlexFormFile:'.$dat['ID_absFile'].chr(10);
+#echo 'DELETE FlexFormFile:'.$dat['ID_absFile'].LF;
                                } else {
-                                       $this->log($table,0,3,0,100,"Delete: Referenced file '".$dat['ID_absFile']."' that was supposed to be deleted together with it's record didn't exist");
+                                       $this->log('', 0, 3, 0, 100, "Delete: Referenced file '" . $dat['ID_absFile'] . "' that was supposed to be deleted together with its record which didn't exist");
                                }
                        }
                }
@@ -4288,14 +4251,22 @@ class t3lib_TCEmain     {
                                $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1);     // returns the branch
                                if ($brExist != -1)     {       // Checks if we had permissions
                                        if ($this->noRecordsFromUnallowedTables($brExist.$uid)) {
-                                               return t3lib_div::trimExplode(',',$brExist.$uid,1);
+                                               $pagesInBranch = t3lib_div::trimExplode(',', $brExist . $uid, 1);
+                                               foreach ($pagesInBranch as $pageInBranch) {
+                                                       if (!$this->BE_USER->recordEditAccessInternals('pages', $pageInBranch, FALSE, FALSE, TRUE)) {
+                                                               return 'Attempt to delete page which has prohibited localizations.';
+                                                       }
+                                               }
+                                               return $pagesInBranch;
                                        } else return 'Attempt to delete records from disallowed tables';
                                } else return 'Attempt to delete pages in branch without permissions';
                        } else {
                                $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1);     // returns the branch
                                if ($brExist == '')     {       // Checks if branch exists
-                                       if ($this->noRecordsFromUnallowedTables($uid))  {
-                                               return array($uid);
+                               if ($this->noRecordsFromUnallowedTables($uid))  {
+                                               if ($this->BE_USER->recordEditAccessInternals('pages', $uid, FALSE, FALSE, TRUE)) {
+                                                       return array($uid);
+                                               } else return 'Attempt to delete page which has prohibited localizations.';
                                        } else return 'Attempt to delete records from disallowed tables';
                                } else return 'Attempt to delete page which has subpages';
                        }
@@ -4326,13 +4297,13 @@ class t3lib_TCEmain     {
         * @return      boolean         Whether the record can be undeleted
         */
        public function isRecordUndeletable($table, $uid) {
-               $result = false;
-               $record = t3lib_BEfunc::getRecord($table, $uid, 'pid', '', false);
+               $result = FALSE;
+               $record = t3lib_BEfunc::getRecord($table, $uid, 'pid', '', FALSE);
                if ($record['pid']) {
-                       $page = t3lib_BEfunc::getRecord('pages', $record['pid'], 'deleted, title, uid', '', false);
+                       $page = t3lib_BEfunc::getRecord('pages', $record['pid'], 'deleted, title, uid', '', FALSE);
                                // The page containing the record is not deleted, thus the record can be undeleted:
                        if (!$page['deleted']) {
-                               $result = true;
+                               $result = TRUE;
                                // The page containing the record is deleted and has to be undeleted first:
                        } else {
                                $this->log(
@@ -4341,6 +4312,9 @@ class t3lib_TCEmain       {
                                                $page['title'] . ' (UID: ' . $page['uid'] . ')" first'
                                );
                        }
+               } else {
+                               // The page containing the record is on rootlevel, so there is no parent record to check, and the record can be undeleted:
+                       $result = TRUE;
                }
                return $result;
        }
@@ -4414,7 +4388,31 @@ class t3lib_TCEmain      {
                }
        }
 
+       /**
+        * Find l10n-overlay records and perform the requested delete action for these records.
+        *
+        * @param       string          $table: Record Table
+        * @param       string          $uid: Record UID
+        * @return      void
+        */
+       function deleteL10nOverlayRecords($table, $uid) {
+                       // Check whether table can be localized or has a different table defined to store localizations:
+               if (!t3lib_BEfunc::isTableLocalizable($table) || !empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) || !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) {
+                       return;
+               }
 
+               $where = '';
+               if (isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
+                       $where = ' AND t3ver_oid=0';
+               }
+
+               $l10nRecords = t3lib_BEfunc::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], $uid, $where);
+               if (is_array($l10nRecords)) {
+                       foreach($l10nRecords as $record) {
+                               $this->deleteAction($table, intval($record['t3ver_oid']) > 0 ? intval($record['t3ver_oid']) : intval($record['uid']));
+                       }
+               }
+       }
 
 
 
@@ -4509,376 +4507,9 @@ class t3lib_TCEmain     {
                } else $this->newlog('Versioning is not supported for this table "'.$table.'" / '.$id,1);
        }
 
-       /**
-        * Creates a new version of a page including content and possible subpages.
-        *
-        * @param       integer         Page uid to create new version of.
-        * @param       string          Version label
-        * @param       integer         Indicating "treeLevel" - "page" (0) or "branch" (>=1) ["element" type must call versionizeRecord() directly]
-        * @return      void
-        * @see copyPages()
-        */
-       function versionizePages($uid,$label,$versionizeTree)   {
-               global $TCA;
-
-               $uid = intval($uid);
-               $brExist = $this->doesBranchExist('',$uid,$this->pMap['show'],1);       // returns the branch
 
-               if ($brExist != -1)     {       // Checks if we had permissions
 
-                               // Finding list of tables ALLOWED to be copied
-                       $allowedTablesArray = $this->admin ? $this->compileAdminTables() : explode(',',$this->BE_USER->groupData['tables_modify']);     // These are the tables, the user may modify
-                       $allowedTablesArray = $this->compileAdminTables();      // These are ALL tables because a new version should be ALL of them regardless of permission of the user executing the request.
-
-                               // Make list of tables that should come along with a new version of the page:
-                       $verTablesArray = array();
-                       $allTables = array_keys($TCA);
-                       foreach($allTables as $tN)      {
-                               if ($tN!='pages' && ($versionizeTree>0 || $TCA[$tN]['ctrl']['versioning_followPages']) && ($this->admin || in_array($tN, $allowedTablesArray))) {
-                                       $verTablesArray[] = $tN;
-                               }
-                       }
 
-                               // Begin to copy pages if we're allowed to:
-                       if ($this->admin || in_array('pages',$allowedTablesArray))      {
-                               if ($this->BE_USER->workspaceVersioningTypeAccess($versionizeTree))     {
-                                               // Versionize this page:
-                                       $theNewRootID = $this->versionizeRecord('pages',$uid,$label,FALSE,$versionizeTree);
-                                       if ($theNewRootID)      {
-                                               $this->rawCopyPageContent($uid,$theNewRootID,$verTablesArray);
-
-                                                       // If we're going to copy recursively...:
-                                               if ($versionizeTree>0)  {
-
-                                                               // Get ALL subpages to copy (read permissions respected - they should NOT be...):
-                                                       $CPtable = $this->int_pageTreeInfo(Array(), $uid, intval($versionizeTree), $theNewRootID);
-
-                                                               // Now copying the subpages:
-                                                       foreach($CPtable as $thePageUid => $thePagePid) {
-                                                               $newPid = $this->copyMappingArray['pages'][$thePagePid];
-                                                               if (isset($newPid))     {
-                                                                       $theNewRootID = $this->copyRecord_raw('pages',$thePageUid,$newPid);
-                                                                       $this->rawCopyPageContent($thePageUid,$theNewRootID,$verTablesArray);
-                                                               } else {
-                                                                       $this->newlog('Something went wrong during copying branch (for versioning)',1);
-                                                                       break;
-                                                               }
-                                                       }
-                                               }       // else the page was not copied. Too bad...
-                                       } else $this->newlog('The root version could not be created!',1);
-                               } else $this->newlog('Versioning type "'.$versionizeTree.'" was not allowed in workspace',1);
-                       } else $this->newlog('Attempt to versionize page without permission to this table',1);
-               } else $this->newlog('Could not read all subpages to versionize.',1);
-       }
-
-       /**
-        * Swapping versions of a record
-        * Version from archive (future/past, called "swap version") will get the uid of the "t3ver_oid", the official element with uid = "t3ver_oid" will get the new versions old uid. PIDs are swapped also
-        *
-        * @param       string          Table name
-        * @param       integer         UID of the online record to swap
-        * @param       integer         UID of the archived version to swap with!
-        * @param       boolean         If set, swaps online into workspace instead of publishing out of workspace.
-        * @return      void
-        */
-       function version_swap($table,$id,$swapWith,$swapIntoWS=0)       {
-               global $TCA;
-
-                       // First, check if we may actually edit the online record
-               if ($this->checkRecordUpdateAccess($table,$id)) {
-
-                               // Select the two versions:
-                       $curVersion = t3lib_BEfunc::getRecord($table,$id,'*');
-                       $swapVersion = t3lib_BEfunc::getRecord($table,$swapWith,'*');
-                       $movePlh = array();
-                       $movePlhID = 0;
-
-                       if (is_array($curVersion) && is_array($swapVersion))    {
-                               if ($this->BE_USER->workspacePublishAccess($swapVersion['t3ver_wsid'])) {
-                                       $wsAccess = $this->BE_USER->checkWorkspace($swapVersion['t3ver_wsid']);
-                                       if ($swapVersion['t3ver_wsid']<=0 || !($wsAccess['publish_access']&1) || (int)$swapVersion['t3ver_stage']===10) {
-                                               if ($this->doesRecordExist($table,$swapWith,'show') && $this->checkRecordUpdateAccess($table,$swapWith)) {
-                                                       if (!$swapIntoWS || $this->BE_USER->workspaceSwapAccess())      {
-
-                                                                       // Check if the swapWith record really IS a version of the original!
-                                                               if ((int)$swapVersion['pid']==-1 && (int)$curVersion['pid']>=0 && !strcmp($swapVersion['t3ver_oid'],$id))       {
-
-                                                                               // Lock file name:
-                                                                       $lockFileName = PATH_site.'typo3temp/swap_locking/'.$table.':'.$id.'.ser';
-
-                                                                       if (!@is_file($lockFileName))   {
-
-                                                                                       // Write lock-file:
-                                                                               t3lib_div::writeFileToTypo3tempDir($lockFileName,serialize(array(
-                                                                                       'tstamp' => $GLOBALS['EXEC_TIME'],
-                                                                                       'user'=>$GLOBALS['BE_USER']->user['username'],
-                                                                                       'curVersion'=>$curVersion,
-                                                                                       'swapVersion'=>$swapVersion
-                                                                               )));
-
-                                                                                       // Find fields to keep
-                                                                               $keepFields = $this->getUniqueFields($table);
-                                                                               if ($TCA[$table]['ctrl']['sortby'])     {
-                                                                                       $keepFields[] = $TCA[$table]['ctrl']['sortby'];
-                                                                               }
-
-                                                                                       // Swap "keepfields"
-                                                                               foreach($keepFields as $fN)     {
-                                                                                       $tmp = $swapVersion[$fN];
-                                                                                       $swapVersion[$fN] = $curVersion[$fN];
-                                                                                       $curVersion[$fN] = $tmp;
-                                                                               }
-
-                                                                                       // Preserve states:
-                                                                               $t3ver_state = array();
-                                                                               $t3ver_state['swapVersion'] = $swapVersion['t3ver_state'];
-                                                                               $t3ver_state['curVersion'] = $curVersion['t3ver_state'];
-
-                                                                                       // Modify offline version to become online:
-                                                                               $tmp_wsid = $swapVersion['t3ver_wsid'];
-                                                                               $swapVersion['pid'] = intval($curVersion['pid']);       // Set pid for ONLINE
-                                                                               $swapVersion['t3ver_oid'] = 0;  // We clear this because t3ver_oid only make sense for offline versions and we want to prevent unintentional misuse of this value for online records.
-                                                                               $swapVersion['t3ver_wsid'] = $swapIntoWS ? ($t3ver_state['swapVersion']>0 ? $this->BE_USER->workspace : intval($curVersion['t3ver_wsid'])) : 0; // In case of swapping and the offline record has a state (like 2 or 4 for deleting or move-pointer) we set the current workspace ID so the record is not deselected in the interface by t3lib_BEfunc::versioningPlaceholderClause()
-                                                                               $swapVersion['t3ver_tstamp'] = $GLOBALS['EXEC_TIME'];
-                                                                               $swapVersion['t3ver_stage'] = 0;
-                                                                               if (!$swapIntoWS)       $swapVersion['t3ver_state'] = 0;
-
-                                                                                       // Moving element.
-                                                                               if ((int)$TCA[$table]['ctrl']['versioningWS']>=2)       {               //  && $t3ver_state['swapVersion']==4   // Maybe we don't need this?
-                                                                                       if ($plhRec = t3lib_BEfunc::getMovePlaceholder($table,$id,'t3ver_state,pid,uid'.($TCA[$table]['ctrl']['sortby']?','.$TCA[$table]['ctrl']['sortby']:'')))        {
-                                                                                               $movePlhID = $plhRec['uid'];
-                                                                                               $movePlh['pid'] = $swapVersion['pid'];
-                                                                                               $swapVersion['pid'] = intval($plhRec['pid']);
-
-                                                                                               $curVersion['t3ver_state'] = intval($swapVersion['t3ver_state']);
-                                                                                               $swapVersion['t3ver_state'] = 0;
-
-                                                                                               if ($TCA[$table]['ctrl']['sortby'])     {
-                                                                                                       $movePlh[$TCA[$table]['ctrl']['sortby']] = $swapVersion[$TCA[$table]['ctrl']['sortby']];        // sortby is a "keepFields" which is why this will work...
-                                                                                                       $swapVersion[$TCA[$table]['ctrl']['sortby']] = $plhRec[$TCA[$table]['ctrl']['sortby']];
-                                                                                               }
-                                                                                       }
-                                                                               }
-
-                                                                                       // Take care of relations in each field (e.g. IRRE):
-                                                                               if (is_array($GLOBALS['TCA'][$table]['columns'])) {
-                                                                                       foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $fieldConf) {
-                                                                                               $this->version_swap_procBasedOnFieldType(
-                                                                                                       $table, $field, $fieldConf['config'], $curVersion, $swapVersion
-                                                                                               );
-                                                                                       }
-                                                                               }
-                                                                               unset($swapVersion['uid']);
-
-                                                                                       // Modify online version to become offline:
-                                                                               unset($curVersion['uid']);
-                                                                               $curVersion['pid'] = -1;        // Set pid for OFFLINE
-                                                                               $curVersion['t3ver_oid'] = intval($id);
-                                                                               $curVersion['t3ver_wsid'] = $swapIntoWS ? intval($tmp_wsid) : 0;
-                                                                               $curVersion['t3ver_tstamp'] = $GLOBALS['EXEC_TIME'];
-                                                                               $curVersion['t3ver_count'] = $curVersion['t3ver_count']+1;      // Increment lifecycle counter
-                                                                               $curVersion['t3ver_stage'] = 0;
-                                                                               if (!$swapIntoWS)       $curVersion['t3ver_state'] = 0;
-
-                                                                               if ($table==='pages') {         // Keeping the swapmode state
-                                                                                               $curVersion['t3ver_swapmode'] = $swapVersion['t3ver_swapmode'];
-                                                                               }
-
-                                                                                       // Registering and swapping MM relations in current and swap records:
-                                                                               $this->version_remapMMForVersionSwap($table,$id,$swapWith);
-
-                                                                                       // Execute swapping:
-                                                                               $sqlErrors = array();
-                                                                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($id),$swapVersion);
-                                                                               if ($GLOBALS['TYPO3_DB']->sql_error())          {
-                                                                                       $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error();
-                                                                               } else {
-                                                                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($swapWith),$curVersion);
-                                                                                       if ($GLOBALS['TYPO3_DB']->sql_error())  {
-                                                                                               $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error();
-                                                                                       } else {
-                                                                                               unlink($lockFileName);
-                                                                                       }
-                                                                               }
-
-                                                                               if (!count($sqlErrors)) {
-
-                                                                                               // If a moving operation took place...:
-                                                                                       if ($movePlhID) {
-                                                                                               if (!$swapIntoWS)       {       // Remove, if normal publishing:
-                                                                                                       $this->deleteEl($table, $movePlhID, TRUE, TRUE);        // For delete + completely delete!
-                                                                                               } else {        // Otherwise update the movePlaceholder:
-                                                                                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($movePlhID),$movePlh);
-                                                                                                       $this->updateRefIndex($table,$movePlhID);
-                                                                                               }
-                                                                                       }
-
-                                                                                               // Checking for delete:
-                                                                                       if (!$swapIntoWS && ((int)$t3ver_state['swapVersion']===1 || (int)$t3ver_state['swapVersion']===2))     {       // Delete only if new/deleted placeholders are there.
-                                                                                               $this->deleteEl($table,$id,TRUE);       // Force delete
-                                                                                       }
-
-                                                                                       $this->newlog2(($swapIntoWS ? 'Swapping' : 'Publishing').' successful for table "'.$table.'" uid '.$id.'=>'.$swapWith, $table, $id, $swapVersion['pid']);
-
-                                                                                               // Update reference index:
-                                                                                       $this->updateRefIndex($table,$id);
-                                                                                       $this->updateRefIndex($table,$swapWith);
-
-                                                                                               // SWAPPING pids for subrecords:
-                                                                                       if ($table=='pages' && $swapVersion['t3ver_swapmode']>=0)       {
-
-                                                                                                       // Collect table names that should be copied along with the tables:
-                                                                                               foreach($TCA as $tN => $tCfg)   {
-                                                                                                       if ($swapVersion['t3ver_swapmode']>0 || $TCA[$tN]['ctrl']['versioning_followPages'])    {       // For "Branch" publishing swap ALL, otherwise for "page" publishing, swap only "versioning_followPages" tables
-                                                                                                               $temporaryPid = -($id+1000000);
-
-                                                                                                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($id),array('pid'=>$temporaryPid));
-                                                                                                               if ($GLOBALS['TYPO3_DB']->sql_error())  $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error();
-
-                                                                                                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($swapWith),array('pid'=>$id));
-                                                                                                               if ($GLOBALS['TYPO3_DB']->sql_error())  $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error();
-
-                                                                                                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($temporaryPid),array('pid'=>$swapWith));
-                                                                                                               if ($GLOBALS['TYPO3_DB']->sql_error())  $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error();
-
-                                                                                                               if (count($sqlErrors))  {
-                                                                                                                       $this->newlog('During Swapping: SQL errors happened: '.implode('; ',$sqlErrors),2);
-                                                                                                               }
-                                                                                                       }
-                                                                                               }
-                                                                                       }
-                                                                                               // Clear cache:
-                                                                                       $this->clear_cache($table,$id);
-
-                                                                                               // Checking for "new-placeholder" and if found, delete it (BUT FIRST after swapping!):
-                                                                                       if (!$swapIntoWS && $t3ver_state['curVersion']>0)       {
-                                                                                               $this->deleteEl($table, $swapWith, TRUE, TRUE);         // For delete + completely delete!
-                                                                                       }
-                                                                               } else $this->newlog('During Swapping: SQL errors happened: '.implode('; ',$sqlErrors),2);
-                                                                       } else $this->newlog('A swapping lock file was present. Either another swap process is already running or a previous swap process failed. Ask your administrator to handle the situation.',2);
-                                                               } else $this->newlog('In swap version, either pid was not -1 or the t3ver_oid didn\'t match the id of the online version as it must!',2);
-                                                       } else $this->newlog('Workspace #'.$swapVersion['t3ver_wsid'].' does not support swapping.',1);
-                                               } else $this->newlog('You cannot publish a record you do not have edit and show permissions for',1);
-                                       } else $this->newlog('Records in workspace #'.$swapVersion['t3ver_wsid'].' can only be published when in "Publish" stage.',1);
-                               } else $this->newlog('User could not publish records from workspace #'.$swapVersion['t3ver_wsid'],1);
-                       } else $this->newlog('Error: Either online or swap version could not be selected!',2);
-               } else $this->newlog('Error: You cannot swap versions for a record you do not have access to edit!',1);
-       }
-
-       /**
-        * Release version from this workspace (and into "Live" workspace but as an offline version).
-        *
-        * @param       string          Table name
-        * @param       integer         Record UID
-        * @param       boolean         If set, will completely delete element
-        * @return      void
-        */
-       function version_clearWSID($table,$id,$flush=FALSE)     {
-               global $TCA;
-
-               if ($errorCode = $this->BE_USER->workspaceCannotEditOfflineVersion($table, $id))        {
-                       $this->newlog('Attempt to reset workspace for record failed: '.$errorCode,1);
-               } elseif ($this->checkRecordUpdateAccess($table,$id)) {
-                       if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'uid,t3ver_state'))      {
-                                       // Clear workspace ID:
-                               $sArray = array();
-                               $sArray['t3ver_wsid'] = 0;
-                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($id),$sArray);
-
-                                       // Clear workspace ID for live version AND DELETE IT as well because it is a new record!
-                               if ((int)$liveRec['t3ver_state']==1 || (int)$liveRec['t3ver_state']==2) {
-                                       $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($liveRec['uid']),$sArray);
-                                       $this->deleteEl($table, $liveRec['uid'], TRUE); // THIS assumes that the record was placeholder ONLY for ONE record (namely $id)
-                               }
-
-                                       // If "deleted" flag is set for the version that got released it doesn't make sense to keep that "placeholder" anymore and we delete it completly.
-                               $wsRec = t3lib_BEfunc::getRecord($table,$id);
-                               if ($flush || ((int)$wsRec['t3ver_state']==1 || (int)$wsRec['t3ver_state']==2)) {
-                                       $this->deleteEl($table, $id, TRUE, TRUE);
-                               }
-
-                                       // Remove the move-placeholder if found for live record.
-                               if ((int)$TCA[$table]['ctrl']['versioningWS']>=2)       {
-                                       if ($plhRec = t3lib_BEfunc::getMovePlaceholder($table,$liveRec['uid'],'uid'))   {
-                                               $this->deleteEl($table, $plhRec['uid'], TRUE, TRUE);
-                                       }
-                               }
-                       }
-               } else $this->newlog('Attempt to reset workspace for record failed because you do not have edit access',1);
-       }
-
-       /**
-        * Setting stage of record
-        *
-        * @param       string          Table name
-        * @param       integer         Record UID
-        * @param       integer         Stage ID to set
-        * @param       string          Comment that goes into log
-        * @param       boolean         Accumulate state changes in memory for compiled notification email?
-        * @return      void
-        */
-       function version_setStage($table,$id,$stageId,$comment='',$accumulateForNotifEmail=FALSE)       {
-               if ($errorCode = $this->BE_USER->workspaceCannotEditOfflineVersion($table, $id))        {
-                       $this->newlog('Attempt to set stage for record failed: '.$errorCode,1);
-               } elseif ($this->checkRecordUpdateAccess($table,$id)) {
-                       $record = t3lib_BEfunc::getRecord($table, $id);
-                       $stat = $this->BE_USER->checkWorkspace($record['t3ver_wsid']);
-
-                       if (t3lib_div::inList('admin,online,offline,reviewer,owner', $stat['_ACCESS']) || ($stageId<=1 && $stat['_ACCESS']==='member')) {
-
-                                       // Set stage of record:
-                               $sArray = array();
-                               $sArray['t3ver_stage'] = $stageId;
-                               $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($id), $sArray);
-                               $this->newlog2('Stage for record was changed to '.$stageId.'. Comment was: "'.substr($comment,0,100).'"',$table,$id);
-// TEMPORARY, except 6-30 as action/detail number which is observed elsewhere!
-$this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stage'=>$stageId));
-
-                               if ((int)$stat['stagechg_notification']>0)      {
-                                       if ($accumulateForNotifEmail)   {
-                                               $this->accumulateForNotifEmail[$stat['uid'].':'.$stageId.':'.$comment]['shared'] = array($stat,$stageId,$comment);
-                                               $this->accumulateForNotifEmail[$stat['uid'].':'.$stageId.':'.$comment]['elements'][] = $table.':'.$id;
-                                       } else {
-                                               $this->notifyStageChange($stat,$stageId,$table,$id,$comment);
-                                       }
-                               }
-                       } else $this->newlog('The member user tried to set a stage value "'.$stageId.'" that was not allowed',1);
-               } else $this->newlog('Attempt to set stage for record failed because you do not have edit access',1);
-       }
-
-       /**
-        * Update relations on version/workspace swapping.
-        *
-        * @param       string          $table: Record Table
-        * @param       string          $field: Record field
-        * @param       array           $conf: TCA configuration of current field
-        * @param       string          $curVersion: Reference to the current (original) record
-        * @param       string          $swapVersion: Reference to the record (workspace/versionized) to publish in or swap with
-        * @return      void
-        */
-       function version_swap_procBasedOnFieldType($table,$field,$conf,&$curVersion,&$swapVersion) {
-               $inlineType = $this->getInlineFieldType($conf);
-
-                       // Process pointer fields on normalized database:
-               if ($inlineType == 'field') {
-                               // Read relations that point to the current record (e.g. live record):
-                       $dbAnalysisCur = t3lib_div::makeInstance('t3lib_loadDBGroup');
-                       $dbAnalysisCur->start('', $conf['foreign_table'], '', $curVersion['uid'], $table, $conf);
-                               // Read relations that point to the record to be swapped with e.g. draft record):
-                       $dbAnalysisSwap = t3lib_div::makeInstance('t3lib_loadDBGroup');
-                       $dbAnalysisSwap->start('', $conf['foreign_table'], '', $swapVersion['uid'], $table, $conf);
-                               // Update relations for both (workspace/versioning) sites:
-                       $dbAnalysisCur->writeForeignField($conf,$curVersion['uid'],$swapVersion['uid']);
-                       $dbAnalysisSwap->writeForeignField($conf,$swapVersion['uid'],$curVersion['uid']);
-
-                       // Swap field values (CSV):
-                       // BUT: These values will be swapped back in the next steps, when the *CHILD RECORD ITSELF* is swapped!
-               } elseif ($inlineType == 'list') {
-                       $tempValue = $curVersion[$field];
-                       $curVersion[$field] = $swapVersion[$field];
-                       $swapVersion[$field] = $tempValue;
-               }
-       }
 
        /**
         * Swaps MM-relations for current/swap record, see version_swap()
@@ -5200,9 +4831,15 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                                $this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate, $table);
 
                        } elseif ($inlineType !== false) {
+                               /** @var $dbAnalysis t3lib_loadDBGroup */
                                $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
                                $dbAnalysis->start($value, $conf['foreign_table'], '', 0, $table, $conf);
 
+                                       // Update child records if using pointer fields ('foreign_field'):
+                               if ($inlineType == 'field') {
+                                       $dbAnalysis->writeForeignField($conf, $uid, $theUidToUpdate);
+                               }
+
                                        // If the current field is set on a page record, update the pid of related child records:
                                if ($table == 'pages') {
                                        $thePidToUpdate = $theUidToUpdate;
@@ -5212,16 +4849,11 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                                        $thePidToUpdate = $this->copyMappingArray_merged['pages'][$thePidToUpdate];
                                }
 
-                                       // Update child records if using pointer fields ('foreign_field'):
-                               if ($inlineType == 'field') {
-                                       $dbAnalysis->writeForeignField($conf, $uid, $theUidToUpdate);
-                               }
-
-                                       // Update child records if change to pid is required:
+                                       // Update child records if change to pid is required (only if the current record is not on a workspace):
                                if ($thePidToUpdate) {
                                        $updateValues = array('pid' => $thePidToUpdate);
                                        foreach ($dbAnalysis->itemArray as $v) {
-                                               if ($v['id'] && $v['table']) {
+                                               if ($v['id'] && $v['table'] && is_null(t3lib_BEfunc::getLiveVersionIdOfRecord($v['table'], $v['id']))) {
                                                        $GLOBALS['TYPO3_DB']->exec_UPDATEquery($v['table'], 'uid='.intval($v['id']), $updateValues);
                                                }
                                        }
@@ -5237,6 +4869,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      void
         */
        function processRemapStack() {
+                       // Processes the remap stack:
                if(is_array($this->remapStack)) {
                        foreach($this->remapStack as $remapAction) {
                                        // If no position index for the arguments was set, skip this remap action:
@@ -5306,9 +4939,72 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                                }
                        }
                }
+                       // Processes the remap stack actions:
+               if ($this->remapStackActions) {
+                       foreach ($this->remapStackActions as $action) {
+                               if (isset($action['callback']) && isset($action['arguments'])) {
+                                       call_user_func_array(
+                                               $action['callback'],
+                                               $action['arguments']
+                                       );
+                               }
+                       }
+               }
+                       // Processes the reference index updates of the remap stack:
+               foreach ($this->remapStackRefIndex as $table => $idArray) {
+                       foreach ($idArray as $id) {
+                               $this->updateRefIndex($table, $id);
+                               unset($this->remapStackRefIndex[$table][$id]);
+                       }
+               }
                        // Reset:
                $this->remapStack = array();
                $this->remapStackRecords = array();
+               $this->remapStackActions = array();
+               $this->remapStackRefIndex = array();
+       }
+
+       /**
+        * Triggers a remap action for a specific record.
+        *
+        * Some records are post-processed by the processRemapStack() method (e.g. IRRE children).
+        * This method determines wether an action/modification is executed directly to a record
+        * or is postponed to happen after remapping data.
+        *
+        * @param string $table Name of the table
+        * @param string $id Id of the record (can also be a "NEW..." string)
+        * @param array $callback The method to be called
+        * @param array $arguments The arguments to be submitted to the callback method
+        * @param boolean $forceRemapStackActions Whether to force to use the stack
+        * @return void
+        *
+        * @see processRemapStack
+        */
+       protected function triggerRemapAction($table, $id, array $callback, array $arguments, $forceRemapStackActions = FALSE) {
+                       // Check whether the affected record is marked to be remapped:
+               if (!$forceRemapStackActions && !isset($this->remapStackRecords[$table][$id]) && !isset($this->remapStackChildIds[$id])) {
+                       call_user_func_array($callback, $arguments);
+               } else {
+                       $this->remapStackActions[] = array(
+                               'affects' => array(
+                                       'table' => $table,
+                                       'id' => $id,
+                               ),
+                               'callback' => $callback,
+                               'arguments' => $arguments,
+                       );
+               }
+       }
+
+       /**
+        * Adds a table-id-pair to the reference index remapping stack.
+        *
+        * @param string $table
+        * @param integer $id
+        * @return void
+        */
+       public function addRemapStackRefIndex($table, $id) {
+               $this->remapStackRefIndex[$table][$id] = $id;
        }
 
        /**
@@ -5411,11 +5107,26 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         *
         * @param       string          Record table
         * @param       integer         Record UID
+        * @param       array           Record data
+        * @param       array           Hook objects
         * @return      boolean         Returns true if the user may update the record given by $table and $id
         */
-       function checkRecordUpdateAccess($table,$id)    {
+       function checkRecordUpdateAccess($table, $id, $data=false, &$hookObjectsArr = false) {
                global $TCA;
-               $res = 0;
+               $res = NULL;
+               if (is_array($hookObjectsArr)) {
+                       foreach($hookObjectsArr as $hookObj) {
+                               if (method_exists($hookObj, 'checkRecordUpdateAccess')) {
+                                       $res = $hookObj->checkRecordUpdateAccess($table, $id, $data, $res, $this);
+                               }
+                       }
+               }
+               if($res === 1 || $res === 0) {
+                       return $res;
+               } else {
+                       $res = 0;
+               }
+
                if ($TCA[$table] && intval($id)>0)      {
                        if (isset($this->recUpdateAccessCache[$table][$id]))    {       // If information is cached, return it
                                return $this->recUpdateAccessCache[$table][$id];
@@ -5530,7 +5241,12 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                        $perms = intval($perms);
                }
 
-               if (!$perms)    {die('Internal ERROR: no permissions to check for non-admin user.');}
+               if (!$perms) {
+                       throw new RuntimeException(
+                               'Internal ERROR: no permissions to check for non-admin user',
+                               1270853920
+                       );
+               }
 
                        // For all tables: Check if record exists:
                if (is_array($TCA[$table]) && $id>0 && ($this->isRecordInWebMount($table,$id) || $this->admin)) {
@@ -6325,13 +6041,23 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                                // Unset the fields which are similar:
                        foreach($fieldArray as $col => $val)    {
                                if (
-                                               !strcmp($val,$currentRecord[$col]) ||   // Unset fields which matched exactly.
-                                               ($cRecTypes[$col]=='int' && $currentRecord[$col]==0 && !strcmp($val,''))        // Now, a situation where TYPO3 tries to put an empty string into an integer field, we should not strcmp the integer-zero and '', but rather accept them to be similar.
-                                       )       {
+                                       !$GLOBALS['TCA'][$table]['columns'][$col]['config']['MM'] && // Do not unset MM relation fields, since equality of the MM count doesn't always mean that relations haven't changed.
+                                       ( !strcmp($val,$currentRecord[$col]) || // Unset fields which matched exactly.
+                                         ($cRecTypes[$col]=='int' && $currentRecord[$col]==0 && !strcmp($val,''))      // Now, a situation where TYPO3 tries to put an empty string into an integer field, we should not strcmp the integer-zero and '', but rather accept them to be similar.
+                                       )
+                               ) {
                                        unset($fieldArray[$col]);
                                } else {
-                                       $this->historyRecords[$table.':'.$id]['oldRecord'][$col] = $currentRecord[$col];
-                                       $this->historyRecords[$table.':'.$id]['newRecord'][$col] = $fieldArray[$col];
+                                       if (!isset($this->mmHistoryRecords[$table . ':' . $id]['oldRecord'][$col])) {
+                                               $this->historyRecords[$table . ':' . $id]['oldRecord'][$col] = $currentRecord[$col];
+                                       } elseif ($this->mmHistoryRecords[$table . ':' . $id]['oldRecord'][$col] != $this->mmHistoryRecords[$table . ':' . $id]['newRecord'][$col]) {
+                                               $this->historyRecords[$table . ':' . $id]['oldRecord'][$col] = $this->mmHistoryRecords[$table . ':' . $id]['oldRecord'][$col];
+                                       }
+                                       if (!isset($this->mmHistoryRecords[$table . ':' . $id]['newRecord'][$col])) {
+                                               $this->historyRecords[$table . ':' . $id]['newRecord'][$col] = $fieldArray[$col];
+                                       } elseif ($this->mmHistoryRecords[$table . ':' . $id]['newRecord'][$col] != $this->mmHistoryRecords[$table . ':' . $id]['oldRecord'][$col]) {
+                                               $this->historyRecords[$table . ':' . $id]['newRecord'][$col] = $this->mmHistoryRecords[$table . ':' . $id]['newRecord'][$col];
+                                       }
                                }
                        }
                } else {        // If the current record does not exist this is an error anyways and we just return an empty array here.
@@ -6477,7 +6203,10 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         */
        function dbAnalysisStoreExec()  {
                foreach ($this->dbAnalysisStore as $action) {
-                       $id = t3lib_BEfunc::wsMapId($action[4], $this->substNEWwithIDs[$action[2]]);
+                       $id = t3lib_BEfunc::wsMapId(
+                               $action[4],
+                               (t3lib_div::testInt($action[2]) ? $action[2] : $this->substNEWwithIDs[$action[2]])
+                       );
                        if ($id)        {
                                $action[0]->writeMM($action[1], $id, $action[3]);
                        }
@@ -6827,179 +6556,6 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                return TRUE;
        }
 
-       /**
-        * Send an email notification to users in workspace
-        *
-        * @param       array           Workspace access array (from t3lib_userauthgroup::checkWorkspace())
-        * @param       integer         New Stage number: 0 = editing, 1= just ready for review, 10 = ready for publication, -1 = rejected!
-        * @param       string          Table name of element (or list of element names if $id is zero)
-        * @param       integer         Record uid of element (if zero, then $table is used as reference to element(s) alone)
-        * @param       string          User comment sent along with action
-        * @return      void
-        */
-       function notifyStageChange($stat,$stageId,$table,$id,$comment)  {
-               $workspaceRec = t3lib_BEfunc::getRecord('sys_workspace', $stat['uid']);
-               $elementName = $id ? $table.':'.$id : $table;   // So, if $id is not set, then $table is taken to be the complete element name!
-
-               if (is_array($workspaceRec))    {
-
-                               // Compile label:
-                       switch((int)$stageId)   {
-                               case 1:
-                                       $newStage = 'Ready for review';
-                               break;
-                               case 10:
-                                       $newStage = 'Ready for publishing';
-                               break;
-                               case -1:
-                                       $newStage = 'Element was rejected!';
-                               break;
-                               case 0:
-                                       $newStage = 'Rejected element was noticed and edited';
-                               break;
-                               default:
-                                       $newStage = 'Unknown state change!?';
-                               break;
-                       }
-
-                               // Compile list of recipients:
-                       $emails = array();
-                       switch((int)$stat['stagechg_notification'])     {
-                               case 1:
-                                       switch((int)$stageId)   {
-                                               case 1:
-                                                       $emails = $this->notifyStageChange_getEmails($workspaceRec['reviewers']);
-                                               break;
-                                               case 10:
-                                                       $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE);
-                                               break;
-                                               case -1:
-#                                                      $emails = $this->notifyStageChange_getEmails($workspaceRec['reviewers']);
-#                                                      $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['members']));
-
-                                                               // List of elements to reject:
-                                                       $allElements = explode(',',$elementName);
-                                                               // Traverse them, and find the history of each
-                                                       foreach($allElements as $elRef) {
-                                                               list($eTable,$eUid) = explode(':',$elRef);
-
-                                                               $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
-                                                                               'log_data,tstamp,userid',
-                                                                               'sys_log',
-                                                                               'action=6 and details_nr=30
-                                                                               AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($eTable,'sys_log').'
-                                                                               AND recuid='.intval($eUid),
-                                                                               '',
-                                                                               'uid DESC'
-                                                               );
-                                                                       // Find all implicated since the last stage-raise from editing to review:
-                                                               foreach($rows as $dat)  {
-                                                                       $data = unserialize($dat['log_data']);
-
-                                                                       //debug($dat['userid'],'Adds user at stage: '.$data['stage']);
-                                                                       $emails = array_merge($emails,$this->notifyStageChange_getEmails($dat['userid'],TRUE));
-
-                                                                       if ($data['stage']==1)  {
-                                                                               break;
-                                                                       }
-                                                               }
-                                                       }
-                                               break;
-                                               case 0:
-                                                       $emails = $this->notifyStageChange_getEmails($workspaceRec['members']);
-                                               break;
-                                               default:
-                                                       $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE);
-                                               break;
-                                       }
-                               break;
-                               case 10:
-                                       $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE);
-                                       $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['reviewers']));
-                                       $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['members']));
-                               break;
-                       }
-                       $emails = array_unique($emails);
-
-                               // Path to record is found:
-                       list($eTable,$eUid) = explode(':',$elementName);
-                       $eUid = intval($eUid);
-                       $rr = t3lib_BEfunc::getRecord($eTable,$eUid);
-                       $recTitle = t3lib_BEfunc::getRecordTitle($eTable,$rr);
-                       if ($eTable!='pages')   {
-                               t3lib_BEfunc::fixVersioningPid($eTable,$rr);
-                               $eUid=$rr['pid'];
-                       }
-                       $path = t3lib_BEfunc::getRecordPath($eUid,'',20);
-
-                               // ALternative messages:
-                       $TSConfig = $this->getTCEMAIN_TSconfig($eUid);
-                       $body = trim($TSConfig['notificationEmail_body']) ? trim($TSConfig['notificationEmail_body']) : '
-At the TYPO3 site "%s" (%s)
-in workspace "%s" (#%s)
-the stage has changed for the element(s) "%11$s" (%s) at location "%10$s" in the page tree:
-
-==> %s
-
-User Comment:
-"%s"
-
-State was change by %s (username: %s)
-                       ';
-                       $subject = trim($TSConfig['notificationEmail_subject']) ? trim($TSConfig['notificationEmail_subject']) : 'TYPO3 Workspace Note: Stage Change for %s';
-
-                               // Send email:
-                       if (count($emails))     {
-                               $message = sprintf($body,
-                               $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
-                               t3lib_div::getIndpEnv('TYPO3_SITE_URL').TYPO3_mainDir,
-                               $workspaceRec['title'],
-                               $workspaceRec['uid'],
-                               $elementName,
-                               $newStage,
-                               $comment,
-                               $this->BE_USER->user['realName'],
-                               $this->BE_USER->user['username'],
-                               $path,
-                               $recTitle);
-
-                               t3lib_div::plainMailEncoded(
-                                       implode(',',$emails),
-                                       sprintf($subject,$elementName),
-                                       trim($message)
-                               );
-
-                               $this->newlog2('Notification email for stage change was sent to "'.implode(', ',$emails).'"',$table,$id);
-                       }
-               }
-       }
-
-       /**
-        * Return emails addresses of be_users from input list.
-        *
-        * @param       string          List of backend users, on the form "be_users_10,be_users_2" or "10,2" in case noTablePrefix is set.
-        * @param       boolean         If set, the input list are integers and not strings.
-        * @return      array           Array of emails
-        */
-       function notifyStageChange_getEmails($listOfUsers,$noTablePrefix=FALSE) {
-               $users = t3lib_div::trimExplode(',',$listOfUsers,1);
-               $emails = array();
-               foreach($users as $userIdent)   {
-                       if ($noTablePrefix)     {
-                               $id = intval($userIdent);
-                       } else {
-                               list($table,$id) = t3lib_div::revExplode('_',$userIdent,2);
-                       }
-                       if ($table==='be_users' || $noTablePrefix)      {
-                               if ($userRecord = t3lib_BEfunc::getRecord('be_users', $id, 'email'))    {
-                                       if (strlen(trim($userRecord['email']))) {
-                                               $emails[$id] = $userRecord['email'];
-                                       }
-                               }
-                       }
-               }
-               return $emails;
-       }
 
        /**
         * Determine if a record was copied or if a record is the result of a copy action.
@@ -7199,9 +6755,11 @@ State was change by %s (username: %s)
         * @param       string          the cache command, see above description
         * @return      void
         */
-       public function clear_cacheCmd($cacheCmd)       {
+       public function clear_cacheCmd($cacheCmd) {
                global $TYPO3_CONF_VARS;
 
+               $this->BE_USER->writelog(3, 1, 0, 0, 'User %s has cleared the cache (cacheCmd=%s)', array($this->BE_USER->user['username'], $cacheCmd));
+
                        // Clear cache for either ALL pages or ALL tables!
                switch($cacheCmd)       {
                        case 'pages':
@@ -7220,24 +6778,27 @@ State was change by %s (username: %s)
 
                                        if (TYPO3_UseCachingFramework) {
                                                if (t3lib_extMgm::isLoaded('cms'))      {
-                                                       $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_treelist', '');
+                                                       $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_treelist');
                                                }
                                        } else {
                                                if (t3lib_extMgm::isLoaded('cms'))      {
-                                                       $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_treelist', '');
-                                                       $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection','');
+                                                       $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_treelist');
+                                                       $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_pagesection');
                                                }
                                                $this->internal_clearPageCache();
-                                               $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_hash','');
+                                               $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_hash');
                                        }
 
                                                // Clearing additional cache tables:
                                        if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables']))        {
                                                foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'] as $tableName)        {
                                                        if (!preg_match('/[^[:alnum:]_]/',$tableName) && substr($tableName,-5)=='cache')        {
-                                                               $GLOBALS['TYPO3_DB']->exec_DELETEquery($tableName,'');
+                                                               $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($tableName);
                                                        } else {
-                                                               die('Fatal Error: Trying to flush table "'.$tableName.'" with "Clear All Cache"');
+                                                               throw new RuntimeException(
+                                                                       'TYPO3 Fatal Error: Trying to flush table "' . $tableName . '" with "Clear All Cache"',
+                                                                       1270853922
+                                                               );
                                                        }
                                                }
                                        }
@@ -7390,49 +6951,18 @@ State was change by %s (username: %s)
                                        'sys_log',
                                        'type=1 AND userid='.intval($this->BE_USER->user['uid']).' AND tstamp='.intval($GLOBALS['EXEC_TIME']).' AND error!=0'
                                );
-               $errorJS = array();
                while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_log)) {
                        $log_data = unserialize($row['log_data']);
-                       $errorJS[] = $row['error'].': '.sprintf($row['details'], $log_data[0],$log_data[1],$log_data[2],$log_data[3],$log_data[4]);
+                       $msg = $row['error'] . ': ' . sprintf($row['details'], $log_data[0], $log_data[1], $log_data[2], $log_data[3], $log_data[4]);
+                       $flashMessage = t3lib_div::makeInstance('t3lib_FlashMessage',
+                               $msg,
+                               '',
+                               t3lib_FlashMessage::ERROR,
+                               TRUE
+                       );
+                       t3lib_FlashMessageQueue::addMessage($flashMessage);
                }
                $GLOBALS['TYPO3_DB']->sql_free_result($res_log);
-
-               if (count($errorJS))    {
-                       $error_doc = t3lib_div::makeInstance('template');
-                       $error_doc->backPath = $GLOBALS['BACK_PATH'];
-
-                       $content.= $error_doc->startPage('tce_db.php Error output');
-
-                       $lines[] = '
-                                       <tr class="bgColor5">
-                                               <td colspan="2" align="center"><strong>Errors:</strong></td>
-                                       </tr>';
-
-                       foreach($errorJS as $line)      {
-                               $lines[] = '
-                                       <tr class="bgColor4">
-                                               <td valign="top"><img'.t3lib_iconWorks::skinImg($error_doc->backPath,'gfx/icon_fatalerror.gif','width="18" height="16"').' alt="" /></td>
-                                               <td>'.htmlspecialchars($line).'</td>
-                                       </tr>';
-                       }
-
-                       $lines[] = '
-                                       <tr>
-                                               <td colspan="2" align="center"><br />'.
-                                               '<form action=""><input type="submit" value="Continue" onclick="'.htmlspecialchars('window.location.href=\''.$redirect.'\';return false;').'"></form>'.
-                                               '</td>
-                                       </tr>';
-
-                       $content.= '
-                               <br/><br/>
-                               <table border="0" cellpadding="1" cellspacing="1" width="300" align="center">
-                                       '.implode('',$lines).'
-                               </table>';
-
-                       $content.= $error_doc->endPage();
-                       echo $content;
-                       exit;
-               }
        }
 
        /*****************************
@@ -7460,173 +6990,85 @@ State was change by %s (username: %s)
                                                t3lib_div::sysLog('Could not remove page cache files in "'.$cacheDir.'"','Core/t3lib_tcemain',2);
                                        }
                                }
-                               $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','');
+                               $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_pages');
                        }
                }
        }
 
-       /**
-        * Finds page UIDs for the element from table <code>$table</code> with UIDs from <code>$idList</code>
-        *
-        * @param       array   $table  Table to search
-        * @param       array   $idList List of records' UIDs
-        * @param       int     $workspaceId    Workspace ID. We need this parameter because user can be in LIVE but he still can publisg DRAFT from ws module!
-        * @param       array   $pageIdList     List of found page UIDs
-        * @param       array   $elementList    List of found element UIDs. Key is table name, value is list of UIDs
-        * @return      void
-        */
-       function findPageIdsForVersionStateChange($table, $idList, $workspaceId, &$pageIdList, &$elementList) {
-               if ($workspaceId != 0) {
-                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('DISTINCT(B.pid)',
-                               $table . ' A,' . $table . ' B',
-                               'A.pid=-1' .            // Offline version
-                               ' AND A.t3ver_wsid=' . $workspaceId .
-                               ' AND A.uid IN (' . implode(',', $idList) . ') AND A.t3ver_oid=B.uid' .
-                               t3lib_BEfunc::deleteClause($table,'A').
-                               t3lib_BEfunc::deleteClause($table,'B')
-                       );
-                       while (false !== ($row = $GLOBALS['TYPO3_DB']->sql_fetch_row($res))) {
-                               $pageIdList[] = $row[0];
-                               // Find ws version
-                               $rec = t3lib_BEfunc::getRecord('pages', $row[0]);
-                               t3lib_BEfunc::workspaceOL('pages', $rec, $workspaceId); // Note: cannot use t3lib_BEfunc::getRecordWSOL() here because it does not accept workspace id!
-                               if ($rec['_ORIG_uid']) {
-                                       $elementList['pages'][$row[0]] = $rec['_ORIG_uid'];
-                               }
-                       }
-                       $GLOBALS['TYPO3_DB']->sql_free_result($res);
-                       // The line below is necessary even with DISTINCT because several elements can be passed by caller
-                       $pageIdList = array_unique($pageIdList);
-               }
-       }
 
        /**
-        * Searches for all elements from all tables on the given pages in the same workspace.
+        * Proprocesses field array based on field type. Some fields must be adjusted
+        * before going to database. This is done on the copy of the field array because
+        * original values are used in remap action later.
         *
-        * @param       array   $pageIdList     List of PIDs to search
-        * @param       int     $workspaceId    Workspace ID
-        * @param       array   $elementList    List of found elements. Key is table name, value is array of element UIDs
-        * @return      void
+        * @param       string  $table  Table name
+        * @param       array   $fieldArray     Field array to check
+        * @return      array   Updated field array
         */
-       function findPageElementsForVersionStageChange($pageIdList, $workspaceId, &$elementList) {
-               global $TCA;
+       function insertUpdateDB_preprocessBasedOnFieldType($table, $fieldArray) {
+               global  $TCA;
 
-               if ($workspaceId != 0) {
-                       // Traversing all tables supporting versioning:
-                       foreach($TCA as $table => $cfg) {
-                               if ($TCA[$table]['ctrl']['versioningWS'] && $table != 'pages')  {
-                                       // Using SELECTquery for better debugging
-                                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('DISTINCT(A.uid)',
-                                               $table . ' A,' . $table . ' B',
-                                               'A.pid=-1' .            // Offline version
-                                               ' AND A.t3ver_wsid=' . $workspaceId .
-                                               ' AND B.pid IN (' . implode(',', $pageIdList) . ') AND A.t3ver_oid=B.uid' .
-                                               t3lib_BEfunc::deleteClause($table,'A').
-                                               t3lib_BEfunc::deleteClause($table,'B')
-                                       );
-                                       while (false !== ($row = $GLOBALS['TYPO3_DB']->sql_fetch_row($res))) {
-                                               $elementList[$table][] = $row[0];
-                                       }
-                                       $GLOBALS['TYPO3_DB']->sql_free_result($res);
-                                       if (is_array($elementList[$table])) {
-                                               // Yes, it is possible to get non-unique array even with DISTINCT above! It happens because several UIDs are passed in the array already.
-                                               $elementList[$table] = array_unique($elementList[$table]);
+               $result = $fieldArray;
+               foreach ($fieldArray as $field => $value) {
+                       switch ($TCA[$table]['columns'][$field]['config']['type']) {
+                               case 'inline':
+                                       if ($TCA[$table]['columns'][$field]['config']['foreign_field']) {
+                                               if (!t3lib_div::testInt($value)) {
+                                                       $result[$field] = count(t3lib_div::trimExplode(',', $value, TRUE));
+                                               }
                                        }
-                               }
+                                       break;
                        }
                }
+               return $result;
        }
 
        /**
-        * Finds real page IDs for state change.
+        * Gets the automatically versionized id of a record.
         *
-        * @param       array   $idList List of page UIDs, possibly versioned
-        * @return      void
+        * @param string $table Name of the table
+        * @param integer $id Uid of the record
+        * @return integer
         */
-       function findRealPageIds(&$idList) {
-               foreach ($idList as $key => $id) {
-                       $rec = t3lib_BEfunc::getRecord('pages', $id, 't3ver_oid');
-                       if ($rec['t3ver_oid'] > 0) {
-                               $idList[$key] = $rec['t3ver_oid'];
-                       }
+       protected function getAutoVersionId($table, $id) {
+               $result = NULL;
+
+               if (isset($this->autoVersionIdMap[$table][$id])) {
+                       $result = $this->autoVersionIdMap[$table][$id];
                }
+
+               return $result;
        }
 
        /**
-        * Finds all elements for swapping versions in workspace
+        * Overlays the automatically versionized id of a record.
         *
-        * @param       string  $table  Table name of the original element to swap
-        * @param       int     $id     UID of the original element to swap (online)
-        * @param       int     $offlineId As above but offline
-        * @return      array   Element data. Key is table name, values are array with first element as online UID, second - offline UID
+        * @param string $table Name of the table
+        * @param integer $id Uid of the record
+        * @return integer
         */
-       function findPageElementsForVersionSwap($table, $id, $offlineId) {
-               global  $TCA;
+       protected function overlayAutoVersionId($table, $id) {
+               $autoVersionId = $this->getAutoVersionId($table, $id);
 
-               $rec = t3lib_BEfunc::getRecord($table, $offlineId, 't3ver_wsid');
-               $workspaceId = $rec['t3ver_wsid'];
-
-               $elementData = array();
-               if ($workspaceId != 0) {
-                       // Get page UID for LIVE and workspace
-                       if ($table != 'pages') {
-                               $rec = t3lib_BEfunc::getRecord($table, $id, 'pid');
-                               $pageId = $rec['pid'];
-                               $rec = t3lib_BEfunc::getRecord('pages', $pageId);
-                               t3lib_BEfunc::workspaceOL('pages', $rec, $workspaceId);
-                               $offlinePageId = $rec['_ORIG_uid'];
-                       }
-                       else {
-                               $pageId = $id;
-                               $offlinePageId = $offlineId;
-                       }
-
-                       // Traversing all tables supporting versioning:
-                       foreach($TCA as $table => $cfg) {
-                               if ($TCA[$table]['ctrl']['versioningWS'] && $table != 'pages')  {
-                                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('A.uid AS offlineUid, B.uid AS uid',
-                                                       $table . ' A,' . $table . ' B',
-                                                       'A.pid=-1 AND B.pid=' . $pageId . ' AND A.t3ver_wsid=' . $workspaceId .
-                                                       ' AND B.uid=A.t3ver_oid' .
-                                                       t3lib_BEfunc::deleteClause($table, 'A') . t3lib_BEfunc::deleteClause($table, 'B'));
-                                       while (false != ($row = $GLOBALS['TYPO3_DB']->sql_fetch_row($res))) {
-                                               $elementData[$table][] = array($row[1], $row[0]);
-                                       }
-                                       $GLOBALS['TYPO3_DB']->sql_free_result($res);
-                               }
-                       }
-                       if ($offlinePageId && $offlinePageId != $pageId) {
-                               $elementData['pages'][] = array($pageId, $offlinePageId);
-                       }
+               if (is_null($autoVersionId) === FALSE) {
+                       $id = $autoVersionId;
                }
-               return $elementData;
+
+               return $id;
        }
 
        /**
-        * Proprocesses field array based on field type. Some fields must be adjusted
-        * before going to database. This is done on the copy of the field array because
-        * original values are used in remap action later.
+        * Adds new values to the remapStackChildIds array.
         *
-        * @param       string  $table  Table name
-        * @param       array   $fieldArray     Field array to check
-        * @return      array   Updated field array
+        * @param array $idValues uid values
+        * @return void
         */
-       function insertUpdateDB_preprocessBasedOnFieldType($table, $fieldArray) {
-               global  $TCA;
-
-               $result = $fieldArray;
-               foreach ($fieldArray as $field => $value) {
-                       switch ($TCA[$table]['columns'][$field]['config']['type']) {
-                               case 'inline':
-                                       if ($TCA[$table]['columns'][$field]['config']['foreign_field']) {
-                                               if (!t3lib_div::testInt($value)) {
-                                                       $result[$field] = count(t3lib_div::trimExplode(',', true));
-                                               }
-                                       }
-                                       break;
+       protected function addNewValuesToRemapStackChildIds(array $idValues) {
+               foreach ($idValues as $idValue) {
+                       if (strpos($idValue, 'NEW') === 0) {
+                               $this->remapStackChildIds[$idValue] = TRUE;
                        }
                }
-               return $result;
        }
 }
 
@@ -7636,4 +7078,4 @@ if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class
        include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tcemain.php']);
 }
 
-?>
+?>
\ No newline at end of file