TYPO3 API  SVNRelease
class.t3lib_tcemain.php
Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003  *  Copyright notice
00004  *
00005  *  (c) 1999-2011 Kasper Skårhøj (kasperYYYY@typo3.com)
00006  *  All rights reserved
00007  *
00008  *  This script is part of the TYPO3 project. The TYPO3 project is
00009  *  free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version.
00013  *
00014  *  The GNU General Public License can be found at
00015  *  http://www.gnu.org/copyleft/gpl.html.
00016  *  A copy is found in the textfile GPL.txt and important notices to the license
00017  *  from the author is found in LICENSE.txt distributed with these scripts.
00018  *
00019  *
00020  *  This script is distributed in the hope that it will be useful,
00021  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023  *  GNU General Public License for more details.
00024  *
00025  *  This copyright notice MUST APPEAR in all copies of the script!
00026  ***************************************************************/
00027 /**
00028  * Contains the TYPO3 Core Engine
00029  *
00030  * $Id: class.t3lib_tcemain.php 10155 2011-01-20 11:11:56Z psychomieze $
00031  * Revised for TYPO3 3.9 October 2005 by Kasper Skårhøj
00032  *
00033  * @author  Kasper Skårhøj <kasperYYYY@typo3.com>
00034  */
00035 /**
00036  * [CLASS/FUNCTION INDEX of SCRIPT]
00037  *
00038  *
00039  *
00040  *  242: class t3lib_TCEmain
00041  *  367:     function start($data,$cmd,$altUserObject='')
00042  *  406:     function setMirror($mirror)
00043  *  431:     function setDefaultsFromUserTS($userTS)
00044  *  454:     function process_uploads($postFiles)
00045  *  492:     function process_uploads_traverseArray(&$outputArr,$inputArr,$keyToSet)
00046  *
00047  *            SECTION: PROCESSING DATA
00048  *  528:     function process_datamap()
00049  *  886:     function placeholderShadowing($table,$id)
00050  *  929:     function fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$realPid,$status,$tscPID)
00051  *
00052  *            SECTION: Evaluation of input values
00053  * 1152:     function checkValue($table,$field,$value,$id,$status,$realPid,$tscPID)
00054  * 1212:     function checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$uploadedFiles,$tscPID)
00055  * 1261:     function checkValue_input($res,$value,$tcaFieldConf,$PP,$field='')
00056  * 1299:     function checkValue_check($res,$value,$tcaFieldConf,$PP)
00057  * 1322:     function checkValue_radio($res,$value,$tcaFieldConf,$PP)
00058  * 1348:     function checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field)
00059  * 1458:     function checkValue_group_select_file($valueArray,$tcaFieldConf,$curValue,$uploadedFileArray,$status,$table,$id,$recFID)
00060  * 1632:     function checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field)
00061  * 1709:     function checkValue_flexArray2Xml($array, $addPrologue=FALSE)
00062  * 1721:     function _DELETE_FLEX_FORMdata(&$valueArrayToRemoveFrom,$deleteCMDS)
00063  * 1743:     function _MOVE_FLEX_FORMdata(&$valueArrayToMoveIn, $moveCMDS, $direction)
00064  * 1783:     function checkValue_inline($res,$value,$tcaFieldConf,$PP,$field)
00065  * 1825:     function checkValue_checkMax($tcaFieldConf, $valueArray)
00066  *
00067  *            SECTION: Helper functions for evaluation functions.
00068  * 1877:     function getUnique($table,$field,$value,$id,$newPid=0)
00069  * 1915:     function checkValue_input_Eval($value,$evalArray,$is_in)
00070  * 2012:     function checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,$type,$currentTable)
00071  * 2058:     function checkValue_group_select_explodeSelectGroupValue($value)
00072  * 2082:     function checkValue_flex_procInData($dataPart,$dataPart_current,$uploadedFiles,$dataStructArray,$pParams,$callBackFunc='')
00073  * 2121:     function checkValue_flex_procInData_travDS(&$dataValues,$dataValues_current,$uploadedFiles,$DSelements,$pParams,$callBackFunc,$structurePath)
00074  *
00075  *            SECTION: PROCESSING COMMANDS
00076  * 2267:     function process_cmdmap()
00077  *
00078  *            SECTION: Cmd: Copying
00079  * 2407:     function copyRecord($table,$uid,$destPid,$first=0,$overrideValues=array(),$excludeFields='')
00080  * 2529:     function copyPages($uid,$destPid)
00081  * 2583:     function copySpecificPage($uid,$destPid,$copyTablesArray,$first=0)
00082  * 2617:     function copyRecord_raw($table,$uid,$pid,$overrideArray=array())
00083  * 2681:     function rawCopyPageContent($old_pid,$new_pid,$copyTablesArray)
00084  * 2705:     function insertNewCopyVersion($table,$fieldArray,$realPid)
00085  * 2757:     function copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf,$realDestPid)
00086  * 2836:     function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)
00087  * 2864:     function copyRecord_procFilesRefs($conf, $uid, $value)
00088  *
00089  *            SECTION: Cmd: Moving, Localizing
00090  * 2933:     function moveRecord($table,$uid,$destPid)
00091  * 3128:     function moveRecord_procFields($table,$uid,$destPid)
00092  * 3148:     function moveRecord_procBasedOnFieldType($table,$uid,$destPid,$field,$value,$conf)
00093  * 3182:     function localize($table,$uid,$language)
00094  *
00095  *            SECTION: Cmd: Deleting
00096  * 3296:     function deleteAction($table, $id)
00097  * 3343:     function deleteEl($table, $uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE)
00098  * 3360:     function deleteVersionsForRecord($table, $uid, $forceHardDelete)
00099  * 3382:     function undeleteRecord($table,$uid)
00100  * 3399:     function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE,$undeleteRecord=FALSE)
00101  * 3512:     function deleteRecord_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj)
00102  * 3539:     function deletePages($uid,$force=FALSE,$forceHardDelete=FALSE)
00103  * 3567:     function deleteSpecificPage($uid,$forceHardDelete=FALSE)
00104  * 3592:     function canDeletePage($uid)
00105  * 3619:     function cannotDeleteRecord($table,$id)
00106  * 3638:     function deleteRecord_procFields($table, $uid, $undeleteRecord = false)
00107  * 3661:     function deleteRecord_procBasedOnFieldType($table, $uid, $field, $value, $conf, $undeleteRecord = false)
00108  *
00109  *            SECTION: Cmd: Versioning
00110  * 3722:     function versionizeRecord($table,$id,$label,$delete=FALSE,$versionizeTree=-1)
00111  * 3798:     function versionizePages($uid,$label,$versionizeTree)
00112  * 3861:     function version_swap($table,$id,$swapWith,$swapIntoWS=0)
00113  * 4032:     function version_clearWSID($table,$id)
00114  * 4066:     function version_setStage($table,$id,$stageId,$comment='')
00115  *
00116  *            SECTION: Cmd: Helper functions
00117  * 4111:     function remapListedDBRecords()
00118  * 4192:     function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)
00119  * 4219:     function remapListedDBRecords_procDBRefs($conf, $value, $MM_localUid, $table)
00120  * 4265:     function remapListedDBRecords_procInline($conf, $value, $uid, $table)
00121  *
00122  *            SECTION: Access control / Checking functions
00123  * 4308:     function checkModifyAccessList($table)
00124  * 4320:     function isRecordInWebMount($table,$id)
00125  * 4334:     function isInWebMount($pid)
00126  * 4348:     function checkRecordUpdateAccess($table,$id)
00127  * 4372:     function checkRecordInsertAccess($insertTable,$pid,$action=1)
00128  * 4406:     function isTableAllowedForThisPage($page_uid, $checkTable)
00129  * 4439:     function doesRecordExist($table,$id,$perms)
00130  * 4504:     function doesRecordExist_pageLookUp($id, $perms)
00131  * 4530:     function doesBranchExist($inList,$pid,$perms,$recurse)
00132  * 4564:     function tableReadOnly($table)
00133  * 4576:     function tableAdminOnly($table)
00134  * 4590:     function destNotInsideSelf($dest,$id)
00135  * 4622:     function getExcludeListArray()
00136  * 4645:     function doesPageHaveUnallowedTables($page_uid,$doktype)
00137  *
00138  *            SECTION: Information lookup
00139  * 4694:     function pageInfo($id,$field)
00140  * 4714:     function recordInfo($table,$id,$fieldList)
00141  * 4735:     function getRecordProperties($table,$id,$noWSOL=FALSE)
00142  * 4751:     function getRecordPropertiesFromRow($table,$row)
00143  *
00144  *            SECTION: Storing data to Database Layer
00145  * 4794:     function updateDB($table,$id,$fieldArray)
00146  * 4846:     function insertDB($table,$id,$fieldArray,$newVersion=FALSE,$suggestedUid=0,$dontSetNewIdIndex=FALSE)
00147  * 4919:     function checkStoredRecord($table,$id,$fieldArray,$action)
00148  * 4956:     function setHistory($table,$id,$logId)
00149  * 4989:     function clearHistory($maxAgeSeconds=604800,$table)
00150  * 5003:     function updateRefIndex($table,$id)
00151  *
00152  *            SECTION: Misc functions
00153  * 5035:     function getSortNumber($table,$uid,$pid)
00154  * 5108:     function resorting($table,$pid,$sortRow, $return_SortNumber_After_This_Uid)
00155  * 5139:     function setTSconfigPermissions($fieldArray,$TSConfig_p)
00156  * 5156:     function newFieldArray($table)
00157  * 5188:     function addDefaultPermittedLanguageIfNotSet($table,&$incomingFieldArray)
00158  * 5212:     function overrideFieldArray($table,$data)
00159  * 5228:     function compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray)
00160  * 5274:     function assemblePermissions($string)
00161  * 5291:     function rmComma($input)
00162  * 5301:     function convNumEntityToByteValue($input)
00163  * 5323:     function destPathFromUploadFolder($folder)
00164  * 5333:     function deleteClause($table)
00165  * 5349:     function getTCEMAIN_TSconfig($tscPID)
00166  * 5364:     function getTableEntries($table,$TSconfig)
00167  * 5377:     function getPID($table,$uid)
00168  * 5390:     function dbAnalysisStoreExec()
00169  * 5406:     function removeRegisteredFiles()
00170  * 5418:     function removeCacheFiles()
00171  * 5432:     function int_pageTreeInfo($CPtable,$pid,$counter, $rootID)
00172  * 5453:     function compileAdminTables()
00173  * 5470:     function fixUniqueInPid($table,$uid)
00174  * 5506:     function fixCopyAfterDuplFields($table,$uid,$prevUid,$update, $newData=array())
00175  * 5531:     function extFileFields($table)
00176  * 5552:     function getUniqueFields($table)
00177  * 5577:     function isReferenceField($conf)
00178  * 5588:     function getInlineFieldType($conf)
00179  * 5611:     function getCopyHeader($table,$pid,$field,$value,$count,$prevTitle='')
00180  * 5640:     function prependLabel($table)
00181  * 5657:     function resolvePid($table,$pid)
00182  * 5687:     function clearPrefixFromValue($table,$value)
00183  * 5702:     function extFileFunctions($table,$field,$filelist,$func)
00184  * 5732:     function noRecordsFromUnallowedTables($inList)
00185  * 5758:     function notifyStageChange($stat,$stageId,$table,$id,$comment)
00186  * 5853:     function notifyStageChange_getEmails($listOfUsers,$noTablePrefix=FALSE)
00187  *
00188  *            SECTION: Clearing cache
00189  * 5899:     function clear_cache($table,$uid)
00190  * 6009:     function clear_cacheCmd($cacheCmd)
00191  *
00192  *            SECTION: Logging
00193  * 6113:     function log($table,$recuid,$action,$recpid,$error,$details,$details_nr=-1,$data=array(),$event_pid=-1,$NEWid='')
00194  * 6130:     function newlog($message, $error=0)
00195  * 6140:     function printLogErrorMessages($redirect)
00196  *
00197  *            SECTION: Internal (do not use outside Core!)
00198  * 6202:     function internal_clearPageCache()
00199  *
00200  * TOTAL FUNCTIONS: 126
00201  * (This index is automatically created/updated by the extension "extdeveval")
00202  *
00203  */
00204 
00205 
00206 /**
00207  * This is the TYPO3 Core Engine class for manipulation of the database
00208  * This class is used by eg. the tce_db.php script which provides an the interface for POST forms to this class.
00209  *
00210  * Dependencies:
00211  * - $GLOBALS['TCA'] must exist
00212  * - $GLOBALS['LANG'] must exist
00213  *
00214  * tce_db.php for further comments and SYNTAX! Also see document 'TYPO3 Core API' for details.
00215  *
00216  * @author  Kasper Skårhøj <kasperYYYY@typo3.com>
00217  * @package TYPO3
00218  * @subpackage t3lib
00219  */
00220 class t3lib_TCEmain {
00221 
00222 
00223     // *********************
00224     // Public variables you can configure before using the class:
00225     // *********************
00226 
00227     var $storeLogMessages = TRUE; // Boolean: If true, the default log-messages will be stored. This should not be necessary if the locallang-file for the log-display is properly configured. So disabling this will just save some database-space as the default messages are not saved.
00228     var $enableLogging = TRUE; // Boolean: If true, actions are logged to sys_log.
00229     var $reverseOrder = FALSE; // Boolean: If true, the datamap array is reversed in the order, which is a nice thing if you're creating a whole new bunch of records.
00230     var $checkSimilar = TRUE; // Boolean: If true, only fields which are different from the database values are saved! In fact, if a whole input array is similar, it's not saved then.
00231     var $stripslashes_values = TRUE; // Boolean: If true, incoming values in the data-array have their slashes stripped. ALWAYS SET THIS TO ZERO and supply an unescaped data array instead. This switch may totally disappear in future versions of this class!
00232     var $checkStoredRecords = TRUE; // Boolean: This will read the record after having updated or inserted it. If anything is not properly submitted an error is written to the log. This feature consumes extra time by selecting records
00233     var $checkStoredRecords_loose = TRUE; // Boolean: If set, values '' and 0 will equal each other when the stored records are checked.
00234     var $deleteTree = FALSE; // Boolean. If this is set, then a page is deleted by deleting the whole branch under it (user must have deletepermissions to it all). If not set, then the page is deleted ONLY if it has no branch
00235     var $neverHideAtCopy = FALSE; // Boolean. If set, then the 'hideAtCopy' flag for tables will be ignored.
00236     var $dontProcessTransformations = FALSE; // Boolean: If set, then transformations are NOT performed on the input.
00237     var $clear_flexFormData_vDEFbase = FALSE; // Boolean: If set, .vDEFbase values are unset in flexforms.
00238     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
00239     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.
00240     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!
00241     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!
00242     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!
00243 
00244     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)
00245 
00246     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
00247 
00248     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)
00249     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!
00250     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.
00251     var $alternativeFilePath = array(); // Array [filename]=alternative_filepath: Same as alternativeFileName but with relative path to the file
00252     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.
00253     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)
00254 
00255     var $callBackObj; // Object. Call back object for flex form traversation. Useful when external classes wants to use the iteration functions inside tcemain for traversing a FlexForm structure.
00256 
00257 
00258     // *********************
00259     // Internal variables (mapping arrays) which can be used (read-only) from outside
00260     // *********************
00261 
00262     var $autoVersionIdMap = array(); // Contains mapping of auto-versionized records.
00263     var $substNEWwithIDs = array(); // When new elements are created, this array contains a map between their "NEW..." string IDs and the eventual UID they got when stored in database
00264     var $substNEWwithIDs_table = array(); // Like $substNEWwithIDs, but where each old "NEW..." id is mapped to the table it was from.
00265     var $newRelatedIDs = array(); // Holds the tables and there the ids of newly created child records from IRRE
00266     var $copyMappingArray_merged = array(); // This array is the sum of all copying operations in this class. May be READ from outside, thus partly public.
00267     var $copiedFileMap = array(); // A map between input file name and final destination for files being attached to records.
00268     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.
00269     var $errorLog = array(); // Errors are collected in this variable.
00270 
00271 
00272     // *********************
00273     // Internal Variables, do not touch.
00274     // *********************
00275 
00276     // Variables set in init() function:
00277     /**
00278      * The user-object the script uses. If not set from outside, this is set to the current global $BE_USER.
00279      *
00280      * @var t3lib_beUserAuth
00281      */
00282     var $BE_USER;
00283     var $userid; // will be set to uid of be_user executing this script
00284     var $username; // will be set to username of be_user executing this script
00285     var $admin; // will be set if user is admin
00286 
00287     var $defaultPermissions = array( // Can be overridden from $TYPO3_CONF_VARS
00288         'user' => 'show,edit,delete,new,editcontent',
00289         'group' => 'show,edit,new,editcontent',
00290         'everybody' => ''
00291     );
00292 
00293     var $exclude_array; // The list of <table>-<fields> that cannot be edited by user. This is compiled from TCA/exclude-flag combined with non_exclude_fields for the user.
00294     var $datamap = array(); // Set with incoming data array
00295     var $cmdmap = array(); // Set with incoming cmd array
00296 
00297         // Internal static:
00298     var $pMap = array( // Permission mapping
00299         'show' => 1, // 1st bit
00300         'edit' => 2, // 2nd bit
00301         'delete' => 4, // 3rd bit
00302         'new' => 8, // 4th bit
00303         'editcontent' => 16 // 5th bit
00304     );
00305     var $sortIntervals = 256; // Integer: The interval between sorting numbers used with tables with a 'sorting' field defined. Min 1
00306 
00307         // Internal caching arrays
00308     var $recUpdateAccessCache = array(); // Used by function checkRecordUpdateAccess() to store whether a record is updateable or not.
00309     var $recInsertAccessCache = array(); // User by function checkRecordInsertAccess() to store whether a record can be inserted on a page id
00310     var $isRecordInWebMount_Cache = array(); // Caching array for check of whether records are in a webmount
00311     var $isInWebMount_Cache = array(); // Caching array for page ids in webmounts
00312     var $cachedTSconfig = array(); // Caching for collecting TSconfig for page ids
00313     var $pageCache = array(); // Used for caching page records in pageInfo()
00314     var $checkWorkspaceCache = array(); // Array caching workspace access for BE_USER
00315 
00316         // Other arrays:
00317     var $dbAnalysisStore = array(); // For accumulation of MM relations that must be written after new records are created.
00318     var $removeFilesStore = array(); // For accumulation of files which must be deleted after processing of all input content
00319     var $uploadedFileArray = array(); // Uploaded files, set by process_uploads()
00320     var $registerDBList = array(); // Used for tracking references that might need correction after operations
00321     var $registerDBPids = array(); // Used for tracking references that might need correction in pid field after operations (e.g. IRRE)
00322     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.
00323     var $remapStack = array(); // array used for remapping uids and values at the end of process_datamap
00324     var $remapStackRecords = array(); // array used for remapping uids and values at the end of process_datamap (e.g. $remapStackRecords[<table>][<uid>] = <index in $remapStack>)
00325     protected $remapStackChildIds = array(); // array used for checking whether new children need to be remapped
00326     protected $remapStackActions = array(); // array used for executing addition actions after remapping happened (sett processRemapStack())
00327     protected $remapStackRefIndex = array(); // array used for executing post-processing on the reference index
00328     var $updateRefIndexStack = array(); // array used for additional calls to $this->updateRefIndex
00329     var $callFromImpExp = FALSE; // tells, that this TCEmain was called from tx_impext - this variable is set by tx_impexp
00330     var $newIndexMap = array(); // Array for new flexform index mapping
00331 
00332     // Various
00333     /**
00334      * basicFileFunctions object
00335      *
00336      * @var t3lib_basicFileFunctions
00337      */
00338     var $fileFunc; // For "singleTon" file-manipulation object
00339     var $checkValue_currentRecord = array(); // Set to "currentRecord" during checking of values.
00340     var $autoVersioningUpdate = FALSE; // A signal flag used to tell file processing that autoversioning has happend and hence certain action should be applied.
00341 
00342     protected $disableDeleteClause = FALSE; // Disable delete clause
00343     protected $checkModifyAccessListHookObjects;
00344 
00345 
00346     /**
00347      * Initializing.
00348      * For details, see 'TYPO3 Core API' document.
00349      * This function does not start the processing of data, but merely initializes the object
00350      *
00351      * @param   array       Data to be modified or inserted in the database
00352      * @param   array       Commands to copy, move, delete, localize, versionize records.
00353      * @param   object      An alternative userobject you can set instead of the default, which is $GLOBALS['BE_USER']
00354      * @return  void
00355      */
00356     public function start($data, $cmd, $altUserObject = '') {
00357 
00358             // Initializing BE_USER
00359         $this->BE_USER = is_object($altUserObject) ? $altUserObject : $GLOBALS['BE_USER'];
00360         $this->userid = $this->BE_USER->user['uid'];
00361         $this->username = $this->BE_USER->user['username'];
00362         $this->admin = $this->BE_USER->user['admin'];
00363 
00364         if ($this->BE_USER->uc['recursiveDelete']) {
00365             $this->deleteTree = 1;
00366         }
00367 
00368         if ($GLOBALS['TYPO3_CONF_VARS']['BE']['explicitConfirmationOfTranslation'] && $this->updateModeL10NdiffData === TRUE) {
00369             $this->updateModeL10NdiffData = FALSE;
00370         }
00371 
00372             // Initializing default permissions for pages
00373         $defaultPermissions = $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPermissions'];
00374         if (isset($defaultPermissions['user'])) {
00375             $this->defaultPermissions['user'] = $defaultPermissions['user'];
00376         }
00377         if (isset($defaultPermissions['group'])) {
00378             $this->defaultPermissions['group'] = $defaultPermissions['group'];
00379         }
00380         if (isset($defaultPermissions['everybody'])) {
00381             $this->defaultPermissions['everybody'] = $defaultPermissions['everybody'];
00382         }
00383 
00384             // generates the excludelist, based on TCA/exclude-flag and non_exclude_fields for the user:
00385         $this->exclude_array = $this->admin ? array() : $this->getExcludeListArray();
00386 
00387             // Setting the data and cmd arrays
00388         if (is_array($data)) {
00389             reset($data);
00390             $this->datamap = $data;
00391         }
00392         if (is_array($cmd)) {
00393             reset($cmd);
00394             $this->cmdmap = $cmd;
00395         }
00396     }
00397 
00398     /**
00399      * Function that can mirror input values in datamap-array to other uid numbers.
00400      * Example: $mirror[table][11] = '22,33' will look for content in $this->datamap[table][11] and copy it to $this->datamap[table][22] and $this->datamap[table][33]
00401      *
00402      * @param   array       This array has the syntax $mirror[table_name][uid] = [list of uids to copy data-value TO!]
00403      * @return  void
00404      */
00405     function setMirror($mirror) {
00406         if (is_array($mirror)) {
00407             foreach ($mirror as $table => $uid_array) {
00408                 if (isset($this->datamap[$table])) {
00409                     reset($uid_array);
00410                     foreach ($uid_array as $id => $uidList) {
00411                         if (isset($this->datamap[$table][$id])) {
00412                             $theIdsInArray = t3lib_div::trimExplode(',', $uidList, 1);
00413                             foreach ($theIdsInArray as $copyToUid) {
00414                                 $this->datamap[$table][$copyToUid] = $this->datamap[$table][$id];
00415                             }
00416                         }
00417                     }
00418                 }
00419             }
00420         }
00421     }
00422 
00423     /**
00424      * Initializes default values coming from User TSconfig
00425      *
00426      * @param   array       User TSconfig array
00427      * @return  void
00428      */
00429     function setDefaultsFromUserTS($userTS) {
00430         global $TCA;
00431         if (is_array($userTS)) {
00432             foreach ($userTS as $k => $v) {
00433                 $k = substr($k, 0, -1);
00434                 if ($k && is_array($v) && isset($TCA[$k])) {
00435                     if (is_array($this->defaultValues[$k])) {
00436                         $this->defaultValues[$k] = array_merge($this->defaultValues[$k], $v);
00437                     } else {
00438                         $this->defaultValues[$k] = $v;
00439                     }
00440                 }
00441             }
00442         }
00443     }
00444 
00445     /**
00446      * Processing of uploaded files.
00447      * It turns out that some versions of PHP arranges submitted data for files different if sent in an array. This function will unify this so the internal array $this->uploadedFileArray will always contain files arranged in the same structure.
00448      *
00449      * @param   array       $_FILES array
00450      * @return  void
00451      */
00452     function process_uploads($postFiles) {
00453 
00454         if (is_array($postFiles)) {
00455 
00456                 // Editing frozen:
00457             if ($this->BE_USER->workspace !== 0 && $this->BE_USER->workspaceRec['freeze']) {
00458                 $this->newlog('All editing in this workspace has been frozen!', 1);
00459                 return FALSE;
00460             }
00461 
00462             reset($postFiles);
00463             $subA = current($postFiles);
00464             if (is_array($subA)) {
00465                 if (is_array($subA['name']) && is_array($subA['type']) && is_array($subA['tmp_name']) && is_array($subA['size'])) {
00466                         // Initialize the uploadedFilesArray:
00467                     $this->uploadedFileArray = array();
00468 
00469                         // For each entry:
00470                     foreach ($subA as $key => $values) {
00471                         $this->process_uploads_traverseArray($this->uploadedFileArray, $values, $key);
00472                     }
00473                 } else {
00474                     $this->uploadedFileArray = $subA;
00475                 }
00476             }
00477         }
00478     }
00479 
00480     /**
00481      * Traverse the upload array if needed to rearrange values.
00482      *
00483      * @param   array       $this->uploadedFileArray passed by reference
00484      * @param   array       Input array  ($_FILES parts)
00485      * @param   string      The current $_FILES array key to set on the outermost level.
00486      * @return  void
00487      * @access private
00488      * @see process_uploads()
00489      */
00490     function process_uploads_traverseArray(&$outputArr, $inputArr, $keyToSet) {
00491         if (is_array($inputArr)) {
00492             foreach ($inputArr as $key => $value) {
00493                 $this->process_uploads_traverseArray($outputArr[$key], $inputArr[$key], $keyToSet);
00494             }
00495         } else {
00496             $outputArr[$keyToSet] = $inputArr;
00497         }
00498     }
00499 
00500 
00501     /*********************************************
00502      *
00503      * HOOKS
00504      *
00505      *********************************************/
00506 
00507     /**
00508      * Hook: processDatamap_afterDatabaseOperations
00509      * (calls $hookObj->processDatamap_afterDatabaseOperations($status, $table, $id, $fieldArray, $this);)
00510      *
00511      * Note: When using the hook after INSERT operations, you will only get the temporary NEW... id passed to your hook as $id,
00512      *       but you can easily translate it to the real uid of the inserted record using the $this->substNEWwithIDs array.
00513      *
00514      * @param   object      $hookObjectsArr: (reference) Array with hook objects
00515      * @param   string      $status: (reference) Status of the current operation, 'new' or 'update
00516      * @param   string      $table: (refrence) The table currently processing data for
00517      * @param   string      $id: (reference) The record uid currently processing data for, [integer] or [string] (like 'NEW...')
00518      * @param   array       $fieldArray: (reference) The field array of a record
00519      * @return  void
00520      */
00521     function hook_processDatamap_afterDatabaseOperations(&$hookObjectsArr, &$status, &$table, &$id, &$fieldArray) {
00522             // Process hook directly:
00523         if (!isset($this->remapStackRecords[$table][$id])) {
00524             foreach ($hookObjectsArr as $hookObj) {
00525                 if (method_exists($hookObj, 'processDatamap_afterDatabaseOperations')) {
00526                     $hookObj->processDatamap_afterDatabaseOperations($status, $table, $id, $fieldArray, $this);
00527                 }
00528             }
00529                 // If this record is in remapStack (e.g. when using IRRE), values will be updated/remapped later on. So the hook will also be called later:
00530         } else {
00531             $this->remapStackRecords[$table][$id]['processDatamap_afterDatabaseOperations'] = array(
00532                 'status' => $status,
00533                 'fieldArray' => $fieldArray,
00534                 'hookObjectsArr' => $hookObjectsArr,
00535             );
00536         }
00537     }
00538 
00539     /**
00540      * Gets the 'checkModifyAccessList' hook objects.
00541      * The first call initializes the accordant objects.
00542      *
00543      * @return  array       The 'checkModifyAccessList' hook objects (if any)
00544      */
00545     protected function getCheckModifyAccessListHookObjects() {
00546         if (!isset($this->checkModifyAccessListHookObjects)) {
00547             $this->checkModifyAccessListHookObjects = array();
00548 
00549             if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkModifyAccessList'])) {
00550                 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['checkModifyAccessList'] as $classData) {
00551                     $hookObject = t3lib_div::getUserObj($classData);
00552 
00553                     if (!($hookObject instanceof t3lib_TCEmain_checkModifyAccessListHook)) {
00554                         throw new UnexpectedValueException('$hookObject must implement interface t3lib_TCEmain_checkModifyAccessListHook', 1251892472);
00555                     }
00556 
00557                     $this->checkModifyAccessListHookObjects[] = $hookObject;
00558                 }
00559             }
00560         }
00561 
00562         return $this->checkModifyAccessListHookObjects;
00563     }
00564 
00565 
00566     /*********************************************
00567      *
00568      * PROCESSING DATA
00569      *
00570      *********************************************/
00571 
00572     /**
00573      * Processing the data-array
00574      * Call this function to process the data-array set by start()
00575      *
00576      * @return  void
00577      */
00578     public function process_datamap() {
00579         global $TCA, $TYPO3_CONF_VARS;
00580             // Keep versionized(!) relations here locally:
00581         $registerDBList = array();
00582 
00583             // Editing frozen:
00584         if ($this->BE_USER->workspace !== 0 && $this->BE_USER->workspaceRec['freeze']) {
00585             $this->newlog('All editing in this workspace has been frozen!', 1);
00586             return FALSE;
00587         }
00588 
00589             // First prepare user defined objects (if any) for hooks which extend this function:
00590         $hookObjectsArr = array();
00591         if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'])) {
00592             foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'] as $classRef) {
00593                 $hookObjectsArr[] = t3lib_div::getUserObj($classRef);
00594             }
00595         }
00596 
00597             // Organize tables so that the pages-table is always processed first. This is required if you want to make sure that content pointing to a new page will be created.
00598         $orderOfTables = array();
00599         if (isset($this->datamap['pages'])) { // Set pages first.
00600             $orderOfTables[] = 'pages';
00601         }
00602         $orderOfTables = array_unique(array_merge($orderOfTables, array_keys($this->datamap)));
00603 
00604             // Process the tables...
00605         foreach ($orderOfTables as $table) {
00606             /* Check if
00607                    - table is set in $TCA,
00608                    - table is NOT readOnly
00609                    - the table is set with content in the data-array (if not, there's nothing to process...)
00610                    - permissions for tableaccess OK
00611                */
00612             $modifyAccessList = $this->checkModifyAccessList($table);
00613             if (!$modifyAccessList) {
00614                 $id = 0;
00615                 $this->log($table, $id, 2, 0, 1, "Attempt to modify table '%s' without permission", 1, array($table));
00616             }
00617             if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->datamap[$table]) && $modifyAccessList) {
00618                 if ($this->reverseOrder) {
00619                     $this->datamap[$table] = array_reverse($this->datamap[$table], 1);
00620                 }
00621 
00622                     // For each record from the table, do:
00623                     // $id is the record uid, may be a string if new records...
00624                     // $incomingFieldArray is the array of fields
00625                 foreach ($this->datamap[$table] as $id => $incomingFieldArray) {
00626                     if (is_array($incomingFieldArray)) {
00627 
00628                             // Hook: processDatamap_preProcessFieldArray
00629                         foreach ($hookObjectsArr as $hookObj) {
00630                             if (method_exists($hookObj, 'processDatamap_preProcessFieldArray')) {
00631                                 $hookObj->processDatamap_preProcessFieldArray($incomingFieldArray, $table, $id, $this);
00632                             }
00633                         }
00634 
00635                             // ******************************
00636                             // Checking access to the record
00637                             // ******************************
00638                         $createNewVersion = FALSE;
00639                         $recordAccess = FALSE;
00640                         $old_pid_value = '';
00641                         $this->autoVersioningUpdate = FALSE;
00642 
00643                         if (!t3lib_div::testInt($id)) { // Is it a new record? (Then Id is a string)
00644                             $fieldArray = $this->newFieldArray($table); // Get a fieldArray with default values
00645                             if (isset($incomingFieldArray['pid'])) { // A pid must be set for new records.
00646                                     // $value = the pid
00647                                 $pid_value = $incomingFieldArray['pid'];
00648 
00649                                     // Checking and finding numerical pid, it may be a string-reference to another value
00650                                 $OK = 1;
00651                                 if (strstr($pid_value, 'NEW')) { // If a NEW... id
00652                                     if (substr($pid_value, 0, 1) == '-') {
00653                                         $negFlag = -1;
00654                                         $pid_value = substr($pid_value, 1);
00655                                     } else {
00656                                         $negFlag = 1;
00657                                     }
00658                                     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.
00659                                         if ($negFlag === 1) {
00660                                             $old_pid_value = $this->substNEWwithIDs[$pid_value];
00661                                         }
00662                                         $pid_value = intval($negFlag * $this->substNEWwithIDs[$pid_value]);
00663                                     } else {
00664                                         $OK = 0;
00665                                     } // If not found in the substArray we must stop the process...
00666                                 } 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.
00667                                     if ($WSdestPage = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, 'pages', $pid_value, 'uid,t3ver_swapmode')) { // Looks for workspace version of page.
00668                                         if ($WSdestPage['t3ver_swapmode'] == 0) { // if swapmode is zero, then change pid value.
00669                                             $pid_value = $WSdestPage['uid'];
00670                                         }
00671                                     }
00672                                 }
00673                                 $pid_value = intval($pid_value);
00674 
00675                                     // The $pid_value is now the numerical pid at this point
00676                                 if ($OK) {
00677                                     $sortRow = $TCA[$table]['ctrl']['sortby'];
00678                                     if ($pid_value >= 0) { // Points to a page on which to insert the element, possibly in the top of the page
00679                                         if ($sortRow) { // If this table is sorted we better find the top sorting number
00680                                             $fieldArray[$sortRow] = $this->getSortNumber($table, 0, $pid_value);
00681                                         }
00682                                         $fieldArray['pid'] = $pid_value; // The numerical pid is inserted in the data array
00683                                     } else { // points to another record before ifself
00684                                         if ($sortRow) { // If this table is sorted we better find the top sorting number
00685                                             $tempArray = $this->getSortNumber($table, 0, $pid_value); // Because $pid_value is < 0, getSortNumber returns an array
00686                                             $fieldArray['pid'] = $tempArray['pid'];
00687                                             $fieldArray[$sortRow] = $tempArray['sortNumber'];
00688                                         } else { // Here we fetch the PID of the record that we point to...
00689                                             $tempdata = $this->recordInfo($table, abs($pid_value), 'pid');
00690                                             $fieldArray['pid'] = $tempdata['pid'];
00691                                         }
00692                                     }
00693                                 }
00694                             }
00695                             $theRealPid = $fieldArray['pid'];
00696 
00697                                 // Now, check if we may insert records on this pid.
00698                             if ($theRealPid >= 0) {
00699                                 $recordAccess = $this->checkRecordInsertAccess($table, $theRealPid); // Checks if records can be inserted on this $pid.
00700                                 if ($recordAccess) {
00701                                     $this->addDefaultPermittedLanguageIfNotSet($table, $incomingFieldArray);
00702                                     $recordAccess = $this->BE_USER->recordEditAccessInternals($table, $incomingFieldArray, TRUE);
00703                                     if (!$recordAccess) {
00704                                         $this->newlog("recordEditAccessInternals() check failed. [" . $this->BE_USER->errorMsg . "]", 1);
00705                                     } elseif (!$this->bypassWorkspaceRestrictions) {
00706                                             // Workspace related processing:
00707                                         if ($res = $this->BE_USER->workspaceAllowLiveRecordsInPID($theRealPid, $table)) { // If LIVE records cannot be created in the current PID due to workspace restrictions, prepare creation of placeholder-record
00708                                             if ($res < 0) {
00709                                                 $recordAccess = FALSE;
00710                                                 $this->newlog('Stage for versioning root point and users access level did not allow for editing', 1);
00711                                             }
00712                                         } else { // So, if no live records were allowed, we have to create a new version of this record:
00713                                             if ($TCA[$table]['ctrl']['versioningWS']) {
00714                                                 $createNewVersion = TRUE;
00715                                             } else {
00716                                                 $recordAccess = FALSE;
00717                                                 $this->newlog('Record could not be created in this workspace in this branch', 1);
00718                                             }
00719                                         }
00720                                     }
00721                                 }
00722                             } else {
00723                                 debug('Internal ERROR: pid should not be less than zero!');
00724                             }
00725                             $status = 'new'; // Yes new record, change $record_status to 'insert'
00726                         } else { // Nope... $id is a number
00727                             $fieldArray = array();
00728                             $recordAccess = $this->checkRecordUpdateAccess($table, $id, $incomingFieldArray, $hookObjectsArr);
00729                             if (!$recordAccess) {
00730                                 $propArr = $this->getRecordProperties($table, $id);
00731                                 $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']);
00732                             } else { // Next check of the record permissions (internals)
00733                                 $recordAccess = $this->BE_USER->recordEditAccessInternals($table, $id);
00734                                 if (!$recordAccess) {
00735                                     $propArr = $this->getRecordProperties($table, $id);
00736                                     $this->newlog("recordEditAccessInternals() check failed. [" . $this->BE_USER->errorMsg . "]", 1);
00737                                 } else { // Here we fetch the PID of the record that we point to...
00738                                     $tempdata = $this->recordInfo($table, $id, 'pid' . ($TCA[$table]['ctrl']['versioningWS'] ? ',t3ver_wsid,t3ver_stage' : ''));
00739                                     $theRealPid = $tempdata['pid'];
00740 
00741                                         // Use the new id of the versionized record we're trying to write to:
00742                                         // (This record is a child record of a parent and has already been versionized.)
00743                                     if ($this->autoVersionIdMap[$table][$id]) {
00744                                             // For the reason that creating a new version of this record, automatically
00745                                             // created related child records (e.g. "IRRE"), update the accordant field:
00746                                         $this->getVersionizedIncomingFieldArray($table, $id, $incomingFieldArray, $registerDBList);
00747 
00748                                             // Use the new id of the copied/versionized record:
00749                                         $id = $this->autoVersionIdMap[$table][$id];
00750                                         $recordAccess = TRUE;
00751                                         $this->autoVersioningUpdate = TRUE;
00752 
00753                                             // Checking access in case of offline workspace:
00754                                     } elseif (!$this->bypassWorkspaceRestrictions && $errorCode = $this->BE_USER->workspaceCannotEditRecord($table, $tempdata)) {
00755                                         $recordAccess = FALSE; // Versioning is required and it must be offline version!
00756 
00757                                             // Auto-creation of version: In offline workspace, test if versioning is enabled and look for workspace version of input record. If there is no versionized record found we will create one and save to that.
00758                                         if ($this->BE_USER->workspaceAllowAutoCreation($table, $id, $theRealPid)) {
00759                                             $tce = t3lib_div::makeInstance('t3lib_TCEmain');
00760                                             /* @var $tce t3lib_TCEmain  */
00761                                             $tce->stripslashes_values = 0;
00762 
00763                                                 // Setting up command for creating a new version of the record:
00764                                             $cmd = array();
00765                                             $cmd[$table][$id]['version'] = array(
00766                                                 'action' => 'new',
00767                                                 'treeLevels' => -1, // Default is to create a version of the individual records... element versioning that is.
00768                                                 'label' => 'Auto-created for WS #' . $this->BE_USER->workspace
00769                                             );
00770                                             $tce->start(array(), $cmd);
00771                                             $tce->process_cmdmap();
00772                                             $this->errorLog = array_merge($this->errorLog, $tce->errorLog);
00773 
00774                                                 // If copying was successful, share the new uids (also of related children):
00775                                             if ($tce->copyMappingArray[$table][$id]) {
00776                                                 foreach ($tce->copyMappingArray as $origTable => $origIdArray) {
00777                                                     foreach ($origIdArray as $origId => $newId) {
00778                                                         $this->uploadedFileArray[$origTable][$newId] = $this->uploadedFileArray[$origTable][$origId];
00779                                                         $this->autoVersionIdMap[$origTable][$origId] = $newId;
00780                                                     }
00781                                                 }
00782                                                 $this->RTEmagic_copyIndex = t3lib_div::array_merge_recursive_overrule($this->RTEmagic_copyIndex, $tce->RTEmagic_copyIndex); // See where RTEmagic_copyIndex is used inside fillInFieldArray() for more information...
00783 
00784                                                     // Update registerDBList, that holds the copied relations to child records:
00785                                                 $registerDBList = array_merge($registerDBList, $tce->registerDBList);
00786                                                     // For the reason that creating a new version of this record, automatically
00787                                                     // created related child records (e.g. "IRRE"), update the accordant field:
00788                                                 $this->getVersionizedIncomingFieldArray($table, $id, $incomingFieldArray, $registerDBList);
00789 
00790                                                     // Use the new id of the copied/versionized record:
00791                                                 $id = $this->autoVersionIdMap[$table][$id];
00792                                                 $recordAccess = TRUE;
00793                                                 $this->autoVersioningUpdate = TRUE;
00794                                             } else {
00795                                                 $this->newlog("Could not be edited in offline workspace in the branch where found (failure state: '" . $errorCode . "'). Auto-creation of version failed!", 1);
00796                                             }
00797                                         } else {
00798                                             $this->newlog("Could not be edited in offline workspace in the branch where found (failure state: '" . $errorCode . "'). Auto-creation of version not allowed in workspace!", 1);
00799                                         }
00800                                     }
00801                                 }
00802                             }
00803                             $status = 'update'; // the default is 'update'
00804                         }
00805 
00806                             // If access was granted above, proceed to create or update record:
00807                         if ($recordAccess) {
00808 
00809                             list($tscPID) = t3lib_BEfunc::getTSCpid($table, $id, $old_pid_value ? $old_pid_value : $fieldArray['pid']); // Here the "pid" is set IF NOT the old pid was a string pointing to a place in the subst-id array.
00810                             $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
00811                             if ($status == 'new' && $table == 'pages' && is_array($TSConfig['permissions.'])) {
00812                                 $fieldArray = $this->setTSconfigPermissions($fieldArray, $TSConfig['permissions.']);
00813                             }
00814                             if ($createNewVersion) {
00815                                 $newVersion_placeholderFieldArray = $fieldArray;
00816                             }
00817 
00818                                 // Processing of all fields in incomingFieldArray and setting them in $fieldArray
00819                             $fieldArray = $this->fillInFieldArray($table, $id, $fieldArray, $incomingFieldArray, $theRealPid, $status, $tscPID);
00820 
00821                                 // NOTICE! All manipulation beyond this point bypasses both "excludeFields" AND possible "MM" relations / file uploads to field!
00822 
00823                                 // Forcing some values unto field array:
00824                             $fieldArray = $this->overrideFieldArray($table, $fieldArray); // NOTICE: This overriding is potentially dangerous; permissions per field is not checked!!!
00825                             if ($createNewVersion) {
00826                                 $newVersion_placeholderFieldArray = $this->overrideFieldArray($table, $newVersion_placeholderFieldArray);
00827                             }
00828 
00829                                 // Setting system fields
00830                             if ($status == 'new') {
00831                                 if ($TCA[$table]['ctrl']['crdate']) {
00832                                     $fieldArray[$TCA[$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
00833                                     if ($createNewVersion) {
00834                                         $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
00835                                     }
00836                                 }
00837                                 if ($TCA[$table]['ctrl']['cruser_id']) {
00838                                     $fieldArray[$TCA[$table]['ctrl']['cruser_id']] = $this->userid;
00839                                     if ($createNewVersion) {
00840                                         $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['cruser_id']] = $this->userid;
00841                                     }
00842                                 }
00843                             } elseif ($this->checkSimilar) { // Removing fields which are equal to the current value:
00844                                 $fieldArray = $this->compareFieldArrayWithCurrentAndUnset($table, $id, $fieldArray);
00845                             }
00846                             if ($TCA[$table]['ctrl']['tstamp'] && count($fieldArray)) {
00847                                 $fieldArray[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
00848                                 if ($createNewVersion) {
00849                                     $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
00850                                 }
00851                             }
00852                             if ($TCA[$table]['ctrl']['versioningWS']) {
00853                                 $fieldArray['t3ver_stage'] = 0;
00854                             }
00855 
00856                                 // Hook: processDatamap_postProcessFieldArray
00857                             foreach ($hookObjectsArr as $hookObj) {
00858                                 if (method_exists($hookObj, 'processDatamap_postProcessFieldArray')) {
00859                                     $hookObj->processDatamap_postProcessFieldArray($status, $table, $id, $fieldArray, $this);
00860                                 }
00861                             }
00862 
00863                                 // Performing insert/update. If fieldArray has been unset by some userfunction (see hook above), don't do anything
00864                                 // Kasper: Unsetting the fieldArray is dangerous; MM relations might be saved already and files could have been uploaded that are now "lost"
00865                             if (is_array($fieldArray)) {
00866                                 if ($status == 'new') {
00867                                     if ($createNewVersion) { // This creates a new version of the record with online placeholder and offline version
00868                                         $versioningType = $table === 'pages' ? $this->BE_USER->workspaceVersioningTypeGetClosest(t3lib_div::intInRange($TYPO3_CONF_VARS['BE']['newPagesVersioningType'], -1, 1)) : -1;
00869                                         if ($this->BE_USER->workspaceVersioningTypeAccess($versioningType)) {
00870                                             $newVersion_placeholderFieldArray['t3ver_label'] = 'INITIAL PLACEHOLDER';
00871                                             $newVersion_placeholderFieldArray['t3ver_state'] = 1; // Setting placeholder state value for temporary record
00872                                             $newVersion_placeholderFieldArray['t3ver_wsid'] = $this->BE_USER->workspace; // Setting workspace - only so display of place holders can filter out those from other workspaces.
00873                                             $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['label']] = '[PLACEHOLDER, WS#' . $this->BE_USER->workspace . ']';
00874                                             $this->insertDB($table, $id, $newVersion_placeholderFieldArray, FALSE); // Saving placeholder as 'original'
00875 
00876                                                 // For the actual new offline version, set versioning values to point to placeholder:
00877                                             $fieldArray['pid'] = -1;
00878                                             $fieldArray['t3ver_oid'] = $this->substNEWwithIDs[$id];
00879                                             $fieldArray['t3ver_id'] = 1;
00880                                             $fieldArray['t3ver_state'] = -1; // Setting placeholder state value for version (so it can know it is currently a new version...)
00881                                             $fieldArray['t3ver_label'] = 'First draft version';
00882                                             $fieldArray['t3ver_wsid'] = $this->BE_USER->workspace;
00883                                             if ($table === 'pages') { // Swap mode set to "branch" so we can build branches for pages.
00884                                                 $fieldArray['t3ver_swapmode'] = $versioningType;
00885                                             }
00886                                             $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!
00887                                             if ($phShadowId) {
00888                                                     // Processes fields of the placeholder record:
00889                                                 $this->triggerRemapAction(
00890                                                     $table,
00891                                                     $id,
00892                                                     array($this, 'placeholderShadowing'),
00893                                                     array($table, $phShadowId)
00894                                                 );
00895                                                     // Hold auto-versionized ids of placeholders:
00896                                                 $this->autoVersionIdMap[$table][$this->substNEWwithIDs[$id]] = $phShadowId;
00897                                             }
00898                                         } else {
00899                                             $this->newlog('Versioning type "' . $versioningType . '" was not allowed, so could not create new record.', 1);
00900                                         }
00901                                     } else {
00902                                         $this->insertDB($table, $id, $fieldArray, FALSE, $incomingFieldArray['uid']);
00903                                     }
00904                                 } else {
00905                                     $this->updateDB($table, $id, $fieldArray);
00906                                     $this->placeholderShadowing($table, $id);
00907                                 }
00908                             }
00909 
00910                             /*
00911                                                              * Hook: processDatamap_afterDatabaseOperations
00912                                                              *
00913                                                              * Note: When using the hook after INSERT operations, you will only get the temporary NEW... id passed to your hook as $id,
00914                                                              *       but you can easily translate it to the real uid of the inserted record using the $this->substNEWwithIDs array.
00915                                                              */
00916                             $this->hook_processDatamap_afterDatabaseOperations($hookObjectsArr, $status, $table, $id, $fieldArray);
00917                         } // if ($recordAccess) {
00918                     } // if (is_array($incomingFieldArray)) {
00919                 }
00920             }
00921         }
00922 
00923             // Process the stack of relations to remap/correct
00924         $this->processRemapStack();
00925         $this->dbAnalysisStoreExec();
00926         $this->removeRegisteredFiles();
00927 
00928         /*
00929          * Hook: processDatamap_afterAllOperations
00930          *
00931          * Note: When this hook gets called, all operations on the submitted data have been finished.
00932          */
00933         foreach ($hookObjectsArr as $hookObj) {
00934             if (method_exists($hookObj, 'processDatamap_afterAllOperations')) {
00935                 $hookObj->processDatamap_afterAllOperations($this);
00936             }
00937         }
00938     }
00939 
00940     /**
00941      * Fix shadowing of data in case we are editing a offline version of a live "New" placeholder record:
00942      *
00943      * @param   string      Table name
00944      * @param   integer     Record uid
00945      * @return  void
00946      */
00947     function placeholderShadowing($table, $id) {
00948         global $TCA;
00949 
00950         t3lib_div::loadTCA($table);
00951         if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table, $id, '*')) {
00952             if ((int) $liveRec['t3ver_state'] > 0) {
00953                 $justStoredRecord = t3lib_BEfunc::getRecord($table, $id);
00954                 $newRecord = array();
00955 
00956                 $shadowCols = $TCA[$table]['ctrl']['shadowColumnsForNewPlaceholders'];
00957                 $shadowCols .= ',' . $TCA[$table]['ctrl']['languageField'];
00958                 $shadowCols .= ',' . $TCA[$table]['ctrl']['transOrigPointerField'];
00959                 $shadowCols .= ',' . $TCA[$table]['ctrl']['type'];
00960                 $shadowCols .= ',' . $TCA[$table]['ctrl']['label'];
00961 
00962                 $shadowColumns = array_unique(t3lib_div::trimExplode(',', $shadowCols, 1));
00963                 foreach ($shadowColumns as $fieldName) {
00964                     if (strcmp($justStoredRecord[$fieldName], $liveRec[$fieldName]) && isset($TCA[$table]['columns'][$fieldName]) && $fieldName !== 'uid' && $fieldName !== 'pid') {
00965                         $newRecord[$fieldName] = $justStoredRecord[$fieldName];
00966                     }
00967                 }
00968 
00969                 if (count($newRecord)) {
00970                     $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']);
00971                     $this->updateDB($table, $liveRec['uid'], $newRecord);
00972                 }
00973             }
00974         }
00975     }
00976 
00977     /**
00978      * Filling in the field array
00979      * $this->exclude_array is used to filter fields if needed.
00980      *
00981      * @param   string      Table name
00982      * @param   integer     Record ID
00983      * @param   array       Default values, Preset $fieldArray with 'pid' maybe (pid and uid will be not be overridden anyway)
00984      * @param   array       $incomingFieldArray is which fields/values you want to set. There are processed and put into $fieldArray if OK
00985      * @param   integer     The real PID value of the record. For updates, this is just the pid of the record. For new records this is the PID of the page where it is inserted.
00986      * @param   string      $status = 'new' or 'update'
00987      * @param   integer     $tscPID: TSconfig PID
00988      * @return  array       Field Array
00989      */
00990     function fillInFieldArray($table, $id, $fieldArray, $incomingFieldArray, $realPid, $status, $tscPID) {
00991         global $TCA;
00992 
00993             // Initialize:
00994         t3lib_div::loadTCA($table);
00995         $originalLanguageRecord = NULL;
00996         $originalLanguage_diffStorage = NULL;
00997         $diffStorageFlag = FALSE;
00998 
00999             // Setting 'currentRecord' and 'checkValueRecord':
01000         if (strstr($id, 'NEW')) {
01001             $currentRecord = $checkValueRecord = $fieldArray; // must have the 'current' array - not the values after processing below...
01002 
01003                 // IF $incomingFieldArray is an array, overlay it.
01004                 // 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...
01005             if (is_array($incomingFieldArray) && is_array($checkValueRecord)) {
01006                 $checkValueRecord = t3lib_div::array_merge_recursive_overrule($checkValueRecord, $incomingFieldArray);
01007             }
01008         } else {
01009             $currentRecord = $checkValueRecord = $this->recordInfo($table, $id, '*'); // We must use the current values as basis for this!
01010 
01011             t3lib_BEfunc::fixVersioningPid($table, $currentRecord); // This is done to make the pid positive for offline versions; Necessary to have diff-view for pages_language_overlay in workspaces.
01012 
01013                 // Get original language record if available:
01014             if (is_array($currentRecord)
01015                 && $TCA[$table]['ctrl']['transOrigDiffSourceField']
01016                 && $TCA[$table]['ctrl']['languageField']
01017                 && $currentRecord[$TCA[$table]['ctrl']['languageField']] > 0
01018                 && $TCA[$table]['ctrl']['transOrigPointerField']
01019                 && intval($currentRecord[$TCA[$table]['ctrl']['transOrigPointerField']]) > 0) {
01020 
01021                 $lookUpTable = $TCA[$table]['ctrl']['transOrigPointerTable'] ? $TCA[$table]['ctrl']['transOrigPointerTable'] : $table;
01022                 $originalLanguageRecord = $this->recordInfo($lookUpTable, $currentRecord[$TCA[$table]['ctrl']['transOrigPointerField']], '*');
01023                 t3lib_BEfunc::workspaceOL($lookUpTable, $originalLanguageRecord);
01024                 $originalLanguage_diffStorage = unserialize($currentRecord[$TCA[$table]['ctrl']['transOrigDiffSourceField']]);
01025             }
01026         }
01027         $this->checkValue_currentRecord = $checkValueRecord;
01028 
01029         /*
01030                In the following all incoming value-fields are tested:
01031                - Are the user allowed to change the field?
01032                - Is the field uid/pid (which are already set)
01033                - perms-fields for pages-table, then do special things...
01034                - If the field is nothing of the above and the field is configured in TCA, the fieldvalues are evaluated by ->checkValue
01035 
01036                If everything is OK, the field is entered into $fieldArray[]
01037            */
01038         foreach ($incomingFieldArray as $field => $fieldValue) {
01039             if (!in_array($table . '-' . $field, $this->exclude_array) && !$this->data_disableFields[$table][$id][$field]) { // The field must be editable.
01040 
01041                     // Checking if a value for language can be changed:
01042                 $languageDeny = $TCA[$table]['ctrl']['languageField'] && !strcmp($TCA[$table]['ctrl']['languageField'], $field) && !$this->BE_USER->checkLanguageAccess($fieldValue);
01043 
01044                 if (!$languageDeny) {
01045                         // Stripping slashes - will probably be removed the day $this->stripslashes_values is removed as an option...
01046                     if ($this->stripslashes_values) {
01047                         if (is_array($fieldValue)) {
01048                             t3lib_div::stripSlashesOnArray($fieldValue);
01049                         } else {
01050                             $fieldValue = stripslashes($fieldValue);
01051                         }
01052                     }
01053 
01054                     switch ($field) {
01055                         case 'uid':
01056                         case 'pid':
01057                             // Nothing happens, already set
01058                         break;
01059                         case 'perms_userid':
01060                         case 'perms_groupid':
01061                         case 'perms_user':
01062                         case 'perms_group':
01063                         case 'perms_everybody':
01064                                 // Permissions can be edited by the owner or the administrator
01065                             if ($table == 'pages' && ($this->admin || $status == 'new' || $this->pageInfo($id, 'perms_userid') == $this->userid)) {
01066                                 $value = intval($fieldValue);
01067                                 switch ($field) {
01068                                     case 'perms_userid':
01069                                         $fieldArray[$field] = $value;
01070                                     break;
01071                                     case 'perms_groupid':
01072                                         $fieldArray[$field] = $value;
01073                                     break;
01074                                     default:
01075                                         if ($value >= 0 && $value < pow(2, 5)) {
01076                                             $fieldArray[$field] = $value;
01077                                         }
01078                                     break;
01079                                 }
01080                             }
01081                         break;
01082                         case 't3ver_oid':
01083                         case 't3ver_id':
01084                         case 't3ver_wsid':
01085                         case 't3ver_state':
01086                         case 't3ver_swapmode':
01087                         case 't3ver_count':
01088                         case 't3ver_stage':
01089                         case 't3ver_tstamp':
01090                             // t3ver_label is not here because it CAN be edited as a regular field!
01091                         break;
01092                         default:
01093                             if (isset($TCA[$table]['columns'][$field])) {
01094                                     // Evaluating the value
01095                                 $res = $this->checkValue($table, $field, $fieldValue, $id, $status, $realPid, $tscPID);
01096                                 if (isset($res['value'])) {
01097                                     $fieldArray[$field] = $res['value'];
01098                                 }
01099 
01100                                     // Add the value of the original record to the diff-storage content:
01101                                 if ($this->updateModeL10NdiffData && $TCA[$table]['ctrl']['transOrigDiffSourceField']) {
01102                                     $originalLanguage_diffStorage[$field] = $this->updateModeL10NdiffDataClear ? '' : $originalLanguageRecord[$field];
01103                                     $diffStorageFlag = TRUE;
01104                                 }
01105 
01106                                     // If autoversioning is happening we need to perform a nasty hack. The case is parallel to a similar hack inside checkValue_group_select_file().
01107                                     // 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.
01108                                     // However, with auto-versioning the submitted content of the field will just overwrite the corrected values. This leaves a) lost RTEmagic files and b) creates a double reference to the old files.
01109                                     // The only solution I can come up with is detecting when auto versioning happens, then see if any RTEmagic images was copied and if so make a stupid string-replace of the content !
01110                                 if ($this->autoVersioningUpdate === TRUE) {
01111                                     if (is_array($this->RTEmagic_copyIndex[$table][$id][$field])) {
01112                                         foreach ($this->RTEmagic_copyIndex[$table][$id][$field] as $oldRTEmagicName => $newRTEmagicName) {
01113                                             $fieldArray[$field] = str_replace(' src="' . $oldRTEmagicName . '"', ' src="' . $newRTEmagicName . '"', $fieldArray[$field]);
01114                                         }
01115                                     }
01116                                 }
01117 
01118                             } elseif ($TCA[$table]['ctrl']['origUid'] === $field) { // Allow value for original UID to pass by...
01119                                 $fieldArray[$field] = $fieldValue;
01120                             }
01121                         break;
01122                     }
01123                 } // Checking language.
01124             } // Check exclude fields / disabled fields...
01125         }
01126             // Add diff-storage information:
01127         if ($diffStorageFlag && !isset($fieldArray[$TCA[$table]['ctrl']['transOrigDiffSourceField']])) { // If the field is set it would probably be because of an undo-operation - in which case we should not update the field of course...
01128             $fieldArray[$TCA[$table]['ctrl']['transOrigDiffSourceField']] = serialize($originalLanguage_diffStorage);
01129         }
01130 
01131             // Checking for RTE-transformations of fields:
01132         $types_fieldConfig = t3lib_BEfunc::getTCAtypes($table, $currentRecord);
01133         $theTypeString = t3lib_BEfunc::getTCAtypeValue($table, $currentRecord);
01134         if (is_array($types_fieldConfig)) {
01135             foreach ($types_fieldConfig as $vconf) {
01136                     // Write file configuration:
01137                 $eFile = t3lib_parsehtml_proc::evalWriteFile($vconf['spec']['static_write'], array_merge($currentRecord, $fieldArray)); // inserted array_merge($currentRecord,$fieldArray) 170502
01138 
01139                     // RTE transformations:
01140                 if (!$this->dontProcessTransformations) {
01141                     if (isset($fieldArray[$vconf['field']])) {
01142                             // Look for transformation flag:
01143                         switch ((string) $incomingFieldArray['_TRANSFORM_' . $vconf['field']]) {
01144                             case 'RTE':
01145                                 $RTEsetup = $this->BE_USER->getTSConfig('RTE', t3lib_BEfunc::getPagesTSconfig($tscPID));
01146                                 $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'], $table, $vconf['field'], $theTypeString);
01147 
01148                                     // Set alternative relative path for RTE images/links:
01149                                 $RTErelPath = is_array($eFile) ? dirname($eFile['relEditFile']) : '';
01150 
01151                                     // Get RTE object, draw form and set flag:
01152                                 $RTEobj = t3lib_BEfunc::RTEgetObj();
01153                                 if (is_object($RTEobj)) {
01154                                     $fieldArray[$vconf['field']] = $RTEobj->transformContent('db', $fieldArray[$vconf['field']], $table, $vconf['field'], $currentRecord, $vconf['spec'], $thisConfig, $RTErelPath, $currentRecord['pid']);
01155                                 } else {
01156                                     debug('NO RTE OBJECT FOUND!');
01157                                 }
01158                             break;
01159                         }
01160                     }
01161                 }
01162 
01163                     // Write file configuration:
01164                 if (is_array($eFile)) {
01165                     $mixedRec = array_merge($currentRecord, $fieldArray);
01166                     $SW_fileContent = t3lib_div::getUrl($eFile['editFile']);
01167                     $parseHTML = t3lib_div::makeInstance('t3lib_parsehtml_proc');
01168                     /* @var $parseHTML t3lib_parsehtml_proc */
01169                     $parseHTML->init('', '');
01170 
01171                     $eFileMarker = $eFile['markerField'] && trim($mixedRec[$eFile['markerField']]) ? trim($mixedRec[$eFile['markerField']]) : '###TYPO3_STATICFILE_EDIT###';
01172                     $insertContent = str_replace($eFileMarker, '', $mixedRec[$eFile['contentField']]); // must replace the marker if present in content!
01173 
01174                     $SW_fileNewContent = $parseHTML->substituteSubpart($SW_fileContent, $eFileMarker, LF . $insertContent . LF, 1, 1);
01175                     t3lib_div::writeFile($eFile['editFile'], $SW_fileNewContent);
01176 
01177                         // Write status:
01178                     if (!strstr($id, 'NEW') && $eFile['statusField']) {
01179                         $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
01180                             $table,
01181                             'uid=' . intval($id),
01182                             array(
01183                                  $eFile['statusField'] => $eFile['relEditFile'] . ' updated ' . date('d-m-Y H:i:s') . ', bytes ' . strlen($mixedRec[$eFile['contentField']])
01184                             )
01185                         );
01186                     }
01187                 } elseif ($eFile && is_string($eFile)) {
01188                     $this->log($table, $id, 2, 0, 1, "Write-file error: '%s'", 13, array($eFile), $realPid);
01189                 }
01190             }
01191         }
01192             // Return fieldArray
01193         return $fieldArray;
01194     }
01195 
01196 
01197     /*********************************************
01198      *
01199      * Evaluation of input values
01200      *
01201      ********************************************/
01202 
01203     /**
01204      * Evaluates a value according to $table/$field settings.
01205      * This function is for real database fields - NOT FlexForm "pseudo" fields.
01206      * NOTICE: Calling this function expects this: 1) That the data is saved! (files are copied and so on) 2) That files registered for deletion IS deleted at the end (with ->removeRegisteredFiles() )
01207      *
01208      * @param   string      Table name
01209      * @param   string      Field name
01210      * @param   string      Value to be evaluated. Notice, this is the INPUT value from the form. The original value (from any existing record) must be manually looked up inside the function if needed - or taken from $currentRecord array.
01211      * @param   string      The record-uid, mainly - but not exclusively - used for logging
01212      * @param   string      'update' or 'new' flag
01213      * @param   integer     The real PID value of the record. For updates, this is just the pid of the record. For new records this is the PID of the page where it is inserted. If $realPid is -1 it means that a new version of the record is being inserted.
01214      * @param   integer     $tscPID
01215      * @return  array       Returns the evaluated $value as key "value" in this array. Can be checked with isset($res['value']) ...
01216      */
01217     function checkValue($table, $field, $value, $id, $status, $realPid, $tscPID) {
01218         global $TCA, $PAGES_TYPES;
01219         t3lib_div::loadTCA($table);
01220 
01221         $res = array(); // result array
01222         $recFID = $table . ':' . $id . ':' . $field;
01223 
01224             // Processing special case of field pages.doktype
01225         if (($table === 'pages' || $table === 'pages_language_overlay') && $field === 'doktype') {
01226                 // If the user may not use this specific doktype, we issue a warning
01227             if (!($this->admin || t3lib_div::inList($this->BE_USER->groupData['pagetypes_select'], $value))) {
01228                 $propArr = $this->getRecordProperties($table, $id);
01229                 $this->log($table, $id, 5, 0, 1, "You cannot change the 'doktype' of page '%s' to the desired value.", 1, array($propArr['header']), $propArr['event_pid']);
01230                 return $res;
01231             }
01232             ;
01233             if ($status == 'update') {
01234                     // This checks 1) if we should check for disallowed tables and 2) if there are records from disallowed tables on the current page
01235                 $onlyAllowedTables = isset($PAGES_TYPES[$value]['onlyAllowedTables']) ? $PAGES_TYPES[$value]['onlyAllowedTables'] : $PAGES_TYPES['default']['onlyAllowedTables'];
01236                 if ($onlyAllowedTables) {
01237                     $theWrongTables = $this->doesPageHaveUnallowedTables($id, $value);
01238                     if ($theWrongTables) {
01239                         $propArr = $this->getRecordProperties($table, $id);
01240                         $this->log($table, $id, 5, 0, 1, "'doktype' of page '%s' could not be changed because the page contains records from disallowed tables; %s", 2, array($propArr['header'], $theWrongTables), $propArr['event_pid']);
01241                         return $res;
01242                     }
01243                 }
01244             }
01245         }
01246 
01247             // Get current value:
01248         $curValueRec = $this->recordInfo($table, $id, $field);
01249         $curValue = $curValueRec[$field];
01250 
01251             // Getting config for the field
01252         $tcaFieldConf = $TCA[$table]['columns'][$field]['config'];
01253 
01254             // Preform processing:
01255         $res = $this->checkValue_SW($res, $value, $tcaFieldConf, $table, $id, $curValue, $status, $realPid, $recFID, $field, $this->uploadedFileArray[$table][$id][$field], $tscPID);
01256 
01257         return $res;
01258     }
01259 
01260     /**
01261      * Branches out evaluation of a field value based on its type as configured in TCA
01262      * Can be called for FlexForm pseudo fields as well, BUT must not have $field set if so.
01263      *
01264      * @param   array       The result array. The processed value (if any!) is set in the "value" key.
01265      * @param   string      The value to set.
01266      * @param   array       Field configuration from TCA
01267      * @param   string      Table name
01268      * @param   integer     Return UID
01269      * @param   [type]      $curValue: ...
01270      * @param   [type]      $status: ...
01271      * @param   integer     The real PID value of the record. For updates, this is just the pid of the record. For new records this is the PID of the page where it is inserted. If $realPid is -1 it means that a new version of the record is being inserted.
01272      * @param   [type]      $recFID: ...
01273      * @param   string      Field name. Must NOT be set if the call is for a flexform field (since flexforms are not allowed within flexforms).
01274      * @param   [type]      $uploadedFiles: ...
01275      * @param   [type]      $tscPID: ...
01276      * @return  array       Returns the evaluated $value as key "value" in this array.
01277      */
01278     function checkValue_SW($res, $value, $tcaFieldConf, $table, $id, $curValue, $status, $realPid, $recFID, $field, $uploadedFiles, $tscPID) {
01279 
01280         $PP = array($table, $id, $curValue, $status, $realPid, $recFID, $tscPID);
01281 
01282         switch ($tcaFieldConf['type']) {
01283             case 'text':
01284                 $res = $this->checkValue_text($res, $value, $tcaFieldConf, $PP, $field);
01285             break;
01286             case 'passthrough':
01287             case 'user':
01288                 $res['value'] = $value;
01289             break;
01290             case 'input':
01291                 $res = $this->checkValue_input($res, $value, $tcaFieldConf, $PP, $field);
01292             break;
01293             case 'check':
01294                 $res = $this->checkValue_check($res, $value, $tcaFieldConf, $PP);
01295             break;
01296             case 'radio':
01297                 $res = $this->checkValue_radio($res, $value, $tcaFieldConf, $PP);
01298             break;
01299             case 'group':
01300             case 'select':
01301                 $res = $this->checkValue_group_select($res, $value, $tcaFieldConf, $PP, $uploadedFiles, $field);
01302             break;
01303             case 'inline':
01304                 $res = $this->checkValue_inline($res, $value, $tcaFieldConf, $PP, $field);
01305             break;
01306             case 'flex':
01307                 if ($field) { // FlexForms are only allowed for real fields.
01308                     $res = $this->checkValue_flex($res, $value, $tcaFieldConf, $PP, $uploadedFiles, $field);
01309                 }
01310             break;
01311             default:
01312                 #debug(array($tcaFieldConf,$res,$value),'NON existing field type:');
01313             break;
01314         }
01315 
01316         return $res;
01317     }
01318 
01319 
01320     /**
01321      * Evaluate "text" type values.
01322      *
01323      * @param   array       The result array. The processed value (if any!) is set in the "value" key.
01324      * @param   string      The value to set.
01325      * @param   array       Field configuration from TCA
01326      * @param   array       Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
01327      * @param   string      Field name
01328      * @return  array       Modified $res array
01329      */
01330     function checkValue_text($res, $value, $tcaFieldConf, $PP, $field = '') {
01331         $evalCodesArray = t3lib_div::trimExplode(',', $tcaFieldConf['eval'], 1);
01332         $res = $this->checkValue_text_Eval($value, $evalCodesArray, $tcaFieldConf['is_in']);
01333         return $res;
01334     }
01335 
01336     /**
01337      * Evaluate "input" type values.
01338      *
01339      * @param   array       The result array. The processed value (if any!) is set in the "value" key.
01340      * @param   string      The value to set.
01341      * @param   array       Field configuration from TCA
01342      * @param   array       Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
01343      * @param   string      Field name
01344      * @return  array       Modified $res array
01345      */
01346     function checkValue_input($res, $value, $tcaFieldConf, $PP, $field = '') {
01347         list($table, $id, $curValue, $status, $realPid, $recFID) = $PP;
01348 
01349             // Secures the string-length to be less than max.
01350         if (intval($tcaFieldConf['max']) > 0) {
01351             $value = $GLOBALS['LANG']->csConvObj->substr($GLOBALS['LANG']->charSet, $value, 0, intval($tcaFieldConf['max']));
01352         }
01353 
01354             // Checking range of value:
01355         if ($tcaFieldConf['range'] && $value != $tcaFieldConf['checkbox'] && $value != $tcaFieldConf['default']) {
01356             if (isset($tcaFieldConf['range']['upper']) && $value > $tcaFieldConf['range']['upper']) {
01357                 $value = $tcaFieldConf['range']['upper'];
01358             }
01359             if (isset($tcaFieldConf['range']['lower']) && $value < $tcaFieldConf['range']['lower']) {
01360                 $value = $tcaFieldConf['range']['lower'];
01361             }
01362         }
01363 
01364             // Process evaluation settings:
01365         $evalCodesArray = t3lib_div::trimExplode(',', $tcaFieldConf['eval'], 1);
01366         $res = $this->checkValue_input_Eval($value, $evalCodesArray, $tcaFieldConf['is_in']);
01367 
01368             // Process UNIQUE settings:
01369         if ($field && $realPid >= 0) { // Field is NOT set for flexForms - which also means that uniqueInPid and unique is NOT available for flexForm fields! Also getUnique should not be done for versioning and if PID is -1 ($realPid<0) then versioning is happening...
01370             if ($res['value'] && in_array('uniqueInPid', $evalCodesArray)) {
01371                 $res['value'] = $this->getUnique($table, $field, $res['value'], $id, $realPid);
01372             }
01373             if ($res['value'] && in_array('unique', $evalCodesArray)) {
01374                 $res['value'] = $this->getUnique($table, $field, $res['value'], $id);
01375             }
01376         }
01377 
01378         return $res;
01379     }
01380 
01381     /**
01382      * Evaluates 'check' type values.
01383      *
01384      * @param   array       The result array. The processed value (if any!) is set in the 'value' key.
01385      * @param   string      The value to set.
01386      * @param   array       Field configuration from TCA
01387      * @param   array       Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
01388      * @return  array       Modified $res array
01389      */
01390     function checkValue_check($res, $value, $tcaFieldConf, $PP) {
01391         list($table, $id, $curValue, $status, $realPid, $recFID) = $PP;
01392 
01393         $itemC = count($tcaFieldConf['items']);
01394         if (!$itemC) {
01395             $itemC = 1;
01396         }
01397         $maxV = pow(2, $itemC);
01398 
01399         if ($value < 0) {
01400             $value = 0;
01401         }
01402         if ($value > $maxV) {
01403             $value = $maxV;
01404         }
01405         $res['value'] = $value;
01406 
01407         return $res;
01408     }
01409 
01410     /**
01411      * Evaluates 'radio' type values.
01412      *
01413      * @param   array       The result array. The processed value (if any!) is set in the 'value' key.
01414      * @param   string      The value to set.
01415      * @param   array       Field configuration from TCA
01416      * @param   array       Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
01417      * @return  array       Modified $res array
01418      */
01419     function checkValue_radio($res, $value, $tcaFieldConf, $PP) {
01420         list($table, $id, $curValue, $status, $realPid, $recFID) = $PP;
01421 
01422         if (is_array($tcaFieldConf['items'])) {
01423             foreach ($tcaFieldConf['items'] as $set) {
01424                 if (!strcmp($set[1], $value)) {
01425                     $res['value'] = $value;
01426                     break;
01427                 }
01428             }
01429         }
01430 
01431         return $res;
01432     }
01433 
01434     /**
01435      * Evaluates 'group' or 'select' type values.
01436      *
01437      * @param   array       The result array. The processed value (if any!) is set in the 'value' key.
01438      * @param   string      The value to set.
01439      * @param   array       Field configuration from TCA
01440      * @param   array       Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
01441      * @param   [type]      $uploadedFiles: ...
01442      * @param   string      Field name
01443      * @return  array       Modified $res array
01444      */
01445     function checkValue_group_select($res, $value, $tcaFieldConf, $PP, $uploadedFiles, $field) {
01446 
01447         list($table, $id, $curValue, $status, $realPid, $recFID) = $PP;
01448 
01449             // Detecting if value sent is an array and if so, implode it around a comma:
01450         if (is_array($value)) {
01451             $value = implode(',', $value);
01452         }
01453 
01454             // This converts all occurencies of '&#123;' to the byte 123 in the string - this is needed in very rare cases where filenames with special characters (like ???, umlaud etc) gets sent to the server as HTML entities instead of bytes. The error is done only by MSIE, not Mozilla and Opera.
01455             // Anyways, this should NOT disturb anything else:
01456         $value = $this->convNumEntityToByteValue($value);
01457 
01458             // When values are sent as group or select they come as comma-separated values which are exploded by this function:
01459         $valueArray = $this->checkValue_group_select_explodeSelectGroupValue($value);
01460 
01461             // If not multiple is set, then remove duplicates:
01462         if (!$tcaFieldConf['multiple']) {
01463             $valueArray = array_unique($valueArray);
01464         }
01465 
01466             // If an exclusive key is found, discard all others:
01467         if ($tcaFieldConf['type'] == 'select' && $tcaFieldConf['exclusiveKeys']) {
01468             $exclusiveKeys = t3lib_div::trimExplode(',', $tcaFieldConf['exclusiveKeys']);
01469             foreach ($valueArray as $kk => $vv) {
01470                 if (in_array($vv, $exclusiveKeys)) { // $vv is the item key!
01471                     $valueArray = array($kk => $vv);
01472                     break;
01473                 }
01474             }
01475         }
01476 
01477             // This could be a good spot for parsing the array through a validation-function which checks if the values are alright (except that database references are not in their final form - but that is the point, isn't it?)
01478             // NOTE!!! Must check max-items of files before the later check because that check would just leave out filenames if there are too many!!
01479 
01480             // Checking for select / authMode, removing elements from $valueArray if any of them is not allowed!
01481         if ($tcaFieldConf['type'] == 'select' && $tcaFieldConf['authMode']) {
01482             $preCount = count($valueArray);
01483             foreach ($valueArray as $kk => $vv) {
01484                 if (!$this->BE_USER->checkAuthMode($table, $field, $vv, $tcaFieldConf['authMode'])) {
01485                     unset($valueArray[$kk]);
01486                 }
01487             }
01488 
01489                 // During the check it turns out that the value / all values were removed - we respond by simply returning an empty array so nothing is written to DB for this field.
01490             if ($preCount && !count($valueArray)) {
01491                 return array();
01492             }
01493         }
01494 
01495             // For group types:
01496         if ($tcaFieldConf['type'] == 'group') {
01497             switch ($tcaFieldConf['internal_type']) {
01498                 case 'file_reference':
01499                 case 'file':
01500                     $valueArray = $this->checkValue_group_select_file(
01501                         $valueArray,
01502                         $tcaFieldConf,
01503                         $curValue,
01504                         $uploadedFiles,
01505                         $status,
01506                         $table,
01507                         $id,
01508                         $recFID
01509                     );
01510                 break;
01511                 case 'db':
01512                     $valueArray = $this->checkValue_group_select_processDBdata($valueArray, $tcaFieldConf, $id, $status, 'group', $table, $field);
01513                 break;
01514             }
01515         }
01516             // For select types which has a foreign table attached:
01517         if ($tcaFieldConf['type'] == 'select' && $tcaFieldConf['foreign_table']) {
01518                 // check, if there is a NEW... id in the value, that should be substituded later
01519             if (strpos($value, 'NEW') !== FALSE) {
01520                 $this->remapStackRecords[$table][$id] = array('remapStackIndex' => count($this->remapStack));
01521                 $this->addNewValuesToRemapStackChildIds($valueArray);
01522                 $this->remapStack[] = array(
01523                     'func' => 'checkValue_group_select_processDBdata',
01524                     'args' => array($valueArray, $tcaFieldConf, $id, $status, 'select', $table, $field),
01525                     'pos' => array('valueArray' => 0, 'tcaFieldConf' => 1, 'id' => 2, 'table' => 5),
01526                     'field' => $field
01527                 );
01528                 $unsetResult = TRUE;
01529             } else {
01530                 $valueArray = $this->checkValue_group_select_processDBdata($valueArray, $tcaFieldConf, $id, $status, 'select', $table, $field);
01531             }
01532         }
01533 
01534         if (!$unsetResult) {
01535             $newVal = $this->checkValue_checkMax($tcaFieldConf, $valueArray);
01536             $res['value'] = implode(',', $newVal);
01537         } else {
01538             unset($res['value']);
01539         }
01540 
01541         return $res;
01542     }
01543 
01544     /**
01545      * Handling files for group/select function
01546      *
01547      * @param   array       Array of incoming file references. Keys are numeric, values are files (basically, this is the exploded list of incoming files)
01548      * @param   array       Configuration array from TCA of the field
01549      * @param   string      Current value of the field
01550      * @param   array       Array of uploaded files, if any
01551      * @param   string      Status ("update" or ?)
01552      * @param   string      tablename of record
01553      * @param   integer     UID of record
01554      * @param   string      Field identifier ([table:uid:field:....more for flexforms?]
01555      * @return  array       Modified value array
01556      * @see checkValue_group_select()
01557      */
01558     function checkValue_group_select_file($valueArray, $tcaFieldConf, $curValue, $uploadedFileArray, $status, $table, $id, $recFID) {
01559 
01560         if (!$this->bypassFileHandling) { // If filehandling should NOT be bypassed, do processing:
01561 
01562                 // If any files are uploaded, add them to value array
01563             if (is_array($uploadedFileArray) &&
01564                 $uploadedFileArray['name'] &&
01565                 strcmp($uploadedFileArray['tmp_name'], 'none')) {
01566                 $valueArray[] = $uploadedFileArray['tmp_name'];
01567                 $this->alternativeFileName[$uploadedFileArray['tmp_name']] = $uploadedFileArray['name'];
01568             }
01569 
01570                 // Creating fileFunc object.
01571             if (!$this->fileFunc) {
01572                 $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions');
01573                 $this->include_filefunctions = 1;
01574             }
01575                 // Setting permitted extensions.
01576             $all_files = array();
01577             $all_files['webspace']['allow'] = $tcaFieldConf['allowed'];
01578             $all_files['webspace']['deny'] = $tcaFieldConf['disallowed'] ? $tcaFieldConf['disallowed'] : '*';
01579             $all_files['ftpspace'] = $all_files['webspace'];
01580             $this->fileFunc->init('', $all_files);
01581         }
01582 
01583             // If there is an upload folder defined:
01584         if ($tcaFieldConf['uploadfolder'] && $tcaFieldConf['internal_type'] == 'file') {
01585             if (!$this->bypassFileHandling) { // If filehandling should NOT be bypassed, do processing:
01586                     // For logging..
01587                 $propArr = $this->getRecordProperties($table, $id);
01588 
01589                     // Get destrination path:
01590                 $dest = $this->destPathFromUploadFolder($tcaFieldConf['uploadfolder']);
01591 
01592                     // If we are updating:
01593                 if ($status == 'update') {
01594 
01595                         // Traverse the input values and convert to absolute filenames in case the update happens to an autoVersionized record.
01596                         // Background: This is a horrible workaround! The problem is that when a record is auto-versionized the files of the record get copied and therefore get new names which is overridden with the names from the original record in the incoming data meaning both lost files and double-references!
01597                         // The only solution I could come up with (except removing support for managing files when autoversioning) was to convert all relative files to absolute names so they are copied again (and existing files deleted). This should keep references intact but means that some files are copied, then deleted after being copied _again_.
01598                         // Actually, the same problem applies to database references in case auto-versioning would include sub-records since in such a case references are remapped - and they would be overridden due to the same principle then.
01599                         // Illustration of the problem comes here:
01600                         // We have a record 123 with a file logo.gif. We open and edit the files header in a workspace. So a new version is automatically made.
01601                         // The versions uid is 456 and the file is copied to "logo_01.gif". But the form data that we sent was based on uid 123 and hence contains the filename "logo.gif" from the original.
01602                         // The file management code below will do two things: First it will blindly accept "logo.gif" as a file attached to the record (thus creating a double reference) and secondly it will find that "logo_01.gif" was not in the incoming filelist and therefore should be deleted.
01603                         // If we prefix the incoming file "logo.gif" with its absolute path it will be seen as a new file added. Thus it will be copied to "logo_02.gif". "logo_01.gif" will still be deleted but since the files are the same the difference is zero - only more processing and file copying for no reason. But it will work.
01604                     if ($this->autoVersioningUpdate === TRUE) {
01605                         foreach ($valueArray as $key => $theFile) {
01606                             if ($theFile === basename($theFile)) { // If it is an already attached file...
01607                                 $valueArray[$key] = PATH_site . $tcaFieldConf['uploadfolder'] . '/' . $theFile;
01608                             }
01609                         }
01610                     }
01611 
01612                         // Finding the CURRENT files listed, either from MM or from the current record.
01613                     $theFileValues = array();
01614                     if ($tcaFieldConf['MM']) { // If MM relations for the files also!
01615                         $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
01616                         /* @var $dbAnalysis t3lib_loadDBGroup */
01617                         $dbAnalysis->start('', 'files', $tcaFieldConf['MM'], $id);
01618                         foreach ($dbAnalysis->itemArray as $item) {
01619                             if ($item['id']) {
01620                                 $theFileValues[] = $item['id'];
01621                             }
01622                         }
01623                     } else {
01624                         $theFileValues = t3lib_div::trimExplode(',', $curValue, 1);
01625                     }
01626                     $currentFilesForHistory = implode(',', $theFileValues);
01627 
01628                         // DELETE files: If existing files were found, traverse those and register files for deletion which has been removed:
01629                     if (count($theFileValues)) {
01630                             // Traverse the input values and for all input values which match an EXISTING value, remove the existing from $theFileValues array (this will result in an array of all the existing files which should be deleted!)
01631                         foreach ($valueArray as $key => $theFile) {
01632                             if ($theFile && !strstr(t3lib_div::fixWindowsFilePath($theFile), '/')) {
01633                                 $theFileValues = t3lib_div::removeArrayEntryByValue($theFileValues, $theFile);
01634                             }
01635                         }
01636 
01637                             // This array contains the filenames in the uploadfolder that should be deleted:
01638                         foreach ($theFileValues as $key => $theFile) {
01639                             $theFile = trim($theFile);
01640                             if (@is_file($dest . '/' . $theFile)) {
01641                                 $this->removeFilesStore[] = $dest . '/' . $theFile;
01642                             } elseif ($theFile) {
01643                                 $this->log($table, $id, 5, 0, 1, "Could not delete file '%s' (does not exist). (%s)", 10, array($dest . '/' . $theFile, $recFID), $propArr['event_pid']);
01644                             }
01645                         }
01646                     }
01647                 }
01648 
01649                     // Traverse the submitted values:
01650                 foreach ($valueArray as $key => $theFile) {
01651                         // NEW FILES? If the value contains '/' it indicates, that the file is new and should be added to the uploadsdir (whether its absolute or relative does not matter here)
01652                     if (strstr(t3lib_div::fixWindowsFilePath($theFile), '/')) {
01653                             // Init:
01654                         $maxSize = intval($tcaFieldConf['max_size']);
01655                         $cmd = '';
01656                         $theDestFile = ''; // Must be cleared. Else a faulty fileref may be inserted if the below code returns an error!
01657 
01658                             // Check various things before copying file:
01659                         if (@is_dir($dest) && (@is_file($theFile) || @is_uploaded_file($theFile))) { // File and destination must exist
01660 
01661                                 // Finding size. For safe_mode we have to rely on the size in the upload array if the file is uploaded.
01662                             if (is_uploaded_file($theFile) && $theFile == $uploadedFileArray['tmp_name']) {
01663                                 $fileSize = $uploadedFileArray['size'];
01664                             } else {
01665                                 $fileSize = filesize($theFile);
01666                             }
01667 
01668                             if (!$maxSize || $fileSize <= ($maxSize * 1024)) { // Check file size:
01669                                     // Prepare filename:
01670                                 $theEndFileName = isset($this->alternativeFileName[$theFile]) ? $this->alternativeFileName[$theFile] : $theFile;
01671                                 $fI = t3lib_div::split_fileref($theEndFileName);
01672 
01673                                     // Check for allowed extension:
01674                                 if ($this->fileFunc->checkIfAllowed($fI['fileext'], $dest, $theEndFileName)) {
01675                                     $theDestFile = $this->fileFunc->getUniqueName($this->fileFunc->cleanFileName($fI['file']), $dest);
01676 
01677                                         // If we have a unique destination filename, then write the file:
01678                                     if ($theDestFile) {
01679                                         t3lib_div::upload_copy_move($theFile, $theDestFile);
01680 
01681                                             // Hook for post-processing the upload action
01682                                         if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processUpload'])) {
01683                                             foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processUpload'] as $classRef) {
01684                                                 $hookObject = t3lib_div::getUserObj($classRef);
01685 
01686                                                 if (!($hookObject instanceof t3lib_TCEmain_processUploadHook)) {
01687                                                     throw new UnexpectedValueException('$hookObject must implement interface t3lib_TCEmain_processUploadHook', 1279962349);
01688                                                 }
01689 
01690                                                 $hookObject->processUpload_postProcessAction($theDestFile, $this);
01691                                             }
01692                                         }
01693 
01694                                         $this->copiedFileMap[$theFile] = $theDestFile;
01695                                         clearstatcache();
01696                                         if (!@is_file($theDestFile)) {
01697                                             $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']);
01698                                         }
01699                                     } else {
01700                                         $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']);
01701                                     }
01702                                 } else {
01703                                     $this->log($table, $id, 5, 0, 1, "File extension '%s' not allowed. (%s)", 12, array($fI['fileext'], $recFID), $propArr['event_pid']);
01704                                 }
01705                             } else {
01706                                 $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']);
01707                             }
01708                         } else {
01709                             $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']);
01710                         }
01711 
01712                             // If the destination file was created, we will set the new filename in the value array, otherwise unset the entry in the value array!
01713                         if (@is_file($theDestFile)) {
01714                             $info = t3lib_div::split_fileref($theDestFile);
01715                             $valueArray[$key] = $info['file']; // The value is set to the new filename
01716                         } else {
01717                             unset($valueArray[$key]); // The value is set to the new filename
01718                         }
01719                     }
01720                 }
01721             }
01722 
01723                 // If MM relations for the files, we will set the relations as MM records and change the valuearray to contain a single entry with a count of the number of files!
01724             if ($tcaFieldConf['MM']) {
01725                 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
01726                 /* @var $dbAnalysis t3lib_loadDBGroup */
01727                 $dbAnalysis->tableArray['files'] = array(); // dummy
01728 
01729                 foreach ($valueArray as $key => $theFile) {
01730                         // explode files
01731                     $dbAnalysis->itemArray[]['id'] = $theFile;
01732                 }
01733                 if ($status == 'update') {
01734                     $dbAnalysis->writeMM($tcaFieldConf['MM'], $id, 0);
01735                     $newFiles = implode(',', $dbAnalysis->getValueArray());
01736                     list(, , $recFieldName) = explode(':', $recFID);
01737                     if ($currentFilesForHistory != $newFiles) {
01738                         $this->mmHistoryRecords[$table . ':' . $id]['oldRecord'][$recFieldName] = $currentFilesForHistory;
01739                         $this->mmHistoryRecords[$table . ':' . $id]['newRecord'][$recFieldName] = $newFiles;
01740                     } else {
01741                         $this->mmHistoryRecords[$table . ':' . $id]['oldRecord'][$recFieldName] = '';
01742                         $this->mmHistoryRecords[$table . ':' . $id]['newRecord'][$recFieldName] = '';
01743                     }
01744                 } else {
01745                     $this->dbAnalysisStore[] = array($dbAnalysis, $tcaFieldConf['MM'], $id, 0); // This will be traversed later to execute the actions
01746                 }
01747                 $valueArray = $dbAnalysis->countItems();
01748             }
01749             //store path relative to site root (if uploadfolder is not set or internal_type is file_reference)
01750         } else {
01751             if (count($valueArray)) {
01752                 if (!$this->bypassFileHandling) { // If filehandling should NOT be bypassed, do processing:
01753                     $propArr = $this->getRecordProperties($table, $id); // For logging..
01754                     foreach ($valueArray as &$theFile) {
01755 
01756                             // if alernative File Path is set for the file, then it was an import
01757                         if ($this->alternativeFilePath[$theFile]) {
01758 
01759                                 // don't import the file if it already exists
01760                             if (@is_file(PATH_site . $this->alternativeFilePath[$theFile])) {
01761                                 $theFile = PATH_site . $this->alternativeFilePath[$theFile];
01762 
01763                                 // import the file
01764                             } elseif (@is_file($theFile)) {
01765                                 $dest = dirname(PATH_site . $this->alternativeFilePath[$theFile]);
01766                                 if (!@is_dir($dest)) {
01767                                     t3lib_div::mkdir_deep(PATH_site, dirname($this->alternativeFilePath[$theFile]) . '/');
01768                                 }
01769 
01770                                     // Init:
01771                                 $maxSize = intval($tcaFieldConf['max_size']);
01772                                 $cmd = '';
01773                                 $theDestFile = ''; // Must be cleared. Else a faulty fileref may be inserted if the below code returns an error!
01774                                 $fileSize = filesize($theFile);
01775 
01776                                 if (!$maxSize || $fileSize <= ($maxSize * 1024)) { // Check file size:
01777                                         // Prepare filename:
01778                                     $theEndFileName = isset($this->alternativeFileName[$theFile]) ? $this->alternativeFileName[$theFile] : $theFile;
01779                                     $fI = t3lib_div::split_fileref($theEndFileName);
01780 
01781                                         // Check for allowed extension:
01782                                     if ($this->fileFunc->checkIfAllowed($fI['fileext'], $dest, $theEndFileName)) {
01783                                         $theDestFile = PATH_site . $this->alternativeFilePath[$theFile];
01784 
01785                                             // Write the file:
01786                                         if ($theDestFile) {
01787                                             t3lib_div::upload_copy_move($theFile, $theDestFile);
01788                                             $this->copiedFileMap[$theFile] = $theDestFile;
01789                                             clearstatcache();
01790                                             if (!@is_file($theDestFile)) {
01791                                                 $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']);
01792                                             }
01793                                         } else {
01794                                             $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']);
01795                                         }
01796                                     } else {
01797                                         $this->log($table, $id, 5, 0, 1, "File extension '%s' not allowed. (%s)", 12, array($fI['fileext'], $recFID), $propArr['event_pid']);
01798                                     }
01799                                 } else {
01800                                     $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']);
01801                                 }
01802 
01803                                     // If the destination file was created, we will set the new filename in the value array, otherwise unset the entry in the value array!
01804                                 if (@is_file($theDestFile)) {
01805                                     $theFile = $theDestFile; // The value is set to the new filename
01806                                 } else {
01807                                     unset($theFile); // The value is set to the new filename
01808                                 }
01809                             }
01810                         }
01811                         $theFile = t3lib_div::fixWindowsFilePath($theFile);
01812                         if (t3lib_div::isFirstPartOfStr($theFile, PATH_site)) {
01813                             $theFile = substr($theFile, strlen(PATH_site));
01814                         }
01815                     }
01816                 }
01817             }
01818         }
01819 
01820         return $valueArray;
01821     }
01822 
01823     /**
01824      * Evaluates 'flex' type values.
01825      *
01826      * @param   array       The result array. The processed value (if any!) is set in the 'value' key.
01827      * @param   string      The value to set.
01828      * @param   array       Field configuration from TCA
01829      * @param   array       Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
01830      * @param   array       Uploaded files for the field
01831      * @param   array       Current record array.
01832      * @param   string      Field name
01833      * @return  array       Modified $res array
01834      */
01835     function checkValue_flex($res, $value, $tcaFieldConf, $PP, $uploadedFiles, $field) {
01836         list($table, $id, $curValue, $status, $realPid, $recFID) = $PP;
01837 
01838         if (is_array($value)) {
01839 
01840                 // This value is necessary for flex form processing to happen on flexform fields in page records when they are copied.
01841                 // The problem is, that when copying a page, flexfrom XML comes along in the array for the new record - but since $this->checkValue_currentRecord does not have a uid or pid for that sake, the t3lib_BEfunc::getFlexFormDS() function returns no good DS. For new records we do know the expected PID so therefore we send that with this special parameter. Only active when larger than zero.
01842             $newRecordPidValue = $status == 'new' ? $realPid : 0;
01843 
01844                 // Get current value array:
01845             $dataStructArray = t3lib_BEfunc::getFlexFormDS($tcaFieldConf, $this->checkValue_currentRecord, $table, '', TRUE, $newRecordPidValue);
01846 
01847             $currentValueArray = t3lib_div::xml2array($curValue);
01848             if (!is_array($currentValueArray)) {
01849                 $currentValueArray = array();
01850             }
01851             if (is_array($currentValueArray['meta']['currentLangId'])) {
01852                 unset($currentValueArray['meta']['currentLangId']);
01853             } // Remove all old meta for languages...
01854 
01855                 // Evaluation of input values:
01856             $value['data'] = $this->checkValue_flex_procInData($value['data'], $currentValueArray['data'], $uploadedFiles['data'], $dataStructArray, $PP);
01857 
01858                 // Create XML and convert charsets from input value:
01859             $xmlValue = $this->checkValue_flexArray2Xml($value, TRUE);
01860 
01861                 // If we wanted to set UTF fixed:
01862                 // $storeInCharset='utf-8';
01863                 // $currentCharset=$GLOBALS['LANG']->charSet;
01864                 // $xmlValue = $GLOBALS['LANG']->csConvObj->conv($xmlValue,$currentCharset,$storeInCharset,1);
01865             $storeInCharset = $GLOBALS['LANG']->charSet;
01866 
01867                 // Merge them together IF they are both arrays:
01868                 // Here we convert the currently submitted values BACK to an array, then merge the two and then BACK to XML again. This is needed to ensure the charsets are the same (provided that the current value was already stored IN the charset that the new value is converted to).
01869             if (is_array($currentValueArray)) {
01870                 $arrValue = t3lib_div::xml2array($xmlValue);
01871                 $arrValue = t3lib_div::array_merge_recursive_overrule($currentValueArray, $arrValue);
01872                 $xmlValue = $this->checkValue_flexArray2Xml($arrValue, TRUE);
01873             }
01874 
01875                 // Action commands (sorting order and removals of elements)
01876             $actionCMDs = t3lib_div::_GP('_ACTION_FLEX_FORMdata');
01877             if (is_array($actionCMDs[$table][$id][$field]['data'])) {
01878                 $arrValue = t3lib_div::xml2array($xmlValue);
01879                 $this->_ACTION_FLEX_FORMdata($arrValue['data'], $actionCMDs[$table][$id][$field]['data']);
01880                 $xmlValue = $this->checkValue_flexArray2Xml($arrValue, TRUE);
01881             }
01882 
01883                 // Create the value XML:
01884             $res['value'] = '';
01885             $res['value'] .= $xmlValue;
01886         } else { // Passthrough...:
01887             $res['value'] = $value;
01888         }
01889 
01890         return $res;
01891     }
01892 
01893     /**
01894      * Converts an array to FlexForm XML
01895      *
01896      * @param   array       Array with FlexForm data
01897      * @param   boolean     If set, the XML prologue is returned as well.
01898      * @return  string      Input array converted to XML
01899      */
01900     function checkValue_flexArray2Xml($array, $addPrologue = FALSE) {
01901         $flexObj = t3lib_div::makeInstance('t3lib_flexformtools');
01902         /* @var $flexObj t3lib_flexformtools */
01903         return $flexObj->flexArray2Xml($array, $addPrologue);
01904     }
01905 
01906     /**
01907      * Actions for flex form element (move, delete)
01908      *
01909      * @param   array       &$valueArrayToRemoveFrom: by reference
01910      * @param   array       $deleteCMDS: ...     *
01911      * @return  void
01912      */
01913     function _ACTION_FLEX_FORMdata(&$valueArray, $actionCMDs) {
01914         if (is_array($valueArray) && is_array($actionCMDs)) {
01915             foreach ($actionCMDs as $key => $value) {
01916                 if ($key == '_ACTION') {
01917                         // First, check if there are "commands":
01918                     if (current($actionCMDs[$key]) !== "") {
01919                         asort($actionCMDs[$key]);
01920                         $newValueArray = array();
01921                         foreach ($actionCMDs[$key] as $idx => $order) {
01922                             if (substr($idx, 0, 3) == "ID-") {
01923                                 $idx = $this->newIndexMap[$idx];
01924                             }
01925                             if ($order != "DELETE") { // Just one reflection here: It is clear that when removing elements from a flexform, then we will get lost files unless we act on this delete operation by traversing and deleting files that were referred to.
01926                                 $newValueArray[$idx] = $valueArray[$idx];
01927                             }
01928                             unset($valueArray[$idx]);
01929                         }
01930                         $valueArray = t3lib_div::array_merge($newValueArray, $valueArray);
01931                     }
01932                 } elseif (is_array($actionCMDs[$key]) && isset($valueArray[$key])) {
01933                     $this->_ACTION_FLEX_FORMdata($valueArray[$key], $actionCMDs[$key]);
01934                 }
01935             }
01936         }
01937     }
01938 
01939     /**
01940      * Evaluates 'inline' type values.
01941      * (partly copied from the select_group function on this issue)
01942      *
01943      * @param   array       The result array. The processed value (if any!) is set in the 'value' key.
01944      * @param   string      The value to set.
01945      * @param   array       Field configuration from TCA
01946      * @param   array       Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
01947      * @param   string      Field name
01948      * @return  array       Modified $res array
01949      */
01950     function checkValue_inline($res, $value, $tcaFieldConf, $PP, $field) {
01951         list($table, $id, $curValue, $status, $realPid, $recFID) = $PP;
01952 
01953         if (!$tcaFieldConf['foreign_table']) {
01954             return FALSE; // Fatal error, inline fields should always have a foreign_table defined
01955         }
01956 
01957             // When values are sent they come as comma-separated values which are exploded by this function:
01958         $valueArray = t3lib_div::trimExplode(',', $value);
01959 
01960             // Remove duplicates: (should not be needed)
01961         $valueArray = array_unique($valueArray);
01962 
01963             // Example for received data:
01964             // $value = 45,NEW4555fdf59d154,12,123
01965             // We need to decide whether we use the stack or can save the relation directly.
01966         if (strpos($value, 'NEW') !== FALSE || !t3lib_div::testInt($id)) {
01967             $this->remapStackRecords[$table][$id] = array('remapStackIndex' => count($this->remapStack));
01968             $this->addNewValuesToRemapStackChildIds($valueArray);
01969             $this->remapStack[] = array(
01970                 'func' => 'checkValue_inline_processDBdata',
01971                 'args' => array($valueArray, $tcaFieldConf, $id, $status, $table, $field),
01972                 'pos' => array('valueArray' => 0, 'tcaFieldConf' => 1, 'id' => 2, 'table' => 4),
01973                 'field' => $field
01974             );
01975             unset($res['value']);
01976         } elseif ($value || t3lib_div::testInt($id)) {
01977             $res['value'] = $this->checkValue_inline_processDBdata($valueArray, $tcaFieldConf, $id, $status, $table, $field);
01978         }
01979 
01980         return $res;
01981     }
01982 
01983     /**
01984      * Checks if a fields has more items than defined via TCA in maxitems.
01985      * If there are more items than allowd, the item list is truncated to the defined number.
01986      *
01987      * @param   array       $tcaFieldConf: Field configuration from TCA
01988      * @param   array       $valueArray: Current value array of items
01989      * @return  array       The truncated value array of items
01990      */
01991     function checkValue_checkMax($tcaFieldConf, $valueArray) {
01992             // BTW, checking for min and max items here does NOT make any sense when MM is used because the above function calls will just return an array with a single item (the count) if MM is used... Why didn't I perform the check before? Probably because we could not evaluate the validity of record uids etc... Hmm...
01993 
01994         $valueArrayC = count($valueArray);
01995 
01996             // NOTE to the comment: It's not really possible to check for too few items, because you must then determine first, if the field is actual used regarding the CType.
01997         $maxI = isset($tcaFieldConf['maxitems']) ? intval($tcaFieldConf['maxitems']) : 1;
01998         if ($valueArrayC > $maxI) {
01999             $valueArrayC = $maxI;
02000         } // Checking for not too many elements
02001 
02002             // Dumping array to list
02003         $newVal = array();
02004         foreach ($valueArray as $nextVal) {
02005             if ($valueArrayC == 0) {
02006                 break;
02007             }
02008             $valueArrayC--;
02009             $newVal[] = $nextVal;
02010         }
02011 
02012         return $newVal;
02013     }
02014 
02015 
02016     /*********************************************
02017      *
02018      * Helper functions for evaluation functions.
02019      *
02020      ********************************************/
02021 
02022     /**
02023      * Gets a unique value for $table/$id/$field based on $value
02024      *
02025      * @param   string      Table name
02026      * @param   string      Field name for which $value must be unique
02027      * @param   string      Value string.
02028      * @param   integer     UID to filter out in the lookup (the record itself...)
02029      * @param   integer     If set, the value will be unique for this PID
02030      * @return  string      Modified value (if not-unique). Will be the value appended with a number (until 100, then the function just breaks).
02031      */
02032     function getUnique($table, $field, $value, $id, $newPid = 0) {
02033         global $TCA;
02034 
02035             // Initialize:
02036         t3lib_div::loadTCA($table);
02037         $whereAdd = '';
02038         $newValue = '';
02039         if (intval($newPid)) {
02040             $whereAdd .= ' AND pid=' . intval($newPid);
02041         } else {
02042             $whereAdd .= ' AND pid>=0';
02043         } // "AND pid>=0" for versioning
02044         $whereAdd .= $this->deleteClause($table);
02045 
02046             // If the field is configured in TCA, proceed:
02047         if (is_array($TCA[$table]) && is_array($TCA[$table]['columns'][$field])) {
02048 
02049                 // Look for a record which might already have the value:
02050             $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($value, $table) . ' AND uid!=' . intval($id) . $whereAdd);
02051             $counter = 0;
02052 
02053                 // For as long as records with the test-value existing, try again (with incremented numbers appended).
02054             while ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) {
02055                 $newValue = $value . $counter;
02056                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($newValue, $table) . ' AND uid!=' . intval($id) . $whereAdd);
02057                 $counter++;
02058                 if ($counter > 100) {
02059                     break;
02060                 } // At "100" it will give up and accept a duplicate - should probably be fixed to a small hash string instead...!
02061             }
02062                 // If the new value is there:
02063             $value = strlen($newValue) ? $newValue : $value;
02064         }
02065         return $value;
02066     }
02067 
02068     function checkValue_text_Eval($value, $evalArray, $is_in) {
02069         $res = array();
02070         $newValue = $value;
02071         $set = TRUE;
02072 
02073         foreach ($evalArray as $func) {
02074             switch ($func) {
02075                 case 'trim':
02076                     $value = trim($value);
02077                 break;
02078                 case 'required':
02079                     if (!$value) {
02080                         $set = 0;
02081                     }
02082                 break;
02083                 default:
02084                     if (substr($func, 0, 3) == 'tx_') {
02085                         $evalObj = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func] . ':&' . $func);
02086                         if (is_object($evalObj) && method_exists($evalObj, 'evaluateFieldValue')) {
02087                             $value = $evalObj->evaluateFieldValue($value, $is_in, $set);
02088                         }
02089                     }
02090                 break;
02091             }
02092         }
02093         if ($set) {
02094             $res['value'] = $value;
02095         }
02096         return $res;
02097     }
02098 
02099     /**
02100      * Evaluation of 'input'-type values based on 'eval' list
02101      *
02102      * @param   string      Value to evaluate
02103      * @param   array       Array of evaluations to traverse.
02104      * @param   string      Is-in string for 'is_in' evaluation
02105      * @return  array       Modified $value in key 'value' or empty array
02106      */
02107     function checkValue_input_Eval($value, $evalArray, $is_in) {
02108         $res = array();
02109         $newValue = $value;
02110         $set = TRUE;
02111 
02112         foreach ($evalArray as $func) {
02113             switch ($func) {
02114                 case 'int':
02115                 case 'year':
02116                 case 'time':
02117                 case 'timesec':
02118                     $value = intval($value);
02119                 break;
02120                 case 'date':
02121                 case 'datetime':
02122                     $value = intval($value);
02123                     if ($value > 0 && !$this->dontProcessTransformations) {
02124                         $value -= date('Z', $value);
02125                     }
02126                 break;
02127                 case 'double2':
02128                     $value = preg_replace('/[^0-9,\.-]/', '', $value);
02129                     $negative = substr($value, 0, 1) == '-';
02130                     $value = strtr($value, array(',' => '.', '-' => ''));
02131                     if (strpos($value, '.') === FALSE) {
02132                         $value .= '.0';
02133                     }
02134                     $valueArray = explode('.', $value);
02135                     $dec = array_pop($valueArray);
02136                     $value = join('', $valueArray) . '.' . $dec;
02137                     if ($negative) {
02138                         $value *= -1;
02139                     }
02140                     $value = number_format($value, 2, '.', '');
02141                 break;
02142                 case 'md5':
02143                     if (strlen($value) != 32) {
02144                         $set = FALSE;
02145                     }
02146                 break;
02147                 case 'trim':
02148                     $value = trim($value);
02149                 break;
02150                 case 'upper':
02151                     $value = $GLOBALS['LANG']->csConvObj->conv_case($GLOBALS['LANG']->charSet, $value, 'toUpper');
02152                 break;
02153                 case 'lower':
02154                     $value = $GLOBALS['LANG']->csConvObj->conv_case($GLOBALS['LANG']->charSet, $value, 'toLower');
02155                 break;
02156                 case 'required':
02157                     if (!isset($value) || $value === '') {
02158                         $set = FALSE;
02159                     }
02160                 break;
02161                 case 'is_in':
02162                     $c = strlen($value);
02163                     if ($c) {
02164                         $newVal = '';
02165                         for ($a = 0; $a < $c; $a++) {
02166                             $char = substr($value, $a, 1);
02167                             if (strpos($is_in, $char) !== FALSE) {
02168                                 $newVal .= $char;
02169                             }
02170                         }
02171                         $value = $newVal;
02172                     }
02173                 break;
02174                 case 'nospace':
02175                     $value = str_replace(' ', '', $value);
02176                 break;
02177                 case 'alpha':
02178                     $value = preg_replace('/[^a-zA-Z]/', '', $value);
02179                 break;
02180                 case 'num':
02181                     $value = preg_replace('/[^0-9]/', '', $value);
02182                 break;
02183                 case 'alphanum':
02184                     $value = preg_replace('/[^a-zA-Z0-9]/', '', $value);
02185                 break;
02186                 case 'alphanum_x':
02187                     $value = preg_replace('/[^a-zA-Z0-9_-]/', '', $value);
02188                 break;
02189                 default:
02190                     if (substr($func, 0, 3) == 'tx_') {
02191                         $evalObj = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func] . ':&' . $func);
02192                         if (is_object($evalObj) && method_exists($evalObj, 'evaluateFieldValue')) {
02193                             $value = $evalObj->evaluateFieldValue($value, $is_in, $set);
02194                         }
02195                     }
02196                 break;
02197             }
02198         }
02199         if ($set) {
02200             $res['value'] = $value;
02201         }
02202         return $res;
02203     }
02204 
02205     /**
02206      * Returns data for group/db and select fields
02207      *
02208      * @param   array       Current value array
02209      * @param   array       TCA field config
02210      * @param   integer     Record id, used for look-up of MM relations (local_uid)
02211      * @param   string      Status string ('update' or 'new')
02212      * @param   string      The type, either 'select', 'group' or 'inline'
02213      * @param   string      Table name, needs to be passed to t3lib_loadDBGroup
02214      * @param   string      field name, needs to be set for writing to sys_history
02215      * @return  array       Modified value array
02216      */
02217     function checkValue_group_select_processDBdata($valueArray, $tcaFieldConf, $id, $status, $type, $currentTable, $currentField) {
02218         $tables = $type == 'group' ? $tcaFieldConf['allowed'] : $tcaFieldConf['foreign_table'] . ',' . $tcaFieldConf['neg_foreign_table'];
02219         $prep = $type == 'group' ? $tcaFieldConf['prepend_tname'] : $tcaFieldConf['neg_foreign_table'];
02220         $newRelations = implode(',', $valueArray);
02221 
02222         $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
02223         /* @var $dbAnalysis t3lib_loadDBGroup */
02224         $dbAnalysis->registerNonTableValues = $tcaFieldConf['allowNonIdValues'] ? 1 : 0;
02225         $dbAnalysis->start($newRelations, $tables, '', 0, $currentTable, $tcaFieldConf);
02226 
02227         if ($tcaFieldConf['MM']) {
02228             if ($status == 'update') {
02229                 $oldRelations_dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
02230                 /* @var $oldRelations_dbAnalysis t3lib_loadDBGroup */
02231                 $oldRelations_dbAnalysis->registerNonTableValues = $tcaFieldConf['allowNonIdValues'] ? 1 : 0;
02232                     // db analysis with $id will initialize with the existing relations
02233                 $oldRelations_dbAnalysis->start('', $tables, $tcaFieldConf['MM'], $id, $currentTable, $tcaFieldConf);
02234                 $oldRelations = implode(',', $oldRelations_dbAnalysis->getValueArray());
02235                 $dbAnalysis->writeMM($tcaFieldConf['MM'], $id, $prep);
02236                 if ($oldRelations != $newRelations) {
02237                     $this->mmHistoryRecords[$currentTable . ':' . $id]['oldRecord'][$currentField] = $oldRelations;
02238                     $this->mmHistoryRecords[$currentTable . ':' . $id]['newRecord'][$currentField] = $newRelations;
02239                 } else {
02240                     $this->mmHistoryRecords[$currentTable . ':' . $id]['oldRecord'][$currentField] = '';
02241                     $this->mmHistoryRecords[$currentTable . ':' . $id]['newRecord'][$currentField] = '';
02242                 }
02243             } else {
02244                 $this->dbAnalysisStore[] = array($dbAnalysis, $tcaFieldConf['MM'], $id, $prep, $currentTable); // This will be traversed later to execute the actions
02245             }
02246             $valueArray = $dbAnalysis->countItems();
02247         } else {
02248             $valueArray = $dbAnalysis->getValueArray($prep);
02249             if ($type == 'select' && $prep) {
02250                 $valueArray = $dbAnalysis->convertPosNeg($valueArray, $tcaFieldConf['foreign_table'], $tcaFieldConf['neg_foreign_table']);
02251             }
02252         }
02253 
02254             // Here we should see if 1) the records exist anymore, 2) which are new and check if the BE_USER has read-access to the new ones.
02255         return $valueArray;
02256     }
02257 
02258     /**
02259      * Explodes the $value, which is a list of files/uids (group select)
02260      *
02261      * @param   string      Input string, comma separated values. For each part it will also be detected if a '|' is found and the first part will then be used if that is the case. Further the value will be rawurldecoded.
02262      * @return  array       The value array.
02263      */
02264     function checkValue_group_select_explodeSelectGroupValue($value) {
02265         $valueArray = t3lib_div::trimExplode(',', $value, 1);
02266         foreach ($valueArray as &$newVal) {
02267             $temp = explode('|', $newVal, 2);
02268             $newVal = str_replace(
02269                 ',',
02270                 '',
02271                 str_replace('|', '', rawurldecode($temp[0]))
02272             );
02273         }
02274         return $valueArray;
02275     }
02276 
02277     /**
02278      * Starts the processing the input data for flexforms. This will traverse all sheets / languages and for each it will traverse the sub-structure.
02279      * See checkValue_flex_procInData_travDS() for more details.
02280      * WARNING: Currently, it traverses based on the actual _data_ array and NOT the _structure_. This means that values for non-valid fields, lKey/vKey/sKeys will be accepted! For traversal of data with a call back function you should rather use class.t3lib_flexformtools.php
02281      *
02282      * @param   array       The 'data' part of the INPUT flexform data
02283      * @param   array       The 'data' part of the CURRENT flexform data
02284      * @param   array       The uploaded files for the 'data' part of the INPUT flexform data
02285      * @param   array       Data structure for the form (might be sheets or not). Only values in the data array which has a configuration in the data structure will be processed.
02286      * @param   array       A set of parameters to pass through for the calling of the evaluation functions
02287      * @param   string      Optional call back function, see checkValue_flex_procInData_travDS()  DEPRICATED, use class.t3lib_flexformtools.php instead for traversal!
02288      * @return  array       The modified 'data' part.
02289      * @see checkValue_flex_procInData_travDS()
02290      */
02291     function checkValue_flex_procInData($dataPart, $dataPart_current, $uploadedFiles, $dataStructArray, $pParams, $callBackFunc = '') {
02292         if (is_array($dataPart)) {
02293             foreach ($dataPart as $sKey => $sheetDef) {
02294                 list ($dataStruct, $actualSheet) = t3lib_div::resolveSheetDefInDS($dataStructArray, $sKey);
02295                 if (is_array($dataStruct) && $actualSheet == $sKey && is_array($sheetDef)) {
02296                     foreach ($sheetDef as $lKey => $lData) {
02297                         $this->checkValue_flex_procInData_travDS(
02298                             $dataPart[$sKey][$lKey],
02299                             $dataPart_current[$sKey][$lKey],
02300                             $uploadedFiles[$sKey][$lKey],
02301                             $dataStruct['ROOT']['el'],
02302                             $pParams,
02303                             $callBackFunc,
02304                             $sKey . '/' . $lKey . '/'
02305                         );
02306                     }
02307                 }
02308             }
02309         }
02310 
02311         return $dataPart;
02312     }
02313 
02314     /**
02315      * Processing of the sheet/language data array
02316      * When it finds a field with a value the processing is done by ->checkValue_SW() by default but if a call back function name is given that method in this class will be called for the processing instead.
02317      *
02318      * @param   array       New values (those being processed): Multidimensional Data array for sheet/language, passed by reference!
02319      * @param   array       Current values: Multidimensional Data array. May be empty array() if not needed (for callBackFunctions)
02320      * @param   array       Uploaded files array for sheet/language. May be empty array() if not needed (for callBackFunctions)
02321      * @param   array       Data structure which fits the data array
02322      * @param   array       A set of parameters to pass through for the calling of the evaluation functions / call back function
02323      * @param   string      Call back function, default is checkValue_SW(). If $this->callBackObj is set to an object, the callback function in that object is called instead.
02324      * @param   [type]      $structurePath: ...
02325      * @return  void
02326      * @see checkValue_flex_procInData()
02327      */
02328     function checkValue_flex_procInData_travDS(&$dataValues, $dataValues_current, $uploadedFiles, $DSelements, $pParams, $callBackFunc, $structurePath) {
02329         if (is_array($DSelements)) {
02330 
02331                 // For each DS element:
02332             foreach ($DSelements as $key => $dsConf) {
02333 
02334                     // Array/Section:
02335                 if ($DSelements[$key]['type'] == 'array') {
02336                     if (is_array($dataValues[$key]['el'])) {
02337                         if ($DSelements[$key]['section']) {
02338                             $newIndexCounter = 0;
02339                             foreach ($dataValues[$key]['el'] as $ik => $el) {
02340                                 if (is_array($el)) {
02341                                     if (!is_array($dataValues_current[$key]['el'])) {
02342                                         $dataValues_current[$key]['el'] = array();
02343                                     }
02344 
02345                                     $theKey = key($el);
02346 
02347                                     if (is_array($dataValues[$key]['el'][$ik][$theKey]['el'])) {
02348                                         $this->checkValue_flex_procInData_travDS(
02349                                             $dataValues[$key]['el'][$ik][$theKey]['el'],
02350                                             is_array($dataValues_current[$key]['el'][$ik]) ? $dataValues_current[$key]['el'][$ik][$theKey]['el'] : array(),
02351                                             $uploadedFiles[$key]['el'][$ik][$theKey]['el'],
02352                                             $DSelements[$key]['el'][$theKey]['el'],
02353                                             $pParams,
02354                                             $callBackFunc,
02355                                             $structurePath . $key . '/el/' . $ik . '/' . $theKey . '/el/'
02356                                         );
02357 
02358                                             // 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:
02359                                             // The fact that the order changes is not important since order is controlled by a separately submitted index.
02360 
02361                                         if (substr($ik, 0, 3) == "ID-") {
02362                                             $newIndexCounter++;
02363                                             $this->newIndexMap[$ik] = (is_array($dataValues_current[$key]['el']) && count($dataValues_current[$key]['el']) ? max(array_keys($dataValues_current[$key]['el'])) : 0) + $newIndexCounter; // Set mapping index
02364                                             $dataValues[$key]['el'][$this->newIndexMap[$ik]] = $dataValues[$key]['el'][$ik]; // Transfer values
02365                                             unset($dataValues[$key]['el'][$ik]); // Unset original
02366                                         }
02367                                     }
02368                                 }
02369                             }
02370                         } else {
02371                             if (!isset($dataValues[$key]['el'])) {
02372                                 $dataValues[$key]['el'] = array();
02373                             }
02374                             $this->checkValue_flex_procInData_travDS(
02375                                 $dataValues[$key]['el'],
02376                                 $dataValues_current[$key]['el'],
02377                                 $uploadedFiles[$key]['el'],
02378                                 $DSelements[$key]['el'],
02379                                 $pParams,
02380                                 $callBackFunc,
02381                                 $structurePath . $key . '/el/'
02382                             );
02383                         }
02384                     }
02385                 } else {
02386                     if (is_array($dsConf['TCEforms']['config']) && is_array($dataValues[$key])) {
02387                         foreach ($dataValues[$key] as $vKey => $data) {
02388 
02389                             if ($callBackFunc) {
02390                                 if (is_object($this->callBackObj)) {
02391                                     $res = $this->callBackObj->$callBackFunc(
02392                                         $pParams,
02393                                         $dsConf['TCEforms']['config'],
02394                                         $dataValues[$key][$vKey],
02395                                         $dataValues_current[$key][$vKey],
02396                                         $uploadedFiles[$key][$vKey],
02397                                         $structurePath . $key . '/' . $vKey . '/'
02398                                     );
02399                                 } else {
02400                                     $res = $this->$callBackFunc(
02401                                         $pParams,
02402                                         $dsConf['TCEforms']['config'],
02403                                         $dataValues[$key][$vKey],
02404                                         $dataValues_current[$key][$vKey],
02405                                         $uploadedFiles[$key][$vKey],
02406                                         $structurePath . $key . '/' . $vKey . '/'
02407                                     );
02408                                 }
02409                             } else { // Default
02410                                 list($CVtable, $CVid, $CVcurValue, $CVstatus, $CVrealPid, $CVrecFID, $CVtscPID) = $pParams;
02411 
02412                                 $res = $this->checkValue_SW(
02413                                     array(),
02414                                     $dataValues[$key][$vKey],
02415                                     $dsConf['TCEforms']['config'],
02416                                     $CVtable,
02417                                     $CVid,
02418                                     $dataValues_current[$key][$vKey],
02419                                     $CVstatus,
02420                                     $CVrealPid,
02421                                     $CVrecFID,
02422                                     '',
02423                                     $uploadedFiles[$key][$vKey],
02424                                     array(),
02425                                     $CVtscPID
02426                                 );
02427 
02428                                     // Look for RTE transformation of field:
02429                                 if ($dataValues[$key]['_TRANSFORM_' . $vKey] == 'RTE' && !$this->dontProcessTransformations) {
02430 
02431                                         // Unsetting trigger field - we absolutely don't want that into the data storage!
02432                                     unset($dataValues[$key]['_TRANSFORM_' . $vKey]);
02433 
02434                                     if (isset($res['value'])) {
02435 
02436                                             // Calculating/Retrieving some values here:
02437                                         list(, , $recFieldName) = explode(':', $CVrecFID);
02438                                         $theTypeString = t3lib_BEfunc::getTCAtypeValue($CVtable, $this->checkValue_currentRecord);
02439                                         $specConf = t3lib_BEfunc::getSpecConfParts('', $dsConf['TCEforms']['defaultExtras']);
02440 
02441                                             // Find, thisConfig:
02442                                         $RTEsetup = $this->BE_USER->getTSConfig('RTE', t3lib_BEfunc::getPagesTSconfig($CVtscPID));
02443                                         $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'], $CVtable, $recFieldName, $theTypeString);
02444 
02445                                             // Get RTE object, draw form and set flag:
02446                                         $RTEobj = t3lib_BEfunc::RTEgetObj();
02447                                         if (is_object($RTEobj)) {
02448                                             $res['value'] = $RTEobj->transformContent('db', $res['value'], $CVtable, $recFieldName, $this->checkValue_currentRecord, $specConf, $thisConfig, '', $CVrealPid);
02449                                         } else {
02450                                             debug('NO RTE OBJECT FOUND!');
02451                                         }
02452                                     }
02453                                 }
02454                             }
02455 
02456                                 // Adding the value:
02457                             if (isset($res['value'])) {
02458                                 $dataValues[$key][$vKey] = $res['value'];
02459                             }
02460 
02461                                 // Finally, check if new and old values are different (or no .vDEFbase value is found) and if so, we record the vDEF value for diff'ing.
02462                                 // We do this after $dataValues has been updated since I expect that $dataValues_current holds evaluated values from database (so this must be the right value to compare with).
02463                             if (substr($vKey, -9) != '.vDEFbase') {
02464                                 if ($this->clear_flexFormData_vDEFbase) {
02465                                     $dataValues[$key][$vKey . '.vDEFbase'] = '';
02466                                 } elseif ($this->updateModeL10NdiffData && $GLOBALS['TYPO3_CONF_VARS']['BE']['flexFormXMLincludeDiffBase'] && $vKey !== 'vDEF' && (strcmp($dataValues[$key][$vKey], $dataValues_current[$key][$vKey]) || !isset($dataValues_current[$key][$vKey . '.vDEFbase']) || $this->updateModeL10NdiffData === 'FORCE_FFUPD')) {
02467                                         // Now, check if a vDEF value is submitted in the input data, if so we expect this has been processed prior to this operation (normally the case since those fields are higher in the form) and we can use that:
02468                                     if (isset($dataValues[$key]['vDEF'])) {
02469                                         $diffValue = $dataValues[$key]['vDEF'];
02470                                     } else { // If not found (for translators with no access to the default language) we use the one from the current-value data set:
02471                                         $diffValue = $dataValues_current[$key]['vDEF'];
02472                                     }
02473                                         // 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.
02474                                     $dataValues[$key][$vKey . '.vDEFbase'] = $this->updateModeL10NdiffDataClear ? '' : $diffValue;
02475                                 }
02476                             }
02477                         }
02478                     }
02479                 }
02480             }
02481         }
02482     }
02483 
02484     /**
02485      * Returns data for inline fields.
02486      *
02487      * @param   array       Current value array
02488      * @param   array       TCA field config
02489      * @param   integer     Record id
02490      * @param   string      Status string ('update' or 'new')
02491      * @param   string      Table name, needs to be passed to t3lib_loadDBGroup
02492      * @param   string      The current field the values are modified for
02493      * @return  string      Modified values
02494      */
02495     protected function checkValue_inline_processDBdata($valueArray, $tcaFieldConf, $id, $status, $table, $field) {
02496         $newValue = '';
02497         $foreignTable = $tcaFieldConf['foreign_table'];
02498 
02499         /*
02500          * Fetch the related child records by using t3lib_loadDBGroup:
02501          * @var $dbAnalysis t3lib_loadDBGroup
02502          */
02503         $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
02504         $dbAnalysis->start(implode(',', $valueArray), $foreignTable, '', 0, $table, $tcaFieldConf);
02505             // If the localizationMode is set to 'keep', the children for the localized parent are kept as in the original untranslated record:
02506         $localizationMode = t3lib_BEfunc::getInlineLocalizationMode($table, $tcaFieldConf);
02507         if ($localizationMode == 'keep' && $status == 'update') {
02508                 // Fetch the current record and determine the original record:
02509             $row = t3lib_BEfunc::getRecordWSOL($table, $id);
02510             if (is_array($row)) {
02511                 $language = intval($row[$GLOBALS['TCA'][$table]['ctrl']['languageField']]);
02512                 $transOrigPointer = intval($row[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']]);
02513                     // If language is set (e.g. 1) and also transOrigPointer (e.g. 123), use transOrigPointer as uid:
02514                 if ($language > 0 && $transOrigPointer) {
02515                     $id = $transOrigPointer;
02516                         // If we're in active localizationMode 'keep', prevent from writing data to the field of the parent record:
02517                         // (on removing the localized parent, the original (untranslated) children would then also be removed)
02518                     $keepTranslation = TRUE;
02519                 }
02520             }
02521         }
02522             // IRRE with a pointer field (database normalization):
02523         if ($tcaFieldConf['foreign_field']) {
02524                 // if the record was imported, sorting was also imported, so skip this
02525             $skipSorting = ($this->callFromImpExp ? TRUE : FALSE);
02526                 // update record in intermediate table (sorting & pointer uid to parent record)
02527             $dbAnalysis->writeForeignField($tcaFieldConf, $id, 0, $skipSorting);
02528             $newValue = ($keepTranslation ? 0 : $dbAnalysis->countItems(FALSE));
02529                 // IRRE with MM relation:
02530         } else {
02531             if ($this->getInlineFieldType($tcaFieldConf) == 'mm') {
02532                     // in order to fully support all the MM stuff, directly call checkValue_group_select_processDBdata instead of repeating the needed code here
02533                 $valueArray = $this->checkValue_group_select_processDBdata($valueArray, $tcaFieldConf, $id, $status, 'select', $table, $field);
02534                 $newValue = ($keepTranslation ? 0 : $valueArray[0]);
02535                     // IRRE with comma separated values:
02536             } else {
02537                 $valueArray = $dbAnalysis->getValueArray();
02538                     // Checking that the number of items is correct:
02539                 $valueArray = $this->checkValue_checkMax($tcaFieldConf, $valueArray);
02540                     // If a valid translation of the 'keep' mode is active, update relations in the original(!) record:
02541                 if ($keepTranslation) {
02542                     $this->updateDB($table, $transOrigPointer, array($field => implode(',', $valueArray)));
02543                 } else {
02544                     $newValue = implode(',', $valueArray);
02545                 }
02546             }
02547         }
02548 
02549         return $newValue;
02550     }
02551 
02552 
02553     /*********************************************
02554      *
02555      * PROCESSING COMMANDS
02556      *
02557      ********************************************/
02558 
02559     /**
02560      * Processing the cmd-array
02561      * See "TYPO3 Core API" for a description of the options.
02562      *
02563      * @return  void
02564      */
02565     public function process_cmdmap() {
02566         global $TCA, $TYPO3_CONF_VARS;
02567 
02568             // Editing frozen:
02569         if ($this->BE_USER->workspace !== 0 && $this->BE_USER->workspaceRec['freeze']) {
02570             $this->newlog('All editing in this workspace has been frozen!', 1);
02571             return FALSE;
02572         }
02573 
02574             // Hook initialization:
02575         $hookObjectsArr = array();
02576         if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'])) {
02577             foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'] as $classRef) {
02578                 $hookObj = t3lib_div::getUserObj($classRef);
02579                 if (method_exists($hookObj, 'processCmdmap_beforeStart')) {
02580                     $hookObj->processCmdmap_beforeStart($this);
02581                 }
02582                 $hookObjectsArr[] = $hookObj;
02583             }
02584         }
02585 
02586             // Traverse command map:
02587         foreach (array_keys($this->cmdmap) as $table) {
02588 
02589                 // Check if the table may be modified!
02590             $modifyAccessList = $this->checkModifyAccessList($table);
02591             if (!$modifyAccessList) {
02592                 $id = 0;
02593                 $this->log($table, $id, 2, 0, 1, "Attempt to modify table '%s' without permission", 1, array($table));
02594             } // FIXME: $id not set here (Comment added by Sebastian Kurfürst)
02595 
02596                 // Check basic permissions and circumstances:
02597             if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->cmdmap[$table]) && $modifyAccessList) {
02598 
02599                     // Traverse the command map:
02600                 foreach ($this->cmdmap[$table] as $id => $incomingCmdArray) {
02601                     if (is_array($incomingCmdArray)) { // have found a command.
02602 
02603                             // Get command and value (notice, only one command is observed at a time!):
02604                         reset($incomingCmdArray);
02605                         $command = key($incomingCmdArray);
02606                         $value = current($incomingCmdArray);
02607 
02608                         foreach ($hookObjectsArr as $hookObj) {
02609                             if (method_exists($hookObj, 'processCmdmap_preProcess')) {
02610                                 $hookObj->processCmdmap_preProcess($command, $table, $id, $value, $this);
02611                             }
02612                         }
02613 
02614                             // Init copyMapping array:
02615                             // Must clear this array before call from here to those functions:
02616                             // Contains mapping information between new and old id numbers.
02617                         $this->copyMappingArray = array();
02618 
02619                             // process the command
02620                         $commandIsProcessed = FALSE;
02621                         foreach ($hookObjectsArr as $hookObj) {
02622                             if (method_exists($hookObj, 'processCmdmap')) {
02623                                 $hookObj->processCmdmap($command, $table, $id, $value, $commandIsProcessed, $this);
02624                             }
02625                         }
02626 
02627                             // only execute default commands if a hook hasn't been processed the command already
02628                         if (!$commandIsProcessed) {
02629                                 // Branch, based on command
02630                             switch ($command) {
02631                                 case 'move':
02632                                     $this->moveRecord($table, $id, $value);
02633                                 break;
02634                                 case 'copy':
02635                                     if ($table === 'pages') {
02636                                         $this->copyPages($id, $value);
02637                                     } else {
02638                                         $this->copyRecord($table, $id, $value, 1);
02639                                     }
02640                                 break;
02641                                 case 'localize':
02642                                     $this->localize($table, $id, $value);
02643                                 break;
02644                                 case 'inlineLocalizeSynchronize':
02645                                     $this->inlineLocalizeSynchronize($table, $id, $value);
02646                                 break;
02647                                 case 'delete':
02648                                     $this->deleteAction($table, $id);
02649                                 break;
02650                                 case 'undelete':
02651                                     $this->undeleteRecord($table, $id);
02652                                 break;
02653                             }
02654                         }
02655 
02656                         foreach ($hookObjectsArr as $hookObj) {
02657                             if (method_exists($hookObj, 'processCmdmap_postProcess')) {
02658                                 $hookObj->processCmdmap_postProcess($command, $table, $id, $value, $this);
02659                             }
02660                         }
02661 
02662                             // Merging the copy-array info together for remapping purposes.
02663                         $this->copyMappingArray_merged = t3lib_div::array_merge_recursive_overrule($this->copyMappingArray_merged, $this->copyMappingArray);
02664                     }
02665                 }
02666             }
02667         }
02668 
02669             // Finally, before exit, check if there are ID references to remap.
02670             // This might be the case if versioning or copying has taken place!
02671         $this->remapListedDBRecords();
02672         $this->processRemapStack();
02673 
02674         foreach ($hookObjectsArr as $hookObj) {
02675             if (method_exists($hookObj, 'processCmdmap_afterFinish')) {
02676                 $hookObj->processCmdmap_afterFinish($this);
02677             }
02678         }
02679 
02680     }
02681 
02682 
02683     /*********************************************
02684      *
02685      * Cmd: Copying
02686      *
02687      ********************************************/
02688 
02689     /**
02690      * Copying a single record
02691      *
02692      * @param   string      Element table
02693      * @param   integer     Element UID
02694      * @param   integer     $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
02695      * @param   boolean     $first is a flag set, if the record copied is NOT a 'slave' to another record copied. That is, if this record was asked to be copied in the cmd-array
02696      * @param   array       Associative array with field/value pairs to override directly. Notice; Fields must exist in the table record and NOT be among excluded fields!
02697      * @param   string      Commalist of fields to exclude from the copy process (might get default values)
02698      * @param   integer     Language ID (from sys_language table)
02699      * @return  integer     ID of new record, if any
02700      */
02701     function copyRecord($table, $uid, $destPid, $first = 0, $overrideValues = array(), $excludeFields = '', $language = 0) {
02702         global $TCA;
02703 
02704         $uid = $origUid = intval($uid);
02705             // Only copy if the table is defined in TCA, a uid is given and the record wasn't copied before:
02706         if ($TCA[$table] && $uid && !$this->isRecordCopied($table, $uid)) {
02707             t3lib_div::loadTCA($table);
02708             /*
02709                    // 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)
02710                if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid'))   {
02711                    $uid = $lookForLiveVersion['uid'];
02712                }
02713                    // Get workspace version of the source record, if any: Then we will copy workspace version instead:
02714                if ($WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid'))    {
02715                    $uid = $WSversion['uid'];
02716                }
02717                    // 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.
02718    */
02719             if ($this->doesRecordExist($table, $uid, 'show')) { // This checks if the record can be selected which is all that a copy action requires.
02720                 $fullLanguageCheckNeeded = ($table != 'pages');
02721                 if (($language > 0 && $this->BE_USER->checkLanguageAccess($language)) ||
02722                     $this->BE_USER->recordEditAccessInternals(
02723                         $table, $uid, FALSE, FALSE, $fullLanguageCheckNeeded
02724                     )
02725                 ) { //Used to check language and general editing rights
02726                     $data = array();
02727 
02728                     $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));
02729 
02730                         // $row = $this->recordInfo($table,$uid,'*');
02731                     $row = t3lib_BEfunc::getRecordWSOL($table, $uid); // So it copies (and localized) content from workspace...
02732                     if (is_array($row)) {
02733 
02734                             // Initializing:
02735                         $theNewID = uniqid('NEW');
02736                         $enableField = isset($TCA[$table]['ctrl']['enablecolumns']) ? $TCA[$table]['ctrl']['enablecolumns']['disabled'] : '';
02737                         $headerField = $TCA[$table]['ctrl']['label'];
02738 
02739                             // Getting default data:
02740                         $defaultData = $this->newFieldArray($table);
02741 
02742                             // Getting "copy-after" fields if applicable:
02743                         $copyAfterFields = $destPid < 0 ? $this->fixCopyAfterDuplFields($table, $uid, abs($destPid), 0) : array();
02744 
02745                             // Page TSconfig related:
02746                         $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...
02747                         $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
02748                         $tE = $this->getTableEntries($table, $TSConfig);
02749 
02750                             // Traverse ALL fields of the selected record:
02751                         foreach ($row as $field => $value) {
02752                             if (!in_array($field, $nonFields)) {
02753 
02754                                     // Get TCA configuration for the field:
02755                                 $conf = $TCA[$table]['columns'][$field]['config'];
02756 
02757                                     // Preparation/Processing of the value:
02758                                 if ($field == 'pid') { // "pid" is hardcoded of course:
02759                                     $value = $destPid;
02760                                 } elseif (isset($overrideValues[$field])) { // Override value...
02761                                     $value = $overrideValues[$field];
02762                                 } elseif (isset($copyAfterFields[$field])) { // Copy-after value if available:
02763                                     $value = $copyAfterFields[$field];
02764                                 } elseif ($TCA[$table]['ctrl']['setToDefaultOnCopy'] && t3lib_div::inList($TCA[$table]['ctrl']['setToDefaultOnCopy'], $field)) { // Revert to default for some fields:
02765                                     $value = $defaultData[$field];
02766                                 } else {
02767                                         // Hide at copy may override:
02768                                     if ($first && $field == $enableField && $TCA[$table]['ctrl']['hideAtCopy'] && !$this->neverHideAtCopy && !$tE['disableHideAtCopy']) {
02769                                         $value = 1;
02770                                     }
02771                                         // Prepend label on copy:
02772                                     if ($first && $field == $headerField && $TCA[$table]['ctrl']['prependAtCopy'] && !$tE['disablePrependAtCopy']) {
02773                                         $value = $this->getCopyHeader($table, $this->resolvePid($table, $destPid), $field, $this->clearPrefixFromValue($table, $value), 0);
02774                                     }
02775                                         // Processing based on the TCA config field type (files, references, flexforms...)
02776                                     $value = $this->copyRecord_procBasedOnFieldType($table, $uid, $field, $value, $row, $conf, $tscPID, $language);
02777                                 }
02778 
02779                                     // Add value to array.
02780                                 $data[$table][$theNewID][$field] = $value;
02781                             }
02782                         }
02783                             // Overriding values:
02784                         if ($TCA[$table]['ctrl']['editlock']) {
02785                             $data[$table][$theNewID][$TCA[$table]['ctrl']['editlock']] = 0;
02786                         }
02787 
02788                             // Setting original UID:
02789                         if ($TCA[$table]['ctrl']['origUid']) {
02790                             $data[$table][$theNewID][$TCA[$table]['ctrl']['origUid']] = $uid;
02791                         }
02792 
02793                             // Do the copy by simply submitting the array through TCEmain:
02794                         $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain');
02795                         /* @var $copyTCE t3lib_TCEmain  */
02796                         $copyTCE->stripslashes_values = 0;
02797                         $copyTCE->copyTree = $this->copyTree;
02798                         $copyTCE->cachedTSconfig = $this->cachedTSconfig; // Copy forth the cached TSconfig
02799                         $copyTCE->dontProcessTransformations = 1; // Transformations should NOT be carried out during copy
02800 
02801                         $copyTCE->start($data, '', $this->BE_USER);
02802                         $copyTCE->process_datamap();
02803 
02804                             // Getting the new UID:
02805                         $theNewSQLID = $copyTCE->substNEWwithIDs[$theNewID];
02806                         if ($theNewSQLID) {
02807                             $this->copyRecord_fixRTEmagicImages($table, t3lib_BEfunc::wsMapId($table, $theNewSQLID));
02808                             $this->copyMappingArray[$table][$origUid] = $theNewSQLID;
02809                                 // Keep automatically versionized record information:
02810                             if (isset($copyTCE->autoVersionIdMap[$table][$theNewSQLID])) {
02811                                 $this->autoVersionIdMap[$table][$theNewSQLID] = $copyTCE->autoVersionIdMap[$table][$theNewSQLID];
02812                             }
02813                         }
02814 
02815                             // Copy back the cached TSconfig
02816                         $this->cachedTSconfig = $copyTCE->cachedTSconfig;
02817                         $this->errorLog = array_merge($this->errorLog, $copyTCE->errorLog);
02818                         unset($copyTCE);
02819 
02820                         if ($language == 0) {
02821                                 //repointing the new translation records to the parent record we just created
02822                             $overrideValues[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']] = $theNewSQLID;
02823                             $this->copyL10nOverlayRecords($table, $uid, $destPid < 0 ? $tscPID : $destPid, $first, $overrideValues, $excludeFields);
02824                         }
02825 
02826                         return $theNewSQLID;
02827                     } else {
02828                         $this->log($table, $uid, 3, 0, 1, 'Attempt to copy record that did not exist!');
02829                     }
02830                 } else {
02831                     $this->log($table, $uid, 3, 0, 1, 'Attempt to copy record without having permissions to do so. [' . $this->BE_USER->errorMsg . '].');
02832                 }
02833             } else {
02834                 $this->log($table, $uid, 3, 0, 1, 'Attempt to copy record without permission');
02835             }
02836         }
02837     }
02838 
02839     /**
02840      * Copying pages
02841      * Main function for copying pages.
02842      *
02843      * @param   integer     Page UID to copy
02844      * @param   integer     Destination PID: >=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
02845      * @return  void
02846      */
02847     function copyPages($uid, $destPid) {
02848 
02849             // Initialize:
02850         $uid = intval($uid);
02851         $destPid = intval($destPid);
02852 
02853             // Finding list of tables to copy.
02854         $copyTablesArray = $this->admin ? $this->compileAdminTables() : explode(',', $this->BE_USER->groupData['tables_modify']); // These are the tables, the user may modify
02855         if (!strstr($this->copyWhichTables, '*')) { // If not all tables are allowed then make a list of allowed tables: That is the tables that figure in both allowed tables AND the copyTable-list
02856             foreach ($copyTablesArray as $k => $table) {
02857                 if (!$table || !t3lib_div::inList($this->copyWhichTables . ',pages', $table)) { // pages are always going...
02858                     unset($copyTablesArray[$k]);
02859                 }
02860             }
02861         }
02862         $copyTablesArray = array_unique($copyTablesArray);
02863 
02864             // Begin to copy pages if we're allowed to:
02865         if ($this->admin || in_array('pages', $copyTablesArray)) {
02866 
02867                 // Copy this page we're on. And set first-flag (this will trigger that the record is hidden if that is configured)!
02868             $theNewRootID = $this->copySpecificPage($uid, $destPid, $copyTablesArray, 1);
02869 
02870                 // If we're going to copy recursively...:
02871             if ($theNewRootID && $this->copyTree) {
02872 
02873                     // Get ALL subpages to copy (read-permissions are respected!):
02874                 $CPtable = $this->int_pageTreeInfo(array(), $uid, intval($this->copyTree), $theNewRootID);
02875 
02876                     // Now copying the subpages:
02877                 foreach ($CPtable as $thePageUid => $thePagePid) {
02878                     $newPid = $this->copyMappingArray['pages'][$thePagePid];
02879                     if (isset($newPid)) {
02880                         $this->copySpecificPage($thePageUid, $newPid, $copyTablesArray);
02881                     } else {
02882                         $this->log('pages', $uid, 5, 0, 1, 'Something went wrong during copying branch');
02883                         break;
02884                     }
02885                 }
02886             } // else the page was not copied. Too bad...
02887         } else {
02888             $this->log('pages', $uid, 5, 0, 1, 'Attempt to copy page without permission to this table');
02889         }
02890     }
02891 
02892     /**
02893      * Copying a single page ($uid) to $destPid and all tables in the array copyTablesArray.
02894      *
02895      * @param   integer     Page uid
02896      * @param   integer     Destination PID: >=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
02897      * @param   array       Table on pages to copy along with the page.
02898      * @param   boolean     $first is a flag set, if the record copied is NOT a 'slave' to another record copied. That is, if this record was asked to be copied in the cmd-array
02899      * @return  integer     The id of the new page, if applicable.
02900      */
02901     function copySpecificPage($uid, $destPid, $copyTablesArray, $first = 0) {
02902         global $TCA;
02903 
02904             // Copy the page itself:
02905         $theNewRootID = $this->copyRecord('pages', $uid, $destPid, $first);
02906 
02907             // If a new page was created upon the copy operation we will proceed with all the tables ON that page:
02908         if ($theNewRootID) {
02909             foreach ($copyTablesArray as $table) {
02910                 if ($table && is_array($TCA[$table]) && $table != 'pages') { // all records under the page is copied.
02911                     $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid=' . intval($uid) . $this->deleteClause($table), '', ($TCA[$table]['ctrl']['sortby'] ? $TCA[$table]['ctrl']['sortby'] . ' DESC' : ''));
02912                     while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
02913                         $this->copyRecord($table, $row['uid'], $theNewRootID); // Copying each of the underlying records...
02914                     }
02915                     $GLOBALS['TYPO3_DB']->sql_free_result($mres);
02916                 }
02917             }
02918             return $theNewRootID;
02919         }
02920     }
02921 
02922     /**
02923      * Copying records, but makes a "raw" copy of a record.
02924      * Basically the only thing observed is field processing like the copying of files and correction of ids. All other fields are 1-1 copied.
02925      * Technically the copy is made with THIS instance of the tcemain class contrary to copyRecord() which creates a new instance and uses the processData() function.
02926      * The copy is created by insertNewCopyVersion() which bypasses most of the regular input checking associated with processData() - maybe copyRecord() should even do this as well!?
02927      * This function is used to create new versions of a record.
02928      * 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.
02929      *
02930      * @param   string      Element table
02931      * @param   integer     Element UID
02932      * @param   integer     Element PID (real PID, not checked)
02933      * @param   array       Override array - must NOT contain any fields not in the table!
02934      * @return  integer     Returns the new ID of the record (if applicable)
02935      */
02936     function copyRecord_raw($table, $uid, $pid, $overrideArray = array()) {
02937         global $TCA;
02938 
02939         $uid = intval($uid);
02940             // Only copy if the table is defined in TCA, a uid is given and the record wasn't copied before:
02941         if ($TCA[$table] && $uid && !$this->isRecordCopied($table, $uid)) {
02942             t3lib_div::loadTCA($table);
02943             if ($this->doesRecordExist($table, $uid, 'show')) {
02944 
02945                     // Set up fields which should not be processed. They are still written - just passed through no-questions-asked!
02946                 $nonFields = array('uid', 'pid', 't3ver_id', 't3ver_oid', 't3ver_wsid', 't3ver_label', 't3ver_state', 't3ver_swapmode', 't3ver_count', 't3ver_stage', 't3ver_tstamp', 'perms_userid', 'perms_groupid', 'perms_user', 'perms_group', 'perms_everybody');
02947 
02948                     // Select main record:
02949                 $row = $this->recordInfo($table, $uid, '*');
02950                 if (is_array($row)) {
02951 
02952                         // Merge in override array.
02953                     $row = array_merge($row, $overrideArray);
02954 
02955                         // Traverse ALL fields of the selected record:
02956                     foreach ($row as $field => $value) {
02957                         if (!in_array($field, $nonFields)) {
02958 
02959                                 // Get TCA configuration for the field:
02960                             $conf = $TCA[$table]['columns'][$field]['config'];
02961                             if (is_array($conf)) {
02962                                     // Processing based on the TCA config field type (files, references, flexforms...)
02963                                 $value = $this->copyRecord_procBasedOnFieldType($table, $uid, $field, $value, $row, $conf, $pid);
02964                             }
02965 
02966                                 // Add value to array.
02967                             $row[$field] = $value;
02968                         }
02969                     }
02970 
02971                         // Force versioning related fields:
02972                     $row['pid'] = $pid;
02973 
02974                         // Setting original UID:
02975                     if ($TCA[$table]['ctrl']['origUid']) {
02976                         $row[$TCA[$table]['ctrl']['origUid']] = $uid;
02977                     }
02978 
02979                         // Do the copy by internal function
02980                     $theNewSQLID = $this->insertNewCopyVersion($table, $row, $pid);
02981                     if ($theNewSQLID) {
02982                         $this->dbAnalysisStoreExec();
02983                         $this->dbAnalysisStore = array();
02984                         $this->copyRecord_fixRTEmagicImages($table, t3lib_BEfunc::wsMapId($table, $theNewSQLID));
02985                         return $this->copyMappingArray[$table][$uid] = $theNewSQLID;
02986                     }
02987                 } else {
02988                     $this->log($table, $uid, 3, 0, 1, 'Attempt to rawcopy/versionize record that did not exist!');
02989                 }
02990             } else {
02991                 $this->log($table, $uid, 3, 0, 1, 'Attempt to rawcopy/versionize record without copy permission');
02992             }
02993         }
02994     }
02995 
02996     /**
02997      * Inserts a record in the database, passing TCA configuration values through checkValue() but otherwise does NOTHING and checks nothing regarding permissions.
02998      * 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...
02999      *
03000      * @param   string      Table name
03001      * @param   array       Field array to insert as a record
03002      * @param   integer     The value of PID field.  -1 is indication that we are creating a new version!
03003      * @return  integer     Returns the new ID of the record (if applicable)
03004      */
03005     function insertNewCopyVersion($table, $fieldArray, $realPid) {
03006         global $TCA;
03007 
03008         $id = uniqid('NEW');
03009 
03010             // $fieldArray is set as current record.
03011             // 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...
03012         $this->checkValue_currentRecord = $fieldArray;
03013 
03014             // Makes sure that transformations aren't processed on the copy.
03015         $backupDontProcessTransformations = $this->dontProcessTransformations;
03016         $this->dontProcessTransformations = TRUE;
03017 
03018             // Traverse record and input-process each value:
03019         foreach ($fieldArray as $field => $fieldValue) {
03020             if (isset($TCA[$table]['columns'][$field])) {
03021                     // Evaluating the value.
03022                 $res = $this->checkValue($table, $field, $fieldValue, $id, 'new', $realPid, 0);
03023                 if (isset($res['value'])) {
03024                     $fieldArray[$field] = $res['value'];
03025                 }
03026             }
03027         }
03028 
03029             // System fields being set:
03030         if ($TCA[$table]['ctrl']['crdate']) {
03031             $fieldArray[$TCA[$table]['ctrl']['crdate']] = $GLOBALS['EXEC_TIME'];
03032         }
03033         if ($TCA[$table]['ctrl']['cruser_id']) {
03034             $fieldArray[$TCA[$table]['ctrl']['cruser_id']] = $this->userid;
03035         }
03036         if ($TCA[$table]['ctrl']['tstamp']) {
03037             $fieldArray[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
03038         }
03039 
03040             // Finally, insert record:
03041         $this->insertDB($table, $id, $fieldArray, TRUE);
03042             // Process the remap stack in case we dealed with relations:
03043         $this->processRemapStack();
03044 
03045             // Resets dontProcessTransformations to the previous state.
03046         $this->dontProcessTransformations = $backupDontProcessTransformations;
03047 
03048             // Return new id:
03049         return $this->substNEWwithIDs[$id];
03050     }
03051 
03052     /**
03053      * Processing/Preparing content for copyRecord() function
03054      *
03055      * @param   string      Table name
03056      * @param   integer     Record uid
03057      * @param   string      Field name being processed
03058      * @param   string      Input value to be processed.
03059      * @param   array       Record array
03060      * @param   array       TCA field configuration
03061      * @param   integer     Real page id (pid) the record is copied to
03062      * @param   integer     Language ID (from sys_language table) used in the duplicated record
03063      * @return  mixed       Processed value. Normally a string/integer, but can be an array for flexforms!
03064      * @access private
03065      * @see copyRecord()
03066      */
03067     function copyRecord_procBasedOnFieldType($table, $uid, $field, $value, $row, $conf, $realDestPid, $language = 0) {
03068         global $TCA;
03069 
03070             // 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)
03071         $value = $this->copyRecord_procFilesRefs($conf, $uid, $value);
03072         $inlineSubType = $this->getInlineFieldType($conf);
03073 
03074             // Register if there are references to take care of or MM is used on an inline field (no change to value):
03075         if ($this->isReferenceField($conf) || $inlineSubType == 'mm') {
03076             $allowedTables = $conf['type'] == 'group' ? $conf['allowed'] : $conf['foreign_table'] . ',' . $conf['neg_foreign_table'];
03077             $prependName = $conf['type'] == 'group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
03078             $localizeReferences = (isset($conf['foreign_table']) && t3lib_BEfunc::isTableLocalizable($conf['foreign_table']) && isset($conf['localizeReferencesAtParentLocalization']) && $conf['localizeReferencesAtParentLocalization']);
03079             if ($conf['MM'] || $language > 0 && $localizeReferences) {
03080                 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
03081                 /* @var $dbAnalysis t3lib_loadDBGroup */
03082                 $dbAnalysis->start($value, $allowedTables, $conf['MM'], $uid, $table, $conf);
03083                 if (!$conf['MM']) {
03084                         // Localize referenced records of select fields:
03085                     foreach ($dbAnalysis->itemArray as $index => $item) {
03086                             // Since select fields can reference many records, check whether there's already a localization:
03087                         $recordLocalization = t3lib_BEfunc::getRecordLocalization($item['table'], $item['id'], $language);
03088                         if (!$recordLocalization) {
03089                             $dbAnalysis->itemArray[$index]['id'] = $this->localize($item['table'], $item['id'], $language);
03090                         } else {
03091                             $dbAnalysis->itemArray[$index]['id'] = $recordLocalization[0]['uid'];
03092                         }
03093                     }
03094                 }
03095                 $value = implode(',', $dbAnalysis->getValueArray($prependName));
03096             }
03097             if ($value) { // Setting the value in this array will notify the remapListedDBRecords() function that this field MAY need references to be corrected
03098                 $this->registerDBList[$table][$uid][$field] = $value;
03099             }
03100 
03101             // If another inline subtype is used (comma-separated-values or the foreign_field property):
03102         } elseif ($inlineSubType !== FALSE) {
03103                 // Get the localization mode for the current (parent) record (keep|select|all):
03104             $localizationMode = t3lib_BEfunc::getInlineLocalizationMode($table, $field);
03105                 // Localization in mode 'keep', isn't a real localization, but keeps the children of the original parent record:
03106             if ($language > 0 && $localizationMode == 'keep') {
03107                 $value = ($inlineSubType == 'field' ? 0 : '');
03108                 // Execute copy or localization actions:
03109             } else {
03110                 /*
03111                  * Fetch the related child records by using t3lib_loadDBGroup:
03112                  * @var $dbAnalysis t3lib_loadDBGroup
03113                  */
03114                 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
03115                 $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
03116 
03117                     // Walk through the items, copy them and remember the new id:
03118                 foreach ($dbAnalysis->itemArray as $k => $v) {
03119                         // If language is set and differs from original record, this isn't a copy action but a localization of our parent/ancestor:
03120                     if ($language > 0 && t3lib_BEfunc::isTableLocalizable($table) && $language != $row[$TCA[$table]['ctrl']['languageField']]) {
03121                             // If children should be localized when the parent gets localized the first time, just do it:
03122                         if ($localizationMode != FALSE && isset($conf['behaviour']['localizeChildrenAtParentLocalization']) && $conf['behaviour']['localizeChildrenAtParentLocalization']) {
03123                             $newId = $this->localize($v['table'], $v['id'], $language);
03124                         }
03125                             // If no language it set, this is a regular copy action:
03126                     } else {
03127                         if (!t3lib_div::testInt($realDestPid)) {
03128                             $newId = $this->copyRecord($v['table'], $v['id'], -$v['id']);
03129                         } elseif ($realDestPid == -1 && t3lib_BEfunc::isTableWorkspaceEnabled($v['table'])) {
03130                             $workspaceVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord(
03131                                 $this->BE_USER->workspace, $v['table'], $v['id'], 'uid'
03132                             );
03133                                 // If workspace version does not exist, create a new one:
03134                             if ($workspaceVersion === FALSE) {
03135                                 $newId = $this->versionizeRecord($v['table'], $v['id'], 'Auto-created for WS #' . $this->BE_USER->workspace);
03136                                 // If workspace version already exists, use it:
03137                             } else {
03138                                 $newId = $workspaceVersion['uid'];
03139                             }
03140                         } else {
03141                             $newId = $this->copyRecord_raw($v['table'], $v['id'], $realDestPid);
03142                         }
03143                     }
03144 
03145                         // If the current field is set on a page record, update the pid of related child records:
03146                     if ($table == 'pages') {
03147                         $this->registerDBPids[$v['table']][$v['id']] = $uid;
03148                         // If the current field has ancestors that have a field on a page record, update the pid of related child records:
03149                     } elseif (isset($this->registerDBPids[$table][$uid])) {
03150                         $this->registerDBPids[$v['table']][$v['id']] = $this->registerDBPids[$table][$uid];
03151                     }
03152 
03153                     $dbAnalysis->itemArray[$k]['id'] = $newId;
03154                 }
03155 
03156                     // Store the new values, we will set up the uids for the subtype later on (exception keep localization from original record):
03157                 $value = implode(',', $dbAnalysis->getValueArray());
03158                 $this->registerDBList[$table][$uid][$field] = $value;
03159             }
03160         }
03161 
03162             // For "flex" fieldtypes we need to traverse the structure for two reasons: If there are file references they have to be prepended with absolute paths and if there are database reference they MIGHT need to be remapped (still done in remapListedDBRecords())
03163         if ($conf['type'] == 'flex') {
03164 
03165                 // Get current value array:
03166             $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $row, $table);
03167             $currentValueArray = t3lib_div::xml2array($value);
03168 
03169                 // Traversing the XML structure, processing files:
03170             if (is_array($currentValueArray)) {
03171                 $currentValueArray['data'] = $this->checkValue_flex_procInData(
03172                     $currentValueArray['data'],
03173                     array(), // Not used.
03174                     array(), // Not used.
03175                     $dataStructArray,
03176                     array($table, $uid, $field, $realDestPid), // Parameters.
03177                     'copyRecord_flexFormCallBack'
03178                 );
03179                 $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.
03180             }
03181         }
03182 
03183         return $value;
03184     }
03185 
03186     /**
03187      * Callback function for traversing the FlexForm structure in relation to creating copied files of file relations inside of flex form structures.
03188      *
03189      * @param   array       Array of parameters in num-indexes: table, uid, field
03190      * @param   array       TCA field configuration (from Data Structure XML)
03191      * @param   string      The value of the flexForm field
03192      * @param   string      Not used.
03193      * @param   string      Not used.
03194      * @return  array       Result array with key "value" containing the value of the processing.
03195      * @see copyRecord(), checkValue_flex_procInData_travDS()
03196      */
03197     function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2) {
03198 
03199             // Extract parameters:
03200         list($table, $uid, $field, $realDestPid) = $pParams;
03201 
03202             // Process references and files, currently that means only the files, prepending absolute paths:
03203         $dataValue = $this->copyRecord_procFilesRefs($dsConf, $uid, $dataValue);
03204 
03205             // If references are set for this field, set flag so they can be corrected later (in ->remapListedDBRecords())
03206         if ($this->isReferenceField($dsConf) && strlen($dataValue)) {
03207             $dataValue = $this->copyRecord_procBasedOnFieldType($table, $uid, $field, $dataValue, array(), $dsConf, $realDestPid);
03208             $this->registerDBList[$table][$uid][$field] = 'FlexForm_reference';
03209         }
03210 
03211             // Return
03212         return array('value' => $dataValue);
03213     }
03214 
03215     /**
03216      * Modifying a field value for any situation regarding files/references:
03217      * For attached files: take current filenames and prepend absolute paths so they get copied.
03218      * For DB references: Nothing done.
03219      *
03220      * @param   array       TCE field config
03221      * @param   integer     Record UID
03222      * @param   string      Field value (eg. list of files)
03223      * @return  string      The (possibly modified) value
03224      * @see copyRecord(), copyRecord_flexFormCallBack()
03225      */
03226     function copyRecord_procFilesRefs($conf, $uid, $value) {
03227 
03228             // Prepend absolute paths to files:
03229         if ($conf['type'] == 'group' && ($conf['internal_type'] == 'file' || $conf['internal_type'] == 'file_reference')) {
03230 
03231                 // Get an array with files as values:
03232             if ($conf['MM']) {
03233                 $theFileValues = array();
03234 
03235                 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
03236                 /* @var $dbAnalysis t3lib_loadDBGroup */
03237                 $dbAnalysis->start('', 'files', $conf['MM'], $uid);
03238 
03239                 foreach ($dbAnalysis->itemArray as $somekey => $someval) {
03240                     if ($someval['id']) {
03241                         $theFileValues[] = $someval['id'];
03242                     }
03243                 }
03244             } else {
03245                 $theFileValues = t3lib_div::trimExplode(',', $value, 1);
03246             }
03247 
03248                 // Traverse this array of files:
03249             $uploadFolder = $conf['internal_type'] == 'file' ? $conf['uploadfolder'] : '';
03250             $dest = $this->destPathFromUploadFolder($uploadFolder);
03251             $newValue = array();
03252 
03253             foreach ($theFileValues as $file) {
03254                 if (trim($file)) {
03255                     $realFile = str_replace('//', '/', $dest . '/' . trim($file));
03256                     if (@is_file($realFile)) {
03257                         $newValue[] = $realFile;
03258                     }
03259                 }
03260             }
03261 
03262                 // Implode the new filelist into the new value (all files have absolute paths now which means they will get copied when entering TCEmain as new values...)
03263             $value = implode(',', $newValue);
03264         }
03265 
03266             // Return the new value:
03267         return $value;
03268     }
03269 
03270     /**
03271      * Copies any "RTEmagic" image files found in record with table/id to new names.
03272      * Usage: After copying a record this function should be called to search for "RTEmagic"-images inside the record. If such are found they should be duplicated to new names so all records have a 1-1 relation to them.
03273      * Reason for copying RTEmagic files: a) if you remove an RTEmagic image from a record it will remove the file - any other record using it will have a lost reference! b) RTEmagic images keeps an original and a copy. The copy always is re-calculated to have the correct physical measures as the HTML tag inserting it defines. This is calculated from the original. Two records using the same image could have difference HTML-width/heights for the image and the copy could only comply with one of them. If you don't want a 1-1 relation you should NOT use RTEmagic files but just insert it as a normal file reference to a file inside fileadmin/ folder
03274      *
03275      * @param   string      Table name
03276      * @param   integer     Record UID
03277      * @return  void
03278      */
03279     function copyRecord_fixRTEmagicImages($table, $theNewSQLID) {
03280         global $TYPO3_DB;
03281 
03282             // Creating fileFunc object.
03283         if (!$this->fileFunc) {
03284             $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions');
03285             $this->include_filefunctions = 1;
03286         }
03287 
03288             // Select all RTEmagic files in the reference table from the table/ID
03289         /* @var $TYPO3_DB t3lib_DB */
03290         $recs = $TYPO3_DB->exec_SELECTgetRows(
03291             '*',
03292             'sys_refindex',
03293             'ref_table=' . $TYPO3_DB->fullQuoteStr('_FILE', 'sys_refindex') .
03294             ' AND ref_string LIKE ' . $TYPO3_DB->fullQuoteStr('%/RTEmagic%', 'sys_refindex') .
03295             ' AND softref_key=' . $TYPO3_DB->fullQuoteStr('images', 'sys_refindex') .
03296             ' AND tablename=' . $TYPO3_DB->fullQuoteStr($table, 'sys_refindex') .
03297             ' AND recuid=' . intval($theNewSQLID),
03298             '',
03299             'sorting DESC'
03300         );
03301 
03302 
03303             // Traverse the files found and copy them:
03304         if (is_array($recs)) {
03305             foreach ($recs as $rec) {
03306                 $filename = basename($rec['ref_string']);
03307                 $fileInfo = array();
03308                 if (t3lib_div::isFirstPartOfStr($filename, 'RTEmagicC_')) {
03309 
03310                     $fileInfo['exists'] = @is_file(PATH_site . $rec['ref_string']);
03311                     $fileInfo['original'] = substr($rec['ref_string'], 0, -strlen($filename)) . 'RTEmagicP_' . preg_replace('/\.[[:alnum:]]+$/', '', substr($filename, 10));
03312                     $fileInfo['original_exists'] = @is_file(PATH_site . $fileInfo['original']);
03313 
03314                         // CODE from tx_impexp and class.rte_images.php adapted for use here:
03315 
03316                     if ($fileInfo['exists'] && $fileInfo['original_exists']) {
03317 
03318                             // Initialize; Get directory prefix for file and set the original name:
03319                         $dirPrefix = dirname($rec['ref_string']) . '/';
03320                         $rteOrigName = basename($fileInfo['original']);
03321 
03322                             // If filename looks like an RTE file, and the directory is in "uploads/", then process as a RTE file!
03323                         if ($rteOrigName && t3lib_div::isFirstPartOfStr($dirPrefix, 'uploads/') && @is_dir(PATH_site . $dirPrefix)) { // RTE:
03324 
03325                                 // From the "original" RTE filename, produce a new "original" destination filename which is unused.
03326                             $origDestName = $this->fileFunc->getUniqueName($rteOrigName, PATH_site . $dirPrefix);
03327 
03328                                 // Create copy file name:
03329                             $pI = pathinfo($rec['ref_string']);
03330                             $copyDestName = dirname($origDestName) . '/RTEmagicC_' . substr(basename($origDestName), 10) . '.' . $pI['extension'];
03331                             if (!@is_file($copyDestName) && !@is_file($origDestName)
03332                                                               && $origDestName === t3lib_div::getFileAbsFileName($origDestName) && $copyDestName === t3lib_div::getFileAbsFileName($copyDestName)) {
03333 
03334                                     // Making copies:
03335                                 t3lib_div::upload_copy_move(PATH_site . $fileInfo['original'], $origDestName);
03336                                 t3lib_div::upload_copy_move(PATH_site . $rec['ref_string'], $copyDestName);
03337                                 clearstatcache();
03338 
03339                                     // Register this:
03340                                 $this->RTEmagic_copyIndex[$rec['tablename']][$rec['recuid']][$rec['field']][$rec['ref_string']] = substr($copyDestName, strlen(PATH_site));
03341 
03342                                     // Check and update the record using the t3lib_refindex class:
03343                                 if (@is_file($copyDestName)) {
03344                                     $sysRefObj = t3lib_div::makeInstance('t3lib_refindex');
03345                                     $error = $sysRefObj->setReferenceValue($rec['hash'], substr($copyDestName, strlen(PATH_site)), FALSE, TRUE);
03346                                     if ($error) {
03347                                         echo $this->newlog('t3lib_refindex::setReferenceValue(): ' . $error, 1);
03348                                     }
03349                                 } else {
03350                                     $this->newlog('File "' . $copyDestName . '" was not created!', 1);
03351                                 }
03352                             } else {
03353                                 $this->newlog('Could not construct new unique names for file!', 1);
03354                             }
03355                         } else {
03356                             $this->newlog('Maybe directory of file was not within "uploads/"?', 1);
03357                         }
03358                     } else {
03359                         $this->newlog('Trying to copy RTEmagic files (' . $rec['ref_string'] . ' / ' . $fileInfo['original'] . ') but one or both were missing', 1);
03360                     }
03361                 }
03362             }
03363         }
03364     }
03365 
03366 
03367     /**
03368      * Find l10n-overlay records and perform the requested move action for these records.
03369      *
03370      * @param   string      $table: Record Table
03371      * @param   string      $uid: Record UID
03372      * @param   string      $destPid: Position to move to
03373      * @return  void
03374      */
03375     function copyL10nOverlayRecords($table, $uid, $destPid, $first = 0, $overrideValues = array(), $excludeFields = '') {
03376             //there's no need to perform this for page-records
03377         if (!t3lib_BEfunc::isTableLocalizable($table) || !empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) || !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) {
03378             return;
03379         }
03380 
03381         $where = '';
03382         if (isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
03383             $where = ' AND t3ver_oid=0';
03384         }
03385 
03386         $l10nRecords = t3lib_BEfunc::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], $uid, $where);
03387         if (is_array($l10nRecords)) {
03388             foreach ($l10nRecords as $record) {
03389                 $this->copyRecord($table, $record['uid'], $destPid, $first, $overrideValues, $excludeFields, $record[$GLOBALS['TCA'][$table]['ctrl']['languageField']]);
03390             }
03391         }
03392     }
03393 
03394 
03395     /*********************************************
03396      *
03397      * Cmd: Moving, Localizing
03398      *
03399      ********************************************/
03400 
03401     /**
03402      * Moving single records
03403      *
03404      * @param   string      Table name to move
03405      * @param   integer     Record uid to move
03406      * @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
03407      * @return  void
03408      */
03409     function moveRecord($table, $uid, $destPid) {
03410         global $TCA, $TYPO3_CONF_VARS;
03411 
03412         if ($TCA[$table]) {
03413 
03414                 // In case the record to be moved turns out to be an offline version,
03415                 // we have to find the live version and work on that one (this case
03416                 // happens for pages with "branch" versioning type)
03417                 // note: as "branch" versioning is deprecated since TYPO3 4.2, this
03418                 // functionality will be removed in TYPO3 4.7 (note by benni: a hook could replace this)
03419             if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table, $uid, 'uid')) {
03420                 $uid = $lookForLiveVersion['uid'];
03421             }
03422 
03423                 // Initialize:
03424             $destPid = intval($destPid);
03425 
03426             $propArr = $this->getRecordProperties($table, $uid); // Get this before we change the pid (for logging)
03427             $moveRec = $this->getRecordProperties($table, $uid, TRUE);
03428             $resolvedPid = $this->resolvePid($table, $destPid); // This is the actual pid of the moving to destination
03429 
03430                 // Finding out, if the record may be moved from where it is. If the record is a non-page, then it depends on edit-permissions.
03431                 // If the record is a page, then there are two options: If the page is moved within itself, (same pid) it's edit-perms of the pid. If moved to another place then its both delete-perms of the pid and new-page perms on the destination.
03432             if ($table != 'pages' || $resolvedPid == $moveRec['pid']) {
03433                 $mayMoveAccess = $this->checkRecordUpdateAccess($table, $uid); // Edit rights for the record...
03434             } else {
03435                 $mayMoveAccess = $this->doesRecordExist($table, $uid, 'delete');
03436             }
03437 
03438                 // Finding out, if the record may be moved TO another place. Here we check insert-rights (non-pages = edit, pages = new), unless the pages are moved on the same pid, then edit-rights are checked
03439             if ($table != 'pages' || $resolvedPid != $moveRec['pid']) {
03440                 $mayInsertAccess = $this->checkRecordInsertAccess($table, $resolvedPid, 4); // Insert rights for the record...
03441             } else {
03442                 $mayInsertAccess = $this->checkRecordUpdateAccess($table, $uid);
03443             }
03444 
03445                 // Checking if there is anything else disallowing moving the record by checking if editing is allowed
03446             $fullLanguageCheckNeeded = ($table != 'pages');
03447             $mayEditAccess = $this->BE_USER->recordEditAccessInternals($table, $uid, FALSE, FALSE, $fullLanguageCheckNeeded);
03448 
03449                 // If moving is allowed, begin the processing:
03450             if ($mayEditAccess) {
03451                 if ($mayMoveAccess) {
03452                     if ($mayInsertAccess) {
03453 
03454                         $recordWasMoved = FALSE;
03455 
03456                             // move the record via a hook, used e.g. for versioning
03457                         if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'])) {
03458                             foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'] as $classRef) {
03459                                 $hookObj = t3lib_div::getUserObj($classRef);
03460                                 if (method_exists($hookObj, 'moveRecord')) {
03461                                     $hookObj->moveRecord($table, $uid, $destPid, $propArr, $moveRec, $resolvedPid, $recordWasMoved, $this);
03462                                 }
03463                             }
03464                         }
03465 
03466                             // move the record if a hook hasn't moved it yet
03467                         if (!$recordWasMoved) {
03468                             $this->moveRecord_raw($table, $uid, $destPid);
03469                         }
03470 
03471                     } else {
03472                         $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']);
03473                     }
03474                 } else {
03475                     $this->log($table, $uid, 4, 0, 1, "Attempt to move record '%s' (%s) without having permissions to do so.", 14, array($propArr['header'], $table . ':' . $uid), $propArr['event_pid']);
03476                 }
03477             } else {
03478                 $this->log($table, $uid, 4, 0, 1, "Attempt to move record '%s' (%s) without having permissions to do so. [" . $this->BE_USER->errorMsg . "]", 14, array($propArr['header'], $table . ':' . $uid), $propArr['event_pid']);
03479             }
03480         }
03481     }
03482 
03483 
03484     /**
03485      * Moves a record without checking security of any sort.
03486      * USE ONLY INTERNALLY
03487      *
03488      * @param   string      Table name to move
03489      * @param   integer     Record uid to move
03490      * @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
03491      * @return  void
03492      * @see moveRecord()
03493      */
03494     function moveRecord_raw($table, $uid, $destPid) {
03495         global $TCA, $TYPO3_CONF_VARS;
03496 
03497         $sortRow = $TCA[$table]['ctrl']['sortby'];
03498         $origDestPid = $destPid;
03499         $resolvedPid = $this->resolvePid($table, $destPid); // This is the actual pid of the moving to destination
03500 
03501             // 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...)
03502         if (($destPid < 0 && !$sortRow) || $destPid >= 0) { // $destPid>=0 because we must correct pid in case of versioning "page" types.
03503             $destPid = $resolvedPid;
03504         }
03505 
03506         $propArr = $this->getRecordProperties($table, $uid); // Get this before we change the pid (for logging)
03507         $moveRec = $this->getRecordProperties($table, $uid, TRUE);
03508 
03509             // Prepare user defined objects (if any) for hooks which extend this function:
03510         $hookObjectsArr = array();
03511         if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'])) {
03512             foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'] as $classRef) {
03513                 $hookObjectsArr[] = t3lib_div::getUserObj($classRef);
03514             }
03515         }
03516 
03517             // Timestamp field:
03518         $updateFields = array();
03519         if ($TCA[$table]['ctrl']['tstamp']) {
03520             $updateFields[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
03521         }
03522 
03523         if ($destPid >= 0) { // insert as first element on page (where uid = $destPid)
03524             if ($table != 'pages' || $this->destNotInsideSelf($destPid, $uid)) {
03525                 $this->clear_cache($table, $uid); // clear cache before moving
03526 
03527                 $updateFields['pid'] = $destPid; // Setting PID
03528 
03529                     // table is sorted by 'sortby'
03530                 if ($sortRow) {
03531                     $sortNumber = $this->getSortNumber($table, $uid, $destPid);
03532                     $updateFields[$sortRow] = $sortNumber;
03533                 }
03534 
03535                     // check for child records that have also to be moved
03536                 $this->moveRecord_procFields($table, $uid, $destPid);
03537                     // Create query for update:
03538                 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($uid), $updateFields);
03539                     // check for the localizations of that element
03540                 $this->moveL10nOverlayRecords($table, $uid, $destPid);
03541 
03542                     // Call post processing hooks:
03543                 foreach ($hookObjectsArr as $hookObj) {
03544                     if (method_exists($hookObj, 'moveRecord_firstElementPostProcess')) {
03545                         $hookObj->moveRecord_firstElementPostProcess($table, $uid, $destPid, $moveRec, $updateFields, $this);
03546                     }
03547                 }
03548 
03549                     // Logging...
03550                 $newPropArr = $this->getRecordProperties($table, $uid);
03551                 $oldpagePropArr = $this->getRecordProperties('pages', $propArr['pid']);
03552                 $newpagePropArr = $this->getRecordProperties('pages', $destPid);
03553 
03554                 if ($destPid != $propArr['pid']) {
03555                     $this->log($table, $uid, 4, $destPid, 0, "Moved record '%s' (%s) to page '%s' (%s)", 2, array($propArr['header'], $table . ':' . $uid, $newpagePropArr['header'], $newPropArr['pid']), $propArr['pid']); // Logged to old page
03556                     $this->log($table, $uid, 4, $destPid, 0, "Moved record '%s' (%s) from page '%s' (%s)", 3, array($propArr['header'], $table . ':' . $uid, $oldpagePropArr['header'], $propArr['pid']), $destPid); // Logged to new page
03557                 } else {
03558                     $this->log($table, $uid, 4, $destPid, 0, "Moved record '%s' (%s) on page '%s' (%s)", 4, array($propArr['header'], $table . ':' . $uid, $oldpagePropArr['header'], $propArr['pid']), $destPid); // Logged to new page
03559                 }
03560                 $this->clear_cache($table, $uid); // clear cache after moving
03561                 $this->fixUniqueInPid($table, $uid);
03562                     // fixCopyAfterDuplFields
03563                 if ($origDestPid < 0) {
03564                     $this->fixCopyAfterDuplFields($table, $uid, abs($origDestPid), 1);
03565                 } // origDestPid is retrieve before it may possibly be converted to resolvePid if the table is not sorted anyway. In this way, copying records to after another records which are not sorted still lets you use this function in order to copy fields from the one before.
03566             } else {
03567                 $destPropArr = $this->getRecordProperties('pages', $destPid);
03568                 $this->log($table, $uid, 4, 0, 1, "Attempt to move page '%s' (%s) to inside of its own rootline (at page '%s' (%s))", 10, array($propArr['header'], $uid, $destPropArr['header'], $destPid), $propArr['pid']);
03569             }
03570         } else { // Put after another record
03571             if ($sortRow) { // table is being sorted
03572                 $sortInfo = $this->getSortNumber($table, $uid, $destPid);
03573                 $destPid = $sortInfo['pid']; // Setting the destPid to the new pid of the record.
03574                 if (is_array($sortInfo)) { // If not an array, there was an error (which is already logged)
03575                     if ($table != 'pages' || $this->destNotInsideSelf($destPid, $uid)) {
03576                         $this->clear_cache($table, $uid); // clear cache before moving
03577 
03578                             // We now update the pid and sortnumber
03579                         $updateFields['pid'] = $destPid;
03580                         $updateFields[$sortRow] = $sortInfo['sortNumber'];
03581 
03582                             // check for child records that have also to be moved
03583                         $this->moveRecord_procFields($table, $uid, $destPid);
03584                             // Create query for update:
03585                         $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($uid), $updateFields);
03586                             // check for the localizations of that element
03587                         $this->moveL10nOverlayRecords($table, $uid, $destPid);
03588 
03589                             // Call post processing hooks:
03590                         foreach ($hookObjectsArr as $hookObj) {
03591                             if (method_exists($hookObj, 'moveRecord_afterAnotherElementPostProcess')) {
03592                                 $hookObj->moveRecord_afterAnotherElementPostProcess($table, $uid, $destPid, $origDestPid, $moveRec, $updateFields, $this);
03593                             }
03594                         }
03595 
03596                             // Logging...
03597                         $newPropArr = $this->getRecordProperties($table, $uid);
03598                         $oldpagePropArr = $this->getRecordProperties('pages', $propArr['pid']);
03599                         if ($destPid != $propArr['pid']) {
03600                             $newpagePropArr = $this->getRecordProperties('pages', $destPid);
03601                             $this->log($table, $uid, 4, 0, 0, "Moved record '%s' (%s) to page '%s' (%s)", 2, array($propArr['header'], $table . ':' . $uid, $newpagePropArr['header'], $newPropArr['pid']), $propArr['pid']); // Logged to old page
03602                             $this->log($table, $uid, 4, 0, 0, "Moved record '%s' (%s) from page '%s' (%s)", 3, array($propArr['header'], $table . ':' . $uid, $oldpagePropArr['header'], $propArr['pid']), $destPid); // Logged to new page
03603                         } else {
03604                             $this->log($table, $uid, 4, 0, 0, "Moved record '%s' (%s) on page '%s' (%s)", 4, array($propArr['header'], $table . ':' . $uid, $oldpagePropArr['header'], $propArr['pid']), $destPid); // Logged to new page
03605                         }
03606 
03607                             // clear cache after moving
03608                         $this->clear_cache($table, $uid);
03609 
03610                             // fixUniqueInPid
03611                         $this->fixUniqueInPid($table, $uid);
03612 
03613                             // fixCopyAfterDuplFields
03614                         if ($origDestPid < 0) {
03615                             $this->fixCopyAfterDuplFields($table, $uid, abs($origDestPid), 1);
03616                         }
03617                     } else {
03618                         $destPropArr = $this->getRecordProperties('pages', $destPid);
03619                         $this->log($table, $uid, 4, 0, 1, "Attempt to move page '%s' (%s) to inside of its own rootline (at page '%s' (%s))", 10, array($propArr['header'], $uid, $destPropArr['header'], $destPid), $propArr['pid']);
03620                     }
03621                 }
03622             } else {
03623                 $this->log($table, $uid, 4, 0, 1, "Attempt to move record '%s' (%s) to after another record, although the table has no sorting row.", 13, array($propArr['header'], $table . ':' . $uid), $propArr['event_pid']);
03624             }
03625         }
03626     }
03627 
03628     /**
03629      * Walk through all fields of the moved record and look for children of e.g. the inline type.
03630      * If child records are found, they are also move to the new $destPid.
03631      *
03632      * @param   string      $table: Record Table
03633      * @param   string      $uid: Record UID
03634      * @param   string      $destPid: Position to move to
03635      * @return  void
03636      */
03637     function moveRecord_procFields($table, $uid, $destPid) {
03638         t3lib_div::loadTCA($table);
03639         $conf = $GLOBALS['TCA'][$table]['columns'];
03640         $row = t3lib_BEfunc::getRecordWSOL($table, $uid);
03641         if (is_array($row)) {
03642             foreach ($row as $field => $value) {
03643                 $this->moveRecord_procBasedOnFieldType($table, $uid, $destPid, $field, $value, $conf[$field]['config']);
03644             }
03645         }
03646     }
03647 
03648     /**
03649      * Move child records depending on the field type of the parent record.
03650      *
03651      * @param   string      $table: Record Table
03652      * @param   string      $uid: Record UID
03653      * @param   string      $destPid: Position to move to
03654      * @param   string      $field: Record field
03655      * @param   string      $value: Record field value
03656      * @param   array       $conf: TCA configuration of current field
03657      * @return  void
03658      */
03659     function moveRecord_procBasedOnFieldType($table, $uid, $destPid, $field, $value, $conf) {
03660         $moveTable = '';
03661         $moveIds = array();
03662 
03663         if ($conf['type'] == 'inline') {
03664             $foreign_table = $conf['foreign_table'];
03665             $moveChildrenWithParent = (!isset($conf['behaviour']['disableMovingChildrenWithParent']) || !$conf['behaviour']['disableMovingChildrenWithParent']);
03666 
03667             if ($foreign_table && $moveChildrenWithParent) {
03668                 $inlineType = $this->getInlineFieldType($conf);
03669                 if ($inlineType == 'list' || $inlineType == 'field') {
03670                     $moveTable = $foreign_table;
03671                     if ($table == 'pages') {
03672                             // If the inline elements are related to a page record,
03673                             // make sure they reside at that page and not at its parent
03674                         $destPid = $uid;
03675                     }
03676                     $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
03677                     $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
03678                 }
03679             }
03680         }
03681 
03682             // Move the records
03683         if (isset($dbAnalysis)) {
03684                 // Moving records to a positive destination will insert each
03685                 // record at the beginning, thus the order is reversed here:
03686             foreach (array_reverse($dbAnalysis->itemArray) as $v) {
03687                 $this->moveRecord($v['table'], $v['id'], $destPid);
03688             }
03689         }
03690     }
03691 
03692     /**
03693      * Find l10n-overlay records and perform the requested move action for these records.
03694      *
03695      * @param   string      $table: Record Table
03696      * @param   string      $uid: Record UID
03697      * @param   string      $destPid: Position to move to
03698      * @return  void
03699      */
03700     function moveL10nOverlayRecords($table, $uid, $destPid) {
03701             //there's no need to perform this for page-records or not localizeable tables
03702         if (!t3lib_BEfunc::isTableLocalizable($table) || !empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) || !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) {
03703             return;
03704         }
03705 
03706         $where = '';
03707         if (isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
03708             $where = ' AND t3ver_oid=0';
03709         }
03710         $l10nRecords = t3lib_BEfunc::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], $uid, $where);
03711         if (is_array($l10nRecords)) {
03712             foreach ($l10nRecords as $record) {
03713                 $this->moveRecord($table, $record['uid'], $destPid);
03714             }
03715         }
03716     }
03717 
03718     /**
03719      * Localizes a record to another system language
03720      * In reality it only works if transOrigPointerTable is not set. For "pages" the implementation is hardcoded
03721      *
03722      * @param   string      Table name
03723      * @param   integer     Record uid (to be localized)
03724      * @param   integer     Language ID (from sys_language table)
03725      * @return  mixed       The uid (integer) of the new translated record or false (boolean) if something went wrong
03726      */
03727     function localize($table, $uid, $language) {
03728         global $TCA;
03729 
03730         $newId = FALSE;
03731         $uid = intval($uid);
03732 
03733         if ($TCA[$table] && $uid) {
03734             t3lib_div::loadTCA($table);
03735 
03736             if (($TCA[$table]['ctrl']['languageField'] && $TCA[$table]['ctrl']['transOrigPointerField'] && !$TCA[$table]['ctrl']['transOrigPointerTable']) || $table === 'pages') {
03737                 if ($langRec = t3lib_BEfunc::getRecord('sys_language', intval($language), 'uid,title')) {
03738                     if ($this->doesRecordExist($table, $uid, 'show')) {
03739 
03740                         $row = t3lib_BEfunc::getRecordWSOL($table, $uid); // Getting workspace overlay if possible - this will localize versions in workspace if any
03741                         if (is_array($row)) {
03742                             if ($row[$TCA[$table]['ctrl']['languageField']] <= 0 || $table === 'pages') {
03743                                 if ($row[$TCA[$table]['ctrl']['transOrigPointerField']] == 0 || $table === 'pages') {
03744                                     if ($table === 'pages') {
03745                                         $pass = $TCA[$table]['ctrl']['transForeignTable'] === 'pages_language_overlay' && !t3lib_BEfunc::getRecordsByField('pages_language_overlay', 'pid', $uid, ' AND ' . $TCA['pages_language_overlay']['ctrl']['languageField'] . '=' . intval($langRec['uid']));
03746                                         $Ttable = 'pages_language_overlay';
03747                                         t3lib_div::loadTCA($Ttable);
03748                                     } else {
03749                                         $pass = !t3lib_BEfunc::getRecordLocalization($table, $uid, $langRec['uid'], 'AND pid=' . intval($row['pid']));
03750                                         $Ttable = $table;
03751                                     }
03752 
03753                                     if ($pass) {
03754 
03755                                             // Initialize:
03756                                         $overrideValues = array();
03757                                         $excludeFields = array();
03758 
03759                                             // Set override values:
03760                                         $overrideValues[$TCA[$Ttable]['ctrl']['languageField']] = $langRec['uid'];
03761                                         $overrideValues[$TCA[$Ttable]['ctrl']['transOrigPointerField']] = $uid;
03762 
03763                                             // Set exclude Fields:
03764                                         foreach ($TCA[$Ttable]['columns'] as $fN => $fCfg) {
03765                                             if ($fCfg['l10n_mode'] == 'prefixLangTitle') { // Check if we are just prefixing:
03766                                                 if (($fCfg['config']['type'] == 'text' || $fCfg['config']['type'] == 'input') && strlen($row[$fN])) {
03767                                                     list($tscPID) = t3lib_BEfunc::getTSCpid($table, $uid, '');
03768                                                     $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
03769 
03770                                                     if (isset($TSConfig['translateToMessage']) && strlen($TSConfig['translateToMessage'])) {
03771                                                         $translateToMsg = @sprintf($TSConfig['translateToMessage'], $langRec['title']);
03772                                                     }
03773                                                     if (!strlen($translateToMsg)) {
03774                                                         $translateToMsg = 'Translate to ' . $langRec['title'] . ':';
03775                                                     }
03776 
03777                                                     $overrideValues[$fN] = '[' . $translateToMsg . '] ' . $row[$fN];
03778                                                 }
03779                                             } elseif (t3lib_div::inList('exclude,noCopy,mergeIfNotBlank', $fCfg['l10n_mode']) && $fN != $TCA[$Ttable]['ctrl']['languageField'] && $fN != $TCA[$Ttable]['ctrl']['transOrigPointerField']) { // Otherwise, do not copy field (unless it is the language field or pointer to the original language)
03780                                                 $excludeFields[] = $fN;
03781                                             }
03782                                         }
03783 
03784                                         if ($Ttable === $table) {
03785 
03786                                                 // Execute the copy:
03787                                             $newId = $this->copyRecord($table, $uid, -$uid, 1, $overrideValues, implode(',', $excludeFields), $language);
03788                                             $autoVersionNewId = $this->getAutoVersionId($table, $newId);
03789                                             if (is_null($autoVersionNewId) === FALSE) {
03790                                                 $this->triggerRemapAction(
03791                                                     $table,
03792                                                     $newId,
03793                                                     array($this, 'placeholderShadowing'),
03794                                                     array($table, $autoVersionNewId),
03795                                                     TRUE
03796                                                 );
03797                                             }
03798                                         } else {
03799 
03800                                                 // Create new record:
03801                                             $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain');
03802                                             /* @var $copyTCE t3lib_TCEmain  */
03803                                             $copyTCE->stripslashes_values = 0;
03804                                             $copyTCE->cachedTSconfig = $this->cachedTSconfig; // Copy forth the cached TSconfig
03805                                             $copyTCE->dontProcessTransformations = 1; // Transformations should NOT be carried out during copy
03806 
03807                                             $copyTCE->start(array($Ttable => array('NEW' => $overrideValues)), '', $this->BE_USER);
03808                                             $copyTCE->process_datamap();
03809 
03810                                                 // Getting the new UID as if it had been copied:
03811                                             $theNewSQLID = $copyTCE->substNEWwithIDs['NEW'];
03812                                             if ($theNewSQLID) {
03813                                                     // If is by design that $Ttable is used and not $table! See "l10nmgr" extension. Could be debated, but this is what I chose for this "pseudo case"
03814                                                 $this->copyMappingArray[$Ttable][$uid] = $theNewSQLID;
03815                                                 $newId = $theNewSQLID;
03816                                             }
03817                                         }
03818                                     } else {
03819                                         $this->newlog('Localization failed; There already was a localization for this language of the record!', 1);
03820                                     }
03821                                 } else {
03822                                     $this->newlog('Localization failed; Source record contained a reference to an original default record (which is strange)!', 1);
03823                                 }
03824                             } else {
03825                                 $this->newlog('Localization failed; Source record had another language than "Default" or "All" defined!', 1);
03826                             }
03827                         } else {
03828                             $this->newlog('Attempt to localize record that did not exist!', 1);
03829                         }
03830                     } else {
03831                         $this->newlog('Attempt to localize record without permission', 1);
03832                     }
03833                 } else {
03834                     $this->newlog('Sys language UID "' . $language . '" not found valid!', 1);
03835                 }
03836             } else {
03837                 $this->newlog('Localization failed; "languageField" and "transOrigPointerField" must be defined for the table!', 1);
03838             }
03839         }
03840         return $newId;
03841     }
03842 
03843 
03844     /**
03845      * Performs localization or synchronization of child records.
03846      *
03847      * @param   string      $table: The table of the localized parent record
03848      * @param   integer     $id: The uid of the localized parent record
03849      * @param   mixed       $command: Defines the type 'localize' or 'synchronize' (string) or a single uid to be localized (integer)
03850      * @return  void
03851      */
03852     protected function inlineLocalizeSynchronize($table, $id, $command) {
03853             // <field>, (localize | synchronize | <uid>):
03854         $parts = t3lib_div::trimExplode(',', $command);
03855         $field = $parts[0];
03856         $type = $parts[1];
03857 
03858         if ($field && (t3lib_div::inList('localize,synchronize', $type) || t3lib_div::testInt($type)) && isset($GLOBALS['TCA'][$table]['columns'][$field]['config'])) {
03859             $config = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
03860             $foreignTable = $config['foreign_table'];
03861             $localizationMode = t3lib_BEfunc::getInlineLocalizationMode($table, $config);
03862 
03863             if ($localizationMode == 'select') {
03864                 $parentRecord = t3lib_BEfunc::getRecordWSOL($table, $id);
03865                 $language = intval($parentRecord[$GLOBALS['TCA'][$table]['ctrl']['languageField']]);
03866                 $transOrigPointer = intval($parentRecord[$GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField']]);
03867                 $childTransOrigPointerField = $GLOBALS['TCA'][$foreignTable]['ctrl']['transOrigPointerField'];
03868 
03869                 if ($parentRecord && is_array($parentRecord) && $language > 0 && $transOrigPointer) {
03870                     $inlineSubType = $this->getInlineFieldType($config);
03871                     $transOrigRecord = t3lib_BEfunc::getRecordWSOL($table, $transOrigPointer);
03872 
03873                     if ($inlineSubType !== FALSE) {
03874                         $removeArray = array();
03875                             // Fetch children from original language parent:
03876                         /** @var $dbAnalysisOriginal t3lib_loadDBGroup */
03877                         $dbAnalysisOriginal = t3lib_div::makeInstance('t3lib_loadDBGroup');
03878                         $dbAnalysisOriginal->start($transOrigRecord[$field], $foreignTable, '', $transOrigRecord['uid'], $table, $config);
03879                         $elementsOriginal = array();
03880                         foreach ($dbAnalysisOriginal->itemArray as $item) {
03881                             $elementsOriginal[$item['id']] = $item;
03882                         }
03883                         unset($dbAnalysisOriginal);
03884                             // Fetch children from current localized parent:
03885                             // @var $dbAnalysisCurrent t3lib_loadDBGroup
03886                         $dbAnalysisCurrent = t3lib_div::makeInstance('t3lib_loadDBGroup');
03887                         $dbAnalysisCurrent->start($parentRecord[$field], $foreignTable, '', $id, $table, $config);
03888                             // Perform synchronization: Possibly removal of already localized records:
03889                         if ($type == 'synchronize') {
03890                             foreach ($dbAnalysisCurrent->itemArray as $index => $item) {
03891                                 $childRecord = t3lib_BEfunc::getRecordWSOL($item['table'], $item['id']);
03892                                 if (isset($childRecord[$childTransOrigPointerField]) && $childRecord[$childTransOrigPointerField] > 0) {
03893                                     $childTransOrigPointer = $childRecord[$childTransOrigPointerField];
03894                                         // If snychronization is requested, child record was translated once, but original record does not exist anymore, remove it:
03895                                     if (!isset($elementsOriginal[$childTransOrigPointer])) {
03896                                         unset($dbAnalysisCurrent->itemArray[$index]);
03897                                         $removeArray[$item['table']][$item['id']]['delete'] = 1;
03898                                     }
03899                                 }
03900                             }
03901                         }
03902                             // Perform synchronization/localization: Possibly add unlocalized records for original language:
03903                         if (t3lib_div::testInt($type) && isset($elementsOriginal[$type])) {
03904                             $item = $elementsOriginal[$type];
03905                             $item['id'] = $this->localize($item['table'], $item['id'], $language);
03906                             $item['id'] = $this->overlayAutoVersionId($item['table'], $item['id']);
03907                             $dbAnalysisCurrent->itemArray[] = $item;
03908                         } elseif (t3lib_div::inList('localize,synchronize', $type)) {
03909                             foreach ($elementsOriginal as $originalId => $item) {
03910                                 $item['id'] = $this->localize($item['table'], $item['id'], $language);
03911                                 $item['id'] = $this->overlayAutoVersionId($item['table'], $item['id']);
03912                                 $dbAnalysisCurrent->itemArray[] = $item;
03913                             }
03914                         }
03915                             // Store the new values, we will set up the uids for the subtype later on (exception keep localization from original record):
03916                         $value = implode(',', $dbAnalysisCurrent->getValueArray());
03917                         $this->registerDBList[$table][$id][$field] = $value;
03918                             // Remove child records (if synchronization requested it):
03919                         if (is_array($removeArray) && count($removeArray)) {
03920                             $tce = t3lib_div::makeInstance('t3lib_TCEmain');
03921                             $tce->stripslashes_values = FALSE;
03922                             $tce->start(array(), $removeArray);
03923                             $tce->process_cmdmap();
03924                             unset($tce);
03925                         }
03926                             // Handle, reorder and store relations:
03927                         if ($inlineSubType == 'list') {
03928                             $updateFields = array($field => $value);
03929                         } elseif ($inlineSubType == 'field') {
03930                             $dbAnalysisCurrent->writeForeignField($config, $id);
03931                             $updateFields = array($field => $dbAnalysisCurrent->countItems(FALSE));
03932                         }
03933                             // Update field referencing to child records of localized parent record:
03934                         if (is_array($updateFields) && count($updateFields)) {
03935                             $this->updateDB($table, $id, $updateFields);
03936                         }
03937                     }
03938                 }
03939             }
03940         }
03941     }
03942 
03943 
03944     /*********************************************
03945      *
03946      * Cmd: Deleting
03947      *
03948      ********************************************/
03949 
03950     /**
03951      * Delete a single record
03952      *
03953      * @param   string      Table name
03954      * @param   integer     Record UID
03955      * @return  void
03956      */
03957     function deleteAction($table, $id) {
03958         global $TYPO3_CONF_VARS;
03959 
03960         $recordToDelete = t3lib_BEfunc::getRecord($table, $id);
03961 
03962             // Record asked to be deleted was found:
03963         if (is_array($recordToDelete)) {
03964             $recordWasDeleted = FALSE;
03965 
03966             if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'])) {
03967                 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'] as $classRef) {
03968                     $hookObj = t3lib_div::getUserObj($classRef);
03969                     if (method_exists($hookObj, 'processCmdmap_deleteAction')) {
03970                         $hookObj->processCmdmap_deleteAction($table, $id, $recordToDelete, $recordWasDeleted, $this);
03971                     }
03972                 }
03973             }
03974 
03975                 // delete the record if a hook hasn't deleted it yet
03976             if (!$recordWasDeleted) {
03977                 $this->deleteEl($table, $id);
03978             }
03979         }
03980     }
03981 
03982     /**
03983      * Delete element from any table
03984      *
03985      * @param   string      Table name
03986      * @param   integer     Record UID
03987      * @param   boolean     Flag: If $noRecordCheck is set, then the function does not check permission to delete record
03988      * @param   boolean     If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
03989      * @return  void
03990      */
03991     function deleteEl($table, $uid, $noRecordCheck = FALSE, $forceHardDelete = FALSE) {
03992         if ($table == 'pages') {
03993             $this->deletePages($uid, $noRecordCheck, $forceHardDelete);
03994         } else {
03995             $this->deleteVersionsForRecord($table, $uid, $forceHardDelete);
03996             $this->deleteRecord($table, $uid, $noRecordCheck, $forceHardDelete);
03997         }
03998     }
03999 
04000     /**
04001      * Delete versions for element from any table
04002      *
04003      * @param   string      Table name
04004      * @param   integer     Record UID
04005      * @param   boolean     If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
04006      * @return  void
04007      */
04008     function deleteVersionsForRecord($table, $uid, $forceHardDelete) {
04009         $versions = t3lib_BEfunc::selectVersionsOfRecord($table, $uid, 'uid,pid');
04010         if (is_array($versions)) {
04011             foreach ($versions as $verRec) {
04012                 if (!$verRec['_CURRENT_VERSION']) {
04013                     if ($table == 'pages') {
04014                         $this->deletePages($verRec['uid'], TRUE, $forceHardDelete);
04015                     } else {
04016                         $this->deleteRecord($table, $verRec['uid'], TRUE, $forceHardDelete);
04017                     }
04018                 }
04019             }
04020         }
04021     }
04022 
04023     /**
04024      * Undelete a single record
04025      *
04026      * @param   string      Table name
04027      * @param   integer     Record UID
04028      * @return  void
04029      */
04030     function undeleteRecord($table, $uid) {
04031         if ($this->isRecordUndeletable($table, $uid)) {
04032             $this->deleteRecord($table, $uid, TRUE, FALSE, TRUE);
04033         }
04034     }
04035 
04036     /**
04037      * Deleting/Undeleting a record
04038      * This function may not be used to delete pages-records unless the underlying records are already deleted
04039      * Deletes a record regardless of versioning state (live or offline, doesn't matter, the uid decides)
04040      * If both $noRecordCheck and $forceHardDelete are set it could even delete a "deleted"-flagged record!
04041      *
04042      * @param   string      Table name
04043      * @param   integer     Record UID
04044      * @param   boolean     Flag: If $noRecordCheck is set, then the function does not check permission to delete record
04045      * @param   boolean     If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
04046      * @param   boolean     If TRUE, the "deleted" flag is set to 0 again and thus, the item is undeleted.
04047      * @return  void
04048      */
04049     function deleteRecord($table, $uid, $noRecordCheck = FALSE, $forceHardDelete = FALSE, $undeleteRecord = FALSE) {
04050         global $TCA;
04051 
04052             // Checking if there is anything else disallowing deleting the record by checking if editing is allowed
04053         $mayEditAccess = $this->BE_USER->recordEditAccessInternals($table, $uid, FALSE, $undeleteRecord, TRUE);
04054 
04055         $uid = intval($uid);
04056         if ($TCA[$table] && $uid) {
04057             if ($mayEditAccess) {
04058                 if ($noRecordCheck || $this->doesRecordExist($table, $uid, 'delete')) {
04059                     $this->clear_cache($table, $uid); // clear cache before deleting the record, else the correct page cannot be identified by clear_cache
04060 
04061                     $propArr = $this->getRecordProperties($table, $uid);
04062                     $pagePropArr = $this->getRecordProperties('pages', $propArr['pid']);
04063 
04064                     $deleteRow = $TCA[$table]['ctrl']['delete'];
04065                     if ($deleteRow && !$forceHardDelete) {
04066                         $value = $undeleteRecord ? 0 : 1;
04067                         $updateFields = array(
04068                             $deleteRow => $value
04069                         );
04070 
04071                         if ($TCA[$table]['ctrl']['tstamp']) {
04072                             $updateFields[$TCA[$table]['ctrl']['tstamp']] = $GLOBALS['EXEC_TIME'];
04073                         }
04074 
04075                             // If the table is sorted, then the sorting number is set very high
04076                         if ($TCA[$table]['ctrl']['sortby'] && !$undeleteRecord) {
04077                             $updateFields[$TCA[$table]['ctrl']['sortby']] = 1000000000;
04078                         }
04079 
04080                             // before (un-)deleting this record, check for child records or references
04081                         $this->deleteRecord_procFields($table, $uid, $undeleteRecord);
04082                         $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($uid), $updateFields);
04083 
04084                             // delete all l10n records aswell, impossible during undelete because it might bring too many records back to life
04085                         if (!$undeleteRecord) {
04086                             $this->deleteL10nOverlayRecords($table, $uid);
04087                         }
04088                     } else {
04089 
04090                             // Fetches all fields with flexforms and look for files to delete:
04091                         t3lib_div::loadTCA($table);
04092                         foreach ($TCA[$table]['columns'] as $fieldName => $cfg) {
04093                             $conf = $cfg['config'];
04094 
04095                             switch ($conf['type']) {
04096                                 case 'flex':
04097                                     $flexObj = t3lib_div::makeInstance('t3lib_flexformtools');
04098                                     $flexObj->traverseFlexFormXMLData($table, $fieldName, t3lib_BEfunc::getRecordRaw($table, 'uid=' . intval($uid)), $this, 'deleteRecord_flexFormCallBack');
04099                                 break;
04100                             }
04101                         }
04102 
04103                             // Fetches all fields that holds references to files
04104                         $fileFieldArr = $this->extFileFields($table);
04105                         if (count($fileFieldArr)) {
04106                             $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',', $fileFieldArr), $table, 'uid=' . intval($uid));
04107                             if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
04108                                 $fArray = $fileFieldArr;
04109                                 foreach ($fArray as $theField) { // MISSING: Support for MM file relations!
04110                                     $this->extFileFunctions($table, $theField, $row[$theField], 'deleteAll'); // This deletes files that belonged to this record.
04111                                 }
04112                             } else {
04113                                 $this->log($table, $uid, 3, 0, 100, 'Delete: Zero rows in result when trying to read filenames from record which should be deleted');
04114                             }
04115                         }
04116 
04117                             // Delete the hard way...:
04118                         $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid=' . intval($uid));
04119 
04120                         $this->deleteL10nOverlayRecords($table, $uid);
04121                     }
04122 
04123                     $state = $undeleteRecord ? 1 : 3; // 1 means insert, 3 means delete
04124                     if (!$GLOBALS['TYPO3_DB']->sql_error()) {
04125                         if ($forceHardDelete) {
04126                             $message = "Record '%s' (%s) was deleted unrecoverable from page '%s' (%s)";
04127                         }
04128                         else {
04129                             $message = $state == 1 ?
04130                                     "Record '%s' (%s) was restored on page '%s' (%s)" :
04131                                     "Record '%s' (%s) was deleted from page '%s' (%s)";
04132                         }
04133                         $this->log($table, $uid, $state, 0, 0,
04134                                    $message, 0,
04135                                    array(
04136                                         $propArr['header'],
04137                                         $table . ':' . $uid,
04138                                         $pagePropArr['header'],
04139                                         $propArr['pid']
04140                                    ),
04141                                    $propArr['event_pid']);
04142 
04143                     } else {
04144                         $this->log($table, $uid, $state, 0, 100, $GLOBALS['TYPO3_DB']->sql_error());
04145                     }
04146 
04147                         // Update reference index:
04148                     $this->updateRefIndex($table, $uid);
04149 
04150                         // if there are entries in the updateRefIndexStack
04151                     if (is_array($this->updateRefIndexStack[$table]) && is_array($this->updateRefIndexStack[$table][$uid])) {
04152                         while ($args = array_pop($this->updateRefIndexStack[$table][$uid])) {
04153                                 // $args[0]: table, $args[1]: uid
04154                             $this->updateRefIndex($args[0], $args[1]);
04155                         }
04156                         unset($this->updateRefIndexStack[$table][$uid]);
04157                     }
04158 
04159                 } else {
04160                     $this->log($table, $uid, 3, 0, 1, 'Attempt to delete record without delete-permissions');
04161                 }
04162             } else {
04163                 $this->log($table, $uid, 3, 0, 1, 'Attempt to delete record without delete-permissions. [' . $this->BE_USER->errorMsg . ']');
04164             }
04165         }
04166     }
04167 
04168     /**
04169      * Call back function for deleting file relations for flexform fields in records which are being completely deleted.
04170      *
04171      * @param   [type]      $dsArr: ...
04172      * @param   [type]      $dataValue: ...
04173      * @param   [type]      $PA: ...
04174      * @param   [type]      $structurePath: ...
04175      * @param   [type]      $pObj: ...
04176      * @return  [type]      ...
04177      */
04178     function deleteRecord_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, $pObj) {
04179 
04180             // Use reference index object to find files in fields:
04181         $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
04182         $files = $refIndexObj->getRelations_procFiles($dataValue, $dsArr['TCEforms']['config'], $PA['uid']);
04183 
04184             // Traverse files and delete them:
04185         if (is_array($files)) {
04186             foreach ($files as $dat) {
04187                 if (@is_file($dat['ID_absFile'])) {
04188                     unlink($dat['ID_absFile']);
04189                     #echo 'DELETE FlexFormFile:'.$dat['ID_absFile'].LF;
04190                 } else {
04191                     $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");
04192                 }
04193             }
04194         }
04195     }
04196 
04197     /**
04198      * Used to delete page because it will check for branch below pages and unallowed tables on the page as well.
04199      *
04200      * @param   integer     Page id
04201      * @param   boolean     If TRUE, pages are not checked for permission.
04202      * @param   boolean     If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
04203      * @return  void
04204      */
04205     function deletePages($uid, $force = FALSE, $forceHardDelete = FALSE) {
04206             // Getting list of pages to delete:
04207         if ($force) {
04208             $brExist = $this->doesBranchExist('', $uid, 0, 1); // returns the branch WITHOUT permission checks (0 secures that)
04209             $res = t3lib_div::trimExplode(',', $brExist . $uid, 1);
04210         } else {
04211             $res = $this->canDeletePage($uid);
04212         }
04213 
04214             // Perform deletion if not error:
04215         if (is_array($res)) {
04216             foreach ($res as $deleteId) {
04217                 $this->deleteSpecificPage($deleteId, $forceHardDelete);
04218             }
04219         } else {
04220             $this->newlog($res, 1);
04221         }
04222     }
04223 
04224     /**
04225      * Delete a page and all records on it.
04226      *
04227      * @param   integer     Page id
04228      * @param   boolean     If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
04229      * @return  void
04230      * @access private
04231      * @see deletePages()
04232      */
04233     function deleteSpecificPage($uid, $forceHardDelete = FALSE) {
04234         $uid = intval($uid);
04235         if ($uid) {
04236             foreach (array_keys($GLOBALS['TCA']) as $table) {
04237                 if ($table != 'pages') {
04238                     $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid=' . intval($uid) . $this->deleteClause($table));
04239                     while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
04240                         $this->deleteVersionsForRecord($table, $row['uid'], $forceHardDelete);
04241                         $this->deleteRecord($table, $row['uid'], TRUE, $forceHardDelete);
04242                     }
04243                     $GLOBALS['TYPO3_DB']->sql_free_result($mres);
04244                 }
04245             }
04246             $this->deleteVersionsForRecord('pages', $uid, $forceHardDelete);
04247             $this->deleteRecord('pages', $uid, TRUE, $forceHardDelete);
04248         }
04249     }
04250 
04251     /**
04252      * Used to evaluate if a page can be deleted
04253      *
04254      * @param   integer     Page id
04255      * @return  mixed       If array: List of page uids to traverse and delete (means OK), if string: error code.
04256      */
04257     function canDeletePage($uid) {
04258         if ($this->doesRecordExist('pages', $uid, 'delete')) { // If we may at all delete this page
04259             if ($this->deleteTree) {
04260                 $brExist = $this->doesBranchExist('', $uid, $this->pMap['delete'], 1); // returns the branch
04261                 if ($brExist != -1) { // Checks if we had permissions
04262                     if ($this->noRecordsFromUnallowedTables($brExist . $uid)) {
04263                         $pagesInBranch = t3lib_div::trimExplode(',', $brExist . $uid, 1);
04264                         foreach ($pagesInBranch as $pageInBranch) {
04265                             if (!$this->BE_USER->recordEditAccessInternals('pages', $pageInBranch, FALSE, FALSE, TRUE)) {
04266                                 return 'Attempt to delete page which has prohibited localizations.';
04267                             }
04268                         }
04269                         return $pagesInBranch;
04270                     } else {
04271                         return 'Attempt to delete records from disallowed tables';
04272                     }
04273                 } else {
04274                     return 'Attempt to delete pages in branch without permissions';
04275                 }
04276             } else {
04277                 $brExist = $this->doesBranchExist('', $uid, $this->pMap['delete'], 1); // returns the branch
04278                 if ($brExist == '') { // Checks if branch exists
04279                     if ($this->noRecordsFromUnallowedTables($uid)) {
04280                         if ($this->BE_USER->recordEditAccessInternals('pages', $uid, FALSE, FALSE, TRUE)) {
04281                             return array($uid);
04282                         } else {
04283                             return 'Attempt to delete page which has prohibited localizations.';
04284                         }
04285                     } else {
04286                         return 'Attempt to delete records from disallowed tables';
04287                     }
04288                 } else {
04289                     return 'Attempt to delete page which has subpages';
04290                 }
04291             }
04292         } else {
04293             return 'Attempt to delete page without permissions';
04294         }
04295     }
04296 
04297     /**
04298      * Returns true if record CANNOT be deleted, otherwise false. Used to check before the versioning API allows a record to be marked for deletion.
04299      *
04300      * @param   string      Record Table
04301      * @param   integer     Record UID
04302      * @return  string      Returns a string IF there is an error (error string explaining). FALSE means record can be deleted
04303      */
04304     function cannotDeleteRecord($table, $id) {
04305         if ($table === 'pages') {
04306             $res = $this->canDeletePage($id);
04307             return is_array($res) ? FALSE : $res;
04308         } else {
04309             return $this->doesRecordExist($table, $id, 'delete') ? FALSE : 'No permission to delete record';
04310         }
04311     }
04312 
04313     /**
04314      * Determines whether a record can be undeleted.
04315      *
04316      * @param   string      $table: Table name of the record
04317      * @param   integer     $uid: uid of the record
04318      * @return  boolean     Whether the record can be undeleted
04319      */
04320     public function isRecordUndeletable($table, $uid) {
04321         $result = FALSE;
04322         $record = t3lib_BEfunc::getRecord($table, $uid, 'pid', '', FALSE);
04323         if ($record['pid']) {
04324             $page = t3lib_BEfunc::getRecord('pages', $record['pid'], 'deleted, title, uid', '', FALSE);
04325                 // The page containing the record is not deleted, thus the record can be undeleted:
04326             if (!$page['deleted']) {
04327                 $result = TRUE;
04328                 // The page containing the record is deleted and has to be undeleted first:
04329             } else {
04330                 $this->log(
04331                     $table, $uid, 'isRecordUndeletable', '', 1,
04332                     'Record cannot be undeleted since the page containing it is deleted! Undelete page "' .
04333                     $page['title'] . ' (UID: ' . $page['uid'] . ')" first'
04334                 );
04335             }
04336         } else {
04337                 // The page containing the record is on rootlevel, so there is no parent record to check, and the record can be undeleted:
04338             $result = TRUE;
04339         }
04340         return $result;
04341     }
04342 
04343     /**
04344      * Before a record is deleted, check if it has references such as inline type or MM references.
04345      * If so, set these child records also to be deleted.
04346      *
04347      * @param   string      $table: Record Table
04348      * @param   string      $uid: Record UID
04349      * @param   boolean     $undeleteRecord: If a record should be undeleted (e.g. from history/undo)
04350      * @return  void
04351      * @see  deleteRecord()
04352      */
04353     function deleteRecord_procFields($table, $uid, $undeleteRecord = FALSE) {
04354         t3lib_div::loadTCA($table);
04355         $conf = $GLOBALS['TCA'][$table]['columns'];
04356         $row = t3lib_BEfunc::getRecord($table, $uid, '*', '', FALSE);
04357 
04358         foreach ($row as $field => $value) {
04359             $this->deleteRecord_procBasedOnFieldType($table, $uid, $field, $value, $conf[$field]['config'], $undeleteRecord);
04360         }
04361     }
04362 
04363     /**
04364      * Process fields of a record to be deleted and search for special handling, like
04365      * inline type, MM records, etc.
04366      *
04367      * @param   string      $table: Record Table
04368      * @param   string      $uid: Record UID
04369      * @param   string      $field: Record field
04370      * @param   string      $value: Record field value
04371      * @param   array       $conf: TCA configuration of current field
04372      * @param   boolean     $undeleteRecord: If a record should be undeleted (e.g. from history/undo)
04373      * @return  void
04374      * @see  deleteRecord()
04375      */
04376     function deleteRecord_procBasedOnFieldType($table, $uid, $field, $value, $conf, $undeleteRecord = FALSE) {
04377         if ($conf['type'] == 'inline') {
04378             $foreign_table = $conf['foreign_table'];
04379 
04380             if ($foreign_table) {
04381                 $inlineType = $this->getInlineFieldType($conf);
04382                 if ($inlineType == 'list' || $inlineType == 'field') {
04383                     $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
04384                     $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
04385                     $dbAnalysis->undeleteRecord = TRUE;
04386 
04387                         // walk through the items and remove them
04388                     foreach ($dbAnalysis->itemArray as $v) {
04389                         if (!$undeleteRecord) {
04390                             $this->deleteAction($v['table'], $v['id']);
04391                         } else {
04392                             $this->undeleteRecord($v['table'], $v['id']);
04393                         }
04394                     }
04395                 }
04396             }
04397 
04398             // no delete action but calls to updateRefIndex *AFTER* this record was deleted
04399         } elseif ($this->isReferenceField($conf)) {
04400             $allowedTables = $conf['type'] == 'group' ? $conf['allowed'] : $conf['foreign_table'] . ',' . $conf['neg_foreign_table'];
04401             $prependName = $conf['type'] == 'group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
04402 
04403             $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
04404             $dbAnalysis->start($value, $allowedTables, $conf['MM'], $uid, $table, $conf);
04405 
04406             foreach ($dbAnalysis->itemArray as $v) {
04407                 $this->updateRefIndexStack[$table][$uid][] = array($v['table'], $v['id']);
04408             }
04409         }
04410     }
04411 
04412     /**
04413      * Find l10n-overlay records and perform the requested delete action for these records.
04414      *
04415      * @param   string      $table: Record Table
04416      * @param   string      $uid: Record UID
04417      * @return  void
04418      */
04419     function deleteL10nOverlayRecords($table, $uid) {
04420             // Check whether table can be localized or has a different table defined to store localizations:
04421         if (!t3lib_BEfunc::isTableLocalizable($table) || !empty($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) || !empty($GLOBALS['TCA'][$table]['ctrl']['transOrigPointerTable'])) {
04422             return;
04423         }
04424 
04425         $where = '';
04426         if (isset($GLOBALS['TCA'][$table]['ctrl']['versioningWS'])) {
04427             $where = ' AND t3ver_oid=0';
04428         }
04429 
04430         $l10nRecords = t3lib_BEfunc::getRecordsByField($table, $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'], $uid, $where);
04431         if (is_array($l10nRecords)) {
04432             foreach ($l10nRecords as $record) {
04433                 $this->deleteAction($table, intval($record['t3ver_oid']) > 0 ? intval($record['t3ver_oid']) : intval($record['uid']));
04434             }
04435         }
04436     }
04437 
04438 
04439     /*********************************************
04440      *
04441      * Cmd: Versioning
04442      *
04443      ********************************************/
04444 
04445     /**
04446      * Creates a new version of a record
04447      * (Requires support in the table)
04448      *
04449      * @param   string      Table name
04450      * @param   integer     Record uid to versionize
04451      * @param   string      Version label
04452      * @param   boolean     If true, the version is created to delete the record.
04453      * @param   integer     Indicating "treeLevel" - or versioning type - "element" (-1), "page" (0) or "branch" (>=1)
04454      * @return  integer     Returns the id of the new version (if any)
04455      * @see copyRecord()
04456      */
04457     function versionizeRecord($table, $id, $label, $delete = FALSE, $versionizeTree = -1) {
04458         global $TCA;
04459 
04460         $id = intval($id);
04461 
04462         if ($TCA[$table] && $TCA[$table]['ctrl']['versioningWS'] && $id > 0) {
04463             if ($this->doesRecordExist($table, $id, 'show')) {
04464                 if ($this->BE_USER->workspaceVersioningTypeAccess($versionizeTree)) {
04465 
04466                         // Select main record:
04467                     $row = $this->recordInfo($table, $id, 'pid,t3ver_id,t3ver_state');
04468                     if (is_array($row)) {
04469                         if ($row['pid'] >= 0) { // record must be online record
04470                             if ($row['t3ver_state'] != 3) { // record must not be placeholder for moving.
04471                                 if (!$delete || !$this->cannotDeleteRecord($table, $id)) {
04472 
04473                                         // Look for next version number:
04474                                     $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
04475                                         't3ver_id',
04476                                         $table,
04477                                         '((pid=-1 && t3ver_oid=' . $id . ') OR uid=' . $id . ')' . $this->deleteClause($table),
04478                                         '',
04479                                         't3ver_id DESC',
04480                                         '1'
04481                                     );
04482                                     list($highestVerNumber) = $GLOBALS['TYPO3_DB']->sql_fetch_row($res);
04483                                     $GLOBALS['TYPO3_DB']->sql_free_result($res);
04484 
04485                                         // Look for version number of the current:
04486                                     $subVer = $row['t3ver_id'] . '.' . ($highestVerNumber + 1);
04487 
04488                                         // Set up the values to override when making a raw-copy:
04489                                     $overrideArray = array(
04490                                         't3ver_id' => $highestVerNumber + 1,
04491                                         't3ver_oid' => $id,
04492                                         't3ver_label' => ($label ? $label : $subVer . ' / ' . date('d-m-Y H:m:s')),
04493                                         't3ver_wsid' => $this->BE_USER->workspace,
04494                                         't3ver_state' => $delete ? 2 : 0,
04495                                         't3ver_count' => 0,
04496                                         't3ver_stage' => 0,
04497                                         't3ver_tstamp' => 0
04498                                     );
04499                                     if ($TCA[$table]['ctrl']['editlock']) {
04500                                         $overrideArray[$TCA[$table]['ctrl']['editlock']] = 0;
04501                                     }
04502                                     if ($table === 'pages') {
04503                                         $overrideArray['t3ver_swapmode'] = $versionizeTree;
04504                                     }
04505 
04506                                         // Checking if the record already has a version in the current workspace of the backend user
04507                                     $workspaceCheck = TRUE;
04508                                     if ($this->BE_USER->workspace !== 0) {
04509                                             // Look for version already in workspace:
04510                                         $workspaceCheck = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $id, 'uid') ? FALSE : TRUE;
04511                                     }
04512 
04513                                     if ($workspaceCheck) {
04514 
04515                                             // Create raw-copy and return result:
04516                                         return $this->copyRecord_raw($table, $id, -1, $overrideArray);
04517                                     } else {
04518                                         $this->newlog('Record "' . $table . ':' . $id . '" you wanted to versionize was already a version in the workspace (wsid=' . $this->BE_USER->workspace . ')!', 1);
04519                                     }
04520                                 } else {
04521                                     $this->newlog('Record cannot be deleted: ' . $this->cannotDeleteRecord($table, $id), 1);
04522                                 }
04523                             } else {
04524                                 $this->newlog('Record cannot be versioned because it is a placeholder for a moving operation', 1);
04525                             }
04526                         } else {
04527                             $this->newlog('Record "' . $table . ':' . $id . '" you wanted to versionize was already a version in archive (pid=-1)!', 1);
04528                         }
04529                     } else {
04530                         $this->newlog('Record "' . $table . ':' . $id . '" you wanted to versionize did not exist!', 1);
04531                     }
04532                 } else {
04533                     $this->newlog('The versioning type ' . $versionizeTree . ' mode you requested was not allowed', 1);
04534                 }
04535             } else {
04536                 $this->newlog('You didnt have correct permissions to make a new version (copy) of this record "' . $table . '" / ' . $id, 1);
04537             }
04538         } else {
04539             $this->newlog('Versioning is not supported for this table "' . $table . '" / ' . $id, 1);
04540         }
04541     }
04542 
04543 
04544     /**
04545      * Swaps MM-relations for current/swap record, see version_swap()
04546      *
04547      * @param   string  Table for the two input records
04548      * @param   integer Current record (about to go offline)
04549      * @param   integer Swap record (about to go online)
04550      * @return void
04551      * @see version_swap()
04552      */
04553     function version_remapMMForVersionSwap($table, $id, $swapWith) {
04554         global $TCA;
04555 
04556             // Actually, selecting the records fully is only need if flexforms are found inside... This could be optimized ...
04557         $currentRec = t3lib_BEfunc::getRecord($table, $id);
04558         $swapRec = t3lib_BEfunc::getRecord($table, $swapWith);
04559 
04560         $this->version_remapMMForVersionSwap_reg = array();
04561 
04562         foreach ($TCA[$table]['columns'] as $field => $fConf) {
04563             $conf = $fConf['config'];
04564 
04565             if ($this->isReferenceField($conf)) {
04566                 $allowedTables = $conf['type'] == 'group' ? $conf['allowed'] : $conf['foreign_table'] . ',' . $conf['neg_foreign_table'];
04567                 $prependName = $conf['type'] == 'group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
04568                 if ($conf['MM']) {
04569 
04570                     $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
04571                     /* @var $dbAnalysis t3lib_loadDBGroup */
04572                     $dbAnalysis->start('', $allowedTables, $conf['MM'], $id, $table, $conf);
04573                     if (count($dbAnalysis->getValueArray($prependName))) {
04574                         $this->version_remapMMForVersionSwap_reg[$id][$field] = array($dbAnalysis, $conf['MM'], $prependName);
04575                     }
04576 
04577                     $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
04578                     /* @var $dbAnalysis t3lib_loadDBGroup */
04579                     $dbAnalysis->start('', $allowedTables, $conf['MM'], $swapWith, $table, $conf);
04580                     if (count($dbAnalysis->getValueArray($prependName))) {
04581                         $this->version_remapMMForVersionSwap_reg[$swapWith][$field] = array($dbAnalysis, $conf['MM'], $prependName);
04582                     }
04583                 }
04584             } elseif ($conf['type'] == 'flex') {
04585 
04586                     // Current record
04587                 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $currentRec, $table);
04588                 $currentValueArray = t3lib_div::xml2array($currentRec[$field]);
04589 
04590                 if (is_array($currentValueArray)) {
04591                     $this->checkValue_flex_procInData(
04592                         $currentValueArray['data'],
04593                         array(), // Not used.
04594                         array(), // Not used.
04595                         $dataStructArray,
04596                         array($table, $id, $field), // Parameters.
04597                         'version_remapMMForVersionSwap_flexFormCallBack'
04598                     );
04599                 }
04600 
04601                     // Swap record
04602                 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $swapRec, $table);
04603                 $currentValueArray = t3lib_div::xml2array($swapRec[$field]);
04604 
04605                 if (is_array($currentValueArray)) {
04606                     $this->checkValue_flex_procInData(
04607                         $currentValueArray['data'],
04608                         array(), // Not used.
04609                         array(), // Not used.
04610                         $dataStructArray,
04611                         array($table, $swapWith, $field), // Parameters.
04612                         'version_remapMMForVersionSwap_flexFormCallBack'
04613                     );
04614                 }
04615             }
04616         }
04617 
04618             // Execute:
04619         $this->version_remapMMForVersionSwap_execSwap($table, $id, $swapWith);
04620     }
04621 
04622     /**
04623      * Callback function for traversing the FlexForm structure in relation to ...
04624      *
04625      * @param   array       Array of parameters in num-indexes: table, uid, field
04626      * @param   array       TCA field configuration (from Data Structure XML)
04627      * @param   string      The value of the flexForm field
04628      * @param   string      Not used.
04629      * @param   string      Not used.
04630      * @param   string      Path in flexforms
04631      * @return  array       Result array with key "value" containing the value of the processing.
04632      * @see version_remapMMForVersionSwap(), checkValue_flex_procInData_travDS()
04633      */
04634     function version_remapMMForVersionSwap_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2, $path) {
04635 
04636             // Extract parameters:
04637         list($table, $uid, $field) = $pParams;
04638 
04639         if ($this->isReferenceField($dsConf)) {
04640             $allowedTables = $dsConf['type'] == 'group' ? $dsConf['allowed'] : $dsConf['foreign_table'] . ',' . $dsConf['neg_foreign_table'];
04641             $prependName = $dsConf['type'] == 'group' ? $dsConf['prepend_tname'] : $dsConf['neg_foreign_table'];
04642             if ($dsConf['MM']) {
04643                 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
04644                 /* @var $dbAnalysis t3lib_loadDBGroup */
04645                 $dbAnalysis->start('', $allowedTables, $dsConf['MM'], $uid, $table, $dsConf);
04646                 $this->version_remapMMForVersionSwap_reg[$uid][$field . '/' . $path] = array($dbAnalysis, $dsConf['MM'], $prependName);
04647             }
04648         }
04649     }
04650 
04651     /**
04652      * Performing the remapping operations found necessary in version_remapMMForVersionSwap()
04653      * It must be done in three steps with an intermediate "fake" uid. The UID can be something else than -$id (fx. 9999999+$id if you dare... :-)- as long as it is unique.
04654      *
04655      * @param   string  Table for the two input records
04656      * @param   integer Current record (about to go offline)
04657      * @param   integer Swap record (about to go online)
04658      * @return void
04659      * @see version_remapMMForVersionSwap()
04660      */
04661     function version_remapMMForVersionSwap_execSwap($table, $id, $swapWith) {
04662 
04663         if (is_array($this->version_remapMMForVersionSwap_reg[$id])) {
04664             foreach ($this->version_remapMMForVersionSwap_reg[$id] as $field => $str) {
04665                 $str[0]->remapMM($str[1], $id, -$id, $str[2]);
04666             }
04667         }
04668 
04669         if (is_array($this->version_remapMMForVersionSwap_reg[$swapWith])) {
04670             foreach ($this->version_remapMMForVersionSwap_reg[$swapWith] as $field => $str) {
04671                 $str[0]->remapMM($str[1], $swapWith, $id, $str[2]);
04672             }
04673         }
04674 
04675         if (is_array($this->version_remapMMForVersionSwap_reg[$id])) {
04676             foreach ($this->version_remapMMForVersionSwap_reg[$id] as $field => $str) {
04677                 $str[0]->remapMM($str[1], -$id, $swapWith, $str[2]);
04678             }
04679         }
04680     }
04681 
04682 
04683     /*********************************************
04684      *
04685      * Cmd: Helper functions
04686      *
04687      ********************************************/
04688 
04689     /**
04690      * Processes the fields with references as registered during the copy process. This includes all FlexForm fields which had references.
04691      *
04692      * @return  void
04693      */
04694     function remapListedDBRecords() {
04695         global $TCA;
04696 
04697         if (count($this->registerDBList)) {
04698             foreach ($this->registerDBList as $table => $records) {
04699                 t3lib_div::loadTCA($table);
04700                 foreach ($records as $uid => $fields) {
04701                     $newData = array();
04702                     $theUidToUpdate = $this->copyMappingArray_merged[$table][$uid];
04703                     $theUidToUpdate_saveTo = t3lib_BEfunc::wsMapId($table, $theUidToUpdate);
04704 
04705                     foreach ($fields as $fieldName => $value) {
04706                         $conf = $TCA[$table]['columns'][$fieldName]['config'];
04707 
04708                         switch ($conf['type']) {
04709                             case 'group':
04710                             case 'select':
04711                                 $vArray = $this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate, $table);
04712                                 if (is_array($vArray)) {
04713                                     $newData[$fieldName] = implode(',', $vArray);
04714                                 }
04715                             break;
04716                             case 'flex':
04717                                 if ($value == 'FlexForm_reference') {
04718                                     $origRecordRow = $this->recordInfo($table, $theUidToUpdate, '*'); // This will fetch the new row for the element
04719 
04720                                     if (is_array($origRecordRow)) {
04721                                         t3lib_BEfunc::workspaceOL($table, $origRecordRow);
04722 
04723                                             // Get current data structure and value array:
04724                                         $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $origRecordRow, $table);
04725                                         $currentValueArray = t3lib_div::xml2array($origRecordRow[$fieldName]);
04726 
04727                                             // Do recursive processing of the XML data:
04728                                         $currentValueArray['data'] = $this->checkValue_flex_procInData(
04729                                             $currentValueArray['data'],
04730                                             array(), // Not used.
04731                                             array(), // Not used.
04732                                             $dataStructArray,
04733                                             array($table, $theUidToUpdate, $fieldName), // Parameters.
04734                                             'remapListedDBRecords_flexFormCallBack'
04735                                         );
04736 
04737                                             // The return value should be compiled back into XML, ready to insert directly in the field (as we call updateDB() directly later):
04738                                         if (is_array($currentValueArray['data'])) {
04739                                             $newData[$fieldName] =
04740                                                     $this->checkValue_flexArray2Xml($currentValueArray, TRUE);
04741                                         }
04742                                     }
04743                                 }
04744                             break;
04745                             case 'inline':
04746                                 $this->remapListedDBRecords_procInline($conf, $value, $uid, $table);
04747                             break;
04748                             default:
04749                                 debug('Field type should not appear here: ' . $conf['type']);
04750                             break;
04751                         }
04752                     }
04753 
04754                     if (count($newData)) { // If any fields were changed, those fields are updated!
04755                         $this->updateDB($table, $theUidToUpdate_saveTo, $newData);
04756                     }
04757                 }
04758             }
04759         }
04760     }
04761 
04762     /**
04763      * Callback function for traversing the FlexForm structure in relation to creating copied files of file relations inside of flex form structures.
04764      *
04765      * @param   array       Set of parameters in numeric array: table, uid, field
04766      * @param   array       TCA config for field (from Data Structure of course)
04767      * @param   string      Field value (from FlexForm XML)
04768      * @param   string      Not used
04769      * @param   string      Not used
04770      * @return  array       Array where the "value" key carries the value.
04771      * @see checkValue_flex_procInData_travDS(), remapListedDBRecords()
04772      */
04773     function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2) {
04774 
04775             // Extract parameters:
04776         list($table, $uid, $field) = $pParams;
04777 
04778             // If references are set for this field, set flag so they can be corrected later:
04779         if ($this->isReferenceField($dsConf) && strlen($dataValue)) {
04780             $vArray = $this->remapListedDBRecords_procDBRefs($dsConf, $dataValue, $uid, $table);
04781             if (is_array($vArray)) {
04782                 $dataValue = implode(',', $vArray);
04783             }
04784         }
04785 
04786             // Return
04787         return array('value' => $dataValue);
04788     }
04789 
04790     /**
04791      * Performs remapping of old UID values to NEW uid values for a DB reference field.
04792      *
04793      * @param   array       TCA field config
04794      * @param   string      Field value
04795      * @param   integer     UID of local record (for MM relations - might need to change if support for FlexForms should be done!)
04796      * @param   string      Table name
04797      * @return  array       Returns array of items ready to implode for field content.
04798      * @see remapListedDBRecords()
04799      */
04800     function remapListedDBRecords_procDBRefs($conf, $value, $MM_localUid, $table) {
04801 
04802             // Initialize variables
04803         $set = FALSE; // Will be set true if an upgrade should be done...
04804         $allowedTables = $conf['type'] == 'group' ? $conf['allowed'] : $conf['foreign_table'] . ',' . $conf['neg_foreign_table']; // Allowed tables for references.
04805         $prependName = $conf['type'] == 'group' ? $conf['prepend_tname'] : ''; // Table name to prepend the UID
04806         $dontRemapTables = t3lib_div::trimExplode(',', $conf['dontRemapTablesOnCopy'], 1); // Which tables that should possibly not be remapped
04807 
04808             // Convert value to list of references:
04809         $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
04810         $dbAnalysis->registerNonTableValues = ($conf['type'] == 'select' && $conf['allowNonIdValues']) ? 1 : 0;
04811         $dbAnalysis->start($value, $allowedTables, $conf['MM'], $MM_localUid, $table, $conf);
04812 
04813             // Traverse those references and map IDs:
04814         foreach ($dbAnalysis->itemArray as $k => $v) {
04815             $mapID = $this->copyMappingArray_merged[$v['table']][$v['id']];
04816             if ($mapID && !in_array($v['table'], $dontRemapTables)) {
04817                 $dbAnalysis->itemArray[$k]['id'] = $mapID;
04818                 $set = TRUE;
04819             }
04820         }
04821 
04822             // If a change has been done, set the new value(s)
04823         if ($set) {
04824             if ($conf['MM']) {
04825                 $dbAnalysis->writeMM($conf['MM'], $MM_localUid, $prependName);
04826             } else {
04827                 $vArray = $dbAnalysis->getValueArray($prependName);
04828                 if ($conf['type'] == 'select') {
04829                     $vArray = $dbAnalysis->convertPosNeg($vArray, $conf['foreign_table'], $conf['neg_foreign_table']);
04830                 }
04831                 return $vArray;
04832             }
04833         }
04834     }
04835 
04836     /**
04837      * Performs remapping of old UID values to NEW uid values for a inline field.
04838      *
04839      * @param   array       $conf: TCA field config
04840      * @param   string      $value: Field value
04841      * @param   integer     $uid: The uid of the ORIGINAL record
04842      * @param   string      $table: Table name
04843      * @return  void
04844      */
04845     function remapListedDBRecords_procInline($conf, $value, $uid, $table) {
04846         $theUidToUpdate = $this->copyMappingArray_merged[$table][$uid];
04847 
04848         if ($conf['foreign_table']) {
04849             $inlineType = $this->getInlineFieldType($conf);
04850 
04851             if ($inlineType == 'mm') {
04852                 $this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate, $table);
04853 
04854             } elseif ($inlineType !== FALSE) {
04855                 /** @var $dbAnalysis t3lib_loadDBGroup */
04856                 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
04857                 $dbAnalysis->start($value, $conf['foreign_table'], '', 0, $table, $conf);
04858 
04859                     // Update child records if using pointer fields ('foreign_field'):
04860                 if ($inlineType == 'field') {
04861                     $dbAnalysis->writeForeignField($conf, $uid, $theUidToUpdate);
04862                 }
04863 
04864                     // If the current field is set on a page record, update the pid of related child records:
04865                 if ($table == 'pages') {
04866                     $thePidToUpdate = $theUidToUpdate;
04867                     // If the current field has ancestors that have a field on a page record, update the pid of related child records:
04868                 } elseif (isset($this->registerDBPids[$table][$uid])) {
04869                     $thePidToUpdate = $this->registerDBPids[$table][$uid];
04870                     $thePidToUpdate = $this->copyMappingArray_merged['pages'][$thePidToUpdate];
04871                 }
04872 
04873                     // Update child records if change to pid is required (only if the current record is not on a workspace):
04874                 if ($thePidToUpdate) {
04875                     $updateValues = array('pid' => $thePidToUpdate);
04876                     foreach ($dbAnalysis->itemArray as $v) {
04877                         if ($v['id'] && $v['table'] && is_null(t3lib_BEfunc::getLiveVersionIdOfRecord($v['table'], $v['id']))) {
04878                             $GLOBALS['TYPO3_DB']->exec_UPDATEquery($v['table'], 'uid=' . intval($v['id']), $updateValues);
04879                         }
04880                     }
04881                 }
04882             }
04883         }
04884     }
04885 
04886     /**
04887      * Processes the $this->remapStack at the end of copying, inserting, etc. actions.
04888      * The remapStack takes care about the correct mapping of new and old uids in case of relational data.
04889      *
04890      * @return  void
04891      */
04892     function processRemapStack() {
04893             // Processes the remap stack:
04894         if (is_array($this->remapStack)) {
04895             foreach ($this->remapStack as $remapAction) {
04896                     // If no position index for the arguments was set, skip this remap action:
04897                 if (!is_array($remapAction['pos'])) {
04898                     continue;
04899                 }
04900 
04901                     // Load values from the argument array in remapAction:
04902                 $field = $remapAction['field'];
04903                 $id = $remapAction['args'][$remapAction['pos']['id']];
04904                 $rawId = $id;
04905                 $table = $remapAction['args'][$remapAction['pos']['table']];
04906                 $valueArray = $remapAction['args'][$remapAction['pos']['valueArray']];
04907                 $tcaFieldConf = $remapAction['args'][$remapAction['pos']['tcaFieldConf']];
04908 
04909                     // The record is new and has one or more new ids (in case of versioning/workspaces):
04910                 if (strpos($id, 'NEW') !== FALSE) {
04911                         // Replace NEW...-ID with real uid:
04912                     $id = $this->substNEWwithIDs[$id];
04913 
04914                         // If the new parent record is on a non-live workspace or versionized, it has another new id:
04915                     if (isset($this->autoVersionIdMap[$table][$id])) {
04916                         $id = $this->autoVersionIdMap[$table][$id];
04917                     }
04918                     $remapAction['args'][$remapAction['pos']['id']] = $id;
04919                 }
04920 
04921                     // Replace relations to NEW...-IDs in field value (uids of child records):
04922                 if (is_array($valueArray)) {
04923                     $foreign_table = $tcaFieldConf['foreign_table'];
04924                     foreach ($valueArray as $key => $value) {
04925                         if (strpos($value, 'NEW') !== FALSE) {
04926                             $value = $this->substNEWwithIDs[$value];
04927                                 // The record is new, but was also auto-versionized and has another new id:
04928                             if (isset($this->autoVersionIdMap[$foreign_table][$value])) {
04929                                 $value = $this->autoVersionIdMap[$foreign_table][$value];
04930                             }
04931                                 // Set a hint that this was a new child record:
04932                             $this->newRelatedIDs[$foreign_table][] = $value;
04933                             $valueArray[$key] = $value;
04934                         }
04935                     }
04936                     $remapAction['args'][$remapAction['pos']['valueArray']] = $valueArray;
04937                 }
04938 
04939                     // Process the arguments with the defined function:
04940                 $newValue = call_user_func_array(
04941                     array($this, $remapAction['func']),
04942                     $remapAction['args']
04943                 );
04944                     // If array is returned, check for maxitems condition, if string is returned this was already done:
04945                 if (is_array($newValue)) {
04946                     $newValue = implode(',', $this->checkValue_checkMax($tcaFieldConf, $newValue));
04947                 }
04948                     // Update in database (list of children (csv) or number of relations (foreign_field)):
04949                 $this->updateDB($table, $id, array($field => $newValue));
04950                     // Process waiting Hook: processDatamap_afterDatabaseOperations:
04951                 if (isset($this->remapStackRecords[$table][$rawId]['processDatamap_afterDatabaseOperations'])) {
04952                     $hookArgs = $this->remapStackRecords[$table][$rawId]['processDatamap_afterDatabaseOperations'];
04953                         // Update field with remapped data:
04954                     $hookArgs['fieldArray'][$field] = $newValue;
04955                         // Process waiting hook objects:
04956                     $hookObjectsArr = $hookArgs['hookObjectsArr'];
04957                     foreach ($hookObjectsArr as $hookObj) {
04958                         if (method_exists($hookObj, 'processDatamap_afterDatabaseOperations')) {
04959                             $hookObj->processDatamap_afterDatabaseOperations($hookArgs['status'], $table, $rawId, $hookArgs['fieldArray'], $this);
04960                         }
04961                     }
04962                 }
04963             }
04964         }
04965             // Processes the remap stack actions:
04966         if ($this->remapStackActions) {
04967             foreach ($this->remapStackActions as $action) {
04968                 if (isset($action['callback']) && isset($action['arguments'])) {
04969                     call_user_func_array(
04970                         $action['callback'],
04971                         $action['arguments']
04972                     );
04973                 }
04974             }
04975         }
04976             // Processes the reference index updates of the remap stack:
04977         foreach ($this->remapStackRefIndex as $table => $idArray) {
04978             foreach ($idArray as $id) {
04979                 $this->updateRefIndex($table, $id);
04980                 unset($this->remapStackRefIndex[$table][$id]);
04981             }
04982         }
04983             // Reset:
04984         $this->remapStack = array();
04985         $this->remapStackRecords = array();
04986         $this->remapStackActions = array();
04987         $this->remapStackRefIndex = array();
04988     }
04989 
04990     /**
04991      * Triggers a remap action for a specific record.
04992      *
04993      * Some records are post-processed by the processRemapStack() method (e.g. IRRE children).
04994      * This method determines wether an action/modification is executed directly to a record
04995      * or is postponed to happen after remapping data.
04996      *
04997      * @param string $table Name of the table
04998      * @param string $id Id of the record (can also be a "NEW..." string)
04999      * @param array $callback The method to be called
05000      * @param array $arguments The arguments to be submitted to the callback method
05001      * @param boolean $forceRemapStackActions Whether to force to use the stack
05002      * @return void
05003      *
05004      * @see processRemapStack
05005      */
05006     protected function triggerRemapAction($table, $id, array $callback, array $arguments, $forceRemapStackActions = FALSE) {
05007             // Check whether the affected record is marked to be remapped:
05008         if (!$forceRemapStackActions && !isset($this->remapStackRecords[$table][$id]) && !isset($this->remapStackChildIds[$id])) {
05009             call_user_func_array($callback, $arguments);
05010         } else {
05011             $this->addRemapAction($table, $id, $callback, $arguments);
05012         }
05013     }
05014 
05015     /**
05016      * Adds an instruction to the remap action stack (used with IRRE).
05017      *
05018      * @param string $table The affected table
05019      * @param integer $id The affected ID
05020      * @param array $callback The callback information (object and method)
05021      * @param array $arguments The arguments to be used with the callback
05022      * @return void
05023      */
05024     public function addRemapAction($table, $id, array $callback, array $arguments) {
05025         $this->remapStackActions[] = array(
05026             'affects' => array(
05027                 'table' => $table,
05028                 'id' => $id,
05029             ),
05030             'callback' => $callback,
05031             'arguments' => $arguments,
05032         );
05033     }
05034 
05035     /**
05036      * Adds a table-id-pair to the reference index remapping stack.
05037      *
05038      * @param string $table
05039      * @param integer $id
05040      * @return void
05041      */
05042     public function addRemapStackRefIndex($table, $id) {
05043         $this->remapStackRefIndex[$table][$id] = $id;
05044     }
05045 
05046     /**
05047      * If a parent record was versionized on a workspace in $this->process_datamap,
05048      * it might be possible, that child records (e.g. on using IRRE) were affected.
05049      * This function finds these relations and updates their uids in the $incomingFieldArray.
05050      * The $incomingFieldArray is updated by reference!
05051      *
05052      * @param   string      $table: Table name of the parent record
05053      * @param   integer     $id: Uid of the parent record
05054      * @param   array       $incomingFieldArray: Reference to the incominfFieldArray of process_datamap
05055      * @param   array       $registerDBList: Reference to the $registerDBList array that was created/updated by versionizing calls to TCEmain in process_datamap.
05056      * @return   void
05057      */
05058     function getVersionizedIncomingFieldArray($table, $id, &$incomingFieldArray, &$registerDBList) {
05059         if (is_array($registerDBList[$table][$id])) {
05060             foreach ($incomingFieldArray as $field => $value) {
05061                 $fieldConf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
05062                 if ($registerDBList[$table][$id][$field] && $foreignTable = $fieldConf['foreign_table']) {
05063                     $newValueArray = array();
05064                     $origValueArray = explode(',', $value);
05065                         // Update the uids of the copied records, but also take care about new records:
05066                     foreach ($origValueArray as $childId) {
05067                         $newValueArray[] = $this->autoVersionIdMap[$foreignTable][$childId]
05068                                 ? $this->autoVersionIdMap[$foreignTable][$childId]
05069                                 : $childId;
05070                     }
05071                         // Set the changed value to the $incomingFieldArray
05072                     $incomingFieldArray[$field] = implode(',', $newValueArray);
05073                 }
05074             }
05075                 // Clean up the $registerDBList array:
05076             unset($registerDBList[$table][$id]);
05077             if (!count($registerDBList[$table])) {
05078                 unset($registerDBList[$table]);
05079             }
05080         }
05081     }
05082 
05083 
05084     /*****************************
05085      *
05086      * Access control / Checking functions
05087      *
05088      *****************************/
05089 
05090     /**
05091      * Checking group modify_table access list
05092      *
05093      * @param   string      Table name
05094      * @return  boolean     Returns true if the user has general access to modify the $table
05095      */
05096     function checkModifyAccessList($table) {
05097         $res = ($this->admin || (!$this->tableAdminOnly($table) && t3lib_div::inList($this->BE_USER->groupData['tables_modify'], $table)));
05098 
05099             // Hook 'checkModifyAccessList': Post-processing of the state of access
05100         foreach ($this->getCheckModifyAccessListHookObjects() as $hookObject) {
05101             /* @var $hookObject t3lib_TCEmain_checkModifyAccessListHook */
05102             $hookObject->checkModifyAccessList($res, $table, $this);
05103         }
05104 
05105         return $res;
05106     }
05107 
05108     /**
05109      * Checking if a record with uid $id from $table is in the BE_USERS webmounts which is required for editing etc.
05110      *
05111      * @param   string      Table name
05112      * @param   integer     UID of record
05113      * @return  boolean     Returns true if OK. Cached results.
05114      */
05115     function isRecordInWebMount($table, $id) {
05116         if (!isset($this->isRecordInWebMount_Cache[$table . ':' . $id])) {
05117             $recP = $this->getRecordProperties($table, $id);
05118             $this->isRecordInWebMount_Cache[$table . ':' . $id] = $this->isInWebMount($recP['event_pid']);
05119         }
05120         return $this->isRecordInWebMount_Cache[$table . ':' . $id];
05121     }
05122 
05123     /**
05124      * Checks if the input page ID is in the BE_USER webmounts
05125      *
05126      * @param   integer     Page ID to check
05127      * @return  boolean     True if OK. Cached results.
05128      */
05129     function isInWebMount($pid) {
05130         if (!isset($this->isInWebMount_Cache[$pid])) {
05131             $this->isInWebMount_Cache[$pid] = $this->BE_USER->isInWebMount($pid);
05132         }
05133         return $this->isInWebMount_Cache[$pid];
05134     }
05135 
05136     /**
05137      * Checks if user may update a record with uid=$id from $table
05138      *
05139      * @param   string      Record table
05140      * @param   integer     Record UID
05141      * @param   array       Record data
05142      * @param   array       Hook objects
05143      * @return  boolean     Returns true if the user may update the record given by $table and $id
05144      */
05145     function checkRecordUpdateAccess($table, $id, $data = FALSE, &$hookObjectsArr = FALSE) {
05146         global $TCA;
05147         $res = NULL;
05148         if (is_array($hookObjectsArr)) {
05149             foreach ($hookObjectsArr as $hookObj) {
05150                 if (method_exists($hookObj, 'checkRecordUpdateAccess')) {
05151                     $res = $hookObj->checkRecordUpdateAccess($table, $id, $data, $res, $this);
05152                 }
05153             }
05154         }
05155         if ($res === 1 || $res === 0) {
05156             return $res;
05157         } else {
05158             $res = 0;
05159         }
05160 
05161         if ($TCA[$table] && intval($id) > 0) {
05162             if (isset($this->recUpdateAccessCache[$table][$id])) { // If information is cached, return it
05163                 return $this->recUpdateAccessCache[$table][$id];
05164                 // Check if record exists and 1) if 'pages' the page may be edited, 2) if page-content the page allows for editing
05165             } elseif ($this->doesRecordExist($table, $id, 'edit')) {
05166                 $res = 1;
05167             }
05168             $this->recUpdateAccessCache[$table][$id] = $res; // Cache the result
05169         }
05170         return $res;
05171     }
05172 
05173     /**
05174      * Checks if user may insert a record from $insertTable on $pid
05175      * Does not check for workspace, use BE_USER->workspaceAllowLiveRecordsInPID for this in addition to this function call.
05176      *
05177      * @param   string      Tablename to check
05178      * @param   integer     Integer PID
05179      * @param   integer     For logging: Action number.
05180      * @return  boolean     Returns true if the user may insert a record from table $insertTable on page $pid
05181      */
05182     function checkRecordInsertAccess($insertTable, $pid, $action = 1) {
05183         global $TCA;
05184 
05185         $res = 0;
05186         $pid = intval($pid);
05187         if ($pid >= 0) {
05188             if (isset($this->recInsertAccessCache[$insertTable][$pid])) { // If information is cached, return it
05189                 return $this->recInsertAccessCache[$insertTable][$pid];
05190             } else {
05191                     // If either admin and root-level or if page record exists and 1) if 'pages' you may create new ones 2) if page-content, new content items may be inserted on the $pid page
05192                 if ((!$pid && $this->admin) || $this->doesRecordExist('pages', $pid, ($insertTable == 'pages' ? $this->pMap['new'] : $this->pMap['editcontent']))) { // Check permissions
05193                     if ($this->isTableAllowedForThisPage($pid, $insertTable)) {
05194                         $res = 1;
05195                         $this->recInsertAccessCache[$insertTable][$pid] = $res; // Cache the result
05196                     } else {
05197                         $propArr = $this->getRecordProperties('pages', $pid);
05198                         $this->log($insertTable, $pid, $action, 0, 1, "Attempt to insert record on page '%s' (%s) where this table, %s, is not allowed", 11, array($propArr['header'], $pid, $insertTable), $propArr['event_pid']);
05199                     }
05200                 } else {
05201                     $propArr = $this->getRecordProperties('pages', $pid);
05202                     $this->log($insertTable, $pid, $action, 0, 1, "Attempt to insert a record on page '%s' (%s) from table '%s' without permissions. Or non-existing page.", 12, array($propArr['header'], $pid, $insertTable), $propArr['event_pid']);
05203                 }
05204             }
05205         }
05206         return $res;
05207     }
05208 
05209     /**
05210      * 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.
05211      *
05212      * @param   integer     Page id for which to check, including 0 (zero) if checking for page tree root.
05213      * @param   string      Table name to check
05214      * @return  boolean     True if OK
05215      */
05216     function isTableAllowedForThisPage($page_uid, $checkTable) {
05217         global $TCA, $PAGES_TYPES;
05218         $page_uid = intval($page_uid);
05219 
05220             // Check if rootLevel flag is set and we're trying to insert on rootLevel - and reversed - and that the table is not "pages" which are allowed anywhere.
05221         if (($TCA[$checkTable]['ctrl']['rootLevel'] xor !$page_uid) && $TCA[$checkTable]['ctrl']['rootLevel'] != -1 && $checkTable != 'pages') {
05222             return FALSE;
05223         }
05224 
05225             // Check root-level
05226         if (!$page_uid) {
05227             if ($this->admin) {
05228                 return TRUE;
05229             }
05230         } else {
05231                 // Check non-root-level
05232             $doktype = $this->pageInfo($page_uid, 'doktype');
05233             $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables'];
05234             $allowedArray = t3lib_div::trimExplode(',', $allowedTableList, 1);
05235             if (strstr($allowedTableList, '*') || in_array($checkTable, $allowedArray)) { // If all tables or the table is listed as a allowed type, return true
05236                 return TRUE;
05237             }
05238         }
05239     }
05240 
05241     /**
05242      * Checks if record can be selected based on given permission criteria
05243      *
05244      * @param   string      Record table name
05245      * @param   integer     Record UID
05246      * @param   mixed       Permission restrictions to observe: Either an integer that will be bitwise AND'ed or a string, which points to a key in the ->pMap array
05247      * @return  boolean     Returns true if the record given by $table, $id and $perms can be selected
05248      */
05249     function doesRecordExist($table, $id, $perms) {
05250         global $TCA;
05251 
05252         if ($this->bypassAccessCheckForRecords) {
05253             return is_array(t3lib_BEfunc::getRecordRaw($table, 'uid=' . intval($id), 'uid'));
05254         }
05255 
05256         $res = 0;
05257         $id = intval($id);
05258 
05259             // Processing the incoming $perms (from possible string to integer that can be AND'ed)
05260         if (!t3lib_div::testInt($perms)) {
05261             if ($table != 'pages') {
05262                 switch ($perms) {
05263                     case 'edit':
05264                     case 'delete':
05265                     case 'new':
05266                         $perms = 'editcontent'; // This holds it all in case the record is not page!!
05267                     break;
05268                 }
05269             }
05270             $perms = intval($this->pMap[$perms]);
05271         } else {
05272             $perms = intval($perms);
05273         }
05274 
05275         if (!$perms) {
05276             throw new RuntimeException(
05277                 'Internal ERROR: no permissions to check for non-admin user',
05278                 1270853920
05279             );
05280         }
05281 
05282             // For all tables: Check if record exists:
05283         if (is_array($TCA[$table]) && $id > 0 && ($this->isRecordInWebMount($table, $id) || $this->admin)) {
05284             if ($table != 'pages') {
05285 
05286                     // Find record without checking page:
05287                 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid', $table, 'uid=' . intval($id) . $this->deleteClause($table)); // THIS SHOULD CHECK FOR editlock I think!
05288                 $output = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres);
05289                 t3lib_BEfunc::fixVersioningPid($table, $output, TRUE);
05290 
05291                     // If record found, check page as well:
05292                 if (is_array($output)) {
05293 
05294                         // Looking up the page for record:
05295                     $mres = $this->doesRecordExist_pageLookUp($output['pid'], $perms);
05296                     $pageRec = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres);
05297                         // Return true if either a page was found OR if the PID is zero AND the user is ADMIN (in which case the record is at root-level):
05298                     if (is_array($pageRec) || (!$output['pid'] && $this->admin)) {
05299                         return TRUE;
05300                     }
05301                 }
05302                 return FALSE;
05303             } else {
05304                 $mres = $this->doesRecordExist_pageLookUp($id, $perms);
05305                 return $GLOBALS['TYPO3_DB']->sql_num_rows($mres);
05306             }
05307         }
05308     }
05309 
05310     /**
05311      * Looks up a page based on permissions.
05312      *
05313      * @param   integer     Page id
05314      * @param   integer     Permission integer
05315      * @return  pointer     MySQL result pointer (from exec_SELECTquery())
05316      * @access private
05317      * @see doesRecordExist()
05318      */
05319     function doesRecordExist_pageLookUp($id, $perms) {
05320         global $TCA;
05321 
05322         return $GLOBALS['TYPO3_DB']->exec_SELECTquery(
05323             'uid',
05324             'pages',
05325             'uid=' . intval($id) .
05326             $this->deleteClause('pages') .
05327             ($perms && !$this->admin ? ' AND ' . $this->BE_USER->getPagePermsClause($perms) : '') .
05328             (!$this->admin && $TCA['pages']['ctrl']['editlock'] && ($perms & (2 + 4 + 16)) ? ' AND ' . $TCA['pages']['ctrl']['editlock'] . '=0' : '') // admin users don't need check
05329         );
05330     }
05331 
05332     /**
05333      * Checks if a whole branch of pages exists
05334      *
05335      * Tests the branch under $pid (like doesRecordExist). It doesn't test the page with $pid as uid. Use doesRecordExist() for this purpose
05336      * Returns an ID-list or "" if OK. Else -1 which means that somewhere there was no permission (eg. to delete).
05337      * if $recurse is set, then the function will follow subpages. This MUST be set, if we need the idlist for deleting pages or else we get an incomplete list
05338      *
05339      * @param   string      List of page uids, this is added to and outputted in the end
05340      * @param   integer     Page ID to select subpages from.
05341      * @param   integer     Perms integer to check each page record for.
05342      * @param   boolean     Recursion flag: If set, it will go out through the branch.
05343      * @return  string      List of integers in branch
05344      */
05345     function doesBranchExist($inList, $pid, $perms, $recurse) {
05346         global $TCA;
05347         $pid = intval($pid);
05348         $perms = intval($perms);
05349 
05350         if ($pid >= 0) {
05351             $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
05352                 'uid, perms_userid, perms_groupid, perms_user, perms_group, perms_everybody',
05353                 'pages',
05354                 'pid=' . intval($pid) . $this->deleteClause('pages'),
05355                 '',
05356                 'sorting'
05357             );
05358             while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
05359                 if ($this->admin || $this->BE_USER->doesUserHaveAccess($row, $perms)) { // IF admin, then it's OK
05360                     $inList .= $row['uid'] . ',';
05361                     if ($recurse) { // Follow the subpages recursively...
05362                         $inList = $this->doesBranchExist($inList, $row['uid'], $perms, $recurse);
05363                         if ($inList == -1) {
05364                             return -1;
05365                         } // No permissions somewhere in the branch
05366                     }
05367                 } else {
05368                     return -1; // No permissions
05369                 }
05370             }
05371             $GLOBALS['TYPO3_DB']->sql_free_result($mres);
05372         }
05373         return $inList;
05374     }
05375 
05376     /**
05377      * Checks if the $table is readOnly
05378      *
05379      * @param   string      Table name
05380      * @return  boolean     True, if readonly
05381      */
05382     function tableReadOnly($table) {
05383             // returns true if table is readonly
05384         global $TCA;
05385         return ($TCA[$table]['ctrl']['readOnly'] ? TRUE : FALSE);
05386     }
05387 
05388     /**
05389      * Checks if the $table is only editable by admin-users
05390      *
05391      * @param   string      Table name
05392      * @return  boolean     True, if readonly
05393      */
05394     function tableAdminOnly($table) {
05395             // returns true if table is admin-only
05396         global $TCA;
05397         return ($TCA[$table]['ctrl']['adminOnly'] ? TRUE : FALSE);
05398     }
05399 
05400     /**
05401      * Checks if piage $id is a uid in the rootline from page id, $dest
05402      * Used when moving a page
05403      *
05404      * @param   integer     Destination Page ID to test
05405      * @param   integer     Page ID to test for presence inside Destination
05406      * @return  boolean     Returns false if ID is inside destination (including equal to)
05407      */
05408     function destNotInsideSelf($dest, $id) {
05409         $loopCheck = 100;
05410         $dest = intval($dest);
05411         $id = intval($id);
05412 
05413         if ($dest == $id) {
05414             return FALSE;
05415         }
05416 
05417         while ($dest != 0 && $loopCheck > 0) {
05418             $loopCheck--;
05419             $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid, uid, t3ver_oid,t3ver_wsid', 'pages', 'uid=' . intval($dest) . $this->deleteClause('pages'));
05420             if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
05421                 t3lib_BEfunc::fixVersioningPid('pages', $row);
05422                 if ($row['pid'] == $id) {
05423                     return FALSE;
05424                 } else {
05425                     $dest = $row['pid'];
05426                 }
05427             } else {
05428                 return FALSE;
05429             }
05430         }
05431         return TRUE;
05432     }
05433 
05434     /**
05435      * Generate an array of fields to be excluded from editing for the user. Based on "exclude"-field in TCA and a look up in non_exclude_fields
05436      * Will also generate this list for admin-users so they must be check for before calling the function
05437      *
05438      * @return  array       Array of [table]-[field] pairs to exclude from editing.
05439      */
05440     function getExcludeListArray() {
05441         $list = array();
05442         foreach (array_keys($GLOBALS['TCA']) as $table) {
05443             t3lib_div::loadTCA($table);
05444             foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $config) {
05445                 if ($config['exclude'] && !t3lib_div::inList($this->BE_USER->groupData['non_exclude_fields'], $table . ':' . $field)) {
05446                     $list[] = $table . '-' . $field;
05447                 }
05448             }
05449         }
05450         return $list;
05451     }
05452 
05453     /**
05454      * Checks if there are records on a page from tables that are not allowed
05455      *
05456      * @param   integer     Page ID
05457      * @param   integer     Page doktype
05458      * @return  array       Returns a list of the tables that are 'present' on the page but not allowed with the page_uid/doktype
05459      */
05460     function doesPageHaveUnallowedTables($page_uid, $doktype) {
05461         global $PAGES_TYPES;
05462 
05463         $page_uid = intval($page_uid);
05464         if (!$page_uid) {
05465             return FALSE; // Not a number. Probably a new page
05466         }
05467 
05468         $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables'];
05469         $allowedArray = t3lib_div::trimExplode(',', $allowedTableList, 1);
05470         if (strstr($allowedTableList, '*')) { // If all tables is OK the return true
05471             return FALSE; // OK...
05472         }
05473 
05474         $tableList = array();
05475         foreach (array_keys($GLOBALS['TCA']) as $table) {
05476             if (!in_array($table, $allowedArray)) { // If the table is not in the allowed list, check if there are records...
05477                 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', $table, 'pid=' . intval($page_uid));
05478                 if ($count) {
05479                     $tableList[] = $table;
05480                 }
05481             }
05482         }
05483         return implode(',', $tableList);
05484     }
05485 
05486 
05487     /*****************************
05488      *
05489      * Information lookup
05490      *
05491      *****************************/
05492 
05493     /**
05494      * Returns the value of the $field from page $id
05495      * NOTICE; the function caches the result for faster delivery next time. You can use this function repeatedly without performanceloss since it doesn't look up the same record twice!
05496      *
05497      * @param   integer     Page uid
05498      * @param   string      Field name for which to return value
05499      * @return  string      Value of the field. Result is cached in $this->pageCache[$id][$field] and returned from there next time!
05500      */
05501     function pageInfo($id, $field) {
05502         if (!isset($this->pageCache[$id])) {
05503             $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'pages', 'uid=' . intval($id));
05504             if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) {
05505                 $this->pageCache[$id] = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
05506             }
05507             $GLOBALS['TYPO3_DB']->sql_free_result($res);
05508         }
05509         return $this->pageCache[$id][$field];
05510     }
05511 
05512     /**
05513      * Returns the row of a record given by $table and $id and $fieldList (list of fields, may be '*')
05514      * NOTICE: No check for deleted or access!
05515      *
05516      * @param   string      Table name
05517      * @param   integer     UID of the record from $table
05518      * @param   string      Field list for the SELECT query, eg. "*" or "uid,pid,..."
05519      * @return  mixed       Returns the selected record on success, otherwise false.
05520      */
05521     function recordInfo($table, $id, $fieldList) {
05522         global $TCA;
05523         if (is_array($TCA[$table])) {
05524             $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fieldList, $table, 'uid=' . intval($id));
05525             if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) {
05526                 $result = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
05527                 $GLOBALS['TYPO3_DB']->sql_free_result($res);
05528                 return $result;
05529             }
05530         }
05531     }
05532 
05533     /**
05534      * Returns an array with record properties, like header and pid
05535      * No check for deleted or access is done!
05536      * For versionized records, pid is resolved to its live versions pid.
05537      * Used for loggin
05538      *
05539      * @param   string      Table name
05540      * @param   integer     Uid of record
05541      * @param   boolean     If set, no workspace overlay is performed
05542      * @return  array       Properties of record
05543      */
05544     function getRecordProperties($table, $id, $noWSOL = FALSE) {
05545         $row = ($table == 'pages' && !$id) ? array('title' => '[root-level]', 'uid' => 0, 'pid' => 0) : $this->recordInfo($table, $id, '*');
05546         if (!$noWSOL) {
05547             t3lib_BEfunc::workspaceOL($table, $row);
05548         }
05549 
05550         return $this->getRecordPropertiesFromRow($table, $row);
05551     }
05552 
05553     /**
05554      * Returns an array with record properties, like header and pid, based on the row
05555      *
05556      * @param   string      Table name
05557      * @param   array       Input row
05558      * @return  array       Output array
05559      */
05560     function getRecordPropertiesFromRow($table, $row) {
05561         global $TCA;
05562         if ($TCA[$table]) {
05563             t3lib_BEfunc::fixVersioningPid($table, $row);
05564 
05565             $out = array(
05566                 'header' => $row[$TCA[$table]['ctrl']['label']],
05567                 'pid' => $row['pid'],
05568                 'event_pid' => $this->eventPid($table, isset($row['_ORIG_pid']) ? $row['t3ver_oid'] : $row['uid'], $row['pid']),
05569                 't3ver_state' => $TCA[$table]['ctrl']['versioningWS'] ? $row['t3ver_state'] : '',
05570                 '_ORIG_pid' => $row['_ORIG_pid']
05571             );
05572             return $out;
05573         }
05574     }
05575 
05576     function eventPid($table, $uid, $pid) {
05577         return $table == 'pages' ? $uid : $pid;
05578     }
05579 
05580 
05581     /*********************************************
05582      *
05583      * Storing data to Database Layer
05584      *
05585      ********************************************/
05586 
05587     /**
05588      * Update database record
05589      * Does not check permissions but expects them to be verified on beforehand
05590      *
05591      * @param   string      Record table name
05592      * @param   integer     Record uid
05593      * @param   array       Array of field=>value pairs to insert. FIELDS MUST MATCH the database FIELDS. No check is done.
05594      * @return  void
05595      */
05596     function updateDB($table, $id, $fieldArray) {
05597         global $TCA;
05598 
05599         if (is_array($fieldArray) && is_array($TCA[$table]) && intval($id)) {
05600             unset($fieldArray['uid']); // Do NOT update the UID field, ever!
05601 
05602             if (count($fieldArray)) {
05603 
05604                 $fieldArray = $this->insertUpdateDB_preprocessBasedOnFieldType($table, $fieldArray);
05605 
05606                     // Execute the UPDATE query:
05607                 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($id), $fieldArray);
05608 
05609                     // If succees, do...:
05610                 if (!$GLOBALS['TYPO3_DB']->sql_error()) {
05611 
05612                     if ($this->checkStoredRecords) {
05613                         $newRow = $this->checkStoredRecord($table, $id, $fieldArray, 2);
05614                     }
05615 
05616                         // Update reference index:
05617                     $this->updateRefIndex($table, $id);
05618 
05619                         // Set log entry:
05620                     $propArr = $this->getRecordPropertiesFromRow($table, $newRow);
05621                     $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']);
05622 
05623                         // Set History data:
05624                     $this->setHistory($table, $id, $theLogId);
05625 
05626                         // Clear cache for relevant pages:
05627                     $this->clear_cache($table, $id);
05628 
05629                         // Unset the pageCache for the id if table was page.
05630                     if ($table == 'pages') {
05631                         unset($this->pageCache[$id]);
05632                     }
05633                 } else {
05634                     $this->log($table, $id, 2, 0, 2, "SQL error: '%s' (%s)", 12, array($GLOBALS['TYPO3_DB']->sql_error(), $table . ':' . $id));
05635                 }
05636             }
05637         }
05638     }
05639 
05640     /**
05641      * Insert into database
05642      * Does not check permissions but expects them to be verified on beforehand
05643      *
05644      * @param   string      Record table name
05645      * @param   string      "NEW...." uid string
05646      * @param   array       Array of field=>value pairs to insert. FIELDS MUST MATCH the database FIELDS. No check is done. "pid" must point to the destination of the record!
05647      * @param   boolean     Set to true if new version is created.
05648      * @param   integer     Suggested UID value for the inserted record. See the array $this->suggestedInsertUids; Admin-only feature
05649      * @param   boolean     If true, the ->substNEWwithIDs array is not updated. Only useful in very rare circumstances!
05650      * @return  integer     Returns ID on success.
05651      */
05652     function insertDB($table, $id, $fieldArray, $newVersion = FALSE, $suggestedUid = 0, $dontSetNewIdIndex = FALSE) {
05653         global $TCA;
05654 
05655         if (is_array($fieldArray) && is_array($TCA[$table]) && isset($fieldArray['pid'])) {
05656             unset($fieldArray['uid']); // Do NOT insert the UID field, ever!
05657 
05658             if (count($fieldArray)) {
05659 
05660                     // Check for "suggestedUid".
05661                     // This feature is used by the import functionality to force a new record to have a certain UID value.
05662                     // This is only recommended for use when the destination server is a passive mirrow of another server.
05663                     // As a security measure this feature is available only for Admin Users (for now)
05664                 $suggestedUid = intval($suggestedUid);
05665                 if ($this->BE_USER->isAdmin() && $suggestedUid && $this->suggestedInsertUids[$table . ':' . $suggestedUid]) {
05666                         // When the value of ->suggestedInsertUids[...] is "DELETE" it will try to remove the previous record
05667                     if ($this->suggestedInsertUids[$table . ':' . $suggestedUid] === 'DELETE') {
05668                             // DELETE:
05669                         $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid=' . intval($suggestedUid));
05670                     }
05671                     $fieldArray['uid'] = $suggestedUid;
05672                 }
05673 
05674                 $fieldArray = $this->insertUpdateDB_preprocessBasedOnFieldType($table, $fieldArray);
05675 
05676                     // Execute the INSERT query:
05677                 $GLOBALS['TYPO3_DB']->exec_INSERTquery($table, $fieldArray);
05678 
05679                     // If succees, do...:
05680                 if (!$GLOBALS['TYPO3_DB']->sql_error()) {
05681 
05682                         // Set mapping for NEW... -> real uid:
05683                     $NEW_id = $id; // the NEW_id now holds the 'NEW....' -id
05684                     $id = $GLOBALS['TYPO3_DB']->sql_insert_id();
05685                     if (!$dontSetNewIdIndex) {
05686                         $this->substNEWwithIDs[$NEW_id] = $id;
05687                         $this->substNEWwithIDs_table[$NEW_id] = $table;
05688                     }
05689 
05690                         // Checking the record is properly saved and writing to log
05691                     if ($this->checkStoredRecords) {
05692                         $newRow = $this->checkStoredRecord($table, $id, $fieldArray, 1);
05693                     }
05694 
05695                         // Update reference index:
05696                     $this->updateRefIndex($table, $id);
05697 
05698                     if ($newVersion) {
05699                         $propArr = $this->getRecordPropertiesFromRow($table, $newRow);
05700                         $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);
05701                     } else {
05702                         $propArr = $this->getRecordPropertiesFromRow($table, $newRow);
05703                         $page_propArr = $this->getRecordProperties('pages', $propArr['pid']);
05704                         $this->log($table, $id, 1, 0, 0, "Record '%s' (%s) was inserted on page '%s' (%s)", 10, array($propArr['header'], $table . ':' . $id, $page_propArr['header'], $newRow['pid']), $newRow['pid'], $NEW_id);
05705 
05706                             // Clear cache for relavant pages:
05707                         $this->clear_cache($table, $id);
05708                     }
05709 
05710                     return $id;
05711                 } else {
05712                     $this->log($table, $id, 1, 0, 2, "SQL error: '%s' (%s)", 12, array($GLOBALS['TYPO3_DB']->sql_error(), $table . ':' . $id));
05713                 }
05714             }
05715         }
05716     }
05717 
05718     /**
05719      * Checking stored record to see if the written values are properly updated.
05720      *
05721      * @param   string      Record table name
05722      * @param   integer     Record uid
05723      * @param   array       Array of field=>value pairs to insert/update
05724      * @param   string      Action, for logging only.
05725      * @return  array       Selected row
05726      * @see insertDB(), updateDB()
05727      */
05728     function checkStoredRecord($table, $id, $fieldArray, $action) {
05729         global $TCA;
05730 
05731         $id = intval($id);
05732         if (is_array($TCA[$table]) && $id) {
05733             $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid=' . intval($id));
05734             if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
05735                     // Traverse array of values that was inserted into the database and compare with the actually stored value:
05736                 $errorString = array();
05737                 foreach ($fieldArray as $key => $value) {
05738                     if ($this->checkStoredRecords_loose && !$value && !$row[$key]) {
05739                         // Nothing...
05740                     } elseif (strcmp($value, $row[$key])) {
05741                         $errorString[] = $key;
05742                     }
05743                 }
05744 
05745                     // Set log message if there were fields with unmatching values:
05746                 if (count($errorString)) {
05747                     $this->log($table, $id, $action, 0, 102, 'These fields are not properly updated in database: (' . implode(',', $errorString) . ') Probably value mismatch with fieldtype.');
05748                 }
05749 
05750                     // Return selected rows:
05751                 return $row;
05752             }
05753             $GLOBALS['TYPO3_DB']->sql_free_result($res);
05754         }
05755     }
05756 
05757     /**
05758      * Setting sys_history record, based on content previously set in $this->historyRecords[$table.':'.$id] (by compareFieldArrayWithCurrentAndUnset())
05759      *
05760      * @param   string      Table name
05761      * @param   integer     Record ID
05762      * @param   integer     Log entry ID, important for linking between log and history views
05763      * @return  void
05764      */
05765     function setHistory($table, $id, $logId) {
05766         if (isset($this->historyRecords[$table . ':' . $id])) {
05767 
05768                 // Initialize settings:
05769             list($tscPID) = t3lib_BEfunc::getTSCpid($table, $id, '');
05770             $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
05771 
05772             $tE = $this->getTableEntries($table, $TSConfig);
05773             $maxAgeSeconds = 60 * 60 * 24 * (strcmp($tE['history.']['maxAgeDays'], '') ? t3lib_div::intInRange($tE['history.']['maxAgeDays'], 0, 365) : 30); // one month
05774 
05775                 // Garbage collect old entries:
05776             $this->clearHistory($maxAgeSeconds, $table);
05777 
05778                 // Set history data:
05779             $fields_values = array();
05780             $fields_values['history_data'] = serialize($this->historyRecords[$table . ':' . $id]);
05781             $fields_values['fieldlist'] = implode(',', array_keys($this->historyRecords[$table . ':' . $id]['newRecord']));
05782             $fields_values['tstamp'] = $GLOBALS['EXEC_TIME'];
05783             $fields_values['tablename'] = $table;
05784             $fields_values['recuid'] = $id;
05785             $fields_values['sys_log_uid'] = $logId;
05786 
05787             $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_history', $fields_values);
05788         }
05789     }
05790 
05791     /**
05792      * Clearing sys_history table from older entries that are expired.
05793      *
05794      * @param   integer     $maxAgeSeconds (int+) however will set a max age in seconds so that any entry older than current time minus the age removed no matter what. If zero, this is not effective.
05795      * @param   string      table where the history should be cleared
05796      * @return  void
05797      */
05798     function clearHistory($maxAgeSeconds = 604800, $table) {
05799         $tstampLimit = $maxAgeSeconds ? $GLOBALS['EXEC_TIME'] - $maxAgeSeconds : 0;
05800 
05801         $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_history', 'tstamp<' . intval($tstampLimit) . ' AND tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history'));
05802     }
05803 
05804     /**
05805      * Update Reference Index (sys_refindex) for a record
05806      * Should be called any almost any update to a record which could affect references inside the record.
05807      *
05808      * @param   string      Table name
05809      * @param   integer     Record UID
05810      * @return  void
05811      */
05812     function updateRefIndex($table, $id) {
05813         $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
05814         /* @var $refIndexObj t3lib_refindex */
05815         $result = $refIndexObj->updateRefIndexTable($table, $id);
05816     }
05817 
05818 
05819     /*********************************************
05820      *
05821      * Misc functions
05822      *
05823      ********************************************/
05824 
05825     /**
05826      * Returning sorting number for tables with a "sortby" column
05827      * Using when new records are created and existing records are moved around.
05828      *
05829      * @param   string      Table name
05830      * @param   integer     Uid of record to find sorting number for. May be zero in case of new.
05831      * @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)
05832      * @return  mixed       Returns integer if PID is >=0, otherwise an array with PID and sorting number. Possibly false in case of error.
05833      */
05834     function getSortNumber($table, $uid, $pid) {
05835         global $TCA;
05836         if ($TCA[$table] && $TCA[$table]['ctrl']['sortby']) {
05837             $sortRow = $TCA[$table]['ctrl']['sortby'];
05838             if ($pid >= 0) { // Sorting number is in the top
05839                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($sortRow . ',pid,uid', $table, 'pid=' . intval($pid) . $this->deleteClause($table), '', $sortRow . ' ASC', '1'); // Fetches the first record under this pid
05840                 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { // There was an element
05841                     if ($row['uid'] == $uid) { // The top record was the record it self, so we return its current sortnumber
05842                         return $row[$sortRow];
05843                     }
05844                     if ($row[$sortRow] < 1) { // If the pages sortingnumber < 1 we must resort the records under this pid
05845                         $this->resorting($table, $pid, $sortRow, 0);
05846                         return $this->sortIntervals; // First sorting number after resorting
05847                     } else {
05848                         return floor($row[$sortRow] / 2); // Sorting number between current top element and zero
05849                     }
05850                 } else { // No pages, so we choose the default value as sorting-number
05851                     return $this->sortIntervals; // First sorting number if no elements.
05852                 }
05853             } else { // Sorting number is inside the list
05854                 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($sortRow . ',pid,uid', $table, 'uid=' . abs($pid) . $this->deleteClause($table)); // Fetches the record which is supposed to be the prev record
05855                 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { // There was a record
05856 
05857                         // Look, if the record UID happens to be an offline record. If so, find its live version. Offline uids will be used when a page is versionized as "branch" so this is when we must correct - otherwise a pid of "-1" and a wrong sort-row number is returned which we don't want.
05858                     if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table, $row['uid'], $sortRow . ',pid,uid')) {
05859                         $row = $lookForLiveVersion;
05860                     }
05861 
05862                         // If the record should be inserted after itself, keep the current sorting information:
05863                     if ($row['uid'] == $uid) {
05864                         $sortNumber = $row[$sortRow];
05865                     } else {
05866                         $subres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
05867                             $sortRow . ',pid,uid',
05868                             $table,
05869                             'pid=' . intval($row['pid']) . ' AND ' . $sortRow . '>=' . intval($row[$sortRow]) . $this->deleteClause($table),
05870                             '',
05871                             $sortRow . ' ASC',
05872                             '2'
05873                         ); // Fetches the next record in order to calculate the in-between sortNumber
05874                         if ($GLOBALS['TYPO3_DB']->sql_num_rows($subres) == 2) { // There was a record afterwards
05875                             $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres); // Forward to the second result...
05876                             $subrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres); // There was a record afterwards
05877                             $sortNumber = $row[$sortRow] + floor(($subrow[$sortRow] - $row[$sortRow]) / 2); // The sortNumber is found in between these values
05878                             if ($sortNumber <= $row[$sortRow] || $sortNumber >= $subrow[$sortRow]) { // The sortNumber happend NOT to be between the two surrounding numbers, so we'll have to resort the list
05879                                 $sortNumber = $this->resorting($table, $row['pid'], $sortRow, $row['uid']); // By this special param, resorting reserves and returns the sortnumber after the uid
05880                             }
05881                         } else { // If after the last record in the list, we just add the sortInterval to the last sortvalue
05882                             $sortNumber = $row[$sortRow] + $this->sortIntervals;
05883                         }
05884                         $GLOBALS['TYPO3_DB']->sql_free_result($subres);
05885                     }
05886                     return array('pid' => $row['pid'], 'sortNumber' => $sortNumber);
05887                 } else {
05888                     $propArr = $this->getRecordProperties($table, $uid);
05889                     $this->log($table, $uid, 4, 0, 1, "Attempt to move record '%s' (%s) to after a non-existing record (uid=%s)", 1, array($propArr['header'], $table . ':' . $uid, abs($pid)), $propArr['pid']); // OK, dont insert $propArr['event_pid'] here...
05890                     return FALSE; // There MUST be a page or else this cannot work
05891                 }
05892             }
05893         }
05894     }
05895 
05896     /**
05897      * Resorts a table.
05898      * Used internally by getSortNumber()
05899      *
05900      * @param   string      Table name
05901      * @param   integer     Pid in which to resort records.
05902      * @param   string      Sorting row
05903      * @param   integer     Uid of record from $table in this $pid and for which the return value will be set to a free sorting number after that record. This is used to return a sortingValue if the list is resorted because of inserting records inside the list and not in the top
05904      * @return  integer     If $return_SortNumber_After_This_Uid is set, will contain usable sorting number after that record if found (otherwise 0)
05905      * @access private
05906      * @see getSortNumber()
05907      */
05908     function resorting($table, $pid, $sortRow, $return_SortNumber_After_This_Uid) {
05909         global $TCA;
05910         if ($TCA[$table] && $sortRow && $TCA[$table]['ctrl']['sortby'] == $sortRow) {
05911             $returnVal = 0;
05912             $intervals = $this->sortIntervals;
05913             $i = $intervals * 2;
05914 
05915             $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid=' . intval($pid) . $this->deleteClause($table), '', $sortRow . ' ASC');
05916             while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
05917                 $uid = intval($row['uid']);
05918                 if ($uid) {
05919                     $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid=' . intval($uid), array($sortRow => $i));
05920                     if ($uid == $return_SortNumber_After_This_Uid) { // This is used to return a sortingValue if the list is resorted because of inserting records inside the list and not in the top
05921                         $i = $i + $intervals;
05922                         $returnVal = $i;
05923                     }
05924                 } else {
05925                     die ('Fatal ERROR!! No Uid at resorting.');
05926                 }
05927                 $i = $i + $intervals;
05928             }
05929             $GLOBALS['TYPO3_DB']->sql_free_result($res);
05930             return $returnVal;
05931         }
05932     }
05933 
05934     /**
05935      * Setting up perms_* fields in $fieldArray based on TSconfig input
05936      * Used for new pages
05937      *
05938      * @param   array       Field Array, returned with modifications
05939      * @param   array       TSconfig properties
05940      * @return  array       Modified Field Array
05941      */
05942     function setTSconfigPermissions($fieldArray, $TSConfig_p) {
05943         if (strcmp($TSConfig_p['userid'], '')) {
05944             $fieldArray['perms_userid'] = intval($TSConfig_p['userid']);
05945         }
05946         if (strcmp($TSConfig_p['groupid'], '')) {
05947             $fieldArray['perms_groupid'] = intval($TSConfig_p['groupid']);
05948         }
05949         if (strcmp($TSConfig_p['user'], '')) {
05950             $fieldArray['perms_user'] = t3lib_div::testInt($TSConfig_p['user']) ? $TSConfig_p['user'] : $this->assemblePermissions($TSConfig_p['user']);
05951         }
05952         if (strcmp($TSConfig_p['group'], '')) {
05953             $fieldArray['perms_group'] = t3lib_div::testInt($TSConfig_p['group']) ? $TSConfig_p['group'] : $this->assemblePermissions($TSConfig_p['group']);
05954         }
05955         if (strcmp($TSConfig_p['everybody'], '')) {
05956             $fieldArray['perms_everybody'] = t3lib_div::testInt($TSConfig_p['everybody']) ? $TSConfig_p['everybody'] : $this->assemblePermissions($TSConfig_p['everybody']);
05957         }
05958 
05959         return $fieldArray;
05960     }
05961 
05962     /**
05963      * Returns a fieldArray with default values. Values will be picked up from the TCA array looking at the config key "default" for each column. If values are set in ->defaultValues they will overrule though.
05964      * Used for new records and during copy operations for defaults
05965      *
05966      * @param   string      Table name for which to set default values.
05967      * @return  array       Array with default values.
05968      */
05969     function newFieldArray($table) {
05970         t3lib_div::loadTCA($table);
05971         $fieldArray = array();
05972         if (is_array($GLOBALS['TCA'][$table]['columns'])) {
05973             foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $content) {
05974                 if (isset($this->defaultValues[$table][$field])) {
05975                     $fieldArray[$field] = $this->defaultValues[$table][$field];
05976                 } elseif (isset($content['config']['default'])) {
05977                     $fieldArray[$field] = $content['config']['default'];
05978                 }
05979             }
05980         }
05981         if ($table === 'pages') { // Set default permissions for a page.
05982             $fieldArray['perms_userid'] = $this->userid;
05983             $fieldArray['perms_groupid'] = intval($this->BE_USER->firstMainGroup);
05984             $fieldArray['perms_user'] = $this->assemblePermissions($this->defaultPermissions['user']);
05985             $fieldArray['perms_group'] = $this->assemblePermissions($this->defaultPermissions['group']);
05986             $fieldArray['perms_everybody'] = $this->assemblePermissions($this->defaultPermissions['everybody']);
05987         }
05988         return $fieldArray;
05989     }
05990 
05991     /**
05992      * If a "languageField" is specified for $table this function will add a possible value to the incoming array if none is found in there already.
05993      *
05994      * @param   string      Table name
05995      * @param   array       Incoming array (passed by reference)
05996      * @return  void
05997      */
05998     function addDefaultPermittedLanguageIfNotSet($table, &$incomingFieldArray) {
05999         global $TCA;
06000 
06001             // Checking languages:
06002         if ($TCA[$table]['ctrl']['languageField']) {
06003             if (!isset($incomingFieldArray[$TCA[$table]['ctrl']['languageField']])) { // Language field must be found in input row - otherwise it does not make sense.
06004                 $rows = array_merge(array(array('uid' => 0)), $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', 'sys_language', 'pid=0' . t3lib_BEfunc::deleteClause('sys_language')), array(array('uid' => -1)));
06005                 foreach ($rows as $r) {
06006                     if ($this->BE_USER->checkLanguageAccess($r['uid'])) {
06007                         $incomingFieldArray[$TCA[$table]['ctrl']['languageField']] = $r['uid'];
06008                         break;
06009                     }
06010                 }
06011             }
06012         }
06013     }
06014 
06015     /**
06016      * Returns the $data array from $table overridden in the fields defined in ->overrideValues.
06017      *
06018      * @param   string      Table name
06019      * @param   array       Data array with fields from table. These will be overlaid with values in $this->overrideValues[$table]
06020      * @return  array       Data array, processed.
06021      */
06022     function overrideFieldArray($table, $data) {
06023         if (is_array($this->overrideValues[$table])) {
06024             $data = array_merge($data, $this->overrideValues[$table]);
06025         }
06026         return $data;
06027     }
06028 
06029     /**
06030      * Compares the incoming field array with the current record and unsets all fields which are the same.
06031      * Used for existing records being updated
06032      *
06033      * @param   string      Record table name
06034      * @param   integer     Record uid
06035      * @param   array       Array of field=>value pairs intended to be inserted into the database. All keys with values matching exactly the current value will be unset!
06036      * @return  array       Returns $fieldArray. If the returned array is empty, then the record should not be updated!
06037      */
06038     function compareFieldArrayWithCurrentAndUnset($table, $id, $fieldArray) {
06039 
06040             // Fetch the original record:
06041         $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid=' . intval($id));
06042         $currentRecord = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
06043 
06044             // If the current record exists (which it should...), begin comparison:
06045         if (is_array($currentRecord)) {
06046 
06047                 // Read all field types:
06048             $c = 0;
06049             $cRecTypes = array();
06050             foreach ($currentRecord as $col => $val) {
06051                 $cRecTypes[$col] = $GLOBALS['TYPO3_DB']->sql_field_type($res, $c);
06052                 $c++;
06053             }
06054 
06055                 // Free result:
06056             $GLOBALS['TYPO3_DB']->sql_free_result($res);
06057 
06058                 // Unset the fields which are similar:
06059             foreach ($fieldArray as $col => $val) {
06060                 if (
06061                     !$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.
06062                     (!strcmp($val, $currentRecord[$col]) || // Unset fields which matched exactly.
06063                      ($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.
06064                     )
06065                 ) {
06066                     unset($fieldArray[$col]);
06067                 } else {
06068                     if (!isset($this->mmHistoryRecords[$table . ':' . $id]['oldRecord'][$col])) {
06069                         $this->historyRecords[$table . ':' . $id]['oldRecord'][$col] = $currentRecord[$col];
06070                     } elseif ($this->mmHistoryRecords[$table . ':' . $id]['oldRecord'][$col] != $this->mmHistoryRecords[$table . ':' . $id]['newRecord'][$col]) {
06071                         $this->historyRecords[$table . ':' . $id]['oldRecord'][$col] = $this->mmHistoryRecords[$table . ':' . $id]['oldRecord'][$col];
06072                     }
06073                     if (!isset($this->mmHistoryRecords[$table . ':' . $id]['newRecord'][$col])) {
06074                         $this->historyRecords[$table . ':' . $id]['newRecord'][$col] = $fieldArray[$col];
06075                     } elseif ($this->mmHistoryRecords[$table . ':' . $id]['newRecord'][$col] != $this->mmHistoryRecords[$table . ':' . $id]['oldRecord'][$col]) {
06076                         $this->historyRecords[$table . ':' . $id]['newRecord'][$col] = $this->mmHistoryRecords[$table . ':' . $id]['newRecord'][$col];
06077                     }
06078                 }
06079             }
06080         } else { // If the current record does not exist this is an error anyways and we just return an empty array here.
06081             $fieldArray = array();
06082         }
06083 
06084         return $fieldArray;
06085     }
06086 
06087     /**
06088      * Calculates the bitvalue of the permissions given in a string, comma-sep
06089      *
06090      * @param   string      List of pMap strings
06091      * @return  integer     Integer mask
06092      * @see setTSconfigPermissions(), newFieldArray()
06093      */
06094     function assemblePermissions($string) {
06095         $keyArr = t3lib_div::trimExplode(',', $string, 1);
06096         $value = 0;
06097         foreach ($keyArr as $key) {
06098             if ($key && isset($this->pMap[$key])) {
06099                 $value |= $this->pMap[$key];
06100             }
06101         }
06102         return $value;
06103     }
06104 
06105     /**
06106      * Returns the $input string without a comma in the end
06107      *
06108      * @param   string      Input string
06109      * @return  string      Output string with any comma in the end removed, if any.
06110      */
06111     function rmComma($input) {
06112         return rtrim($input, ',');
06113     }
06114 
06115     /**
06116      * Converts a HTML entity (like &#123;) to the character '123'
06117      *
06118      * @param   string      Input string
06119      * @return  string      Output string
06120      */
06121     function convNumEntityToByteValue($input) {
06122         $token = md5(microtime());
06123         $parts = explode($token, preg_replace('/(&#([0-9]+);)/', $token . '\2' . $token, $input));
06124 
06125         foreach ($parts as $k => $v) {
06126             if ($k % 2) {
06127                 $v = intval($v);
06128                 if ($v > 32) { // Just to make sure that control bytes are not converted.
06129                     $parts[$k] = chr(intval($v));
06130                 }
06131             }
06132         }
06133 
06134         return implode('', $parts);
06135     }
06136 
06137     /**
06138      * Returns absolute destination path for the uploadfolder, $folder
06139      *
06140      * @param   string      Upload folder name, relative to PATH_site
06141      * @return  string      Input string prefixed with PATH_site
06142      */
06143     function destPathFromUploadFolder($folder) {
06144         return PATH_site . $folder;
06145     }
06146 
06147     /**
06148      * Disables the delete clause for fetching records.
06149      * In general only undeleted records will be used. If the delete
06150      * clause is disabled, also deleted records are taken into account.
06151      *
06152      * @return  void
06153      */
06154     public function disableDeleteClause() {
06155         $this->disableDeleteClause = TRUE;
06156     }
06157 
06158     /**
06159      * Returns delete-clause for the $table
06160      *
06161      * @param   string      Table name
06162      * @return  string      Delete clause
06163      */
06164     function deleteClause($table) {
06165             // Returns the proper delete-clause if any for a table from TCA
06166         global $TCA;
06167         if (!$this->disableDeleteClause && $TCA[$table]['ctrl']['delete']) {
06168             return ' AND ' . $table . '.' . $TCA[$table]['ctrl']['delete'] . '=0';
06169         } else {
06170             return '';
06171         }
06172     }
06173 
06174     /**
06175      * Return TSconfig for a page id
06176      *
06177      * @param   integer     Page id (PID) from which to get configuration.
06178      * @return  array       TSconfig array, if any
06179      */
06180     function getTCEMAIN_TSconfig($tscPID) {
06181         if (!isset($this->cachedTSconfig[$tscPID])) {
06182             $this->cachedTSconfig[$tscPID] = $this->BE_USER->getTSConfig('TCEMAIN', t3lib_BEfunc::getPagesTSconfig($tscPID));
06183         }
06184         return $this->cachedTSconfig[$tscPID]['properties'];
06185     }
06186 
06187     /**
06188      * Extract entries from TSconfig for a specific table. This will merge specific and default configuration together.
06189      *
06190      * @param   string      Table name
06191      * @param   array       TSconfig for page
06192      * @return  array       TSconfig merged
06193      * @see getTCEMAIN_TSconfig()
06194      */
06195     function getTableEntries($table, $TSconfig) {
06196         $tA = is_array($TSconfig['table.'][$table . '.']) ? $TSconfig['table.'][$table . '.'] : array();
06197         ;
06198         $dA = is_array($TSconfig['default.']) ? $TSconfig['default.'] : array();
06199         return t3lib_div::array_merge_recursive_overrule($dA, $tA);
06200     }
06201 
06202     /**
06203      * Returns the pid of a record from $table with $uid
06204      *
06205      * @param   string      Table name
06206      * @param   integer     Record uid
06207      * @return  integer     PID value (unless the record did not exist in which case FALSE)
06208      */
06209     function getPID($table, $uid) {
06210         $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', $table, 'uid=' . intval($uid));
06211         if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) {
06212             return $row['pid'];
06213         }
06214     }
06215 
06216     /**
06217      * Executing dbAnalysisStore
06218      * This will save MM relations for new records but is executed after records are created because we need to know the ID of them
06219      *
06220      * @return  void
06221      */
06222     function dbAnalysisStoreExec() {
06223         foreach ($this->dbAnalysisStore as $action) {
06224             $id = t3lib_BEfunc::wsMapId(
06225                 $action[4],
06226                 (t3lib_div::testInt($action[2]) ? $action[2] : $this->substNEWwithIDs[$action[2]])
06227             );
06228             if ($id) {
06229                 $action[0]->writeMM($action[1], $id, $action[3]);
06230             }
06231         }
06232     }
06233 
06234     /**
06235      * Removing files registered for removal before exit
06236      *
06237      * @return  void
06238      */
06239     function removeRegisteredFiles() {
06240         foreach ($this->removeFilesStore as $file) {
06241             unlink($file);
06242         }
06243     }
06244 
06245     /**
06246      * Unlink (delete) typo3conf/temp_CACHED_*.php cache files
06247      *
06248      * @return  integer     The number of files deleted
06249      */
06250     function removeCacheFiles() {
06251         return t3lib_extMgm::removeCacheFiles();
06252     }
06253 
06254     /**
06255      * Returns array, $CPtable, of pages under the $pid going down to $counter levels.
06256      * Selecting ONLY pages which the user has read-access to!
06257      *
06258      * @param   array       Accumulation of page uid=>pid pairs in branch of $pid
06259      * @param   integer     Page ID for which to find subpages
06260      * @param   integer     Number of levels to go down.
06261      * @param   integer     ID of root point for new copied branch: The idea seems to be that a copy is not made of the already new page!
06262      * @return  array       Return array.
06263      */
06264     function int_pageTreeInfo($CPtable, $pid, $counter, $rootID) {
06265         if ($counter) {
06266             $addW = !$this->admin ? ' AND ' . $this->BE_USER->getPagePermsClause($this->pMap['show']) : '';
06267             $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', 'pid=' . intval($pid) . $this->deleteClause('pages') . $addW, '', 'sorting DESC');
06268             while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
06269                 if ($row['uid'] != $rootID) {
06270                     $CPtable[$row['uid']] = $pid;
06271                     if ($counter - 1) { // If the uid is NOT the rootID of the copyaction and if we are supposed to walk further down
06272                         $CPtable = $this->int_pageTreeInfo($CPtable, $row['uid'], $counter - 1, $rootID);
06273                     }
06274                 }
06275             }
06276             $GLOBALS['TYPO3_DB']->sql_free_result($mres);
06277         }
06278         return $CPtable;
06279     }
06280 
06281     /**
06282      * List of all tables (those administrators has access to = array_keys of $TCA)
06283      *
06284      * @return  array       Array of all TCA table names
06285      */
06286     function compileAdminTables() {
06287         return array_keys($GLOBALS['TCA']);
06288     }
06289 
06290     /**
06291      * Checks if any uniqueInPid eval input fields are in the record and if so, they are re-written to be correct.
06292      *
06293      * @param   string      Table name
06294      * @param   integer     Record UID
06295      * @return  void
06296      */
06297     function fixUniqueInPid($table, $uid) {
06298         if ($GLOBALS['TCA'][$table]) {
06299             t3lib_div::loadTCA($table);
06300             $curData = $this->recordInfo($table, $uid, '*');
06301             $newData = array();
06302             foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $conf) {
06303                 if ($conf['config']['type'] == 'input') {
06304                     $evalCodesArray = t3lib_div::trimExplode(',', $conf['config']['eval'], 1);
06305                     if (in_array('uniqueInPid', $evalCodesArray)) {
06306                         $newV = $this->getUnique($table, $field, $curData[$field], $uid, $curData['pid']);
06307                         if (strcmp($newV, $curData[$field])) {
06308                             $newData[$field] = $newV;
06309                         }
06310                     }
06311                 }
06312             }
06313                 // IF there are changed fields, then update the database
06314             if (count($newData)) {
06315                 $this->updateDB($table, $uid, $newData);
06316             }
06317         }
06318     }
06319 
06320     /**
06321      * When er record is copied you can specify fields from the previous record which should be copied into the new one
06322      * This function is also called with new elements. But then $update must be set to zero and $newData containing the data array. In that case data in the incoming array is NOT overridden. (250202)
06323      *
06324      * @param   string      Table name
06325      * @param   integer     Record UID
06326      * @param   integer     UID of previous record
06327      * @param   boolean     If set, updates the record
06328      * @param   array       Input array. If fields are already specified AND $update is not set, values are not set in output array.
06329      * @return  array       Output array (For when the copying operation needs to get the information instead of updating the info)
06330      */
06331     function fixCopyAfterDuplFields($table, $uid, $prevUid, $update, $newData = array()) {
06332         global $TCA;
06333         if ($TCA[$table] && $TCA[$table]['ctrl']['copyAfterDuplFields']) {
06334             t3lib_div::loadTCA($table);
06335             $prevData = $this->recordInfo($table, $prevUid, '*');
06336             $theFields = t3lib_div::trimExplode(',', $TCA[$table]['ctrl']['copyAfterDuplFields'], 1);
06337             foreach ($theFields as $field) {
06338                 if ($TCA[$table]['columns'][$field] && ($update || !isset($newData[$field]))) {
06339                     $newData[$field] = $prevData[$field];
06340                 }
06341             }
06342             if ($update && count($newData)) {
06343                 $this->updateDB($table, $uid, $newData);
06344             }
06345         }
06346         return $newData;
06347     }
06348 
06349     /**
06350      * Returns all fieldnames from a table which are a list of files
06351      *
06352      * @param   string      Table name
06353      * @return  array       Array of fieldnames that are either "group" or "file" types.
06354      */
06355     function extFileFields($table) {
06356         $listArr = array();
06357         t3lib_div::loadTCA($table);
06358         if (isset($GLOBALS['TCA'][$table]['columns'])) {
06359             foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $configArr) {
06360                 if ($configArr['config']['type'] == 'group' &&
06361                     ($configArr['config']['internal_type'] == 'file' ||
06362                      $configArr['config']['internal_type'] == 'file_reference')) {
06363                     $listArr[] = $field;
06364                 }
06365             }
06366         }
06367         return $listArr;
06368     }
06369 
06370     /**
06371      * Returns all fieldnames from a table which have the unique evaluation type set.
06372      *
06373      * @param   string      Table name
06374      * @return  array       Array of fieldnames
06375      */
06376     function getUniqueFields($table) {
06377         $listArr = array();
06378         t3lib_div::loadTCA($table);
06379         if ($GLOBALS['TCA'][$table]['columns']) {
06380             foreach ($GLOBALS['TCA'][$table]['columns'] as $field => $configArr) {
06381                 if ($configArr['config']['type'] === 'input') {
06382                     $evalCodesArray = t3lib_div::trimExplode(',', $configArr['config']['eval'], 1);
06383                     if (in_array('uniqueInPid', $evalCodesArray) || in_array('unique', $evalCodesArray)) {
06384                         $listArr[] = $field;
06385                     }
06386                 }
06387             }
06388         }
06389         return $listArr;
06390     }
06391 
06392     /**
06393      * Returns true if the TCA/columns field type is a DB reference field
06394      *
06395      * @param   array       config array for TCA/columns field
06396      * @return  boolean     True if DB reference field (group/db or select with foreign-table)
06397      */
06398     function isReferenceField($conf) {
06399         return ($conf['type'] == 'group' && $conf['internal_type'] == 'db' || $conf['type'] == 'select' && $conf['foreign_table']);
06400     }
06401 
06402     /**
06403      * Returns the subtype as a string of an inline field.
06404      * If it's not a inline field at all, it returns false.
06405      *
06406      * @param   array       config array for TCA/columns field
06407      * @return  mixed       string: inline subtype (field|mm|list), boolean: false
06408      */
06409     function getInlineFieldType($conf) {
06410         if ($conf['type'] == 'inline' && $conf['foreign_table']) {
06411             if ($conf['foreign_field']) {
06412                 return 'field';
06413             } // the reference to the parent is stored in a pointer field in the child record
06414             elseif ($conf['MM'])
06415             {
06416                 return 'mm';
06417             } // regular MM intermediate table is used to store data
06418             else
06419             {
06420                 return 'list';
06421             } // an item list (separated by comma) is stored (like select type is doing)
06422         }
06423         return FALSE;
06424     }
06425 
06426 
06427     /**
06428      * Get modified header for a copied record
06429      *
06430      * @param   string      Table name
06431      * @param   integer     PID value in which other records to test might be
06432      * @param   string      Field name to get header value for.
06433      * @param   string      Current field value
06434      * @param   integer     Counter (number of recursions)
06435      * @param   string      Previous title we checked for (in previous recursion)
06436      * @return  string      The field value, possibly appended with a "copy label"
06437      */
06438     function getCopyHeader($table, $pid, $field, $value, $count, $prevTitle = '') {
06439         global $TCA;
06440 
06441             // Set title value to check for:
06442         if ($count) {
06443             $checkTitle = $value . rtrim(' ' . sprintf($this->prependLabel($table), $count));
06444         } else {
06445             $checkTitle = $value;
06446         }
06447 
06448             // Do check:
06449         if ($prevTitle != $checkTitle || $count < 100) {
06450             $rowCount = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
06451                 'uid',
06452                 $table,
06453                 'pid=' . intval($pid) .
06454                 ' AND ' . $field . '=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($checkTitle, $table) .
06455                 $this->deleteClause($table)
06456             );
06457             if ($rowCount) {
06458                 return $this->getCopyHeader($table, $pid, $field, $value, $count + 1, $checkTitle);
06459             }
06460         }
06461 
06462             // Default is to just return the current input title if no other was returned before:
06463         return $checkTitle;
06464     }
06465 
06466     /**
06467      * Return "copy" label for a table. Although the name is "prepend" it actually APPENDs the label (after ...)
06468      *
06469      * @param   string      Table name
06470      * @return  string      Label to append, containing "%s" for the number
06471      * @see getCopyHeader()
06472      */
06473     function prependLabel($table) {
06474         global $TCA;
06475         if (is_object($GLOBALS['LANG'])) {
06476             $label = $GLOBALS['LANG']->sL($TCA[$table]['ctrl']['prependAtCopy']);
06477         } else {
06478             list($label) = explode('|', $TCA[$table]['ctrl']['prependAtCopy']);
06479         }
06480         return $label;
06481     }
06482 
06483     /**
06484      * Get the final pid based on $table and $pid ($destPid type... pos/neg)
06485      *
06486      * @param   string      Table name
06487      * @param   integer     "Destination pid" : If the value is >= 0 it's just returned directly (through intval() though) but if the value is <0 then the method looks up the record with the uid equal to abs($pid) (positive number) and returns the PID of that record! The idea is that negative numbers point to the record AFTER WHICH the position is supposed to be!
06488      * @return  integer
06489      */
06490     function resolvePid($table, $pid) {
06491         global $TCA;
06492         $pid = intval($pid);
06493         if ($pid < 0) {
06494             $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', $table, 'uid=' . abs($pid));
06495             $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
06496 
06497                 // Look, if the record UID happens to be an offline record. If so, find its live version. Offline uids will be used when a page is versionized as "branch" so this is when we must correct - otherwise a pid of "-1" and a wrong sort-row number is returned which we don't want.
06498             if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table, abs($pid), 'pid')) {
06499                 $row = $lookForLiveVersion;
06500             }
06501 
06502             $pid = intval($row['pid']);
06503         } elseif ($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.
06504             if ($WSdestPage = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, 'pages', $pid, 'uid,t3ver_swapmode')) { // Looks for workspace version of page.
06505                 if ($WSdestPage['t3ver_swapmode'] == 0) { // if swapmode is zero, then change pid value.
06506                     $pid = $WSdestPage['uid'];
06507                 }
06508             }
06509         }
06510         return $pid;
06511     }
06512 
06513     /**
06514      * Removes the prependAtCopy prefix on values
06515      *
06516      * @param   string      Table name
06517      * @param   string      The value to fix
06518      * @return  string      Clean name
06519      */
06520     function clearPrefixFromValue($table, $value) {
06521         global $TCA;
06522         $regex = '/' . sprintf(quotemeta($this->prependLabel($table)), '[0-9]*') . '$/';
06523         return @preg_replace($regex, '', $value);
06524     }
06525 
06526     /**
06527      * File functions on external file references. eg. deleting files when deleting record
06528      *
06529      * @param   string      Table name
06530      * @param   string      Field name
06531      * @param   string      List of files to work on from field
06532      * @param   string      Function, eg. "deleteAll" which will delete all files listed.
06533      * @return  void
06534      */
06535     function extFileFunctions($table, $field, $filelist, $func) {
06536         global $TCA;
06537         t3lib_div::loadTCA($table);
06538         $uploadFolder = $TCA[$table]['columns'][$field]['config']['uploadfolder'];
06539         if ($uploadFolder && trim($filelist) && $TCA[$table]['columns'][$field]['config']['internal_type'] == 'file') {
06540             $uploadPath = $this->destPathFromUploadFolder($uploadFolder);
06541             $fileArray = explode(',', $filelist);
06542             foreach ($fileArray as $theFile) {
06543                 $theFile = trim($theFile);
06544                 if ($theFile) {
06545                     switch ($func) {
06546                         case 'deleteAll':
06547                             if (@is_file($uploadPath . '/' . $theFile)) {
06548                                 unlink($uploadPath . '/' . $theFile);
06549                             } else {
06550                                 $this->log($table, 0, 3, 0, 100, "Delete: Referenced file that was supposed to be deleted together with it's record didn't exist");
06551                             }
06552                         break;
06553                     }
06554                 }
06555             }
06556         }
06557     }
06558 
06559     /**
06560      * Used by the deleteFunctions to check if there are records from disallowed tables under the pages to be deleted.
06561      *
06562      * @param   string      List of page integers
06563      * @return  boolean     Return true, if permission granted
06564      */
06565     function noRecordsFromUnallowedTables($inList) {
06566         $inList = trim($this->rmComma(trim($inList)));
06567         if ($inList && !$this->admin) {
06568             foreach (array_keys($GLOBALS['TCA']) as $table) {
06569                 $count = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
06570                     'uid',
06571                     $table,
06572                     'pid IN (' . $inList . ')' . t3lib_BEfunc::deleteClause($table)
06573                 );
06574                 if ($count && ($this->tableReadOnly($table) || !$this->checkModifyAccessList($table))) {
06575                     return FALSE;
06576                 }
06577             }
06578         }
06579         return TRUE;
06580     }
06581 
06582 
06583     /**
06584      * Determine if a record was copied or if a record is the result of a copy action.
06585      *
06586      * @param   string      $table: The tablename of the record
06587      * @param   integer     $uid: The uid of the record
06588      * @return  boolean     Returns true if the record is copied or is the result of a copy action
06589      */
06590     function isRecordCopied($table, $uid) {
06591             // If the record was copied:
06592         if (isset($this->copyMappingArray[$table][$uid])) {
06593             return TRUE;
06594             // If the record is the result of a copy action:
06595         } elseif (isset($this->copyMappingArray[$table]) && in_array($uid, array_values($this->copyMappingArray[$table]))) {
06596             return TRUE;
06597         }
06598         return FALSE;
06599     }
06600 
06601 
06602     /******************************
06603      *
06604      * Clearing cache
06605      *
06606      ******************************/
06607 
06608     /**
06609      * Clearing the cache based on a page being updated
06610      * If the $table is 'pages' then cache is cleared for all pages on the same level (and subsequent?)
06611      * Else just clear the cache for the parent page of the record.
06612      *
06613      * @param   string      Table name of record that was just updated.
06614      * @param   integer     UID of updated / inserted record
06615      * @return  void
06616      */
06617     function clear_cache($table, $uid) {
06618         global $TCA, $TYPO3_CONF_VARS;
06619 
06620         $uid = intval($uid);
06621         $pageUid = 0;
06622         if (is_array($TCA[$table]) && $uid > 0) {
06623 
06624                 // Get Page TSconfig relavant:
06625             list($tscPID) = t3lib_BEfunc::getTSCpid($table, $uid, '');
06626             $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
06627 
06628             if (!$TSConfig['clearCache_disable']) {
06629 
06630                     // If table is "pages":
06631                 if (t3lib_extMgm::isLoaded('cms')) {
06632                     $list_cache = array();
06633 
06634                     if ($table === 'pages' || $table === 'pages_language_overlay') {
06635 
06636                         if ($table === 'pages_language_overlay') {
06637                             $pageUid = $this->getPID($table, $uid);
06638                         } else {
06639                             $pageUid = $uid;
06640                         }
06641 
06642                             // Builds list of pages on the SAME level as this page (siblings)
06643                         $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
06644                             'A.pid AS pid, B.uid AS uid',
06645                             'pages A, pages B',
06646                             'A.uid=' . intval($pageUid) . ' AND B.pid=A.pid AND B.deleted=0'
06647                         );
06648 
06649                         $pid_tmp = 0;
06650                         while ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) {
06651                             $list_cache[] = $row_tmp['uid'];
06652                             $pid_tmp = $row_tmp['pid'];
06653 
06654                                 // Add children as well:
06655                             if ($TSConfig['clearCache_pageSiblingChildren']) {
06656                                 $res_tmp2 = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
06657                                     'uid',
06658                                     'pages',
06659                                     'pid=' . intval($row_tmp['uid']) . ' AND deleted=0'
06660                                 );
06661                                 while ($row_tmp2 = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp2)) {
06662                                     $list_cache[] = $row_tmp2['uid'];
06663                                 }
06664                                 $GLOBALS['TYPO3_DB']->sql_free_result($res_tmp2);
06665                             }
06666                         }
06667                         $GLOBALS['TYPO3_DB']->sql_free_result($res_tmp);
06668 
06669                             // Finally, add the parent page as well:
06670                         $list_cache[] = $pid_tmp;
06671 
06672                             // Add grand-parent as well:
06673                         if ($TSConfig['clearCache_pageGrandParent']) {
06674                             $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
06675                                 'pid',
06676                                 'pages',
06677                                 'uid=' . intval($pid_tmp)
06678                             );
06679                             if ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) {
06680                                 $list_cache[] = $row_tmp['pid'];
06681                             }
06682                         }
06683                     } else { // For other tables than "pages", delete cache for the records "parent page".
06684                         $list_cache[] = $pageUid = intval($this->getPID($table, $uid));
06685                     }
06686 
06687                         // Call pre-processing function for clearing of cache for page ids:
06688                     if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) {
06689                         foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) {
06690                             $_params = array('pageIdArray' => &$list_cache, 'table' => $table, 'uid' => $uid, 'functionID' => 'clear_cache()');
06691                                 // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array!
06692                             t3lib_div::callUserFunction($funcName, $_params, $this);
06693                         }
06694                     }
06695 
06696                         // Delete cache for selected pages:
06697                     if (is_array($list_cache)) {
06698                         if (TYPO3_UseCachingFramework) {
06699                             $pageCache = $GLOBALS['typo3CacheManager']->getCache(
06700                                 'cache_pages'
06701                             );
06702                             $pageSectionCache = $GLOBALS['typo3CacheManager']->getCache(
06703                                 'cache_pagesection'
06704                             );
06705 
06706                             $pageIds = $GLOBALS['TYPO3_DB']->cleanIntArray($list_cache);
06707                             foreach ($pageIds as $pageId) {
06708                                 $pageCache->flushByTag('pageId_' . $pageId);
06709                                 $pageSectionCache->flushByTag('pageId_' . $pageId);
06710                             }
06711                         } else {
06712                             $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages', 'page_id IN (' . implode(',', $GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)) . ')');
06713                             $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection', 'page_id IN (' . implode(',', $GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)) . ')');
06714 
06715                         }
06716                     }
06717                 }
06718             }
06719 
06720                 // Clear cache for pages entered in TSconfig:
06721             if ($TSConfig['clearCacheCmd']) {
06722                 $Commands = t3lib_div::trimExplode(',', strtolower($TSConfig['clearCacheCmd']), 1);
06723                 $Commands = array_unique($Commands);
06724                 foreach ($Commands as $cmdPart) {
06725                     $this->clear_cacheCmd($cmdPart);
06726                 }
06727             }
06728 
06729                 // Call post processing function for clear-cache:
06730             if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) {
06731                 $_params = array('table' => $table, 'uid' => $uid, 'uid_page' => $pageUid, 'TSConfig' => $TSConfig);
06732                 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) {
06733                     t3lib_div::callUserFunction($_funcRef, $_params, $this);
06734                 }
06735             }
06736         }
06737     }
06738 
06739     /**
06740      * Clears the cache based on the command $cacheCmd.
06741      *
06742      * $cacheCmd='pages':   Clears cache for all pages. Requires admin-flag to
06743      * be set for BE_USER.
06744      *
06745      * $cacheCmd='all':     Clears all cache_tables. This is necessary if
06746      * templates are updated. Requires admin-flag to be set for BE_USER.
06747      *
06748      * $cacheCmd=[integer]: Clears cache for the page pointed to by $cacheCmd
06749      * (an integer).
06750      *
06751      * Can call a list of post processing functions as defined in
06752      * $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc']
06753      * (numeric array with values being the function references, called by
06754      * t3lib_div::callUserFunction()).
06755      *
06756      * Note: The following cache_* are intentionally not cleared by
06757      * $cacheCmd='all':
06758      *
06759      * - cache_md5params:   Clearing this table would destroy all simulateStatic
06760      *                       URLs, simulates file name and RDCT redirects.
06761      * - cache_imagesizes:  Clearing this table would cause a lot of unneeded
06762      *                       Imagemagick calls because the size informations have
06763      *                       to be fetched again after clearing.
06764      * - cache_extensions:  Clearing this table would make the extension manager
06765      *                       unusable until a new extension list is fetched from
06766      *                       the TER.
06767      *
06768      * @param   string      the cache command, see above description
06769      * @return  void
06770      */
06771     public function clear_cacheCmd($cacheCmd) {
06772         global $TYPO3_CONF_VARS;
06773 
06774         $this->BE_USER->writelog(3, 1, 0, 0, 'User %s has cleared the cache (cacheCmd=%s)', array($this->BE_USER->user['username'], $cacheCmd));
06775 
06776             // Clear cache for either ALL pages or ALL tables!
06777         switch ($cacheCmd) {
06778             case 'pages':
06779                 if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.pages')) {
06780                     $this->internal_clearPageCache();
06781                 }
06782             break;
06783             case 'all':
06784                 if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.all')) {
06785 
06786                         // Clear all caching framework caches if it is initialized:
06787                         // (it could be disabled by initialized by an extension)
06788                     if (t3lib_cache::isCachingFrameworkInitialized()) {
06789                         $GLOBALS['typo3CacheManager']->flushCaches();
06790                     }
06791 
06792                     if (TYPO3_UseCachingFramework) {
06793                         if (t3lib_extMgm::isLoaded('cms')) {
06794                             $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_treelist');
06795                         }
06796                     } else {
06797                         if (t3lib_extMgm::isLoaded('cms')) {
06798                             $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_treelist');
06799                             $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_pagesection');
06800                         }
06801                         $this->internal_clearPageCache();
06802                         $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_hash');
06803                     }
06804 
06805                         // Clearing additional cache tables:
06806                     if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'])) {
06807                         foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'] as $tableName) {
06808                             if (!preg_match('/[^[:alnum:]_]/', $tableName) && substr($tableName, -5) == 'cache') {
06809                                 $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery($tableName);
06810                             } else {
06811                                 throw new RuntimeException(
06812                                     'TYPO3 Fatal Error: Trying to flush table "' . $tableName . '" with "Clear All Cache"',
06813                                     1270853922
06814                                 );
06815                             }
06816                         }
06817                     }
06818                 }
06819                 if ($this->admin && $TYPO3_CONF_VARS['EXT']['extCache']) {
06820                     $this->removeCacheFiles();
06821                 }
06822             break;
06823             case 'temp_CACHED':
06824                 if ($this->admin && $TYPO3_CONF_VARS['EXT']['extCache']) {
06825                     $this->removeCacheFiles();
06826                 }
06827             break;
06828         }
06829 
06830             // Clear cache for a page ID!
06831         if (t3lib_div::testInt($cacheCmd)) {
06832             if (t3lib_extMgm::isLoaded('cms')) {
06833 
06834                 $list_cache = array($cacheCmd);
06835 
06836                     // Call pre-processing function for clearing of cache for page ids:
06837                 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) {
06838                     foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) {
06839                         $_params = array('pageIdArray' => &$list_cache, 'cacheCmd' => $cacheCmd, 'functionID' => 'clear_cacheCmd()');
06840                             // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array!
06841                         t3lib_div::callUserFunction($funcName, $_params, $this);
06842                     }
06843                 }
06844 
06845                     // Delete cache for selected pages:
06846                 if (is_array($list_cache)) {
06847 
06848                     if (TYPO3_UseCachingFramework) {
06849                         $pageCache = $GLOBALS['typo3CacheManager']->getCache(
06850                             'cache_pages'
06851                         );
06852                         $pageSectionCache = $GLOBALS['typo3CacheManager']->getCache(
06853                             'cache_pagesection'
06854                         );
06855 
06856                         foreach ($list_cache as $pageId) {
06857                             $pageCache->flushByTag('pageId_' . (int) $pageId);
06858                             $pageSectionCache->flushByTag('pageId_' . (int) $pageId);
06859                         }
06860                     } else {
06861                         $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages', 'page_id IN (' . implode(',', $GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)) . ')');
06862                         $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!
06863 
06864                     }
06865                 }
06866             }
06867         }
06868 
06869             // Call post processing function for clear-cache:
06870         if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) {
06871             $_params = array('cacheCmd' => $cacheCmd);
06872             foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) {
06873                 t3lib_div::callUserFunction($_funcRef, $_params, $this);
06874             }
06875         }
06876     }
06877 
06878 
06879     /*****************************
06880      *
06881      * Logging
06882      *
06883      *****************************/
06884 
06885     /**
06886      * Logging actions from TCEmain
06887      *
06888      * @param   string      Table name the log entry is concerned with. Blank if NA
06889      * @param   integer     Record UID. Zero if NA
06890      * @param   integer     Action number: 0=No category, 1=new record, 2=update record, 3= delete record, 4= move record, 5= Check/evaluate
06891      * @param   integer     Normally 0 (zero). If set, it indicates that this log-entry is used to notify the backend of a record which is moved to another location
06892      * @param   integer     The severity: 0 = message, 1 = error, 2 = System Error, 3 = security notice (admin)
06893      * @param   string      Default error message in english
06894      * @param   integer     This number is unique for every combination of $type and $action. This is the error-message number, which can later be used to translate error messages. 0 if not categorized, -1 if temporary
06895      * @param   array       Array with special information that may go into $details by '%s' marks / sprintf() when the log is shown
06896      * @param   integer     The page_uid (pid) where the event occurred. Used to select log-content for specific pages.
06897      * @param   string      NEW id for new records
06898      * @return  integer     Log entry UID
06899      * @see class.t3lib_userauthgroup.php
06900      */
06901     function log($table, $recuid, $action, $recpid, $error, $details, $details_nr = -1, $data = array(), $event_pid = -1, $NEWid = '') {
06902         if ($this->enableLogging) {
06903             $type = 1; // Type value for tce_db.php
06904             if (!$this->storeLogMessages) {
06905                 $details = '';
06906             }
06907             if ($error > 0) {
06908                 $this->errorLog[] = '[' . $type . '.' . $action . '.' . $details_nr . ']: ' . $details;
06909             }
06910             return $this->BE_USER->writelog($type, $action, $error, $details_nr, $details, $data, $table, $recuid, $recpid, $event_pid, $NEWid);
06911         }
06912     }
06913 
06914     /**
06915      * Simple logging function meant to be used when logging messages is not yet fixed.
06916      *
06917      * @param   string      Message string
06918      * @param   integer     Error code, see log()
06919      * @return  integer     Log entry UID
06920      * @see log()
06921      */
06922     function newlog($message, $error = 0) {
06923         return $this->log('', 0, 0, 0, $error, '[newlog()] ' . $message, -1);
06924     }
06925 
06926     /**
06927      * Simple logging function meant to bridge the gap between newlog() and log() with a littme more info, in particular the record table/uid and event_pid so we can filter messages pr page.
06928      *
06929      * @param   string      Message string
06930      * @param   string      Table name
06931      * @param   integer     Record uid
06932      * @param   integer     Record PID (from page tree). Will be turned into an event_pid internally in function: Meaning that the PID for a page will be its own UID, not its page tree PID.
06933      * @param   integer     Error code, see log()
06934      * @return  integer     Log entry UID
06935      * @see log()
06936      */
06937     function newlog2($message, $table, $uid, $pid = FALSE, $error = 0) {
06938         if ($pid === FALSE) {
06939             $propArr = $this->getRecordProperties($table, $uid);
06940             $pid = $propArr['pid'];
06941         }
06942 
06943         return $this->log($table, $uid, 0, 0, $error, $message, -1, array(), $this->eventPid($table, $uid, $pid));
06944     }
06945 
06946     /**
06947      * Print log error messages from the operations of this script instance
06948      *
06949      * @param   string      Redirect URL (for creating link in message)
06950      * @return  void        (Will exit on error)
06951      */
06952     function printLogErrorMessages($redirect) {
06953 
06954         $res_log = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
06955             '*',
06956             'sys_log',
06957             'type=1 AND userid=' . intval($this->BE_USER->user['uid']) . ' AND tstamp=' . intval($GLOBALS['EXEC_TIME']) . ' AND error!=0'
06958         );
06959         while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_log)) {
06960             $log_data = unserialize($row['log_data']);
06961             $msg = $row['error'] . ': ' . sprintf($row['details'], $log_data[0], $log_data[1], $log_data[2], $log_data[3], $log_data[4]);
06962             $flashMessage = t3lib_div::makeInstance('t3lib_FlashMessage',
06963                                                     $msg,
06964                                                     '',
06965                                                     t3lib_FlashMessage::ERROR,
06966                                                     TRUE
06967             );
06968             t3lib_FlashMessageQueue::addMessage($flashMessage);
06969         }
06970         $GLOBALS['TYPO3_DB']->sql_free_result($res_log);
06971     }
06972 
06973     /*****************************
06974      *
06975      * Internal (do not use outside Core!)
06976      *
06977      *****************************/
06978 
06979     /**
06980      * Clears page cache. Takes into account file cache.
06981      *
06982      * @return  void
06983      */
06984     function internal_clearPageCache() {
06985         if (TYPO3_UseCachingFramework) {
06986             if (t3lib_extMgm::isLoaded('cms')) {
06987                 $GLOBALS['typo3CacheManager']->getCache('cache_pages')->flush();
06988             }
06989         } else {
06990             if (t3lib_extMgm::isLoaded('cms')) {
06991                 if ($GLOBALS['TYPO3_CONF_VARS']['FE']['pageCacheToExternalFiles']) {
06992                     $cacheDir = PATH_site . 'typo3temp/cache_pages';
06993                     $retVal = t3lib_div::rmdir($cacheDir, TRUE);
06994                     if (!$retVal) {
06995                         t3lib_div::sysLog('Could not remove page cache files in "' . $cacheDir . '"', 'Core/t3lib_tcemain', 2);
06996                     }
06997                 }
06998                 $GLOBALS['TYPO3_DB']->exec_TRUNCATEquery('cache_pages');
06999             }
07000         }
07001     }
07002 
07003 
07004     /**
07005      * Proprocesses field array based on field type. Some fields must be adjusted
07006      * before going to database. This is done on the copy of the field array because
07007      * original values are used in remap action later.
07008      *
07009      * @param   string  $table  Table name
07010      * @param   array   $fieldArray Field array to check
07011      * @return  array   Updated field array
07012      */
07013     function insertUpdateDB_preprocessBasedOnFieldType($table, $fieldArray) {
07014         global $TCA;
07015 
07016         $result = $fieldArray;
07017         foreach ($fieldArray as $field => $value) {
07018             switch ($TCA[$table]['columns'][$field]['config']['type']) {
07019                 case 'inline':
07020                     if ($TCA[$table]['columns'][$field]['config']['foreign_field']) {
07021                         if (!t3lib_div::testInt($value)) {
07022                             $result[$field] = count(t3lib_div::trimExplode(',', $value, TRUE));
07023                         }
07024                     }
07025                 break;
07026             }
07027         }
07028         return $result;
07029     }
07030 
07031     /**
07032      * Gets the automatically versionized id of a record.
07033      *
07034      * @param string $table Name of the table
07035      * @param integer $id Uid of the record
07036      * @return integer
07037      */
07038     protected function getAutoVersionId($table, $id) {
07039         $result = NULL;
07040 
07041         if (isset($this->autoVersionIdMap[$table][$id])) {
07042             $result = $this->autoVersionIdMap[$table][$id];
07043         }
07044 
07045         return $result;
07046     }
07047 
07048     /**
07049      * Overlays the automatically versionized id of a record.
07050      *
07051      * @param string $table Name of the table
07052      * @param integer $id Uid of the record
07053      * @return integer
07054      */
07055     protected function overlayAutoVersionId($table, $id) {
07056         $autoVersionId = $this->getAutoVersionId($table, $id);
07057 
07058         if (is_null($autoVersionId) === FALSE) {
07059             $id = $autoVersionId;
07060         }
07061 
07062         return $id;
07063     }
07064 
07065     /**
07066      * Adds new values to the remapStackChildIds array.
07067      *
07068      * @param array $idValues uid values
07069      * @return void
07070      */
07071     protected function addNewValuesToRemapStackChildIds(array $idValues) {
07072         foreach ($idValues as $idValue) {
07073             if (strpos($idValue, 'NEW') === 0) {
07074                 $this->remapStackChildIds[$idValue] = TRUE;
07075             }
07076         }
07077     }
07078 }
07079 
07080 
07081 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tcemain.php'])) {
07082     include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tcemain.php']);
07083 }
07084 
07085 ?>