[TASK] Remove t3lib_superadmin
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_tcemain.php
index 1b90a48..2367eef 100644 (file)
  *
  * @author     Kasper Skårhøj <kasperYYYY@typo3.com>
  */
-/**
- * [CLASS/FUNCTION INDEX of SCRIPT]
- *
- *
- *
- *  242: class t3lib_TCEmain
- *  367:        function start($data,$cmd,$altUserObject='')
- *  406:        function setMirror($mirror)
- *  431:        function setDefaultsFromUserTS($userTS)
- *  454:        function process_uploads($postFiles)
- *  492:        function process_uploads_traverseArray(&$outputArr,$inputArr,$keyToSet)
- *
- *                       SECTION: PROCESSING DATA
- *  528:        function process_datamap()
- *  886:        function placeholderShadowing($table,$id)
- *  929:        function fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$realPid,$status,$tscPID)
- *
- *                       SECTION: Evaluation of input values
- * 1152:        function checkValue($table,$field,$value,$id,$status,$realPid,$tscPID)
- * 1212:        function checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$uploadedFiles,$tscPID)
- * 1261:        function checkValue_input($res,$value,$tcaFieldConf,$PP,$field='')
- * 1299:        function checkValue_check($res,$value,$tcaFieldConf,$PP)
- * 1322:        function checkValue_radio($res,$value,$tcaFieldConf,$PP)
- * 1348:        function checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field)
- * 1458:        function checkValue_group_select_file($valueArray,$tcaFieldConf,$curValue,$uploadedFileArray,$status,$table,$id,$recFID)
- * 1632:        function checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field)
- * 1709:        function checkValue_flexArray2Xml($array, $addPrologue=FALSE)
- * 1721:        function _DELETE_FLEX_FORMdata(&$valueArrayToRemoveFrom,$deleteCMDS)
- * 1743:        function _MOVE_FLEX_FORMdata(&$valueArrayToMoveIn, $moveCMDS, $direction)
- * 1783:        function checkValue_inline($res,$value,$tcaFieldConf,$PP,$field)
- * 1825:        function checkValue_checkMax($tcaFieldConf, $valueArray)
- *
- *                       SECTION: Helper functions for evaluation functions.
- * 1877:        function getUnique($table,$field,$value,$id,$newPid=0)
- * 1915:        function checkValue_input_Eval($value,$evalArray,$is_in)
- * 2012:        function checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,$type,$currentTable)
- * 2058:        function checkValue_group_select_explodeSelectGroupValue($value)
- * 2082:        function checkValue_flex_procInData($dataPart,$dataPart_current,$uploadedFiles,$dataStructArray,$pParams,$callBackFunc='')
- * 2121:        function checkValue_flex_procInData_travDS(&$dataValues,$dataValues_current,$uploadedFiles,$DSelements,$pParams,$callBackFunc,$structurePath)
- *
- *                       SECTION: PROCESSING COMMANDS
- * 2267:        function process_cmdmap()
- *
- *                       SECTION: Cmd: Copying
- * 2407:        function copyRecord($table,$uid,$destPid,$first=0,$overrideValues=array(),$excludeFields='')
- * 2529:        function copyPages($uid,$destPid)
- * 2583:        function copySpecificPage($uid,$destPid,$copyTablesArray,$first=0)
- * 2617:        function copyRecord_raw($table,$uid,$pid,$overrideArray=array())
- * 2681:        function rawCopyPageContent($old_pid,$new_pid,$copyTablesArray)
- * 2705:        function insertNewCopyVersion($table,$fieldArray,$realPid)
- * 2757:        function copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf,$realDestPid)
- * 2836:        function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)
- * 2864:        function copyRecord_procFilesRefs($conf, $uid, $value)
- *
- *                       SECTION: Cmd: Moving, Localizing
- * 2933:        function moveRecord($table,$uid,$destPid)
- * 3128:        function moveRecord_procFields($table,$uid,$destPid)
- * 3148:        function moveRecord_procBasedOnFieldType($table,$uid,$destPid,$field,$value,$conf)
- * 3182:        function localize($table,$uid,$language)
- *
- *                       SECTION: Cmd: Deleting
- * 3296:        function deleteAction($table, $id)
- * 3343:        function deleteEl($table, $uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE)
- * 3360:        function deleteVersionsForRecord($table, $uid, $forceHardDelete)
- * 3382:        function undeleteRecord($table,$uid)
- * 3399:        function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE,$undeleteRecord=FALSE)
- * 3512:        function deleteRecord_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj)
- * 3539:        function deletePages($uid,$force=FALSE,$forceHardDelete=FALSE)
- * 3567:        function deleteSpecificPage($uid,$forceHardDelete=FALSE)
- * 3592:        function canDeletePage($uid)
- * 3619:        function cannotDeleteRecord($table,$id)
- * 3638:        function deleteRecord_procFields($table, $uid, $undeleteRecord = false)
- * 3661:        function deleteRecord_procBasedOnFieldType($table, $uid, $field, $value, $conf, $undeleteRecord = false)
- *
- *                       SECTION: Cmd: Versioning
- * 3722:        function versionizeRecord($table,$id,$label,$delete=FALSE,$versionizeTree=-1)
- * 3798:        function versionizePages($uid,$label,$versionizeTree)
- * 3861:        function version_swap($table,$id,$swapWith,$swapIntoWS=0)
- * 4032:        function version_clearWSID($table,$id)
- * 4066:        function version_setStage($table,$id,$stageId,$comment='')
- *
- *                       SECTION: Cmd: Helper functions
- * 4111:        function remapListedDBRecords()
- * 4192:        function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)
- * 4219:        function remapListedDBRecords_procDBRefs($conf, $value, $MM_localUid, $table)
- * 4265:        function remapListedDBRecords_procInline($conf, $value, $uid, $table)
- *
- *                       SECTION: Access control / Checking functions
- * 4308:        function checkModifyAccessList($table)
- * 4320:        function isRecordInWebMount($table,$id)
- * 4334:        function isInWebMount($pid)
- * 4348:        function checkRecordUpdateAccess($table,$id)
- * 4372:        function checkRecordInsertAccess($insertTable,$pid,$action=1)
- * 4406:        function isTableAllowedForThisPage($page_uid, $checkTable)
- * 4439:        function doesRecordExist($table,$id,$perms)
- * 4504:        function doesRecordExist_pageLookUp($id, $perms)
- * 4530:        function doesBranchExist($inList,$pid,$perms,$recurse)
- * 4564:        function tableReadOnly($table)
- * 4576:        function tableAdminOnly($table)
- * 4590:        function destNotInsideSelf($dest,$id)
- * 4622:        function getExcludeListArray()
- * 4645:        function doesPageHaveUnallowedTables($page_uid,$doktype)
- *
- *                       SECTION: Information lookup
- * 4694:        function pageInfo($id,$field)
- * 4714:        function recordInfo($table,$id,$fieldList)
- * 4735:        function getRecordProperties($table,$id,$noWSOL=FALSE)
- * 4751:        function getRecordPropertiesFromRow($table,$row)
- *
- *                       SECTION: Storing data to Database Layer
- * 4794:        function updateDB($table,$id,$fieldArray)
- * 4846:        function insertDB($table,$id,$fieldArray,$newVersion=FALSE,$suggestedUid=0,$dontSetNewIdIndex=FALSE)
- * 4919:        function checkStoredRecord($table,$id,$fieldArray,$action)
- * 4956:        function setHistory($table,$id,$logId)
- * 4989:        function clearHistory($maxAgeSeconds=604800,$table)
- * 5003:        function updateRefIndex($table,$id)
- *
- *                       SECTION: Misc functions
- * 5035:        function getSortNumber($table,$uid,$pid)
- * 5108:        function resorting($table,$pid,$sortRow, $return_SortNumber_After_This_Uid)
- * 5139:        function setTSconfigPermissions($fieldArray,$TSConfig_p)
- * 5156:        function newFieldArray($table)
- * 5188:        function addDefaultPermittedLanguageIfNotSet($table,&$incomingFieldArray)
- * 5212:        function overrideFieldArray($table,$data)
- * 5228:        function compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray)
- * 5274:        function assemblePermissions($string)
- * 5291:        function rmComma($input)
- * 5301:        function convNumEntityToByteValue($input)
- * 5323:        function destPathFromUploadFolder($folder)
- * 5333:        function deleteClause($table)
- * 5349:        function getTCEMAIN_TSconfig($tscPID)
- * 5364:        function getTableEntries($table,$TSconfig)
- * 5377:        function getPID($table,$uid)
- * 5390:        function dbAnalysisStoreExec()
- * 5406:        function removeRegisteredFiles()
- * 5418:        function removeCacheFiles()
- * 5432:        function int_pageTreeInfo($CPtable,$pid,$counter, $rootID)
- * 5453:        function compileAdminTables()
- * 5470:        function fixUniqueInPid($table,$uid)
- * 5506:        function fixCopyAfterDuplFields($table,$uid,$prevUid,$update, $newData=array())
- * 5531:        function extFileFields($table)
- * 5552:        function getUniqueFields($table)
- * 5577:        function isReferenceField($conf)
- * 5588:        function getInlineFieldType($conf)
- * 5611:        function getCopyHeader($table,$pid,$field,$value,$count,$prevTitle='')
- * 5640:        function prependLabel($table)
- * 5657:        function resolvePid($table,$pid)
- * 5687:        function clearPrefixFromValue($table,$value)
- * 5702:        function extFileFunctions($table,$field,$filelist,$func)
- * 5732:        function noRecordsFromUnallowedTables($inList)
- * 5758:        function notifyStageChange($stat,$stageId,$table,$id,$comment)
- * 5853:        function notifyStageChange_getEmails($listOfUsers,$noTablePrefix=FALSE)
- *
- *                       SECTION: Clearing cache
- * 5899:        function clear_cache($table,$uid)
- * 6009:        function clear_cacheCmd($cacheCmd)
- *
- *                       SECTION: Logging
- * 6113:        function log($table,$recuid,$action,$recpid,$error,$details,$details_nr=-1,$data=array(),$event_pid=-1,$NEWid='')
- * 6130:        function newlog($message, $error=0)
- * 6140:        function printLogErrorMessages($redirect)
- *
- *                       SECTION: Internal (do not use outside Core!)
- * 6202:        function internal_clearPageCache()
- *
- * TOTAL FUNCTIONS: 126
- * (This index is automatically created/updated by the extension "extdeveval")
- *
- */
 
 
 /**
@@ -283,7 +114,7 @@ class t3lib_TCEmain {
        var $username; // will be set to username of be_user executing this script
        var $admin; // will be set if user is admin
 
-       var $defaultPermissions = array( // Can be overridden from $TYPO3_CONF_VARS
+       var $defaultPermissions = array( // Can be overridden from $GLOBALS['TYPO3_CONF_VARS']
                'user' => 'show,edit,delete,new,editcontent',
                'group' => 'show,edit,new,editcontent',
                'everybody' => ''
@@ -342,6 +173,12 @@ class t3lib_TCEmain {
        protected $checkModifyAccessListHookObjects;
        protected $version_remapMMForVersionSwap_reg;
 
+       /**
+        * The outer most instance of t3lib_TCEmain
+        * (t3lib_TCEmain instantiates itself on versioning and localization)
+        * @var t3lib_TCEmain
+        */
+       protected $outerMostInstance = NULL;
 
        /**
         * Initializing.
@@ -406,7 +243,6 @@ class t3lib_TCEmain {
                if (is_array($mirror)) {
                        foreach ($mirror as $table => $uid_array) {
                                if (isset($this->datamap[$table])) {
-                                       reset($uid_array);
                                        foreach ($uid_array as $id => $uidList) {
                                                if (isset($this->datamap[$table][$id])) {
                                                        $theIdsInArray = t3lib_div::trimExplode(',', $uidList, 1);
@@ -458,8 +294,7 @@ class t3lib_TCEmain {
                                return FALSE;
                        }
 
-                       reset($postFiles);
-                       $subA = current($postFiles);
+                       $subA = reset($postFiles);
                        if (is_array($subA)) {
                                if (is_array($subA['name']) && is_array($subA['type']) && is_array($subA['tmp_name']) && is_array($subA['size'])) {
                                                // Initialize the uploadedFilesArray:
@@ -578,6 +413,9 @@ class t3lib_TCEmain {
                        // Keep versionized(!) relations here locally:
                $registerDBList = array();
 
+               $this->registerElementsToBeDeleted();
+               $this->datamap = $this->unsetElementsToBeDeleted($this->datamap);
+
                        // Editing frozen:
                if ($this->BE_USER->workspace !== 0 && $this->BE_USER->workspaceRec['freeze']) {
                        $this->newlog('All editing in this workspace has been frozen!', 1);
@@ -638,7 +476,7 @@ class t3lib_TCEmain {
                                                $old_pid_value = '';
                                                $this->autoVersioningUpdate = FALSE;
 
-                                               if (!t3lib_div::testInt($id)) { // Is it a new record? (Then Id is a string)
+                                               if (!t3lib_utility_Math::canBeInterpretedAsInteger($id)) { // Is it a new record? (Then Id is a string)
                                                        $fieldArray = $this->newFieldArray($table); // Get a fieldArray with default values
                                                        if (isset($incomingFieldArray['pid'])) { // A pid must be set for new records.
                                                                        // $value = the pid
@@ -855,6 +693,7 @@ class t3lib_TCEmain {
                                                                        $newVersion_placeholderFieldArray[$GLOBALS['TCA'][$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
                                                                }
                                                        }
+                                                               // Set stage to "Editing" to make sure we restart the workflow
                                                        if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
                                                                $fieldArray['t3ver_stage'] = 0;
                                                        }
@@ -873,7 +712,7 @@ class t3lib_TCEmain {
                                                                        if ($createNewVersion) { // This creates a new version of the record with online placeholder and offline version
                                                                                $versioningType = ($table === 'pages'
                                                                                        ? $this->BE_USER->workspaceVersioningTypeGetClosest(
-                                                                                               t3lib_div::intInRange($GLOBALS['TYPO3_CONF_VARS']['BE']['newPagesVersioningType'], -1, 1))
+                                                                                               t3lib_utility_Math::forceIntegerInRange($GLOBALS['TYPO3_CONF_VARS']['BE']['newPagesVersioningType'], -1, 1))
                                                                                        : -1);
                                                                                if ($this->BE_USER->workspaceVersioningTypeAccess($versioningType)) {
                                                                                        $newVersion_placeholderFieldArray['t3ver_label'] = 'INITIAL PLACEHOLDER';
@@ -944,6 +783,10 @@ class t3lib_TCEmain {
                                $hookObj->processDatamap_afterAllOperations($this);
                        }
                }
+
+               if ($this->isOuterMostInstance()) {
+                       $this->resetElementsToBeDeleted();
+               }
        }
 
        /**
@@ -1826,6 +1669,7 @@ class t3lib_TCEmain {
                                                        $theFile = substr($theFile, strlen(PATH_site));
                                                }
                                        }
+                                       unset($theFile);
                                }
                        }
                }
@@ -1976,7 +1820,7 @@ class t3lib_TCEmain {
                        // Example for received data:
                        // $value = 45,NEW4555fdf59d154,12,123
                        // We need to decide whether we use the stack or can save the relation directly.
-               if (strpos($value, 'NEW') !== FALSE || !t3lib_div::testInt($id)) {
+               if (strpos($value, 'NEW') !== FALSE || !t3lib_utility_Math::canBeInterpretedAsInteger($id)) {
                        $this->remapStackRecords[$table][$id] = array('remapStackIndex' => count($this->remapStack));
                        $this->addNewValuesToRemapStackChildIds($valueArray);
                        $this->remapStack[] = array(
@@ -1986,7 +1830,7 @@ class t3lib_TCEmain {
                                'field' => $field
                        );
                        unset($res['value']);
-               } elseif ($value || t3lib_div::testInt($id)) {
+               } elseif ($value || t3lib_utility_Math::canBeInterpretedAsInteger($id)) {
                        $res['value'] = $this->checkValue_inline_processDBdata($valueArray, $tcaFieldConf, $id, $status, $table, $field);
                }
 
@@ -2093,7 +1937,7 @@ class t3lib_TCEmain {
                                        }
                                break;
                                default:
-                                       if (substr($func, 0, 3) == 'tx_') {
+                                       if (t3lib_div::hasValidClassPrefix($func)) {
                                                $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);
@@ -2198,8 +2042,17 @@ class t3lib_TCEmain {
                                case 'alphanum_x':
                                        $value = preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
                                break;
+                               case 'domainname':
+                                       if (!preg_match('/^[a-z0-9\.\-]*$/i', $value)) {
+                                               t3lib_div::requireOnce(PATH_typo3 . 'contrib/idna/idna_convert.class.php');
+                                               $idnaConvert = new idna_convert();
+                                               $idnaConvert->set_parameter('idn_version', '2008');
+                                               $value = $idnaConvert->encode($value);
+                                               unset($idnaConvert);
+                                       }
+                               break;
                                default:
-                                       if (substr($func, 0, 3) == 'tx_') {
+                                       if (t3lib_div::hasValidClassPrefix($func)) {
                                                $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);
@@ -2283,6 +2136,7 @@ class t3lib_TCEmain {
                                str_replace('|', '', rawurldecode($temp[0]))
                        );
                }
+               unset($newVal);
                return $valueArray;
        }
 
@@ -2688,6 +2542,9 @@ class t3lib_TCEmain {
                        }
                }
 
+               if ($this->isOuterMostInstance()) {
+                       $this->resetNestedElementCalls();
+               }
        }
 
 
@@ -2941,14 +2798,22 @@ class t3lib_TCEmain {
         * This function is used to create new versions of a record.
         * NOTICE: DOES NOT CHECK PERMISSIONS to create! And since page permissions are just passed through and not changed to the user who executes the copy we cannot enforce permissions without getting an incomplete copy - unless we change permissions of course.
         *
-        * @param       string          Element table
-        * @param       integer         Element UID
-        * @param       integer         Element PID (real PID, not checked)
-        * @param       array           Override array - must NOT contain any fields not in the table!
+        * @param       string          $table Element table
+        * @param       integer         $uid Element UID
+        * @param       integer         $pid Element PID (real PID, not checked)
+        * @param       array           $overrideArray Override array - must NOT contain any fields not in the table!
+        * @return      array           $workspaceOptions Options to be forwarded if actions happen on a workspace currently
         * @return      integer         Returns the new ID of the record (if applicable)
         */
-       function copyRecord_raw($table, $uid, $pid, $overrideArray = array()) {
+       function copyRecord_raw($table, $uid, $pid, $overrideArray = array(), array $workspaceOptions = array()) {
                $uid = intval($uid);
+
+               // Stop any actions if the record is marked to be deleted:
+               // (this can occur if IRRE elements are versionized and child elements are removed)
+               if ($this->isElementToBeDeleted($table, $uid)) {
+                       return NULL;
+               }
+
                        // Only copy if the table is defined in TCA, a uid is given and the record wasn't copied before:
                if ($GLOBALS['TCA'][$table] && $uid && !$this->isRecordCopied($table, $uid)) {
                        t3lib_div::loadTCA($table);
@@ -2972,7 +2837,7 @@ class t3lib_TCEmain {
                                                        $conf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
                                                        if (is_array($conf)) {
                                                                        // Processing based on the TCA config field type (files, references, flexforms...)
-                                                               $value = $this->copyRecord_procBasedOnFieldType($table, $uid, $field, $value, $row, $conf, $pid);
+                                                               $value = $this->copyRecord_procBasedOnFieldType($table, $uid, $field, $value, $row, $conf, $pid, 0, $workspaceOptions);
                                                        }
 
                                                                // Add value to array.
@@ -3062,55 +2927,78 @@ class t3lib_TCEmain {
        /**
         * Processing/Preparing content for copyRecord() function
         *
-        * @param       string          Table name
-        * @param       integer         Record uid
-        * @param       string          Field name being processed
-        * @param       string          Input value to be processed.
-        * @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) used in the duplicated record
-        * @return      mixed           Processed value. Normally a string/integer, but can be an array for flexforms!
+        * @param       string          $table Table name
+        * @param       integer         $uid Record uid
+        * @param       string          $field Field name being processed
+        * @param       string          $value Input value to be processed.
+        * @param       array           $row Record array
+        * @param       array           $conf TCA field configuration
+        * @param       integer         $realDestPid Real page id (pid) the record is copied to
+        * @param       integer         $language Language ID (from sys_language table) used in the duplicated record
+        * @return      array           $workspaceOptions Options to be forwarded if actions happen on a workspace currently
         * @access private
         * @see copyRecord()
         */
-       function copyRecord_procBasedOnFieldType($table, $uid, $field, $value, $row, $conf, $realDestPid, $language = 0) {
+       function copyRecord_procBasedOnFieldType($table, $uid, $field, $value, $row, $conf, $realDestPid, $language = 0, array $workspaceOptions = array()) {
 
                        // Process references and files, currently that means only the files, prepending absolute paths (so the TCEmain engine will detect the file as new and one that should be made into a copy)
                $value = $this->copyRecord_procFilesRefs($conf, $uid, $value);
                $inlineSubType = $this->getInlineFieldType($conf);
 
+                       // Get the localization mode for the current (parent) record (keep|select):
+               $localizationMode = t3lib_BEfunc::getInlineLocalizationMode($table, $field);
+
                        // Register if there are references to take care of or MM is used on an inline field (no change to value):
                if ($this->isReferenceField($conf) || $inlineSubType == 'mm') {
                        $allowedTables = $conf['type'] == 'group' ? $conf['allowed'] : $conf['foreign_table'] . ',' . $conf['neg_foreign_table'];
                        $prependName = $conf['type'] == 'group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
-                       $localizeReferences = (isset($conf['foreign_table']) && t3lib_BEfunc::isTableLocalizable($conf['foreign_table']) && isset($conf['localizeReferencesAtParentLocalization']) && $conf['localizeReferencesAtParentLocalization']);
-                       if ($conf['MM'] || $language > 0 && $localizeReferences) {
-                               $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
-                               /** @var $dbAnalysis t3lib_loadDBGroup */
-                               $dbAnalysis->start($value, $allowedTables, $conf['MM'], $uid, $table, $conf);
-                               if (!$conf['MM']) {
-                                               // Localize referenced records of select fields:
-                                       foreach ($dbAnalysis->itemArray as $index => $item) {
-                                                       // Since select fields can reference many records, check whether there's already a localization:
-                                               $recordLocalization = t3lib_BEfunc::getRecordLocalization($item['table'], $item['id'], $language);
-                                               if (!$recordLocalization) {
-                                                       $dbAnalysis->itemArray[$index]['id'] = $this->localize($item['table'], $item['id'], $language);
-                                               } else {
-                                                       $dbAnalysis->itemArray[$index]['id'] = $recordLocalization[0]['uid'];
-                                               }
+
+                       $mmTable = (isset($conf['MM']) && $conf['MM'] ? $conf['MM'] : '');
+                       $localizeForeignTable = (isset($conf['foreign_table']) && t3lib_BEfunc::isTableLocalizable($conf['foreign_table']));
+                       $localizeReferences = ($localizeForeignTable && isset($conf['localizeReferencesAtParentLocalization']) && $conf['localizeReferencesAtParentLocalization']);
+                       $localizeChildren = ($localizeForeignTable && isset($conf['behaviour']['localizeChildrenAtParentLocalization']) && $conf['behaviour']['localizeChildrenAtParentLocalization']);
+
+                       /** @var $dbAnalysis t3lib_loadDBGroup */
+                       $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
+                       $dbAnalysis->start($value, $allowedTables, $mmTable, $uid, $table, $conf);
+
+                               // Localize referenced records of select fields:
+                       if ($language > 0 && ($localizeReferences && empty($mmTable) || $localizeChildren && $localizationMode === 'select' && $inlineSubType === 'mm')) {
+                               foreach ($dbAnalysis->itemArray as $index => $item) {
+                                               // Since select fields can reference many records, check whether there's already a localization:
+                                       $recordLocalization = t3lib_BEfunc::getRecordLocalization($item['table'], $item['id'], $language);
+                                       if ($recordLocalization) {
+                                               $dbAnalysis->itemArray[$index]['id'] = $recordLocalization[0]['uid'];
+                                       } elseif ($this->isNestedElementCallRegistered($item['table'], $item['id'], 'localize') === FALSE) {
+                                               $dbAnalysis->itemArray[$index]['id'] = $this->localize($item['table'], $item['id'], $language);
+                                       }
+                               }
+                               $value = implode(',', $dbAnalysis->getValueArray($prependName));
+
+                               // If IRRE MM references are not followed on localization, use at least the existing ones:
+                       } elseif ($language > 0 && $localizeChildren === FALSE && $localizationMode === 'select' && $inlineSubType === 'mm') {
+                               foreach ($dbAnalysis->itemArray as $index => $item) {
+                                               // Since select fields can reference many records, check whether there's already a localization:
+                                       $recordLocalization = t3lib_BEfunc::getRecordLocalization($item['table'], $item['id'], $language);
+                                       if ($recordLocalization) {
+                                               $dbAnalysis->itemArray[$index]['id'] = $recordLocalization[0]['uid'];
+                                       } elseif ($this->isNestedElementCallRegistered($item['table'], $item['id'], 'localize') === FALSE) {
+                                               unset($dbAnalysis->itemArray[$index]);
                                        }
                                }
                                $value = implode(',', $dbAnalysis->getValueArray($prependName));
+
+                               // Just ensure that the references are correct by using the existing ones:
+                       } elseif ($mmTable) {
+                               $value = implode(',', $dbAnalysis->getValueArray($prependName));
                        }
+
                        if ($value) { // Setting the value in this array will notify the remapListedDBRecords() function that this field MAY need references to be corrected
                                $this->registerDBList[$table][$uid][$field] = $value;
                        }
 
                        // 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):
-                       $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 && $localizationMode == 'keep') {
                                $value = ($inlineSubType == 'field' ? 0 : '');
@@ -3134,7 +3022,7 @@ class t3lib_TCEmain {
                                                }
                                                        // If no language it set, this is a regular copy action:
                                        } else {
-                                               if (!t3lib_div::testInt($realDestPid)) {
+                                               if (!t3lib_utility_Math::canBeInterpretedAsInteger($realDestPid)) {
                                                        $newId = $this->copyRecord($v['table'], $v['id'], -$v['id']);
                                                } elseif ($realDestPid == -1 && t3lib_BEfunc::isTableWorkspaceEnabled($v['table'])) {
                                                        $workspaceVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord(
@@ -3142,13 +3030,17 @@ class t3lib_TCEmain {
                                                        );
                                                                // 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);
+                                                               $newId = $this->versionizeRecord(
+                                                                       $v['table'], $v['id'],
+                                                                       (isset($workspaceOptions['label']) ? $workspaceOptions['label'] : 'Auto-created for WS #' . $this->BE_USER->workspace),
+                                                                       (isset($workspaceOptions['delete']) ? $workspaceOptions['delete'] : FALSE)
+                                                               );
                                                                // If workspace version already exists, use it:
                                                        } else {
                                                                $newId = $workspaceVersion['uid'];
                                                        }
                                                } else {
-                                                       $newId = $this->copyRecord_raw($v['table'], $v['id'], $realDestPid);
+                                                       $newId = $this->copyRecord_raw($v['table'], $v['id'], $realDestPid, array(), $workspaceOptions);
                                                }
                                        }
 
@@ -3287,8 +3179,6 @@ class t3lib_TCEmain {
         * @return      void
         */
        function copyRecord_fixRTEmagicImages($table, $theNewSQLID) {
-               global $TYPO3_DB;
-
                        // Creating fileFunc object.
                if (!$this->fileFunc) {
                        $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions');
@@ -3296,14 +3186,13 @@ class t3lib_TCEmain {
                }
 
                        // Select all RTEmagic files in the reference table from the table/ID
-               /* @var $TYPO3_DB t3lib_DB */
-               $recs = $TYPO3_DB->exec_SELECTgetRows(
+               $recs = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
                        '*',
                        'sys_refindex',
-                       'ref_table=' . $TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex') .
-                       ' AND ref_string LIKE ' . $TYPO3_DB->fullQuoteStr('%/RTEmagic%', 'sys_refindex') .
-                       ' AND softref_key=' . $TYPO3_DB->fullQuoteStr('images', 'sys_refindex') .
-                       ' AND tablename=' . $TYPO3_DB->fullQuoteStr($table, 'sys_refindex') .
+                       'ref_table=' . $GLOBALS['TYPO3_DB']->fullQuoteStr('_FILE', 'sys_refindex') .
+                       ' AND ref_string LIKE ' . $GLOBALS['TYPO3_DB']->fullQuoteStr('%/RTEmagic%', 'sys_refindex') .
+                       ' AND softref_key=' . $GLOBALS['TYPO3_DB']->fullQuoteStr('images', 'sys_refindex') .
+                       ' AND tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_refindex') .
                        ' AND recuid=' . intval($theNewSQLID),
                        '',
                        'sorting DESC'
@@ -3422,7 +3311,7 @@ class t3lib_TCEmain {
                                // 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
+                               // @deprecated 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'];
@@ -3728,13 +3617,14 @@ class t3lib_TCEmain {
         * @param       string          Table name
         * @param       integer         Record uid (to be localized)
         * @param       integer         Language ID (from sys_language table)
-        * @return      mixed           The uid (integer) of the new translated record or false (boolean) if something went wrong
+        * @return      mixed           The uid (integer) of the new translated record or FALSE (boolean) if something went wrong
         */
        function localize($table, $uid, $language) {
                $newId = FALSE;
                $uid = intval($uid);
 
-               if ($GLOBALS['TCA'][$table] && $uid) {
+               if ($GLOBALS['TCA'][$table] && $uid && $this->isNestedElementCallRegistered($table, $uid, 'localize') === FALSE) {
+                       $this->registerNestedElementCall($table, $uid, 'localize');
                        t3lib_div::loadTCA($table);
 
                        if (($GLOBALS['TCA'][$table]['ctrl']['languageField'] && $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']
@@ -3865,7 +3755,7 @@ class t3lib_TCEmain {
                $field = $parts[0];
                $type = $parts[1];
 
-               if ($field && (t3lib_div::inList('localize,synchronize', $type) || t3lib_div::testInt($type)) && isset($GLOBALS['TCA'][$table]['columns'][$field]['config'])) {
+               if ($field && (t3lib_div::inList('localize,synchronize', $type) || t3lib_utility_Math::canBeInterpretedAsInteger($type)) && isset($GLOBALS['TCA'][$table]['columns'][$field]['config'])) {
                        $config = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
                        $foreignTable = $config['foreign_table'];
                        $localizationMode = t3lib_BEfunc::getInlineLocalizationMode($table, $config);
@@ -3882,19 +3772,20 @@ class t3lib_TCEmain {
 
                                        if ($inlineSubType !== FALSE) {
                                                $removeArray = array();
+                                               $mmTable = ($inlineSubType == 'mm' && isset($config['MM']) && $config['MM'] ? $config['MM'] : '');
                                                        // Fetch children from original language parent:
                                                /** @var $dbAnalysisOriginal t3lib_loadDBGroup */
                                                $dbAnalysisOriginal = t3lib_div::makeInstance('t3lib_loadDBGroup');
-                                               $dbAnalysisOriginal->start($transOrigRecord[$field], $foreignTable, '', $transOrigRecord['uid'], $table, $config);
+                                               $dbAnalysisOriginal->start($transOrigRecord[$field], $foreignTable, $mmTable, $transOrigRecord['uid'], $table, $config);
                                                $elementsOriginal = array();
                                                foreach ($dbAnalysisOriginal->itemArray as $item) {
                                                        $elementsOriginal[$item['id']] = $item;
                                                }
                                                unset($dbAnalysisOriginal);
                                                        // Fetch children from current localized parent:
-                                                       // @var $dbAnalysisCurrent t3lib_loadDBGroup
+                                               /** @var $dbAnalysisCurrent t3lib_loadDBGroup */
                                                $dbAnalysisCurrent = t3lib_div::makeInstance('t3lib_loadDBGroup');
-                                               $dbAnalysisCurrent->start($parentRecord[$field], $foreignTable, '', $id, $table, $config);
+                                               $dbAnalysisCurrent->start($parentRecord[$field], $foreignTable, $mmTable, $id, $table, $config);
                                                        // Perform synchronization: Possibly removal of already localized records:
                                                if ($type == 'synchronize') {
                                                        foreach ($dbAnalysisCurrent->itemArray as $index => $item) {
@@ -3910,7 +3801,7 @@ class t3lib_TCEmain {
                                                        }
                                                }
                                                        // Perform synchronization/localization: Possibly add unlocalized records for original language:
-                                               if (t3lib_div::testInt($type) && isset($elementsOriginal[$type])) {
+                                               if (t3lib_utility_Math::canBeInterpretedAsInteger($type) && isset($elementsOriginal[$type])) {
                                                        $item = $elementsOriginal[$type];
                                                        $item['id'] = $this->localize($item['table'], $item['id'], $language);
                                                        $item['id'] = $this->overlayAutoVersionId($item['table'], $item['id']);
@@ -3939,6 +3830,9 @@ class t3lib_TCEmain {
                                                } elseif ($inlineSubType == 'field') {
                                                        $dbAnalysisCurrent->writeForeignField($config, $id);
                                                        $updateFields = array($field => $dbAnalysisCurrent->countItems(FALSE));
+                                               } elseif ($inlineSubType == 'mm') {
+                                                       $dbAnalysisCurrent->writeMM($config['MM'], $id);
+                                                       $updateFields = array($field => $dbAnalysisCurrent->countItems(FALSE));
                                                }
                                                        // Update field referencing to child records of localized parent record:
                                                if (is_array($updateFields) && count($updateFields)) {
@@ -3965,16 +3859,14 @@ class t3lib_TCEmain {
         * @return      void
         */
        function deleteAction($table, $id) {
-               global $TYPO3_CONF_VARS;
-
                $recordToDelete = t3lib_BEfunc::getRecord($table, $id);
 
                        // Record asked to be deleted was found:
                if (is_array($recordToDelete)) {
                        $recordWasDeleted = FALSE;
 
-                       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) {
+                       if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'])) {
+                               foreach ($GLOBALS['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);
@@ -4304,7 +4196,7 @@ class t3lib_TCEmain {
        }
 
        /**
-        * Returns TRUE if record CANNOT be deleted, otherwise false. Used to check before the versioning API allows a record to be marked for deletion.
+        * Returns TRUE if record CANNOT be deleted, otherwise FALSE. Used to check before the versioning API allows a record to be marked for deletion.
         *
         * @param       string          Record Table
         * @param       integer         Record UID
@@ -4466,6 +4358,12 @@ class t3lib_TCEmain {
        function versionizeRecord($table, $id, $label, $delete = FALSE, $versionizeTree = -1) {
                $id = intval($id);
 
+               // Stop any actions if the record is marked to be deleted:
+               // (this can occur if IRRE elements are versionized and child elements are removed)
+               if ($this->isElementToBeDeleted($table, $id)) {
+                       return NULL;
+               }
+
                if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] && $id > 0) {
                        if ($this->doesRecordExist($table, $id, 'show')) {
                                if ($this->BE_USER->workspaceVersioningTypeAccess($versionizeTree)) {
@@ -4520,7 +4418,16 @@ class t3lib_TCEmain {
                                                                        if ($workspaceCheck) {
 
                                                                                        // Create raw-copy and return result:
-                                                                               return $this->copyRecord_raw($table, $id, -1, $overrideArray);
+                                                                                       // The information of the label to be used for the workspace record
+                                                                                       // as well as the information whether the record shall be removed
+                                                                                       // must be forwarded (creating remove placeholders on a workspace are
+                                                                                       // done by copying the record and override several fields).
+                                                                               $workspaceOptions = array();
+                                                                               if ($delete) {
+                                                                                       $workspaceOptions['delete'] = $delete;
+                                                                                       $workspaceOptions['label'] = $label;
+                                                                               }
+                                                                               return $this->copyRecord_raw($table, $id, -1, $overrideArray, $workspaceOptions);
                                                                        } else {
                                                                                $this->newlog('Record "' . $table . ':' . $id . '" you wanted to versionize was already a version in the workspace (wsid=' . $this->BE_USER->workspace . ')!', 1);
                                                                        }
@@ -5257,7 +5164,7 @@ class t3lib_TCEmain {
                $id = intval($id);
 
                        // Processing the incoming $perms (from possible string to integer that can be AND'ed)
-               if (!t3lib_div::testInt($perms)) {
+               if (!t3lib_utility_Math::canBeInterpretedAsInteger($perms)) {
                        if ($table != 'pages') {
                                switch ($perms) {
                                        case 'edit':
@@ -5398,7 +5305,7 @@ class t3lib_TCEmain {
         *
         * @param       integer         Destination Page ID to test
         * @param       integer         Page ID to test for presence inside Destination
-        * @return      boolean         Returns false if ID is inside destination (including equal to)
+        * @return      boolean         Returns FALSE if ID is inside destination (including equal to)
         */
        function destNotInsideSelf($dest, $id) {
                $loopCheck = 100;
@@ -5453,14 +5360,12 @@ class t3lib_TCEmain {
         * @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 $PAGES_TYPES;
-
                $page_uid = intval($page_uid);
                if (!$page_uid) {
                        return FALSE; // Not a number. Probably a new page
                }
 
-               $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables'];
+               $allowedTableList = isset($GLOBALS['PAGES_TYPES'][$doktype]['allowedTables']) ? $GLOBALS['PAGES_TYPES'][$doktype]['allowedTables'] : $GLOBALS['PAGES_TYPES']['default']['allowedTables'];
                $allowedArray = t3lib_div::trimExplode(',', $allowedTableList, 1);
                if (strstr($allowedTableList, '*')) { // If all tables is OK the return TRUE
                        return FALSE; // OK...
@@ -5511,7 +5416,7 @@ class t3lib_TCEmain {
         * @param       string          Table name
         * @param       integer         UID of the record from $table
         * @param       string          Field list for the SELECT query, eg. "*" or "uid,pid,..."
-        * @return      mixed           Returns the selected record on success, otherwise false.
+        * @return      mixed           Returns the selected record on success, otherwise FALSE.
         */
        function recordInfo($table, $id, $fieldList) {
                if (is_array($GLOBALS['TCA'][$table])) {
@@ -5556,7 +5461,7 @@ class t3lib_TCEmain {
                        t3lib_BEfunc::fixVersioningPid($table, $row);
 
                        $out = array(
-                               'header' => $row[$GLOBALS['TCA'][$table]['ctrl']['label']],
+                               'header' => t3lib_BEfunc::getRecordTitle($table, $row),
                                'pid' => $row['pid'],
                                'event_pid' => $this->eventPid($table, isset($row['_ORIG_pid']) ? $row['t3ver_oid'] : $row['uid'], $row['pid']),
                                't3ver_state' => $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ? $row['t3ver_state'] : '',
@@ -5757,7 +5662,7 @@ class t3lib_TCEmain {
                        $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
 
                        $tE = $this->getTableEntries($table, $TSConfig);
-                       $maxAgeSeconds = 60 * 60 * 24 * (strcmp($tE['history.']['maxAgeDays'], '') ? t3lib_div::intInRange($tE['history.']['maxAgeDays'], 0, 365) : 30); // one month
+                       $maxAgeSeconds = 60 * 60 * 24 * (strcmp($tE['history.']['maxAgeDays'], '') ? t3lib_utility_Math::forceIntegerInRange($tE['history.']['maxAgeDays'], 0, 365) : 30); // one month
 
                                // Garbage collect old entries:
                        $this->clearHistory($maxAgeSeconds, $table);
@@ -5816,7 +5721,7 @@ class t3lib_TCEmain {
         * @param       string          Table name
         * @param       integer         Uid of record to find sorting number for. May be zero in case of new.
         * @param       integer         Positioning PID, either >=0 (pointing to page in which case we find sorting number for first record in page) or <0 (pointing to record in which case to find next sorting number after this record)
-        * @return      mixed           Returns integer if PID is >=0, otherwise an array with PID and sorting number. Possibly false in case of error.
+        * @return      mixed           Returns integer if PID is >=0, otherwise an array with PID and sorting number. Possibly FALSE in case of error.
         */
        function getSortNumber($table, $uid, $pid) {
                if ($GLOBALS['TCA'][$table] && $GLOBALS['TCA'][$table]['ctrl']['sortby']) {
@@ -5932,13 +5837,13 @@ class t3lib_TCEmain {
                        $fieldArray['perms_groupid'] = intval($TSConfig_p['groupid']);
                }
                if (strcmp($TSConfig_p['user'], '')) {
-                       $fieldArray['perms_user'] = t3lib_div::testInt($TSConfig_p['user']) ? $TSConfig_p['user'] : $this->assemblePermissions($TSConfig_p['user']);
+                       $fieldArray['perms_user'] = t3lib_utility_Math::canBeInterpretedAsInteger($TSConfig_p['user']) ? $TSConfig_p['user'] : $this->assemblePermissions($TSConfig_p['user']);
                }
                if (strcmp($TSConfig_p['group'], '')) {
-                       $fieldArray['perms_group'] = t3lib_div::testInt($TSConfig_p['group']) ? $TSConfig_p['group'] : $this->assemblePermissions($TSConfig_p['group']);
+                       $fieldArray['perms_group'] = t3lib_utility_Math::canBeInterpretedAsInteger($TSConfig_p['group']) ? $TSConfig_p['group'] : $this->assemblePermissions($TSConfig_p['group']);
                }
                if (strcmp($TSConfig_p['everybody'], '')) {
-                       $fieldArray['perms_everybody'] = t3lib_div::testInt($TSConfig_p['everybody']) ? $TSConfig_p['everybody'] : $this->assemblePermissions($TSConfig_p['everybody']);
+                       $fieldArray['perms_everybody'] = t3lib_utility_Math::canBeInterpretedAsInteger($TSConfig_p['everybody']) ? $TSConfig_p['everybody'] : $this->assemblePermissions($TSConfig_p['everybody']);
                }
 
                return $fieldArray;
@@ -6211,7 +6116,7 @@ class t3lib_TCEmain {
                foreach ($this->dbAnalysisStore as $action) {
                        $id = t3lib_BEfunc::wsMapId(
                                $action[4],
-                               (t3lib_div::testInt($action[2]) ? $action[2] : $this->substNEWwithIDs[$action[2]])
+                               (t3lib_utility_Math::canBeInterpretedAsInteger($action[2]) ? $action[2] : $this->substNEWwithIDs[$action[2]])
                        );
                        if ($id) {
                                $action[0]->writeMM($action[1], $id, $action[3]);
@@ -6388,26 +6293,26 @@ class t3lib_TCEmain {
 
        /**
         * Returns the subtype as a string of an inline field.
-        * If it's not a inline field at all, it returns false.
+        * If it's not a inline field at all, it returns FALSE.
         *
         * @param       array           config array for TCA/columns field
-        * @return      mixed           string: inline subtype (field|mm|list), boolean: false
+        * @return      mixed           string: inline subtype (field|mm|list), boolean: FALSE
         */
        function getInlineFieldType($conf) {
-               if ($conf['type'] == 'inline' && $conf['foreign_table']) {
-                       if ($conf['foreign_field']) {
-                               return 'field';
-                       } // the reference to the parent is stored in a pointer field in the child record
-                       elseif ($conf['MM'])
-                       {
-                               return 'mm';
-                       } // regular MM intermediate table is used to store data
-                       else
-                       {
-                               return 'list';
-                       } // an item list (separated by comma) is stored (like select type is doing)
+               if ($conf['type'] !== 'inline' || !$conf['foreign_table']) {
+                       return FALSE;
+               }
+
+               if ($conf['foreign_field']) {
+                               // the reference to the parent is stored in a pointer field in the child record
+                       return 'field';
+               } elseif ($conf['MM']) {
+                               // regular MM intermediate table is used to store data
+                       return 'mm';
+               } else {
+                               // an item list (separated by comma) is stored (like select type is doing)
+                       return 'list';
                }
-               return FALSE;
        }
 
 
@@ -6672,30 +6577,19 @@ class t3lib_TCEmain {
                                        if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) {
                                                foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) {
                                                        $_params = array('pageIdArray' => &$list_cache, 'table' => $table, 'uid' => $uid, 'functionID' => 'clear_cache()');
-                                                               // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array!
+                                                               // Returns the array of ids to clear, FALSE if nothing should be cleared! Never an empty array!
                                                        t3lib_div::callUserFunction($funcName, $_params, $this);
                                                }
                                        }
 
                                                // Delete cache for selected pages:
                                        if (is_array($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)) . ')');
-
+                                               $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);
                                                }
                                        }
                                }
@@ -6733,7 +6627,7 @@ class t3lib_TCEmain {
         * (an integer).
         *
         * Can call a list of post processing functions as defined in
-        * $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc']
+        * $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc']
         * (numeric array with values being the function references, called by
         * t3lib_div::callUserFunction()).
         *
@@ -6753,8 +6647,6 @@ class t3lib_TCEmain {
         * @return      void
         */
        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!
@@ -6766,29 +6658,16 @@ class t3lib_TCEmain {
                        break;
                        case 'all':
                                if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.all')) {
+                                               // Clear all caching framework caches
+                                       $GLOBALS['typo3CacheManager']->flushCaches();
 
-                                               // 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_TRUNCATEquery('cache_hash');
+                                       if (t3lib_extMgm::isLoaded('cms')) {
+                                               $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_treelist');
                                        }
 
                                                // 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 (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'])) {
+                                               foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'] as $tableName) {
                                                        if (!preg_match('/[^[:alnum:]_]/', $tableName) && substr($tableName, -5) == 'cache') {
                                                                $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($tableName);
                                                        } else {
@@ -6800,28 +6679,28 @@ class t3lib_TCEmain {
                                                }
                                        }
                                }
-                               if ($this->admin && $TYPO3_CONF_VARS['EXT']['extCache']) {
+                               if ($this->admin && $GLOBALS['TYPO3_CONF_VARS']['EXT']['extCache']) {
                                        $this->removeCacheFiles();
                                }
                        break;
                        case 'temp_CACHED':
-                               if ($this->admin && $TYPO3_CONF_VARS['EXT']['extCache']) {
+                               if ($this->admin && $GLOBALS['TYPO3_CONF_VARS']['EXT']['extCache']) {
                                        $this->removeCacheFiles();
                                }
                        break;
                }
 
                        // Clear cache for a page ID!
-               if (t3lib_div::testInt($cacheCmd)) {
+               if (t3lib_utility_Math::canBeInterpretedAsInteger($cacheCmd)) {
                        if (t3lib_extMgm::isLoaded('cms')) {
 
                                $list_cache = array($cacheCmd);
 
                                        // Call pre-processing function for clearing of cache for page ids:
-                               if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) {
-                                       foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) {
+                               if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) {
+                                       foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) {
                                                $_params = array('pageIdArray' => &$list_cache, 'cacheCmd' => $cacheCmd, 'functionID' => 'clear_cacheCmd()');
-                                                       // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array!
+                                                       // Returns the array of ids to clear, FALSE if nothing should be cleared! Never an empty array!
                                                t3lib_div::callUserFunction($funcName, $_params, $this);
                                        }
                                }
@@ -6829,31 +6708,25 @@ class t3lib_TCEmain {
                                        // Delete cache for selected pages:
                                if (is_array($list_cache)) {
 
-                                       if (TYPO3_UseCachingFramework) {
-                                               $pageCache = $GLOBALS['typo3CacheManager']->getCache(
-                                                       'cache_pages'
-                                               );
-                                               $pageSectionCache = $GLOBALS['typo3CacheManager']->getCache(
-                                                       'cache_pagesection'
-                                               );
-
-                                               foreach ($list_cache as $pageId) {
-                                                       $pageCache->flushByTag('pageId_' . (int) $pageId);
-                                                       $pageSectionCache->flushByTag('pageId_' . (int) $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)) . ')'); // Originally, cache_pagesection was not cleared with cache_pages!
+                                       $pageCache = $GLOBALS['typo3CacheManager']->getCache(
+                                               'cache_pages'
+                                       );
+                                       $pageSectionCache = $GLOBALS['typo3CacheManager']->getCache(
+                                               'cache_pagesection'
+                                       );
 
+                                       foreach ($list_cache as $pageId) {
+                                               $pageCache->flushByTag('pageId_' . (int)$pageId);
+                                               $pageSectionCache->flushByTag('pageId_' . (int)$pageId);
                                        }
                                }
                        }
                }
 
                        // Call post processing function for clear-cache:
-               if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) {
+               if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) {
                        $_params = array('cacheCmd' => $cacheCmd);
-                       foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) {
+                       foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) {
                                t3lib_div::callUserFunction($_funcRef, $_params, $this);
                        }
                }
@@ -6944,7 +6817,7 @@ class t3lib_TCEmain {
                        $log_data = unserialize($row['log_data']);
                        $msg = $row['error'] . ': ' . sprintf($row['details'], $log_data[0], $log_data[1], $log_data[2], $log_data[3], $log_data[4]);
                        $flashMessage = t3lib_div::makeInstance('t3lib_FlashMessage',
-                                                                                                       $msg,
+                                                                                                       htmlspecialchars($msg),
                                                                                                        '',
                                                                                                        t3lib_FlashMessage::ERROR,
                                                                                                        TRUE
@@ -6966,21 +6839,8 @@ class t3lib_TCEmain {
         * @return      void
         */
        function internal_clearPageCache() {
-               if (TYPO3_UseCachingFramework) {
-                       if (t3lib_extMgm::isLoaded('cms')) {
-                               $GLOBALS['typo3CacheManager']->getCache('cache_pages')->flush();
-                       }
-               } else {
-                       if (t3lib_extMgm::isLoaded('cms')) {
-                               if ($GLOBALS['TYPO3_CONF_VARS']['FE']['pageCacheToExternalFiles']) {
-                                       $cacheDir = PATH_site . 'typo3temp/cache_pages';
-                                       $retVal = t3lib_div::rmdir($cacheDir, TRUE);
-                                       if (!$retVal) {
-                                               t3lib_div::sysLog('Could not remove page cache files in "' . $cacheDir . '"', 'Core/t3lib_tcemain', 2);
-                                       }
-                               }
-                               $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_pages');
-                       }
+               if (t3lib_extMgm::isLoaded('cms')) {
+                       $GLOBALS['typo3CacheManager']->getCache('cache_pages')->flush();
                }
        }
 
@@ -7000,7 +6860,7 @@ class t3lib_TCEmain {
                        switch ($GLOBALS['TCA'][$table]['columns'][$field]['config']['type']) {
                                case 'inline':
                                        if ($GLOBALS['TCA'][$table]['columns'][$field]['config']['foreign_field']) {
-                                               if (!t3lib_div::testInt($value)) {
+                                               if (!t3lib_utility_Math::canBeInterpretedAsInteger($value)) {
                                                        $result[$field] = count(t3lib_div::trimExplode(',', $value, TRUE));
                                                }
                                        }
@@ -7057,6 +6917,169 @@ class t3lib_TCEmain {
                        }
                }
        }
+
+       /**
+        * Gets the outer most instance of t3lib_TCEmain.
+        * Since t3lib_TCEmain can create nested objects of itself,
+        * this method helps to determine the first (= outer most) one.
+        *
+        * @return t3lib_TCEmain
+        */
+       protected function getOuterMostInstance() {
+               if (!isset($this->outerMostInstance)) {
+                       $stack = array_reverse(debug_backtrace());
+
+                       foreach ($stack as $stackItem) {
+                               if (isset($stackItem['object']) && $stackItem['object'] instanceof t3lib_TCEmain) {
+                                       $this->outerMostInstance = $stackItem['object'];
+                                       break;
+                               }
+                       }
+               }
+
+               return $this->outerMostInstance;
+       }
+
+       /**
+        * Determines whether the this object is the outer most instance of t3lib_TCEmain.
+        * Since t3lib_TCEmain can create nested objects of itself,
+        * this method helps to determine the first (= outer most) one.
+        *
+        * @return boolean
+        */
+       public function isOuterMostInstance() {
+               return ($this->getOuterMostInstance() === $this);
+       }
+
+       /**
+        * Gets an instance of the memory cache.
+        *
+        * @return t3lib_cache_frontend_VariableFrontend
+        */
+       protected function getMemoryCache() {
+               return $GLOBALS['typo3CacheManager']->getCache('cache_runtime');
+       }
+
+       /**
+        * Determines nested element calls.
+        *
+        * @param string $table Name of the table
+        * @param integer $id Uid of the record
+        * @param string $identifier Name of the action to be checked
+        * @return boolean
+        */
+       protected function isNestedElementCallRegistered($table, $id, $identifier) {
+               $nestedElementCalls = (array) $this->getMemoryCache()->get('nestedElementCalls');
+               return isset($nestedElementCalls[$identifier][$table][$id]);
+       }
+
+       /**
+        * Registers nested elements calls.
+        * This is used to track nested calls (e.g. for following m:n relations).
+        *
+        * @param string $table Name of the table
+        * @param integer $id Uid of the record
+        * @param string $identifier Name of the action to be tracked
+        * @return void
+        */
+       protected function registerNestedElementCall($table, $id, $identifier) {
+               $nestedElementCalls = (array) $this->getMemoryCache()->get('nestedElementCalls');
+               $nestedElementCalls[$identifier][$table][$id] = TRUE;
+               $this->getMemoryCache()->set(
+                       'nestedElementCalls',
+                       $nestedElementCalls
+               );
+       }
+
+       /**
+        * Resets the nested element calls.
+        *
+        * @return void
+        */
+       protected function resetNestedElementCalls() {
+               $this->getMemoryCache()->remove('nestedElementCalls');
+       }
+
+       /**
+        * Determines whether an element was registered to be deleted in the registry.
+        *
+        * @param string $table Name of the table
+        * @param integer $id Uid of the record
+        * @return boolean
+        * @see registerElementsToBeDeleted
+        * @see resetElementsToBeDeleted
+        * @see copyRecord_raw
+        * @see versionizeRecord
+        */
+       protected function isElementToBeDeleted($table, $id) {
+               $elementsToBeDeleted = (array) $this->getMemoryCache()->get('core-t3lib_TCEmain-elementsToBeDeleted');
+               return (isset($elementsToBeDeleted[$table][$id]));
+       }
+
+       /**
+        * Registers elements to be deleted in the registry.
+        *
+        * @return void
+        * @see process_datamap
+        */
+       protected function registerElementsToBeDeleted() {
+               $elementsToBeDeleted = (array) $this->getMemoryCache()->get('core-t3lib_TCEmain-elementsToBeDeleted');
+               $this->getMemoryCache()->set(
+                       'core-t3lib_TCEmain-elementsToBeDeleted',
+                       array_merge($elementsToBeDeleted, $this->getCommandMapElements('delete'))
+               );
+       }
+
+       /**
+        * Resets the elements to be deleted in the registry.
+        *
+        * @return void
+        * @see process_datamap
+        */
+       protected function resetElementsToBeDeleted() {
+               $this->getMemoryCache()->remove('core-t3lib_TCEmain-elementsToBeDeleted');
+       }
+
+       /**
+        * Unsets elements (e.g. of the data map) that shall be deleted.
+        * This avoids to modify records that will be deleted later on.
+        *
+        * @param array $elements Elements to be modified
+        * @return array
+        */
+       protected function unsetElementsToBeDeleted(array $elements) {
+               $elements = t3lib_div::arrayDiffAssocRecursive($elements, $this->getCommandMapElements('delete'));
+
+               foreach ($elements as $key => $value) {
+                       if (empty($value)) {
+                               unset($elements[$key]);
+                       }
+               }
+
+               return $elements;
+       }
+
+       /**
+        * Gets elements of the command map that match a particular command.
+        *
+        * @param string $needle The command to be matched
+        * @return array
+        */
+       protected function getCommandMapElements($needle) {
+               $elements = array();
+
+               foreach ($this->cmdmap as $tableName => $idArray) {
+                       foreach ($idArray as $id => $commandArray) {
+                               foreach ($commandArray as $command => $value) {
+                                       if ($value && $command == $needle) {
+                                               $elements[$tableName][$id] = TRUE;
+                                       }
+                               }
+                       }
+               }
+
+               return $elements;
+       }
 }