Fixed bug #14050: CleanUp - CGL format of t3lib files - t3lib_positionmap
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_tcemain.php
old mode 100755 (executable)
new mode 100644 (file)
index e94c908..2a23844
@@ -2,7 +2,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 1999-2008 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]
 
 
 
-// *******************************
-// Including necessary libraries
-// *******************************
-require_once (PATH_t3lib.'class.t3lib_loaddbgroup.php');
-require_once (PATH_t3lib.'class.t3lib_parsehtml_proc.php');
-require_once (PATH_t3lib.'class.t3lib_stdgraphic.php');
-require_once (PATH_t3lib.'class.t3lib_basicfilefunc.php');
-require_once (PATH_t3lib.'class.t3lib_refindex.php');
-require_once (PATH_t3lib.'class.t3lib_flexformtools.php');
 
 
 
@@ -235,7 +226,7 @@ require_once (PATH_t3lib.'class.t3lib_flexformtools.php');
  *
  * 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
  */
@@ -258,18 +249,19 @@ class t3lib_TCEmain       {
        var $dontProcessTransformations = FALSE;        // Boolean: If set, then transformations are NOT performed on the input.
        var $clear_flexFormData_vDEFbase = FALSE;       // Boolean: If set, .vDEFbase values are unset in flexforms.
        var $updateModeL10NdiffData = TRUE;             // Boolean/Mixed: TRUE: (traditional) Updates when record is saved. For flexforms, updates if change is made to the localized value. FALSE: Will not update anything. "FORCE_FFUPD" (string): Like TRUE, but will force update to the FlexForm Field
+       var $updateModeL10NdiffDataClear = FALSE;       // Boolean: If true, the translation diff. fields will in fact be reset so that they indicate that all needs to change again! It's meant as the opposite of declaring the record translated.
        var $bypassWorkspaceRestrictions = FALSE;       // Boolean: If true, workspace restrictions are bypassed on edit an create actions (process_datamap()). YOU MUST KNOW what you do if you use this feature!
        var $bypassFileHandling = FALSE;                        // Boolean: If true, file handling of attached files (addition, deletion etc) is bypassed - the value is saved straight away. YOU MUST KNOW what you are doing with this feature!
        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
 
        var $defaultValues = array();                   // Array [table][fields]=value: New records are created with default values and you can set this array on the form $defaultValues[$table][$field] = $value to override the default values fetched from TCA. If ->setDefaultsFromUserTS is called UserTSconfig default values will overrule existing values in this array (thus UserTSconfig overrules externally set defaults which overrules TCA defaults)
        var $overrideValues = array();                  // Array [table][fields]=value: You can set this array on the form $overrideValues[$table][$field] = $value to override the incoming data. You must set this externally. You must make sure the fields in this array are also found in the table, because it's not checked. All columns can be set by this array!
        var $alternativeFileName = array();             // Array [filename]=alternative_filename: Use this array to force another name onto a file. Eg. if you set ['/tmp/blablabal'] = 'my_file.txt' and '/tmp/blablabal' is set for a certain file-field, then 'my_file.txt' will be used as the name instead.
+       var $alternativeFilePath = array();             // Array [filename]=alternative_filepath: Same as alternativeFileName but with relative path to the file
        var $data_disableFields=array();                // If entries are set in this array corresponding to fields for update, they are ignored and thus NOT updated. You could set this array from a series of checkboxes with value=0 and hidden fields before the checkbox with 1. Then an empty checkbox will disable the field.
        var $suggestedInsertUids=array();               // Use this array to validate suggested uids for tables by setting [table]:[uid]. This is a dangerous option since it will force the inserted record to have a certain UID. The value just have to be true, but if you set it to "DELETE" it will make sure any record with that UID will be deleted first (raw delete). The option is used for import of T3D files when synchronizing between two mirrored servers. As a security measure this feature is available only for Admin Users (for now)
 
@@ -289,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.
 
 
 
@@ -346,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
@@ -360,6 +354,8 @@ class t3lib_TCEmain {
        var $checkValue_currentRecord=array();          // Set to "currentRecord" during checking of values.
        var $autoVersioningUpdate = FALSE;                      // A signal flag used to tell file processing that autoversioning has happend and hence certain action should be applied.
 
+       protected $disableDeleteClause = false;         // Disable delete clause
+       protected $checkModifyAccessListHookObjects;
 
 
 
@@ -381,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'];
@@ -389,10 +385,10 @@ 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;
                }
-               
+
                if ($GLOBALS['TYPO3_CONF_VARS']['BE']['explicitConfirmationOfTranslation'] && $this->updateModeL10NdiffData===TRUE)     {
                        $this->updateModeL10NdiffData = FALSE;
                }
@@ -426,14 +422,13 @@ class t3lib_TCEmain       {
         */
        function setMirror($mirror)     {
                if (is_array($mirror))  {
-                       reset($mirror);
-                       while(list($table,$uid_array)=each($mirror))    {
+                       foreach ($mirror as $table => $uid_array) {
                                if (isset($this->datamap[$table]))      {
                                        reset($uid_array);
-                                       while (list($id,$uidList) = each($uid_array))   {
+                                       foreach ($uid_array as $id => $uidList) {
                                                if (isset($this->datamap[$table][$id])) {
                                                        $theIdsInArray = t3lib_div::trimExplode(',',$uidList,1);
-                                                       while(list(,$copyToUid)=each($theIdsInArray))   {
+                                                       foreach ($theIdsInArray as $copyToUid) {
                                                                $this->datamap[$table][$copyToUid] = $this->datamap[$table][$id];
                                                        }
                                                }
@@ -572,6 +567,31 @@ class t3lib_TCEmain        {
                }
        }
 
+       /**
+        * Gets the 'checkModifyAccessList' hook objects.
+        * The first call initializes the accordant objects.
+        *
+        * @return      array           The 'checkModifyAccessList' hook objects (if any)
+        */
+       protected function getCheckModifyAccessListHookObjects() {
+               if (!isset($this->checkModifyAccessListHookObjects)) {
+                       $this->checkModifyAccessListHookObjects = array();
+
+                       if(is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkModifyAccessList'])) {
+                               foreach($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkModifyAccessList'] as $classData) {
+                                       $hookObject = t3lib_div::getUserObj($classData);
+
+                                       if(!($hookObject instanceof t3lib_TCEmain_checkModifyAccessListHook)) {
+                                               throw new UnexpectedValueException('$hookObject must implement interface t3lib_TCEmain_checkModifyAccessListHook', 1251892472);
+                                       }
+
+                                       $this->checkModifyAccessListHookObjects[] = $hookObject;
+                               }
+                       }
+               }
+
+               return $this->checkModifyAccessListHookObjects;
+       }
 
 
 
@@ -597,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();
@@ -612,7 +632,7 @@ class t3lib_TCEmain {
                $hookObjectsArr = array();
                if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'])) {
                        foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'] as $classRef) {
-                               $hookObjectsArr[] = &t3lib_div::getUserObj($classRef);
+                               $hookObjectsArr[] = t3lib_div::getUserObj($classRef);
                        }
                }
 
@@ -621,12 +641,7 @@ class t3lib_TCEmain        {
                if (isset($this->datamap['pages']))     {               // Set pages first.
                        $orderOfTables[]='pages';
                }
-               reset($this->datamap);
-               while (list($table,) = each($this->datamap))    {
-                       if ($table!='pages')    {
-                               $orderOfTables[]=$table;
-                       }
-               }
+               $orderOfTables = array_unique(array_merge($orderOfTables, array_keys($this->datamap)));
 
                        // Process the tables...
                foreach($orderOfTables as $table)       {
@@ -652,7 +667,7 @@ class t3lib_TCEmain {
                                foreach($this->datamap[$table] as $id => $incomingFieldArray)   {
                                        if (is_array($incomingFieldArray))      {
 
-                                                       // Hook: processDatamap_preProcessIncomingFieldArray
+                                                       // Hook: processDatamap_preProcessFieldArray
                                                foreach($hookObjectsArr as $hookObj)    {
                                                        if (method_exists($hookObj, 'processDatamap_preProcessFieldArray')) {
                                                                $hookObj->processDatamap_preProcessFieldArray($incomingFieldArray, $table, $id, $this);
@@ -665,7 +680,6 @@ class t3lib_TCEmain {
                                                $createNewVersion = FALSE;
                                                $recordAccess = FALSE;
                                                $old_pid_value = '';
-                                               $resetRejected = FALSE;
                                                $this->autoVersioningUpdate = FALSE;
 
                                                if (!t3lib_div::testInt($id)) {               // Is it a new record? (Then Id is a string)
@@ -679,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.
@@ -744,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']);
@@ -757,11 +773,6 @@ class t3lib_TCEmain        {
                                                                        $tempdata = $this->recordInfo($table,$id,'pid'.($TCA[$table]['ctrl']['versioningWS']?',t3ver_wsid,t3ver_stage':''));
                                                                        $theRealPid = $tempdata['pid'];
 
-                                                                               // Prepare the reset of the rejected flag if set:
-                                                                       if ($TCA[$table]['ctrl']['versioningWS'] && $tempdata['t3ver_stage']<0) {
-                                                                               $resetRejected = TRUE;
-                                                                       }
-
                                                                        // Use the new id of the versionized record we're trying to write to:
                                                                                // (This record is a child record of a parent and has already been versionized.)
                                                                        if ($this->autoVersionIdMap[$table][$id]) {
@@ -849,8 +860,10 @@ class t3lib_TCEmain        {
                                                                // Setting system fields
                                                        if ($status=='new')     {
                                                                if ($TCA[$table]['ctrl']['crdate'])     {
-                                                                       $fieldArray[$TCA[$table]['ctrl']['crdate']]=time();
-                                                                       if ($createNewVersion)  $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['crdate']]=time();
+                                                                       $fieldArray[$TCA[$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
+                                                                       if ($createNewVersion) {
+                                                                               $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
+                                                                       }
                                                                }
                                                                if ($TCA[$table]['ctrl']['cruser_id'])  {
                                                                        $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid;
@@ -860,10 +873,12 @@ class t3lib_TCEmain       {
                                                                $fieldArray = $this->compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray);
                                                        }
                                                        if ($TCA[$table]['ctrl']['tstamp'] && count($fieldArray))       {
-                                                               $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
-                                                               if ($createNewVersion)  $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
+                                                               $fieldArray[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
+                                                               if ($createNewVersion) {
+                                                                       $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
+                                                               }
                                                        }
-                                                       if ($resetRejected)     {
+                                                       if ($TCA[$table]['ctrl']['versioningWS']) {
                                                                $fieldArray['t3ver_stage'] = 0;
                                                        }
 
@@ -899,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;
                                                                                        }
@@ -933,7 +954,7 @@ class t3lib_TCEmain {
 
                /*
                 * Hook: processDatamap_afterAllOperations
-                * 
+                *
                 * Note: When this hook gets called, all operations on the submitted data have been finished.
                 */
                foreach($hookObjectsArr as $hookObj) {
@@ -973,7 +994,7 @@ class t3lib_TCEmain {
                                }
 
                                if (count($newRecord))  {
-                                       $this->newlog('Shadowing done on fields '.implode(',',array_keys($newRecord)).' in Placeholder record '.$table.':'.$liveRec['uid'].' (offline version UID='.$id.')');
+                                       $this->newlog2('Shadowing done on fields <i>'.implode(',',array_keys($newRecord)).'</i> in placeholder record '.$table.':'.$liveRec['uid'].' (offline version UID='.$id.')', $table, $liveRec['uid'], $liveRec['pid']);
                                        $this->updateDB($table,$liveRec['uid'],$newRecord);
                                }
                        }
@@ -1095,18 +1116,17 @@ class t3lib_TCEmain     {
                                                break;
                                                default:
                                                        if (isset($TCA[$table]['columns'][$field]))     {
-                                                                       // Evaluating the value.
+                                                                       // Evaluating the value
                                                                $res = $this->checkValue($table,$field,$fieldValue,$id,$status,$realPid,$tscPID);
                                                                if (isset($res['value']))       {
-                                                                       $fieldArray[$field]=$res['value'];
-
-                                                                               // Add the value of the original record to the diff-storage content:
-                                                                       if ($this->updateModeL10NdiffData && $TCA[$table]['ctrl']['transOrigDiffSourceField'])  {
-                                                                               $originalLanguage_diffStorage[$field] = $originalLanguageRecord[$field];
-                                                                               $diffStorageFlag = TRUE;
-                                                                       }
+                                                                       $fieldArray[$field] = $res['value'];
                                                                }
 
+                                                                       // Add the value of the original record to the diff-storage content:
+                                                               if ($this->updateModeL10NdiffData && $TCA[$table]['ctrl']['transOrigDiffSourceField'])  {
+                                                                       $originalLanguage_diffStorage[$field] = $this->updateModeL10NdiffDataClear ? '' : $originalLanguageRecord[$field];
+                                                                       $diffStorageFlag = TRUE;
+                                                               }
 
                                                                        // If autoversioning is happening we need to perform a nasty hack. The case is parallel to a similar hack inside checkValue_group_select_file().
                                                                        // When a copy or version is made of a record, a search is made for any RTEmagic* images in fields having the "images" soft reference parser applied. That should be true for RTE fields. If any are found they are duplicated to new names and the file reference in the bodytext is updated accordingly.
@@ -1137,8 +1157,7 @@ class t3lib_TCEmain       {
                $types_fieldConfig = t3lib_BEfunc::getTCAtypes($table,$currentRecord);
                $theTypeString = t3lib_BEfunc::getTCAtypeValue($table,$currentRecord);
                if (is_array($types_fieldConfig))       {
-                       reset($types_fieldConfig);
-                       while(list(,$vconf) = each($types_fieldConfig)) {
+                       foreach ($types_fieldConfig as $vconf) {
                                        // Write file configuration:
                                $eFile = t3lib_parsehtml_proc::evalWriteFile($vconf['spec']['static_write'],array_merge($currentRecord,$fieldArray));   // inserted array_merge($currentRecord,$fieldArray) 170502
 
@@ -1155,7 +1174,7 @@ class t3lib_TCEmain       {
                                                                $RTErelPath = is_array($eFile) ? dirname($eFile['relEditFile']) : '';
 
                                                                        // Get RTE object, draw form and set flag:
-                                                               $RTEobj = &t3lib_BEfunc::RTEgetObj();
+                                                               $RTEobj = t3lib_BEfunc::RTEgetObj();
                                                                if (is_object($RTEobj)) {
                                                                        $fieldArray[$vconf['field']] = $RTEobj->transformContent('db',$fieldArray[$vconf['field']],$table,$vconf['field'],$currentRecord,$vconf['spec'],$thisConfig,$RTErelPath,$currentRecord['pid']);
                                                                } else {
@@ -1177,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:
@@ -1238,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);
@@ -1296,6 +1315,8 @@ class t3lib_TCEmain       {
 
                switch ($tcaFieldConf['type']) {
                        case 'text':
+                               $res = $this->checkValue_text($res,$value,$tcaFieldConf,$PP,$field);
+                       break;
                        case 'passthrough':
                        case 'user':
                                $res['value'] = $value;
@@ -1329,6 +1350,23 @@ class t3lib_TCEmain      {
                return $res;
        }
 
+
+       /**
+        * Evaluate "text" type values.
+        *
+        * @param       array           The result array. The processed value (if any!) is set in the "value" key.
+        * @param       string          The value to set.
+        * @param       array           Field configuration from TCA
+        * @param       array           Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
+        * @param       string          Field name
+        * @return      array           Modified $res array
+        */
+       function checkValue_text($res,$value,$tcaFieldConf,$PP,$field='')       {
+               $evalCodesArray = t3lib_div::trimExplode(',',$tcaFieldConf['eval'],1);
+               $res = $this->checkValue_text_Eval($value,$evalCodesArray,$tcaFieldConf['is_in']);
+               return $res;
+       }
+
        /**
         * Evaluate "input" type values.
         *
@@ -1342,8 +1380,10 @@ class t3lib_TCEmain      {
        function checkValue_input($res,$value,$tcaFieldConf,$PP,$field='')      {
                list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
 
-                       // Secures the string-length to be less than max. Will probably make problems with multi-byte strings!
-               if (intval($tcaFieldConf['max'])>0)     {$value = substr($value,0,intval($tcaFieldConf['max']));}
+                       // Secures the string-length to be less than max.
+               if (intval($tcaFieldConf['max']) > 0) {
+                       $value = $GLOBALS['LANG']->csConvObj->substr($GLOBALS['LANG']->charSet, $value, 0, intval($tcaFieldConf['max']));
+               }
 
                        // Checking range of value:
                if ($tcaFieldConf['range'] && $value!=$tcaFieldConf['checkbox'])        {       // If value is not set to the allowed checkbox-value then it is checked against the ranges
@@ -1479,6 +1519,7 @@ class t3lib_TCEmain       {
                        // For group types:
                if ($tcaFieldConf['type']=='group')     {
                        switch($tcaFieldConf['internal_type'])  {
+                               case 'file_reference':
                                case 'file':
                                        $valueArray = $this->checkValue_group_select_file(
                                                $valueArray,
@@ -1492,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;
                        }
                }
@@ -1501,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);
                        }
                }
 
@@ -1563,7 +1605,7 @@ class t3lib_TCEmain       {
                }
 
                        // If there is an upload folder defined:
-               if ($tcaFieldConf['uploadfolder'])      {
+               if ($tcaFieldConf['uploadfolder'] && $tcaFieldConf['internal_type'] == 'file') {
                        if (!$this->bypassFileHandling) {       // If filehandling should NOT be bypassed, do processing:
                                        // For logging..
                                $propArr = $this->getRecordProperties($table,$id);
@@ -1597,15 +1639,15 @@ class t3lib_TCEmain     {
                                                $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
                                                /* @var $dbAnalysis t3lib_loadDBGroup */
                                                $dbAnalysis->start('','files',$tcaFieldConf['MM'],$id);
-                                               reset($dbAnalysis->itemArray);
-                                               while (list($somekey,$someval)=each($dbAnalysis->itemArray))    {
-                                                       if ($someval['id'])     {
-                                                               $theFileValues[]=$someval['id'];
+                                               foreach ($dbAnalysis->itemArray as $item) {
+                                                       if ($item['id']) {
+                                                               $theFileValues[] = $item['id'];
                                                        }
                                                }
                                        } 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))      {
@@ -1659,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']);
 
@@ -1684,18 +1740,87 @@ class t3lib_TCEmain     {
                                /* @var $dbAnalysis t3lib_loadDBGroup */
                                $dbAnalysis->tableArray['files']=array();       // dummy
 
-                               reset($valueArray);
-                               while (list($key,$theFile)=each($valueArray))   {
+                               foreach ($valueArray as $key => $theFile) {
                                                // explode files
                                                $dbAnalysis->itemArray[]['id']=$theFile;
                                }
                                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
                                }
                                $valueArray = $dbAnalysis->countItems();
                        }
+                       //store path relative to site root (if uploadfolder is not set or internal_type is file_reference)
+               } else {
+                       if (count($valueArray)){
+                               if (!$this->bypassFileHandling) {       // If filehandling should NOT be bypassed, do processing:
+                                       $propArr = $this->getRecordProperties($table, $id); // For logging..
+                                       foreach($valueArray as &$theFile){
+
+                                                       // if alernative File Path is set for the file, then it was an import
+                                               if ($this->alternativeFilePath[$theFile]){
+
+                                                               // don't import the file if it already exists
+                                                       if (@is_file(PATH_site . $this->alternativeFilePath[$theFile])) {
+                                                               $theFile = PATH_site . $this->alternativeFilePath[$theFile];
+
+                                                               // import the file
+                                                       } elseif (@is_file($theFile)){
+                                                               $dest = dirname(PATH_site . $this->alternativeFilePath[$theFile]);
+                                                               if (!@is_dir($dest)) {
+                                                                       t3lib_div::mkdir_deep(PATH_site, dirname($this->alternativeFilePath[$theFile]) . '/');
+                                                               }
+
+                                                                       // Init:
+                                                               $maxSize = intval($tcaFieldConf['max_size']);
+                                                               $cmd = '';
+                                                               $theDestFile = '';              // Must be cleared. Else a faulty fileref may be inserted if the below code returns an error!
+                                                               $fileSize = filesize($theFile);
+
+                                                               if (!$maxSize || $fileSize <= ($maxSize * 1024))        {       // Check file size:
+                                                                               // Prepare filename:
+                                                                       $theEndFileName = isset($this->alternativeFileName[$theFile]) ? $this->alternativeFileName[$theFile] : $theFile;
+                                                                       $fI = t3lib_div::split_fileref($theEndFileName);
+
+                                                                               // Check for allowed extension:
+                                                                       if ($this->fileFunc->checkIfAllowed($fI['fileext'], $dest, $theEndFileName)) {
+                                                                               $theDestFile = PATH_site . $this->alternativeFilePath[$theFile];
+
+                                                                                       // Write the file:
+                                                                               if ($theDestFile)       {
+                                                                                       t3lib_div::upload_copy_move($theFile, $theDestFile);
+                                                                                       $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, "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!
+                                                               if (@is_file($theDestFile))     {
+                                                                       $theFile = $theDestFile; // The value is set to the new filename
+                                                               } else {
+                                                                       unset($theFile); // The value is set to the new filename
+                                                               }
+                                                       }
+                                               }
+                                               $theFile = t3lib_div::fixWindowsFilePath($theFile);
+                                               if (t3lib_div::isFirstPartOfStr($theFile, PATH_site)) {
+                                                       $theFile = substr($theFile, strlen(PATH_site));
+                                               }
+                                       }
+                               }
+                       }
                }
 
                return $valueArray;
@@ -1842,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),
@@ -1946,13 +2072,40 @@ class t3lib_TCEmain     {
                return $value;
        }
 
+       function checkValue_text_Eval($value,$evalArray,$is_in) {
+               $res = Array();
+               $newValue = $value;
+               $set = true;
+
+               foreach ($evalArray as $func) {
+                       switch ($func) {
+                               case 'trim':
+                                       $value = trim($value);
+                               break;
+                               case 'required':
+                                       if (!$value)    {$set=0;}
+                               break;
+                               default:
+                                       if (substr($func, 0, 3) == 'tx_')       {
+                                               $evalObj = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func].':&'.$func);
+                                               if (is_object($evalObj) && method_exists($evalObj, 'evaluateFieldValue'))       {
+                                                       $value = $evalObj->evaluateFieldValue($value, $is_in, $set);
+                                               }
+                                       }
+                               break;
+                       }
+               }
+               if ($set)       {$res['value'] = $value;}
+               return $res;
+       }
+
        /**
         * Evaluation of 'input'-type values based on 'eval' list
         *
         * @param       string          Value to evaluate
         * @param       array           Array of evaluations to traverse.
-        * @param       string          Is-in string
-        * @return      string          Modified $value
+        * @param       string          Is-in string for 'is_in' evaluation
+        * @return      array           Modified $value in key 'value' or empty array
         */
        function checkValue_input_Eval($value,$evalArray,$is_in)        {
                $res = Array();
@@ -1970,21 +2123,24 @@ class t3lib_TCEmain     {
                                case 'date':
                                case 'datetime':
                                        $value = intval($value);
-                                       if ($value>0)   {
-                                               $value -= date('Z');
+                                       if ($value>0 && !$this->dontProcessTransformations)     {
+                                               $value -= date('Z', $value);
                                        }
                                break;
                                case 'double2':
-                                       $theDec = 0;
-                                       for ($a=strlen($value); $a>0; $a--)     {
-                                               if (substr($value,$a-1,1)=='.' || substr($value,$a-1,1)==',')   {
-                                                       $theDec = substr($value,$a);
-                                                       $value = substr($value,0,$a-1);
-                                                       break;
-                                               }
+                                       $value = preg_replace('/[^0-9,\.-]/', '', $value);
+                                       $negative = substr($value, 0, 1) == '-';
+                                       $value = strtr($value, array(',' => '.', '-' => ''));
+                                       if (strpos($value, '.') === false) {
+                                               $value .= '.0';
+                                       }
+                                       $valueArray = explode('.', $value);
+                                       $dec = array_pop($valueArray);
+                                       $value = join('', $valueArray) . '.' . $dec;
+                                       if ($negative) {
+                                               $value *= -1;
                                        }
-                                       $theDec = ereg_replace('[^0-9]','',$theDec).'00';
-                                       $value = intval(str_replace(' ','',$value)).'.'.substr($theDec,0,2);
+                                       $value = number_format($value, 2, '.', '');
                                break;
                                case 'md5':
                                        if (strlen($value)!=32){$set=false;}
@@ -1999,7 +2155,9 @@ class t3lib_TCEmain       {
                                        $value = $GLOBALS['LANG']->csConvObj->conv_case($GLOBALS['LANG']->charSet, $value, 'toLower');
                                break;
                                case 'required':
-                                       if (!$value)    {$set=0;}
+                                       if (!isset($value) || $value === '') {
+                                               $set = false;
+                                       }
                                break;
                                case 'is_in':
                                        $c=strlen($value);
@@ -2007,7 +2165,7 @@ class t3lib_TCEmain       {
                                                $newVal = '';
                                                for ($a=0;$a<$c;$a++)   {
                                                        $char = substr($value,$a,1);
-                                                       if (strstr($is_in,$char))       {
+                                                       if (strpos($is_in,$char) !== false) {
                                                                $newVal.=$char;
                                                        }
                                                }
@@ -2018,16 +2176,16 @@ class t3lib_TCEmain     {
                                        $value = str_replace(' ','',$value);
                                break;
                                case 'alpha':
-                                       $value = ereg_replace('[^a-zA-Z]','',$value);
+                                       $value = preg_replace('/[^a-zA-Z]/','',$value);
                                break;
                                case 'num':
-                                       $value = ereg_replace('[^0-9]','',$value);
+                                       $value = preg_replace('/[^0-9]/','',$value);
                                break;
                                case 'alphanum':
-                                       $value = ereg_replace('[^a-zA-Z0-9]','',$value);
+                                       $value = preg_replace('/[^a-zA-Z0-9]/','',$value);
                                break;
                                case 'alphanum_x':
-                                       $value = ereg_replace('[^a-zA-Z0-9_-]','',$value);
+                                       $value = preg_replace('/[^a-zA-Z0-9_-]/','',$value);
                                break;
                                default:
                                        if (substr($func, 0, 3) == 'tx_')       {
@@ -2052,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
                        }
@@ -2089,10 +2262,13 @@ class t3lib_TCEmain     {
         */
        function checkValue_group_select_explodeSelectGroupValue($value)        {
                $valueArray = t3lib_div::trimExplode(',',$value,1);
-               reset($valueArray);
-               while(list($key,$newVal)=each($valueArray))     {
+               foreach ($valueArray as &$newVal) {
                        $temp=explode('|',$newVal,2);
-                       $valueArray[$key] = str_replace(',','',str_replace('|','',rawurldecode($temp[0])));
+                       $newVal = str_replace(
+                               ',',
+                               '',
+                               str_replace('|', '', rawurldecode($temp[0]))
+                       );
                }
                return $valueArray;
        }
@@ -2162,7 +2338,7 @@ class t3lib_TCEmain       {
                                                        foreach($dataValues[$key]['el'] as $ik => $el)  {
                                                                if (is_array($el))      {
                                                                        if (!is_array($dataValues_current[$key]['el'])) $dataValues_current[$key]['el']=array();
-                                                                       
+
                                                                        $theKey = key($el);
 
                                                                        if (is_array($dataValues[$key]['el'][$ik][$theKey]['el']))      {
@@ -2175,7 +2351,7 @@ class t3lib_TCEmain       {
                                                                                                $callBackFunc,
                                                                                                $structurePath.$key.'/el/'.$ik.'/'.$theKey.'/el/'
                                                                                        );
-                                                                               
+
                                                                                        // If element is added dynamically in the flexform of TCEforms, we map the ID-string to the next numerical index we can have in that particular section of elements:
                                                                                        // The fact that the order changes is not important since order is controlled by a separately submitted index.
 
@@ -2262,7 +2438,7 @@ class t3lib_TCEmain       {
                                                                                $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'],$CVtable,$recFieldName,$theTypeString);
 
                                                                                        // Get RTE object, draw form and set flag:
-                                                                               $RTEobj = &t3lib_BEfunc::RTEgetObj();
+                                                                               $RTEobj = t3lib_BEfunc::RTEgetObj();
                                                                                if (is_object($RTEobj)) {
                                                                                        $res['value'] = $RTEobj->transformContent('db',$res['value'],$CVtable,$recFieldName,$this->checkValue_currentRecord,$specConf,$thisConfig,'',$CVrealPid);
                                                                                } else {
@@ -2290,7 +2466,7 @@ class t3lib_TCEmain       {
                                                                                $diffValue = $dataValues_current[$key]['vDEF'];
                                                                        }
                                                                                // Setting the reference value for vDEF for this translation. This will be used for translation tools to make a diff between the vDEF and vDEFbase to see if an update would be fitting.
-                                                                       $dataValues[$key][$vKey.'.vDEFbase'] = $diffValue;
+                                                                       $dataValues[$key][$vKey.'.vDEFbase'] = $this->updateModeL10NdiffDataClear ? '' : $diffValue;
                                                                }
                                                        }
                                                }
@@ -2321,7 +2497,7 @@ class t3lib_TCEmain       {
                 */
                $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
                $dbAnalysis->start(implode(',', $valueArray), $foreignTable, '', 0, $table, $tcaFieldConf);
-                       // If the localizazionMode is set to 'keep', the children for the localized parent are kept as in the original untranslated record:
+                       // If the localizationMode is set to 'keep', the children for the localized parent are kept as in the original untranslated record:
                $localizationMode = t3lib_BEfunc::getInlineLocalizationMode($table, $tcaFieldConf);
                if ($localizationMode=='keep' && $status=='update') {
                                // Fetch the current record and determine the original record:
@@ -2338,13 +2514,18 @@ class t3lib_TCEmain     {
                                }
                        }
                }
-                       // IRRE with a pointer field (database normalization): 
+                       // IRRE with a pointer field (database normalization):
                if ($tcaFieldConf['foreign_field']) {
                                // if the record was imported, sorting was also imported, so skip this
                        $skipSorting = ($this->callFromImpExp ? true : false);
                                // 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();
@@ -2387,7 +2568,7 @@ class t3lib_TCEmain       {
         *
         * @return      void
         */
-       function process_cmdmap() {
+       public function process_cmdmap() {
                global $TCA, $TYPO3_CONF_VARS;
 
                        // Editing frozen:
@@ -2400,23 +2581,23 @@ 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:
-               reset($this->cmdmap);
-               while(list($table,) = each($this->cmdmap))      {
+               foreach (array_keys($this->cmdmap) as $table) {
 
                                // Check if the table may be modified!
                        $modifyAccessList = $this->checkModifyAccessList($table);
                        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)       {
@@ -2430,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.
+                                                       // 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);
+                                                       }
+                                               }
 
-                                                       // 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);
-                                                                                       }
-                                                                               }
-                                                                               
-                                                                               foreach ($elementList as $tbl => $elementIdList) {
-                                                                                       foreach($elementIdList as $id)  {
-                                                                                               $this->version_setStage($tbl,$id,$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");
        }
 
 
@@ -2608,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');
                }
        }
@@ -2781,6 +2921,7 @@ class t3lib_TCEmain       {
                                        while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))     {
                                                $this->copyRecord($table,$row['uid'], $theNewRootID);   // Copying each of the underlying records...
                                        }
+                                       $GLOBALS['TYPO3_DB']->sql_free_result($mres);
                                }
                        }
                        return $theNewRootID;
@@ -2858,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...
         *
@@ -2905,6 +3016,10 @@ class t3lib_TCEmain      {
                        // The point is that when new records are created as copies with flex type fields there might be a field containing information about which DataStructure to use and without that information the flexforms cannot be correctly processed.... This should be OK since the $checkValueRecord is used by the flexform evaluation only anyways...
                $this->checkValue_currentRecord = $fieldArray;
 
+                       // Makes sure that transformations aren't processed on the copy.
+               $backupDontProcessTransformations = $this->dontProcessTransformations;
+               $this->dontProcessTransformations = TRUE;
+
                        // Traverse record and input-process each value:
                foreach($fieldArray as $field => $fieldValue)   {
                        if (isset($TCA[$table]['columns'][$field]))     {
@@ -2918,13 +3033,13 @@ class t3lib_TCEmain     {
 
                        // System fields being set:
                if ($TCA[$table]['ctrl']['crdate'])     {
-                       $fieldArray[$TCA[$table]['ctrl']['crdate']]=time();
+                       $fieldArray[$TCA[$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
                }
                if ($TCA[$table]['ctrl']['cruser_id'])  {
                        $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid;
                }
                if ($TCA[$table]['ctrl']['tstamp'])     {
-                       $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
+                       $fieldArray[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
                }
 
                        // Finally, insert record:
@@ -2932,6 +3047,9 @@ class t3lib_TCEmain       {
                        // Process the remap stack in case we dealed with relations:
                $this->processRemapStack();
 
+                       // Resets dontProcessTransformations to the previous state.
+               $this->dontProcessTransformations = $backupDontProcessTransformations;
+
                        // Return new id:
                return $this->substNEWwithIDs[$id];
        }
@@ -2946,7 +3064,7 @@ class t3lib_TCEmain       {
         * @param       array           Record array
         * @param       array           TCA field configuration
         * @param       integer         Real page id (pid) the record is copied to
-        * @param       integer         Language ID (from sys_language table)
+        * @param       integer         Language ID (from sys_language table) used in the duplicated record
         * @return      mixed           Processed value. Normally a string/integer, but can be an array for flexforms!
         * @access private
         * @see copyRecord()
@@ -2988,9 +3106,9 @@ class t3lib_TCEmain       {
                        // If another inline subtype is used (comma-separated-values or the foreign_field property):
                } elseif ($inlineSubType !== false) {
                                // Get the localization mode for the current (parent) record (keep|select|all):
-                       $localizazionMode = t3lib_BEfunc::getInlineLocalizationMode($table, $field);
+                       $localizationMode = t3lib_BEfunc::getInlineLocalizationMode($table, $field);
                                // Localization in mode 'keep', isn't a real localization, but keeps the children of the original parent record:
-                       if ($language>0 && $localizazionMode=='keep') {
+                       if ($language>0 && $localizationMode=='keep') {
                                $value = ($inlineSubType=='field' ? 0 : '');
                                // Execute copy or localization actions:
                        } else {
@@ -3003,18 +3121,27 @@ 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 ($localizazionMode!=false && isset($conf['behaviour']['localizeChildrenAtParentLocalization']) && $conf['behaviour']['localizeChildrenAtParentLocalization']) {
+                                               if ($localizationMode!=false && isset($conf['behaviour']['localizeChildrenAtParentLocalization']) && $conf['behaviour']['localizeChildrenAtParentLocalization']) {
                                                        $newId = $this->localize($v['table'], $v['id'], $language);
                                                }
                                                // If no language it set, this is a regular copy action:
                                        } 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);
                                                }
@@ -3051,7 +3178,7 @@ class t3lib_TCEmain       {
                                                        array(),        // Not used.
                                                        array(),        // Not used.
                                                        $dataStructArray,
-                                                       array($table,$uid,$field),      // Parameters.
+                                                       array($table, $uid, $field, $realDestPid),      // Parameters.
                                                        'copyRecord_flexFormCallBack'
                                                );
                                $value = $currentValueArray;    // Setting value as an array! -> which means the input will be processed according to the 'flex' type when the new copy is created.
@@ -3075,13 +3202,14 @@ class t3lib_TCEmain     {
        function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)   {
 
                        // Extract parameters:
-               list($table, $uid, $field) = $pParams;
+               list($table, $uid, $field, $realDestPid) = $pParams;
 
                        // Process references and files, currently that means only the files, prepending absolute paths:
                $dataValue = $this->copyRecord_procFilesRefs($dsConf, $uid, $dataValue);
 
                        // If references are set for this field, set flag so they can be corrected later (in ->remapListedDBRecords())
                if ($this->isReferenceField($dsConf) && strlen($dataValue)) {
+                       $dataValue = $this->copyRecord_procBasedOnFieldType($table, $uid, $field, $dataValue, array(), $dsConf, $realDestPid);
                        $this->registerDBList[$table][$uid][$field] = 'FlexForm_reference';
                }
 
@@ -3103,7 +3231,7 @@ class t3lib_TCEmain       {
        function copyRecord_procFilesRefs($conf, $uid, $value)  {
 
                        // Prepend absolute paths to files:
-               if ($conf['type']=='group' && $conf['internal_type']=='file')   {
+               if ($conf['type'] == 'group' && ($conf['internal_type'] == 'file' || $conf['internal_type'] == 'file_reference')) {
 
                                // Get an array with files as values:
                        if ($conf['MM'])        {
@@ -3123,13 +3251,13 @@ class t3lib_TCEmain     {
                        }
 
                                // Traverse this array of files:
-                       $uploadFolder = $conf['uploadfolder'];
+                       $uploadFolder = $conf['internal_type'] == 'file' ? $conf['uploadfolder'] : '';
                        $dest = $this->destPathFromUploadFolder($uploadFolder);
                        $newValue = array();
 
                        foreach($theFileValues as $file)        {
                                if (trim($file))        {
-                                       $realFile = $dest.'/'.trim($file);
+                                       $realFile = str_replace('//', '/', $dest . '/' . trim($file));
                                        if (@is_file($realFile))        {
                                                $newValue[] = $realFile;
                                        }
@@ -3185,7 +3313,7 @@ class t3lib_TCEmain       {
                                if (t3lib_div::isFirstPartOfStr($filename,'RTEmagicC_'))        {
 
                                        $fileInfo['exists'] = @is_file(PATH_site.$rec['ref_string']);
-                                       $fileInfo['original'] = substr($rec['ref_string'],0,-strlen($filename)).'RTEmagicP_'.ereg_replace('\.[[:alnum:]]+$','',substr($filename,10));
+                                       $fileInfo['original'] = substr($rec['ref_string'],0,-strlen($filename)).'RTEmagicP_'.preg_replace('/\.[[:alnum:]]+$/','',substr($filename,10));
                                        $fileInfo['original_exists'] = @is_file(PATH_site.$fileInfo['original']);
 
                                        // CODE from tx_impexp and class.rte_images.php adapted for use here:
@@ -3234,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']]);
+                       }
+               }
+       }
 
 
 
@@ -3259,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'];
                        }
@@ -3291,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']);
                                        }
@@ -3356,95 +3485,44 @@ class t3lib_TCEmain     {
                }
        }
 
+
        /**
-        * Creates a move placeholder for workspaces.
+        * Moves a record without checking security of any sort.
         * 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         Record uid to move
         * @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;
+       function moveRecord_raw($table,$uid,$destPid)   {
+               global $TCA, $TYPO3_CONF_VARS;
 
-               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']] = 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']] = time();
-                       }
+               $sortRow = $TCA[$table]['ctrl']['sortby'];
+               $origDestPid = $destPid;
+               $resolvedPid = $this->resolvePid($table,$destPid);      // This is the actual pid of the moving to destination
 
-                       $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.']';
+                       // Checking if the pid is negative, but no sorting row is defined. In that case, find the correct pid. Basically this check make the error message 4-13 meaning less... But you can always remove this check if you prefer the error instead of a no-good action (which is to move the record to its own page...)
+               if (($destPid<0 && !$sortRow) || $destPid>=0)   {       // $destPid>=0 because we must correct pid in case of versioning "page" types.
+                       $destPid = $resolvedPid;
+               }
 
-                       $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.
-        * USE ONLY INTERNALLY
-        *
-        * @param       string          Table name to move
-        * @param       integer         Record uid to move
-        * @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
-        * @return      void
-        * @see moveRecord()
-        */
-       function moveRecord_raw($table,$uid,$destPid)   {
-               global $TCA, $TYPO3_CONF_VARS;
-
-               $sortRow = $TCA[$table]['ctrl']['sortby'];
-               $origDestPid = $destPid;
-               $resolvedPid = $this->resolvePid($table,$destPid);      // This is the actual pid of the moving to destination
-
-                       // Checking if the pid is negative, but no sorting row is defined. In that case, find the correct pid. Basically this check make the error message 4-13 meaning less... But you can always remove this check if you prefer the error instead of a no-good action (which is to move the record to its own page...)
-               if (($destPid<0 && !$sortRow) || $destPid>=0)   {       // $destPid>=0 because we must correct pid in case of versioning "page" types.
-                       $destPid = $resolvedPid;
-               }
-
-               $propArr = $this->getRecordProperties($table,$uid);     // Get this before we change the pid (for logging)
-               $moveRec = $this->getRecordProperties($table,$uid,TRUE);
+               $propArr = $this->getRecordProperties($table,$uid);     // Get this before we change the pid (for logging)
+               $moveRec = $this->getRecordProperties($table,$uid,TRUE);
 
                        // Prepare user defined objects (if any) for hooks which extend this function:
                $hookObjectsArr = array();
                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) {
-                               $hookObjectsArr[] = &t3lib_div::getUserObj($classRef);
+                               $hookObjectsArr[] = t3lib_div::getUserObj($classRef);
                        }
                }
 
                        // Timestamp field:
                $updateFields = array();
                if ($TCA[$table]['ctrl']['tstamp'])     {
-                       $updateFields[$TCA[$table]['ctrl']['tstamp']] = time();
+                       $updateFields[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
                }
 
                if ($destPid>=0)        {       // insert as first element on page (where uid = $destPid)
@@ -3463,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) {
@@ -3506,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) {
@@ -3575,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']);
 
@@ -3587,16 +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)) {
-                       foreach ($dbAnalysis->itemArray as $v) {
-                               $this->moveRecord($v['table'],$v['id'],$destPid);
+                               // 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);
+                       }
+               }
+       }
+
+       /**
+        * 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);
                        }
                }
        }
@@ -3671,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:
@@ -3712,7 +3837,7 @@ class t3lib_TCEmain       {
         * @return      void
         */
        protected function inlineLocalizeSynchronize($table, $id, $command) {
-                       // <field>, (localize | synchronize | <uid>): 
+                       // <field>, (localize | synchronize | <uid>):
                $parts = t3lib_div::trimExplode(',', $command);
                $field = $parts[0];
                $type = $parts[1];
@@ -3761,15 +3886,17 @@ class t3lib_TCEmain     {
                                                                }
                                                        }
                                                }
-                                                       // Perform synchronization/localization: Possibly add unlocalized records for original language: 
+                                                       // Perform synchronization/localization: Possibly add unlocalized records for original language:
                                                if (t3lib_div::testInt($type) && isset($elementsOriginal[$type])) {
                                                        $item = $elementsOriginal[$type];
                                                        $item['id'] = $this->localize($item['table'], $item['id'], $language);
-                                                       $dbAnalysisCurrent->itemArray[] = $item; 
+                                                       $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);
-                                                               $dbAnalysisCurrent->itemArray[] = $item; 
+                                                               $item['id'] = $this->overlayAutoVersionId($item['table'], $item['id']);
+                                                               $dbAnalysisCurrent->itemArray[] = $item;
                                                        }
                                                }
                                                        // Store the new values, we will set up the uids for the subtype later on (exception keep localization from original record):
@@ -3823,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);
                        }
                }
        }
@@ -3919,7 +4023,9 @@ class t3lib_TCEmain       {
         * @return      void
         */
        function undeleteRecord($table,$uid)    {
-               $this->deleteRecord($table,$uid,TRUE,FALSE,TRUE);
+               if ($this->isRecordUndeletable($table, $uid)) {
+                       $this->deleteRecord($table, $uid, true, false, true);
+               }
        }
 
        /**
@@ -3935,11 +4041,11 @@ class t3lib_TCEmain     {
         * @param       boolean         If TRUE, the "deleted" flag is set to 0 again and thus, the item is undeleted.
         * @return      void
         */
-       function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE,$undeleteRecord=FALSE)  {
+       function deleteRecord($table,$uid, $noRecordCheck = FALSE, $forceHardDelete = FALSE, $undeleteRecord = FALSE) {
                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);
+               $mayEditAccess = $this->BE_USER->recordEditAccessInternals($table, $uid, FALSE, $undeleteRecord, TRUE);
 
                $uid = intval($uid);
                if ($TCA[$table] && $uid)       {
@@ -3958,7 +4064,7 @@ class t3lib_TCEmain       {
                                                );
 
                                                if ($TCA[$table]['ctrl']['tstamp']) {
-                                                       $updateFields[$TCA[$table]['ctrl']['tstamp']] = time();
+                                                       $updateFields[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
                                                }
 
                                                        // If the table is sorted, then the sorting number is set very high
@@ -3969,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:
@@ -4000,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
@@ -4020,7 +4133,7 @@ class t3lib_TCEmain       {
                                                                                $pagePropArr['header'],
                                                                                $propArr['pid']
                                                                                ),
-                                                                       $propArr['pid']);
+                                                                       $propArr['event_pid']);
 
                                        } else {
                                                $this->log($table,$uid,$state,0,100,$GLOBALS['TYPO3_DB']->sql_error());
@@ -4053,7 +4166,7 @@ class t3lib_TCEmain       {
         * @param       [type]          $pObj: ...
         * @return      [type]          ...
         */
-       function deleteRecord_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj) {
+       function deleteRecord_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, $pObj) {
 
                        // Use reference index object to find files in fields:
                $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
@@ -4064,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");
                                }
                        }
                }
@@ -4109,17 +4222,16 @@ class t3lib_TCEmain     {
         * @see deletePages()
         */
        function deleteSpecificPage($uid,$forceHardDelete=FALSE)        {
-               global $TCA;
-               reset ($TCA);
                $uid = intval($uid);
                if ($uid)       {
-                       while (list($table)=each($TCA)) {
+                       foreach (array_keys($GLOBALS['TCA']) as $table) {
                                if ($table!='pages')    {
                                        $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table));
                                        while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))     {
                                                $this->deleteVersionsForRecord($table,$row['uid'],$forceHardDelete);
                                                $this->deleteRecord($table,$row['uid'], TRUE, $forceHardDelete);
                                        }
+                                       $GLOBALS['TYPO3_DB']->sql_free_result($mres);
                                }
                        }
                        $this->deleteVersionsForRecord('pages',$uid,$forceHardDelete);
@@ -4139,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';
                        }
@@ -4170,7 +4290,37 @@ class t3lib_TCEmain      {
        }
 
        /**
-        * Beford a record is deleted, check if it has references such as inline type or MM references.
+        * Determines whether a record can be undeleted.
+        *
+        * @param       string          $table: Table name of the record
+        * @param       integer         $uid: uid of the record
+        * @return      boolean         Whether the record can be undeleted
+        */
+       public function isRecordUndeletable($table, $uid) {
+               $result = FALSE;
+               $record = t3lib_BEfunc::getRecord($table, $uid, 'pid', '', FALSE);
+               if ($record['pid']) {
+                       $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;
+                               // The page containing the record is deleted and has to be undeleted first:
+                       } else {
+                               $this->log(
+                                       $table, $uid, 'isRecordUndeletable', '', 1,
+                                       'Record cannot be undeleted since the page containing it is deleted! Undelete page "' .
+                                               $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;
+       }
+
+       /**
+        * Before a record is deleted, check if it has references such as inline type or MM references.
         * If so, set these child records also to be deleted.
         *
         * @param       string          $table: Record Table
@@ -4238,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']));
+                       }
+               }
+       }
 
 
 
@@ -4333,376 +4507,11 @@ 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'=>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'] = 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'] = 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->newlog('Swapping successful for table "'.$table.'" uid '.$id.'=>'.$swapWith);
-
-                                                                                               // 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 happend: '.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 happend: '.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)) {
-                       $stat = $this->BE_USER->checkWorkspaceCurrent();
-                       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->newlog('Stage for record was changed to '.$stageId.'. Comment was: "'.substr($comment,0,100).'"');
-// 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()
         *
         * @param       string  Table for the two input records
@@ -4713,32 +4522,32 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         */
        function version_remapMMForVersionSwap($table,$id,$swapWith)    {
                global $TCA;
-               
+
                        // Actually, selecting the records fully is only need if flexforms are found inside... This could be optimized ...
                $currentRec = t3lib_BEfunc::getRecord($table,$id);
                $swapRec = t3lib_BEfunc::getRecord($table,$swapWith);
-               
+
                $this->version_remapMMForVersionSwap_reg = array();
-               
+
                foreach($TCA[$table]['columns'] as $field => $fConf) {
                        $conf = $fConf['config'];
-                       
+
                        if ($this->isReferenceField($conf))     {
                                $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table'];
                                $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
                                if ($conf['MM'])        {
-                                       
+
                                        $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
                                        /* @var $dbAnalysis t3lib_loadDBGroup */
                                        $dbAnalysis->start('', $allowedTables, $conf['MM'], $id, $table, $conf);
-                                       if (count($dbAnalysis->getValueArray($prependName)))    {       
+                                       if (count($dbAnalysis->getValueArray($prependName)))    {
                                                $this->version_remapMMForVersionSwap_reg[$id][$field] = array($dbAnalysis, $conf['MM'], $prependName);
                                        }
-                                                                               
+
                                        $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
                                        /* @var $dbAnalysis t3lib_loadDBGroup */
                                        $dbAnalysis->start('', $allowedTables, $conf['MM'], $swapWith, $table, $conf);
-                                       if (count($dbAnalysis->getValueArray($prependName)))    {       
+                                       if (count($dbAnalysis->getValueArray($prependName)))    {
                                                $this->version_remapMMForVersionSwap_reg[$swapWith][$field] = array($dbAnalysis, $conf['MM'], $prependName);
                                        }
                                }
@@ -4775,7 +4584,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                                }
                        }
                }
-               
+
                        // Execute:
                $this->version_remapMMForVersionSwap_execSwap($table,$id,$swapWith);
        }
@@ -4803,7 +4612,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                        if ($dsConf['MM'])      {
                                $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
                                /* @var $dbAnalysis t3lib_loadDBGroup */
-                               $dbAnalysis->start('', $allowedTables, $dsConf['MM'], $uid, $table, $dsConf);                           
+                               $dbAnalysis->start('', $allowedTables, $dsConf['MM'], $uid, $table, $dsConf);
                                $this->version_remapMMForVersionSwap_reg[$uid][$field.'/'.$path] = array($dbAnalysis, $dsConf['MM'], $prependName);
                        }
                }
@@ -4820,7 +4629,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @see version_remapMMForVersionSwap()
         */
        function version_remapMMForVersionSwap_execSwap($table,$id,$swapWith)   {
-               
+
                if (is_array($this->version_remapMMForVersionSwap_reg[$id]))    {
                        foreach($this->version_remapMMForVersionSwap_reg[$id] as $field => $str)        {
                                $str[0]->remapMM($str[1],$id,-$id,$str[2]);
@@ -4865,11 +4674,9 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                global $TCA;
 
                if (count($this->registerDBList))       {
-                       reset($this->registerDBList);
-                       while(list($table,$records)=each($this->registerDBList))        {
+                       foreach ($this->registerDBList as $table => $records) {
                                t3lib_div::loadTCA($table);
-                               reset($records);
-                               while(list($uid,$fields)=each($records))        {
+                               foreach ($records as $uid => $fields) {
                                        $newData = array();
                                        $theUidToUpdate = $this->copyMappingArray_merged[$table][$uid];
                                        $theUidToUpdate_saveTo = t3lib_BEfunc::wsMapId($table,$theUidToUpdate);
@@ -5024,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;
@@ -5036,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);
                                                }
                                        }
@@ -5061,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:
@@ -5130,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;
        }
 
        /**
@@ -5192,6 +5064,13 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         */
        function checkModifyAccessList($table)  {
                $res = ($this->admin || (!$this->tableAdminOnly($table) && t3lib_div::inList($this->BE_USER->groupData['tables_modify'],$table)));
+
+                       // Hook 'checkModifyAccessList': Post-processing of the state of access
+               foreach($this->getCheckModifyAccessListHookObjects() as $hookObject) {
+                       /* @var $hookObject t3lib_TCEmain_checkModifyAccessListHook */
+                       $hookObject->checkModifyAccessList($res, $table, $this);
+               }
+
                return $res;
        }
 
@@ -5228,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];
@@ -5280,7 +5174,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                }
                return $res;
        }
-       
+
        /**
         * Checks if a table is allowed on a certain page id according to allowed tables set for the page "doktype" and its [ctrl][rootLevel]-settings if any.
         *
@@ -5347,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)) {
@@ -5436,6 +5335,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                                        return -1;              // No permissions
                                }
                        }
+                       $GLOBALS['TYPO3_DB']->sql_free_result($mres);
                }
                return $inList;
        }
@@ -5505,13 +5405,10 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      array           Array of [table]-[field] pairs to exclude from editing.
         */
        function getExcludeListArray()  {
-               global $TCA;
-
                $list = array();
-               reset($TCA);
-               while (list($table)=each($TCA)) {
+               foreach (array_keys($GLOBALS['TCA']) as $table) {
                        t3lib_div::loadTCA($table);
-                       while (list($field,$config)=each($TCA[$table]['columns']))      {
+                       foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $config) {
                                if ($config['exclude'] && !t3lib_div::inList($this->BE_USER->groupData['non_exclude_fields'],$table.':'.$field))        {
                                        $list[]=$table.'-'.$field;
                                }
@@ -5528,7 +5425,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      array           Returns a list of the tables that are 'present' on the page but not allowed with the page_uid/doktype
         */
        function doesPageHaveUnallowedTables($page_uid,$doktype)        {
-               global $TCA, $PAGES_TYPES;
+               global $PAGES_TYPES;
 
                $page_uid = intval($page_uid);
                if (!$page_uid) {
@@ -5541,13 +5438,11 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                        return FALSE;   // OK...
                }
 
-               reset ($TCA);
                $tableList = array();
-               while (list($table)=each($TCA)) {
+               foreach (array_keys($GLOBALS['TCA']) as $table) {
                        if (!in_array($table,$allowedArray))    {       // If the table is not in the allowed list, check if there are records...
-                               $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid='.intval($page_uid));
-                               $count = $GLOBALS['TYPO3_DB']->sql_fetch_row($mres);
-                               if ($count[0])  {
+                               $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', $table, 'pid=' . intval($page_uid));
+                               if ($count) {
                                        $tableList[]=$table;
                                }
                        }
@@ -5624,7 +5519,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                if (!$noWSOL)   {
                        t3lib_BEfunc::workspaceOL($table,$row);
                }
-               t3lib_BEfunc::fixVersioningPid($table,$row);
+
                return $this->getRecordPropertiesFromRow($table,$row);
        }
 
@@ -5638,10 +5533,12 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
        function getRecordPropertiesFromRow($table,$row)        {
                global $TCA;
                if ($TCA[$table])       {
+                       t3lib_BEfunc::fixVersioningPid($table,$row);
+
                        $out = array(
                                'header' => $row[$TCA[$table]['ctrl']['label']],
                                'pid' => $row['pid'],
-                               'event_pid' => ($table=='pages'?$row['uid']:$row['pid']),
+                               'event_pid' => $this->eventPid($table,isset($row['_ORIG_pid'])?$row['t3ver_oid']:$row['uid'],$row['pid']),
                                't3ver_state' => $TCA[$table]['ctrl']['versioningWS'] ? $row['t3ver_state'] : '',
                                '_ORIG_pid' => $row['_ORIG_pid']
                        );
@@ -5649,6 +5546,10 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                }
        }
 
+       function eventPid($table,$uid,$pid)     {
+               return $table=='pages' ? $uid : $pid;
+       }
+
 
 
 
@@ -5703,7 +5604,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
 
                                                // Set log entry:
                                        $propArr = $this->getRecordPropertiesFromRow($table,$newRow);
-                                       $theLogId = $this->log($table,$id,2,$propArr['pid'],0,"Record '%s' (%s) was updated.",10,array($propArr['header'],$table.':'.$id),$propArr['event_pid']);
+                                       $theLogId = $this->log($table,$id,2,$propArr['pid'],0,"Record '%s' (%s) was updated.".($propArr['_ORIG_pid']==-1?' (Offline version).':' (Online).'),10,array($propArr['header'],$table.':'.$id),$propArr['event_pid']);
 
                                                // Set History data:
                                        $this->setHistory($table,$id,$theLogId);
@@ -5779,7 +5680,8 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                                        $this->updateRefIndex($table,$id);
 
                                        if ($newVersion)        {
-                                               $this->log($table,$id,1,0,0,"New version created of table '%s', uid '%s'",10,array($table,$fieldArray['t3ver_oid']),$newRow['pid'],$NEW_id);
+                                               $propArr = $this->getRecordPropertiesFromRow($table,$newRow);
+                                               $this->log($table,$id,1,0,0,"New version created of table '%s', uid '%s'. UID of new version is '%s'",10,array($table,$fieldArray['t3ver_oid'],$id),$propArr['event_pid'],$NEW_id);
                                        } else {
                                                $propArr = $this->getRecordPropertiesFromRow($table,$newRow);
                                                $page_propArr = $this->getRecordProperties('pages',$propArr['pid']);
@@ -5861,7 +5763,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                        $fields_values = array();
                        $fields_values['history_data'] = serialize($this->historyRecords[$table.':'.$id]);
                        $fields_values['fieldlist'] = implode(',',array_keys($this->historyRecords[$table.':'.$id]['newRecord']));
-                       $fields_values['tstamp'] = time();
+                       $fields_values['tstamp'] = $GLOBALS['EXEC_TIME'];
                        $fields_values['tablename'] = $table;
                        $fields_values['recuid'] = $id;
                        $fields_values['sys_log_uid'] = $logId;
@@ -5878,7 +5780,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      void
         */
        function clearHistory($maxAgeSeconds=604800,$table)     {
-               $tstampLimit = $maxAgeSeconds ? time()-$maxAgeSeconds : 0;
+               $tstampLimit = $maxAgeSeconds ? $GLOBALS['EXEC_TIME'] - $maxAgeSeconds : 0;
 
                $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_history', 'tstamp<'.intval($tstampLimit).' AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history'));
                }
@@ -6017,6 +5919,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                                } else {die ('Fatal ERROR!! No Uid at resorting.');}
                                $i = $i+$intervals;
                        }
+                       $GLOBALS['TYPO3_DB']->sql_free_result($res);
                        return $returnVal;
                }
        }
@@ -6047,13 +5950,10 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      array           Array with default values.
         */
        function newFieldArray($table)  {
-               global $TCA;
-
                t3lib_div::loadTCA($table);
                $fieldArray=Array();
-               if (is_array($TCA[$table]['columns']))  {
-                       reset ($TCA[$table]['columns']);
-                       while (list($field,$content)=each($TCA[$table]['columns']))     {
+               if (is_array($GLOBALS['TCA'][$table]['columns'])) {
+                       foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $content) {
                                if (isset($this->defaultValues[$table][$field]))        {
                                        $fieldArray[$field] = $this->defaultValues[$table][$field];
                                } elseif (isset($content['config']['default'])) {
@@ -6141,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.
@@ -6167,7 +6077,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
        function assemblePermissions($string)   {
                $keyArr = t3lib_div::trimExplode(',',$string,1);
                $value=0;
-               while(list(,$key)=each($keyArr))        {
+               foreach ($keyArr as $key) {
                        if ($key && isset($this->pMap[$key]))   {
                                $value |= $this->pMap[$key];
                        }
@@ -6182,7 +6092,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      string          Output string with any comma in the end removed, if any.
         */
        function rmComma($input)        {
-               return ereg_replace(',$','',$input);
+               return rtrim($input, ',');
        }
 
        /**
@@ -6193,7 +6103,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         */
        function convNumEntityToByteValue($input)       {
                $token = md5(microtime());
-               $parts = explode($token,ereg_replace('(&#([0-9]+);)',$token.'\2'.$token,$input));
+               $parts = explode($token,preg_replace('/(&#([0-9]+);)/',$token.'\2'.$token,$input));
 
                foreach($parts as $k => $v)     {
                        if ($k%2)       {
@@ -6218,6 +6128,17 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
        }
 
        /**
+        * Disables the delete clause for fetching records.
+        * In general only undeleted records will be used. If the delete
+        * clause is disabled, also deleted records are taken into account.
+        *
+        * @return      void
+        */
+       public function disableDeleteClause() {
+               $this->disableDeleteClause = true;
+       }
+
+       /**
         * Returns delete-clause for the $table
         *
         * @param       string          Table name
@@ -6226,7 +6147,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
        function deleteClause($table)   {
                        // Returns the proper delete-clause if any for a table from TCA
                global $TCA;
-               if ($TCA[$table]['ctrl']['delete'])     {
+               if (!$this->disableDeleteClause && $TCA[$table]['ctrl']['delete']) {
                        return ' AND '.$table.'.'.$TCA[$table]['ctrl']['delete'].'=0';
                } else {
                        return '';
@@ -6281,12 +6202,13 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      void
         */
        function dbAnalysisStoreExec()  {
-               reset($this->dbAnalysisStore);
-               while(list($k,$v)=each($this->dbAnalysisStore)) {
-                       $id = t3lib_BEfunc::wsMapId($v[4],$this->substNEWwithIDs[$v[2]]);
+               foreach ($this->dbAnalysisStore as $action) {
+                       $id = t3lib_BEfunc::wsMapId(
+                               $action[4],
+                               (t3lib_div::testInt($action[2]) ? $action[2] : $this->substNEWwithIDs[$action[2]])
+                       );
                        if ($id)        {
-                               $v[2] = $id;
-                               $v[0]->writeMM($v[1],$v[2],$v[3]);
+                               $action[0]->writeMM($action[1], $id, $action[3]);
                        }
                }
        }
@@ -6297,9 +6219,8 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      void
         */
        function removeRegisteredFiles()        {
-               reset($this->removeFilesStore);
-               while(list($k,$v)=each($this->removeFilesStore))        {
-                       unlink($v);
+               foreach ($this->removeFilesStore as $file) {
+                       unlink($file);
                }
        }
 
@@ -6334,6 +6255,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                                        }
                                }
                        }
+                       $GLOBALS['TYPO3_DB']->sql_free_result($mres);
                }
                return $CPtable;
        }
@@ -6344,13 +6266,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      array           Array of all TCA table names
         */
        function compileAdminTables()   {
-               global $TCA;
-               reset ($TCA);
-               $listArr = array();
-               while (list($table)=each($TCA)) {
-                       $listArr[]=$table;
-               }
-               return $listArr;
+               return array_keys($GLOBALS['TCA']);
        }
 
        /**
@@ -6361,13 +6277,11 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      void
         */
        function fixUniqueInPid($table,$uid)    {
-               global $TCA;
-               if ($TCA[$table])       {
+               if ($GLOBALS['TCA'][$table]) {
                        t3lib_div::loadTCA($table);
-                       reset ($TCA[$table]['columns']);
                        $curData=$this->recordInfo($table,$uid,'*');
                        $newData=array();
-                       while (list($field,$conf)=each($TCA[$table]['columns']))        {
+                       foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $conf) {
                                if ($conf['config']['type']=='input')   {
                                        $evalCodesArray = t3lib_div::trimExplode(',',$conf['config']['eval'],1);
                                        if (in_array('uniqueInPid',$evalCodesArray))    {
@@ -6402,8 +6316,7 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                        t3lib_div::loadTCA($table);
                        $prevData=$this->recordInfo($table,$prevUid,'*');
                        $theFields = t3lib_div::trimExplode(',',$TCA[$table]['ctrl']['copyAfterDuplFields'],1);
-                       reset($theFields);
-                       while(list(,$field)=each($theFields))   {
+                       foreach ($theFields as $field) {
                                if ($TCA[$table]['columns'][$field] && ($update || !isset($newData[$field])))   {
                                        $newData[$field]=$prevData[$field];
                                }
@@ -6422,15 +6335,15 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      array           Array of fieldnames that are either "group" or "file" types.
         */
        function extFileFields($table)  {
-               global $TCA;
                $listArr=array();
                t3lib_div::loadTCA($table);
-               if ($TCA[$table]['columns'])    {
-                       reset($TCA[$table]['columns']);
-                       while (list($field,$configArr)=each($TCA[$table]['columns']))   {
-                               if ($configArr['config']['type']=='group' && $configArr['config']['internal_type']=='file')     {
-                                       $listArr[]=$field;
-                               }
+               if (isset($GLOBALS['TCA'][$table]['columns'])) {
+                       foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $configArr) {
+                               if ($configArr['config']['type'] == 'group' &&
+                                       ($configArr['config']['internal_type'] == 'file' ||
+                                        $configArr['config']['internal_type'] == 'file_reference')) {
+                                       $listArr[] = $field;
+                               }
                        }
                }
                return $listArr;
@@ -6443,13 +6356,10 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      array           Array of fieldnames
         */
        function getUniqueFields($table)        {
-               global $TCA;
-
                $listArr=array();
                t3lib_div::loadTCA($table);
-               if ($TCA[$table]['columns'])    {
-                       reset($TCA[$table]['columns']);
-                       while (list($field,$configArr)=each($TCA[$table]['columns']))   {
+               if ($GLOBALS['TCA'][$table]['columns']) {
+                       foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $configArr) {
                                if ($configArr['config']['type']==='input')     {
                                        $evalCodesArray = t3lib_div::trimExplode(',',$configArr['config']['eval'],1);
                                        if (in_array('uniqueInPid',$evalCodesArray) || in_array('unique',$evalCodesArray))      {
@@ -6514,8 +6424,14 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
 
                        // Do check:
                if ($prevTitle != $checkTitle || $count<100)    {
-                       $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($pid).' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($checkTitle, $table).$this->deleteClause($table), '', '', '1');
-                       if ($GLOBALS['TYPO3_DB']->sql_num_rows($res))   {
+                       $rowCount = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
+                               'uid',
+                               $table,
+                               'pid=' . intval($pid) .
+                                       ' AND ' . $field . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($checkTitle, $table) .
+                                       $this->deleteClause($table)
+                       );
+                       if ($rowCount) {
                                return $this->getCopyHeader($table,$pid,$field,$value,$count+1,$checkTitle);
                        }
                }
@@ -6580,8 +6496,8 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         */
        function clearPrefixFromValue($table,$value)    {
                global $TCA;
-               $regex = sprintf(quotemeta($this->prependLabel($table)),'[0-9]*').'$';
-               return @ereg_replace($regex,'',$value);
+               $regex = '/'.sprintf(quotemeta($this->prependLabel($table)),'[0-9]*').'$/';
+               return @preg_replace($regex,'',$value);
        }
 
        /**
@@ -6597,10 +6513,10 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
                global $TCA;
                t3lib_div::loadTCA($table);
                $uploadFolder = $TCA[$table]['columns'][$field]['config']['uploadfolder'];
-               if ($uploadFolder && trim($filelist))   {
+               if ($uploadFolder && trim($filelist) && $TCA[$table]['columns'][$field]['config']['internal_type'] == 'file') {
                        $uploadPath = $this->destPathFromUploadFolder($uploadFolder);
                        $fileArray = explode(',',$filelist);
-                       while (list(,$theFile)=each($fileArray))        {
+                       foreach ($fileArray as $theFile) {
                                $theFile=trim($theFile);
                                if ($theFile)   {
                                        switch($func)   {
@@ -6624,14 +6540,15 @@ $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stag
         * @return      boolean         Return true, if permission granted
         */
        function noRecordsFromUnallowedTables($inList)  {
-               global $TCA;
-               reset ($TCA);
                $inList = trim($this->rmComma(trim($inList)));
                if ($inList && !$this->admin)   {
-                       while (list($table) = each($TCA))       {
-                               $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid IN ('.$inList.')'.t3lib_BEfunc::deleteClause($table));
-                               $count = $GLOBALS['TYPO3_DB']->sql_fetch_row($mres);
-                               if ($count[0] && ($this->tableReadOnly($table) || !$this->checkModifyAccessList($table)))       {
+                       foreach (array_keys($GLOBALS['TCA']) as $table) {
+                               $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
+                                       'uid',
+                                       $table,
+                                       'pid IN (' . $inList . ')' . t3lib_BEfunc::deleteClause($table)
+                               );
+                               if ($count && ($this->tableReadOnly($table) || !$this->checkModifyAccessList($table))) {
                                        return FALSE;
                                }
                        }
@@ -6639,149 +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']));
-                                               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)
-                               );
-                       }
-               }
-       }
-
-       /**
-        * 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.
@@ -6831,6 +6605,7 @@ State was change by %s (username: %s)
                global $TCA, $TYPO3_CONF_VARS;
 
                $uid = intval($uid);
+               $pageUid = 0;
                if (is_array($TCA[$table]) && $uid > 0) {
 
                                // Get Page TSconfig relavant:
@@ -6842,13 +6617,20 @@ State was change by %s (username: %s)
                                        // If table is "pages":
                                if (t3lib_extMgm::isLoaded('cms'))      {
                                        $list_cache = array();
-                                       if ($table=='pages')    {
+
+                                       if ($table === 'pages' || $table === 'pages_language_overlay')  {
+
+                                               if($table === 'pages_language_overlay') {
+                                                       $pageUid = $this->getPID($table,$uid);
+                                               } else {
+                                                       $pageUid = $uid;
+                                               }
 
                                                        // Builds list of pages on the SAME level as this page (siblings)
                                                $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
                                                                                'A.pid AS pid, B.uid AS uid',
                                                                                'pages A, pages B',
-                                                                               'A.uid='.intval($uid).' AND B.pid=A.pid AND B.deleted=0'
+                                                                               'A.uid='.intval($pageUid).' AND B.pid=A.pid AND B.deleted=0'
                                                                        );
 
                                                $pid_tmp = 0;
@@ -6866,8 +6648,10 @@ State was change by %s (username: %s)
                                                                while ($row_tmp2 = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp2))    {
                                                                        $list_cache[] = $row_tmp2['uid'];
                                                                }
+                                                               $GLOBALS['TYPO3_DB']->sql_free_result($res_tmp2);
                                                        }
                                                }
+                                               $GLOBALS['TYPO3_DB']->sql_free_result($res_tmp);
 
                                                        // Finally, add the parent page as well:
                                                $list_cache[] = $pid_tmp;
@@ -6884,7 +6668,7 @@ State was change by %s (username: %s)
                                                        }
                                                }
                                        } else {        // For other tables than "pages", delete cache for the records "parent page".
-                                               $list_cache[] = intval($this->getPID($table,$uid));
+                                               $list_cache[] = $pageUid = intval($this->getPID($table,$uid));
                                        }
 
                                                // Call pre-processing function for clearing of cache for page ids:
@@ -6898,8 +6682,24 @@ State was change by %s (username: %s)
 
                                                // Delete cache for selected pages:
                                        if (is_array($list_cache))      {
-                                               $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')');
-                                               $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection', 'page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')');
+                                               if (TYPO3_UseCachingFramework) {
+                                                       $pageCache = $GLOBALS['typo3CacheManager']->getCache(
+                                                               'cache_pages'
+                                                       );
+                                                       $pageSectionCache = $GLOBALS['typo3CacheManager']->getCache(
+                                                               'cache_pagesection'
+                                                       );
+
+                                                       $pageIds = $GLOBALS['TYPO3_DB']->cleanIntArray($list_cache);
+                                                       foreach ($pageIds as $pageId) {
+                                                               $pageCache->flushByTag('pageId_' . $pageId);
+                                                               $pageSectionCache->flushByTag('pageId_' . $pageId);
+                                                       }
+                                               } else {
+                                                       $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')');
+                                                       $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection', 'page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')');
+
+                                               }
                                        }
                                }
                        }
@@ -6914,10 +6714,8 @@ State was change by %s (username: %s)
                        }
 
                                // Call post processing function for clear-cache:
-                       global $TYPO3_CONF_VARS;
                        if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc']))    {
-// FIXME $uid_page is undefined
-                               $_params = array('table' => $table,'uid' => $uid,'uid_page' => $uid_page,'TSConfig' => $TSConfig);
+                               $_params = array('table' => $table,'uid' => $uid,'uid_page' => $pageUid,'TSConfig' => $TSConfig);
                                foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef)     {
                                        t3lib_div::callUserFunction($_funcRef,$_params,$this);
                                }
@@ -6926,20 +6724,42 @@ State was change by %s (username: %s)
        }
 
        /**
-        * Clears the cache based on a command, $cacheCmd
+        * Clears the cache based on the command $cacheCmd.
         *
-        * $cacheCmd='pages':   Clears cache for all pages. Requires admin-flag to be set for BE_USER
-        * $cacheCmd='all':             Clears all cache_tables. This is necessary if templates are updated. Requires admin-flag to be set for BE_USER
-        * $cacheCmd=[integer]:         Clears cache for the page pointed to by $cacheCmd (an integer).
+        * $cacheCmd='pages':   Clears cache for all pages. Requires admin-flag to
+        * be set for BE_USER.
         *
-        * Can call a list of post processing functions as defined in $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] (num array with values being the function references, called by t3lib_div::callUserFunction())
+        * $cacheCmd='all':             Clears all cache_tables. This is necessary if
+        * templates are updated. Requires admin-flag to be set for BE_USER.
         *
-        * @param       string          The cache comment, see above description.
+        * $cacheCmd=[integer]: Clears cache for the page pointed to by $cacheCmd
+        * (an integer).
+        *
+        * Can call a list of post processing functions as defined in
+        * $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc']
+        * (numeric array with values being the function references, called by
+        * t3lib_div::callUserFunction()).
+        *
+        * Note: The following cache_* are intentionally not cleared by
+        * $cacheCmd='all':
+        *
+        * - cache_md5params:   Clearing this table would destroy all simulateStatic
+        *                                              URLs, simulates file name and RDCT redirects.
+        * - cache_imagesizes:  Clearing this table would cause a lot of unneeded
+        *                                              Imagemagick calls because the size informations have
+        *                                              to be fetched again after clearing.
+        * - cache_extensions:  Clearing this table would make the extension manager
+        *                                              unusable until a new extension list is fetched from
+        *                                              the TER.
+        *
+        * @param       string          the cache command, see above description
         * @return      void
         */
-       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':
@@ -6949,26 +6769,46 @@ State was change by %s (username: %s)
                        break;
                        case 'all':
                                if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.all'))   {
-                                       if (t3lib_extMgm::isLoaded('cms'))      {
+
+                                               // Clear all caching framework caches if it is initialized:
+                                               // (it could be disabled by initialized by an extension)
+                                       if (t3lib_cache::isCachingFrameworkInitialized()) {
+                                               $GLOBALS['typo3CacheManager']->flushCaches();
+                                       }
+
+                                       if (TYPO3_UseCachingFramework) {
+                                               if (t3lib_extMgm::isLoaded('cms'))      {
+                                                       $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_treelist');
+                                               }
+                                       } else {
+                                               if (t3lib_extMgm::isLoaded('cms'))      {
+                                                       $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_treelist');
+                                                       $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_pagesection');
+                                               }
                                                $this->internal_clearPageCache();
-                                               $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection','');
+                                               $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_hash');
                                        }
-                                       $GLOBALS['TYPO3_DB']->exec_DELETEquery('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 (!ereg('[^[:alnum:]_]',$tableName) && substr($tableName,-5)=='cache')        {
-                                                               $GLOBALS['TYPO3_DB']->exec_DELETEquery($tableName,'');
+                                                       if (!preg_match('/[^[:alnum:]_]/',$tableName) && substr($tableName,-5)=='cache')        {
+                                                               $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
+                                                               );
                                                        }
                                                }
                                        }
                                }
+                               if ($this->admin && $TYPO3_CONF_VARS['EXT']['extCache']) {
+                                       $this->removeCacheFiles();
+                               }
                        break;
                        case 'temp_CACHED':
-                               if ($this->admin && $TYPO3_CONF_VARS['EXT']['extCache'])        {
+                               if ($this->admin && $TYPO3_CONF_VARS['EXT']['extCache']) {
                                        $this->removeCacheFiles();
                                }
                        break;
@@ -6990,9 +6830,25 @@ State was change by %s (username: %s)
                                }
 
                                        // Delete cache for selected pages:
-                               if (is_array($list_cache