[ Index ]
 

Code source de Typo3 4.1.3

Accédez au Source d'autres logiciels libres

Classes | Fonctions | Variables | Constantes | Tables

title

Body

[fermer]

/t3lib/ -> class.t3lib_tcemain.php (source)

   1  <?php
   2  /***************************************************************
   3  *  Copyright notice
   4  *
   5  *  (c) 1999-2007 Kasper Skaarhoj (kasperYYYY@typo3.com)
   6  *  All rights reserved
   7  *
   8  *  This script is part of the TYPO3 project. The TYPO3 project is
   9  *  free software; you can redistribute it and/or modify
  10  *  it under the terms of the GNU General Public License as published by
  11  *  the Free Software Foundation; either version 2 of the License, or
  12  *  (at your option) any later version.
  13  *
  14  *  The GNU General Public License can be found at
  15  *  http://www.gnu.org/copyleft/gpl.html.
  16  *  A copy is found in the textfile GPL.txt and important notices to the license
  17  *  from the author is found in LICENSE.txt distributed with these scripts.
  18  *
  19  *
  20  *  This script is distributed in the hope that it will be useful,
  21  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  22  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23  *  GNU General Public License for more details.
  24  *
  25  *  This copyright notice MUST APPEAR in all copies of the script!
  26  ***************************************************************/
  27  /**
  28   * Contains the TYPO3 Core Engine
  29   *
  30   * $Id: class.t3lib_tcemain.php 2574 2007-10-16 17:35:29Z mundaun $
  31   * Revised for TYPO3 3.9 October 2005 by Kasper Skaarhoj
  32   *
  33   * @author    Kasper Skaarhoj <kasperYYYY@typo3.com>
  34   */
  35  /**
  36   * [CLASS/FUNCTION INDEX of SCRIPT]
  37   *
  38   *
  39   *
  40   *  242: class t3lib_TCEmain
  41   *  367:     function start($data,$cmd,$altUserObject='')
  42   *  406:     function setMirror($mirror)
  43   *  431:     function setDefaultsFromUserTS($userTS)
  44   *  454:     function process_uploads($postFiles)
  45   *  492:     function process_uploads_traverseArray(&$outputArr,$inputArr,$keyToSet)
  46   *
  47   *              SECTION: PROCESSING DATA
  48   *  528:     function process_datamap()
  49   *  886:     function placeholderShadowing($table,$id)
  50   *  929:     function fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$realPid,$status,$tscPID)
  51   *
  52   *              SECTION: Evaluation of input values
  53   * 1152:     function checkValue($table,$field,$value,$id,$status,$realPid,$tscPID)
  54   * 1212:     function checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$uploadedFiles,$tscPID)
  55   * 1261:     function checkValue_input($res,$value,$tcaFieldConf,$PP,$field='')
  56   * 1299:     function checkValue_check($res,$value,$tcaFieldConf,$PP)
  57   * 1322:     function checkValue_radio($res,$value,$tcaFieldConf,$PP)
  58   * 1348:     function checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field)
  59   * 1458:     function checkValue_group_select_file($valueArray,$tcaFieldConf,$curValue,$uploadedFileArray,$status,$table,$id,$recFID)
  60   * 1632:     function checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field)
  61   * 1709:     function checkValue_flexArray2Xml($array, $addPrologue=FALSE)
  62   * 1721:     function _DELETE_FLEX_FORMdata(&$valueArrayToRemoveFrom,$deleteCMDS)
  63   * 1743:     function _MOVE_FLEX_FORMdata(&$valueArrayToMoveIn, $moveCMDS, $direction)
  64   * 1783:     function checkValue_inline($res,$value,$tcaFieldConf,$PP,$field)
  65   * 1825:     function checkValue_checkMax($tcaFieldConf, $valueArray)
  66   *
  67   *              SECTION: Helper functions for evaluation functions.
  68   * 1877:     function getUnique($table,$field,$value,$id,$newPid=0)
  69   * 1915:     function checkValue_input_Eval($value,$evalArray,$is_in)
  70   * 2012:     function checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,$type,$currentTable)
  71   * 2058:     function checkValue_group_select_explodeSelectGroupValue($value)
  72   * 2082:     function checkValue_flex_procInData($dataPart,$dataPart_current,$uploadedFiles,$dataStructArray,$pParams,$callBackFunc='')
  73   * 2121:     function checkValue_flex_procInData_travDS(&$dataValues,$dataValues_current,$uploadedFiles,$DSelements,$pParams,$callBackFunc,$structurePath)
  74   *
  75   *              SECTION: PROCESSING COMMANDS
  76   * 2267:     function process_cmdmap()
  77   *
  78   *              SECTION: Cmd: Copying
  79   * 2407:     function copyRecord($table,$uid,$destPid,$first=0,$overrideValues=array(),$excludeFields='')
  80   * 2529:     function copyPages($uid,$destPid)
  81   * 2583:     function copySpecificPage($uid,$destPid,$copyTablesArray,$first=0)
  82   * 2617:     function copyRecord_raw($table,$uid,$pid,$overrideArray=array())
  83   * 2681:     function rawCopyPageContent($old_pid,$new_pid,$copyTablesArray)
  84   * 2705:     function insertNewCopyVersion($table,$fieldArray,$realPid)
  85   * 2757:     function copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf,$realDestPid)
  86   * 2836:     function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)
  87   * 2864:     function copyRecord_procFilesRefs($conf, $uid, $value)
  88   *
  89   *              SECTION: Cmd: Moving, Localizing
  90   * 2933:     function moveRecord($table,$uid,$destPid)
  91   * 3128:     function moveRecord_procFields($table,$uid,$destPid)
  92   * 3148:     function moveRecord_procBasedOnFieldType($table,$uid,$destPid,$field,$value,$conf)
  93   * 3182:     function localize($table,$uid,$language)
  94   *
  95   *              SECTION: Cmd: Deleting
  96   * 3296:     function deleteAction($table, $id)
  97   * 3343:     function deleteEl($table, $uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE)
  98   * 3360:     function deleteVersionsForRecord($table, $uid, $forceHardDelete)
  99   * 3382:     function undeleteRecord($table,$uid)
 100   * 3399:     function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE,$undeleteRecord=FALSE)
 101   * 3512:     function deleteRecord_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj)
 102   * 3539:     function deletePages($uid,$force=FALSE,$forceHardDelete=FALSE)
 103   * 3567:     function deleteSpecificPage($uid,$forceHardDelete=FALSE)
 104   * 3592:     function canDeletePage($uid)
 105   * 3619:     function cannotDeleteRecord($table,$id)
 106   * 3638:     function deleteRecord_procFields($table, $uid, $undeleteRecord = false)
 107   * 3661:     function deleteRecord_procBasedOnFieldType($table, $uid, $field, $value, $conf, $undeleteRecord = false)
 108   *
 109   *              SECTION: Cmd: Versioning
 110   * 3722:     function versionizeRecord($table,$id,$label,$delete=FALSE,$versionizeTree=-1)
 111   * 3798:     function versionizePages($uid,$label,$versionizeTree)
 112   * 3861:     function version_swap($table,$id,$swapWith,$swapIntoWS=0)
 113   * 4032:     function version_clearWSID($table,$id)
 114   * 4066:     function version_setStage($table,$id,$stageId,$comment='')
 115   *
 116   *              SECTION: Cmd: Helper functions
 117   * 4111:     function remapListedDBRecords()
 118   * 4192:     function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)
 119   * 4219:     function remapListedDBRecords_procDBRefs($conf, $value, $MM_localUid, $table)
 120   * 4265:     function remapListedDBRecords_procInline($conf, $value, $uid, $table)
 121   *
 122   *              SECTION: Access control / Checking functions
 123   * 4308:     function checkModifyAccessList($table)
 124   * 4320:     function isRecordInWebMount($table,$id)
 125   * 4334:     function isInWebMount($pid)
 126   * 4348:     function checkRecordUpdateAccess($table,$id)
 127   * 4372:     function checkRecordInsertAccess($insertTable,$pid,$action=1)
 128   * 4406:     function isTableAllowedForThisPage($page_uid, $checkTable)
 129   * 4439:     function doesRecordExist($table,$id,$perms)
 130   * 4504:     function doesRecordExist_pageLookUp($id, $perms)
 131   * 4530:     function doesBranchExist($inList,$pid,$perms,$recurse)
 132   * 4564:     function tableReadOnly($table)
 133   * 4576:     function tableAdminOnly($table)
 134   * 4590:     function destNotInsideSelf($dest,$id)
 135   * 4622:     function getExcludeListArray()
 136   * 4645:     function doesPageHaveUnallowedTables($page_uid,$doktype)
 137   *
 138   *              SECTION: Information lookup
 139   * 4694:     function pageInfo($id,$field)
 140   * 4714:     function recordInfo($table,$id,$fieldList)
 141   * 4735:     function getRecordProperties($table,$id,$noWSOL=FALSE)
 142   * 4751:     function getRecordPropertiesFromRow($table,$row)
 143   *
 144   *              SECTION: Storing data to Database Layer
 145   * 4794:     function updateDB($table,$id,$fieldArray)
 146   * 4846:     function insertDB($table,$id,$fieldArray,$newVersion=FALSE,$suggestedUid=0,$dontSetNewIdIndex=FALSE)
 147   * 4919:     function checkStoredRecord($table,$id,$fieldArray,$action)
 148   * 4956:     function setHistory($table,$id,$logId)
 149   * 4989:     function clearHistory($maxAgeSeconds=604800,$table)
 150   * 5003:     function updateRefIndex($table,$id)
 151   *
 152   *              SECTION: Misc functions
 153   * 5035:     function getSortNumber($table,$uid,$pid)
 154   * 5108:     function resorting($table,$pid,$sortRow, $return_SortNumber_After_This_Uid)
 155   * 5139:     function setTSconfigPermissions($fieldArray,$TSConfig_p)
 156   * 5156:     function newFieldArray($table)
 157   * 5188:     function addDefaultPermittedLanguageIfNotSet($table,&$incomingFieldArray)
 158   * 5212:     function overrideFieldArray($table,$data)
 159   * 5228:     function compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray)
 160   * 5274:     function assemblePermissions($string)
 161   * 5291:     function rmComma($input)
 162   * 5301:     function convNumEntityToByteValue($input)
 163   * 5323:     function destPathFromUploadFolder($folder)
 164   * 5333:     function deleteClause($table)
 165   * 5349:     function getTCEMAIN_TSconfig($tscPID)
 166   * 5364:     function getTableEntries($table,$TSconfig)
 167   * 5377:     function getPID($table,$uid)
 168   * 5390:     function dbAnalysisStoreExec()
 169   * 5406:     function removeRegisteredFiles()
 170   * 5418:     function removeCacheFiles()
 171   * 5432:     function int_pageTreeInfo($CPtable,$pid,$counter, $rootID)
 172   * 5453:     function compileAdminTables()
 173   * 5470:     function fixUniqueInPid($table,$uid)
 174   * 5506:     function fixCopyAfterDuplFields($table,$uid,$prevUid,$update, $newData=array())
 175   * 5531:     function extFileFields($table)
 176   * 5552:     function getUniqueFields($table)
 177   * 5577:     function isReferenceField($conf)
 178   * 5588:     function getInlineFieldType($conf)
 179   * 5611:     function getCopyHeader($table,$pid,$field,$value,$count,$prevTitle='')
 180   * 5640:     function prependLabel($table)
 181   * 5657:     function resolvePid($table,$pid)
 182   * 5687:     function clearPrefixFromValue($table,$value)
 183   * 5702:     function extFileFunctions($table,$field,$filelist,$func)
 184   * 5732:     function noRecordsFromUnallowedTables($inList)
 185   * 5758:     function notifyStageChange($stat,$stageId,$table,$id,$comment)
 186   * 5853:     function notifyStageChange_getEmails($listOfUsers,$noTablePrefix=FALSE)
 187   *
 188   *              SECTION: Clearing cache
 189   * 5899:     function clear_cache($table,$uid)
 190   * 6009:     function clear_cacheCmd($cacheCmd)
 191   *
 192   *              SECTION: Logging
 193   * 6113:     function log($table,$recuid,$action,$recpid,$error,$details,$details_nr=-1,$data=array(),$event_pid=-1,$NEWid='')
 194   * 6130:     function newlog($message, $error=0)
 195   * 6140:     function printLogErrorMessages($redirect)
 196   *
 197   *              SECTION: Internal (do not use outside Core!)
 198   * 6202:     function internal_clearPageCache()
 199   *
 200   * TOTAL FUNCTIONS: 126
 201   * (This index is automatically created/updated by the extension "extdeveval")
 202   *
 203   */
 204  
 205  
 206  
 207  
 208  // *******************************
 209  // Including necessary libraries
 210  // *******************************
 211  require_once (PATH_t3lib.'class.t3lib_loaddbgroup.php');
 212  require_once (PATH_t3lib.'class.t3lib_parsehtml_proc.php');
 213  require_once (PATH_t3lib.'class.t3lib_stdgraphic.php');
 214  require_once (PATH_t3lib.'class.t3lib_basicfilefunc.php');
 215  require_once (PATH_t3lib.'class.t3lib_refindex.php');
 216  require_once (PATH_t3lib.'class.t3lib_flexformtools.php');
 217  
 218  
 219  
 220  
 221  
 222  
 223  
 224  
 225  
 226  
 227  
 228  /**
 229   * This is the TYPO3 Core Engine class for manipulation of the database
 230   * This class is used by eg. the tce_db.php script which provides an the interface for POST forms to this class.
 231   *
 232   * Dependencies:
 233   * - $GLOBALS['TCA'] must exist
 234   * - $GLOBALS['LANG'] (languageobject) may be preferred, but not fatal.
 235   *
 236   * tce_db.php for further comments and SYNTAX! Also see document 'TYPO3 Core API' for details.
 237   *
 238   * @author    Kasper Skaarhoj <kasperYYYY@typo3.com>
 239   * @package TYPO3
 240   * @subpackage t3lib
 241   */
 242  class t3lib_TCEmain    {
 243  
 244  
 245          // *********************
 246          // Public variables you can configure before using the class:
 247          // *********************
 248  
 249      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.
 250      var $enableLogging = TRUE;                // Boolean: If true, actions are logged to sys_log.
 251      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.
 252      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.
 253      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!
 254      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
 255      var $checkStoredRecords_loose = TRUE;    // Boolean: If set, values '' and 0 will equal each other when the stored records are checked.
 256      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
 257      var $neverHideAtCopy = FALSE;            // Boolean. If set, then the 'hideAtCopy' flag for tables will be ignored.
 258      var $dontProcessTransformations = FALSE;    // Boolean: If set, then transformations are NOT performed on the input.
 259      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!
 260      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!
 261      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!
 262  
 263      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)
 264      var $generalComment = '';                // General comment, eg. for staging in workspaces.
 265  
 266      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
 267  
 268      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)
 269      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!
 270      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.
 271      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.
 272      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)
 273  
 274      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.
 275  
 276  
 277  
 278  
 279          // *********************
 280          // Internal variables (mapping arrays) which can be used (read-only) from outside
 281          // *********************
 282      var $autoVersionIdMap = Array();            // Contains mapping of auto-versionized records.
 283      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
 284      var $substNEWwithIDs_table = Array();        // Like $substNEWwithIDs, but where each old "NEW..." id is mapped to the table it was from.
 285      var $newRelatedIDs = Array();                // Holds the tables and there the ids of newly created child records from IRRE
 286      var $copyMappingArray_merged = Array();        // This array is the sum of all copying operations in this class. May be READ from outside, thus partly public.
 287      var $copiedFileMap = Array();                // A map between input file name and final destination for files being attached to records.
 288      var    $errorLog = Array();                    // Errors are collected in this variable.
 289  
 290  
 291  
 292          // *********************
 293          // Internal Variables, do not touch.
 294          // *********************
 295  
 296          // Variables set in init() function:
 297      var $BE_USER;        // The user-object the script uses. If not set from outside, this is set to the current global $BE_USER.
 298      var $userid;        // will be set to uid of be_user executing this script
 299      var $username;        // will be set to username of be_user executing this script
 300      var $admin;            // will be set if user is admin
 301  
 302      var $defaultPermissions = array(        // Can be overridden from $TYPO3_CONF_VARS
 303          'user' => 'show,edit,delete,new,editcontent',
 304          'group' => 'show,edit,new,editcontent',
 305          'everybody' => ''
 306      );
 307  
 308      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.
 309      var $datamap = Array();        // Set with incoming data array
 310      var $cmdmap = Array();        // Set with incoming cmd array
 311  
 312          // Internal static:
 313      var $pMap = Array(        // Permission mapping
 314          'show' => 1,            // 1st bit
 315          'edit' => 2,            // 2nd bit
 316          'delete' => 4,            // 3rd bit
 317          'new' => 8,                // 4th bit
 318          'editcontent' => 16        // 5th bit
 319      );
 320      var $sortIntervals = 256;                    // Integer: The interval between sorting numbers used with tables with a 'sorting' field defined. Min 1
 321  
 322          // Internal caching arrays
 323      var $recUpdateAccessCache = Array();        // Used by function checkRecordUpdateAccess() to store whether a record is updateable or not.
 324      var $recInsertAccessCache = Array();        // User by function checkRecordInsertAccess() to store whether a record can be inserted on a page id
 325      var $isRecordInWebMount_Cache=array();        // Caching array for check of whether records are in a webmount
 326      var $isInWebMount_Cache=array();            // Caching array for page ids in webmounts
 327      var $cachedTSconfig = array();                // Caching for collecting TSconfig for page ids
 328      var $pageCache = Array();                    // Used for caching page records in pageInfo()
 329      var $checkWorkspaceCache = Array();            // Array caching workspace access for BE_USER
 330  
 331          // Other arrays:
 332      var $dbAnalysisStore=array();                // For accumulation of MM relations that must be written after new records are created.
 333      var $removeFilesStore=array();                // For accumulation of files which must be deleted after processing of all input content
 334      var $uploadedFileArray = array();            // Uploaded files, set by process_uploads()
 335      var $registerDBList=array();                // Used for tracking references that might need correction after operations
 336      var $registerDBPids=array();                // Used for tracking references that might need correction in pid field after operations (e.g. IRRE)
 337      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.
 338      var $remapStack = array();                    // array used for remapping uids and values at the end of process_datamap
 339      var $remapStackRecords = array();            // array used for remapping uids and values at the end of process_datamap (e.g. $remapStackRecords[<table>][<uid>] = <index in $remapStack>)
 340      var $updateRefIndexStack = array();            // array used for additional calls to $this->updateRefIndex
 341      var $callFromImpExp = false;                // tells, that this TCEmain was called from tx_impext - this variable is set by tx_impexp
 342  
 343          // Various
 344      var $fileFunc;                                // For "singleTon" file-manipulation object
 345      var $checkValue_currentRecord=array();        // Set to "currentRecord" during checking of values.
 346      var $autoVersioningUpdate = FALSE;            // A signal flag used to tell file processing that autoversioning has happend and hence certain action should be applied.
 347  
 348  
 349  
 350  
 351  
 352  
 353  
 354  
 355  
 356  
 357  
 358  
 359      /**
 360       * Initializing.
 361       * For details, see 'TYPO3 Core API' document.
 362       * This function does not start the processing of data, but merely initializes the object
 363       *
 364       * @param    array        Data to be modified or inserted in the database
 365       * @param    array        Commands to copy, move, delete, localize, versionize records.
 366       * @param    object        An alternative userobject you can set instead of the default, which is $GLOBALS['BE_USER']
 367       * @return    void
 368       */
 369  	function start($data,$cmd,$altUserObject='')    {
 370  
 371              // Initializing BE_USER
 372          $this->BE_USER = is_object($altUserObject) ? $altUserObject : $GLOBALS['BE_USER'];
 373          $this->userid = $this->BE_USER->user['uid'];
 374          $this->username = $this->BE_USER->user['username'];
 375          $this->admin = $this->BE_USER->user['admin'];
 376  
 377          if ($GLOBALS['BE_USER']->uc['recursiveDelete'])    {
 378              $this->deleteTree = 1;
 379          }
 380  
 381              // Initializing default permissions for pages
 382          $defaultPermissions = $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPermissions'];
 383          if (isset($defaultPermissions['user']))        {$this->defaultPermissions['user'] = $defaultPermissions['user'];}
 384          if (isset($defaultPermissions['group']))        {$this->defaultPermissions['group'] = $defaultPermissions['group'];}
 385          if (isset($defaultPermissions['everybody']))        {$this->defaultPermissions['everybody'] = $defaultPermissions['everybody'];}
 386  
 387              // generates the excludelist, based on TCA/exclude-flag and non_exclude_fields for the user:
 388          $this->exclude_array = $this->admin ? array() : $this->getExcludeListArray();
 389  
 390              // Setting the data and cmd arrays
 391          if (is_array($data)) {
 392              reset($data);
 393              $this->datamap = $data;
 394          }
 395          if (is_array($cmd))    {
 396              reset($cmd);
 397              $this->cmdmap = $cmd;
 398          }
 399      }
 400  
 401      /**
 402       * Function that can mirror input values in datamap-array to other uid numbers.
 403       * 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]
 404       *
 405       * @param    array        This array has the syntax $mirror[table_name][uid] = [list of uids to copy data-value TO!]
 406       * @return    void
 407       */
 408  	function setMirror($mirror)    {
 409          if (is_array($mirror))    {
 410              reset($mirror);
 411              while(list($table,$uid_array)=each($mirror))    {
 412                  if (isset($this->datamap[$table]))    {
 413                      reset($uid_array);
 414                      while (list($id,$uidList) = each($uid_array))    {
 415                          if (isset($this->datamap[$table][$id]))    {
 416                              $theIdsInArray = t3lib_div::trimExplode(',',$uidList,1);
 417                              while(list(,$copyToUid)=each($theIdsInArray))    {
 418                                  $this->datamap[$table][$copyToUid] = $this->datamap[$table][$id];
 419                              }
 420                          }
 421                      }
 422                  }
 423              }
 424          }
 425      }
 426  
 427      /**
 428       * Initializes default values coming from User TSconfig
 429       *
 430       * @param    array        User TSconfig array
 431       * @return    void
 432       */
 433  	function setDefaultsFromUserTS($userTS)    {
 434          global $TCA;
 435          if (is_array($userTS))    {
 436              foreach($userTS as $k => $v)    {
 437                  $k = substr($k,0,-1);
 438                  if ($k && is_array($v) && isset($TCA[$k]))    {
 439                      if (is_array($this->defaultValues[$k]))    {
 440                          $this->defaultValues[$k] = array_merge($this->defaultValues[$k],$v);
 441                      } else {
 442                          $this->defaultValues[$k] = $v;
 443                      }
 444                  }
 445              }
 446          }
 447      }
 448  
 449      /**
 450       * Processing of uploaded files.
 451       * 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.
 452       *
 453       * @param    array        $_FILES array
 454       * @return    void
 455       */
 456  	function process_uploads($postFiles)    {
 457  
 458          if (is_array($postFiles))    {
 459  
 460                  // Editing frozen:
 461              if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze'])    {
 462                  $this->newlog('All editing in this workspace has been frozen!',1);
 463                  return FALSE;
 464              }
 465  
 466              reset($postFiles);
 467              $subA = current($postFiles);
 468              if (is_array($subA))    {
 469                  if (is_array($subA['name']) && is_array($subA['type']) && is_array($subA['tmp_name']) && is_array($subA['size']))    {
 470                          // Initialize the uploadedFilesArray:
 471                      $this->uploadedFileArray=array();
 472  
 473                          // For each entry:
 474                      foreach($subA as $key => $values)    {
 475                          $this->process_uploads_traverseArray($this->uploadedFileArray,$values,$key);
 476                      }
 477                  } else {
 478                      $this->uploadedFileArray=$subA;
 479                  }
 480              }
 481          }
 482      }
 483  
 484      /**
 485       * Traverse the upload array if needed to rearrange values.
 486       *
 487       * @param    array        $this->uploadedFileArray passed by reference
 488       * @param    array        Input array  ($_FILES parts)
 489       * @param    string        The current $_FILES array key to set on the outermost level.
 490       * @return    void
 491       * @access private
 492       * @see process_uploads()
 493       */
 494  	function process_uploads_traverseArray(&$outputArr,$inputArr,$keyToSet)    {
 495          if (is_array($inputArr))    {
 496              foreach($inputArr as $key => $value)    {
 497                  $this->process_uploads_traverseArray($outputArr[$key],$inputArr[$key],$keyToSet);
 498              }
 499          } else {
 500              $outputArr[$keyToSet]=$inputArr;
 501          }
 502      }
 503  
 504  
 505  
 506  
 507  
 508  
 509  
 510  
 511  
 512  
 513  
 514  
 515  
 516  
 517  
 518      /*********************************************
 519       *
 520       * HOOKS
 521       *
 522       *********************************************/
 523  
 524      /**
 525       * Hook: processDatamap_afterDatabaseOperations
 526       * (calls $hookObj->processDatamap_afterDatabaseOperations($status, $table, $id, $fieldArray, $this);)
 527       *
 528       * Note: When using the hook after INSERT operations, you will only get the temporary NEW... id passed to your hook as $id,
 529       *         but you can easily translate it to the real uid of the inserted record using the $this->substNEWwithIDs array.
 530       *
 531       * @param    object        $hookObjectsArr: (reference) Array with hook objects
 532       * @param    string        $status: (reference) Status of the current operation, 'new' or 'update
 533       * @param    string        $table: (refrence) The table currently processing data for
 534       * @param    string        $id: (reference) The record uid currently processing data for, [integer] or [string] (like 'NEW...')
 535       * @param    array        $fieldArray: (reference) The field array of a record
 536       * @return    void
 537       */
 538  	function hook_processDatamap_afterDatabaseOperations(&$hookObjectsArr, &$status, &$table, &$id, &$fieldArray) {
 539              // Process hook directly:
 540          if (!isset($this->remapStackRecords[$table][$id])) {
 541              foreach($hookObjectsArr as $hookObj)    {
 542                  if (method_exists($hookObj, 'processDatamap_afterDatabaseOperations')) {
 543                      $hookObj->processDatamap_afterDatabaseOperations($status, $table, $id, $fieldArray, $this);
 544                  }
 545              }
 546              // 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:
 547          } else {
 548              $this->remapStackRecords[$table][$id]['processDatamap_afterDatabaseOperations'] = array(
 549                  'status' => $status,
 550                  'fieldArray' => $fieldArray,
 551                  'hookObjectsArr' => $hookObjectsArr,
 552              );
 553          }
 554      }
 555  
 556  
 557  
 558  
 559  
 560  
 561  
 562  
 563  
 564  
 565  
 566  
 567  
 568  
 569      /*********************************************
 570       *
 571       * PROCESSING DATA
 572       *
 573       *********************************************/
 574  
 575      /**
 576       * Processing the data-array
 577       * Call this function to process the data-array set by start()
 578       *
 579       * @return    void
 580       */
 581  	function process_datamap() {
 582          global $TCA, $TYPO3_CONF_VARS;
 583  
 584              // Editing frozen:
 585          if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze'])    {
 586              $this->newlog('All editing in this workspace has been frozen!',1);
 587              return FALSE;
 588          }
 589  
 590              // First prepare user defined objects (if any) for hooks which extend this function:
 591          $hookObjectsArr = array();
 592          if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'])) {
 593              foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'] as $classRef) {
 594                  $hookObjectsArr[] = &t3lib_div::getUserObj($classRef);
 595              }
 596          }
 597  
 598              // 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.
 599          $orderOfTables = Array();
 600          if (isset($this->datamap['pages']))    {        // Set pages first.
 601              $orderOfTables[]='pages';
 602          }
 603          reset($this->datamap);
 604          while (list($table,) = each($this->datamap))    {
 605              if ($table!='pages')    {
 606                  $orderOfTables[]=$table;
 607              }
 608          }
 609  
 610              // Process the tables...
 611          foreach($orderOfTables as $table)    {
 612                  /* Check if
 613                      - table is set in $TCA,
 614                      - table is NOT readOnly
 615                      - the table is set with content in the data-array (if not, there's nothing to process...)
 616                      - permissions for tableaccess OK
 617                  */
 618              $modifyAccessList = $this->checkModifyAccessList($table);
 619              if (!$modifyAccessList)    {
 620                  $id = 0;
 621                  $this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table));
 622              }
 623              if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->datamap[$table]) && $modifyAccessList)    {
 624                  if ($this->reverseOrder)    {
 625                      $this->datamap[$table] = array_reverse($this->datamap[$table], 1);
 626                  }
 627  
 628                      // For each record from the table, do:
 629                      // $id is the record uid, may be a string if new records...
 630                      // $incomingFieldArray is the array of fields
 631                  foreach($this->datamap[$table] as $id => $incomingFieldArray)    {
 632                      if (is_array($incomingFieldArray))    {
 633  
 634                              // Hook: processDatamap_preProcessIncomingFieldArray
 635                          foreach($hookObjectsArr as $hookObj)    {
 636                              if (method_exists($hookObj, 'processDatamap_preProcessFieldArray')) {
 637                                  $hookObj->processDatamap_preProcessFieldArray($incomingFieldArray, $table, $id, $this);
 638                              }
 639                          }
 640  
 641                              // ******************************
 642                              // Checking access to the record
 643                              // ******************************
 644                          $createNewVersion = FALSE;
 645                          $recordAccess = FALSE;
 646                          $old_pid_value = '';
 647                          $resetRejected = FALSE;
 648                          $this->autoVersioningUpdate = FALSE;
 649  
 650                          if (!t3lib_div::testInt($id)) {               // Is it a new record? (Then Id is a string)
 651                              $fieldArray = $this->newFieldArray($table);    // Get a fieldArray with default values
 652                              if (isset($incomingFieldArray['pid']))    {    // A pid must be set for new records.
 653                                      // $value = the pid
 654                                  $pid_value = $incomingFieldArray['pid'];
 655  
 656                                      // Checking and finding numerical pid, it may be a string-reference to another value
 657                                  $OK = 1;
 658                                  if (strstr($pid_value,'NEW'))    {    // If a NEW... id
 659                                      if (substr($pid_value,0,1)=='-') {$negFlag=-1;$pid_value=substr($pid_value,1);} else {$negFlag=1;}
 660                                      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.
 661                                          $old_pid_value = $pid_value;
 662                                          $pid_value=intval($negFlag*$this->substNEWwithIDs[$pid_value]);
 663                                      } else {$OK = 0;}    // If not found in the substArray we must stop the process...
 664                                  } 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.
 665                                      if ($WSdestPage = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, 'pages', $pid_value, 'uid,t3ver_swapmode'))    {    // Looks for workspace version of page.
 666                                          if ($WSdestPage['t3ver_swapmode']==0)    {    // if swapmode is zero, then change pid value.
 667                                              $pid_value = $WSdestPage['uid'];
 668                                          }
 669                                      }
 670                                  }
 671                                  $pid_value = intval($pid_value);
 672  
 673                                      // The $pid_value is now the numerical pid at this point
 674                                  if ($OK)    {
 675                                      $sortRow = $TCA[$table]['ctrl']['sortby'];
 676                                      if ($pid_value>=0)    {    // Points to a page on which to insert the element, possibly in the top of the page
 677                                          if ($sortRow)    {    // If this table is sorted we better find the top sorting number
 678                                              $fieldArray[$sortRow] = $this->getSortNumber($table,0,$pid_value);
 679                                          }
 680                                          $fieldArray['pid'] = $pid_value;    // The numerical pid is inserted in the data array
 681                                      } else {    // points to another record before ifself
 682                                          if ($sortRow)    {    // If this table is sorted we better find the top sorting number
 683                                              $tempArray=$this->getSortNumber($table,0,$pid_value);    // Because $pid_value is < 0, getSortNumber returns an array
 684                                              $fieldArray['pid'] = $tempArray['pid'];
 685                                              $fieldArray[$sortRow] = $tempArray['sortNumber'];
 686                                          } else {    // Here we fetch the PID of the record that we point to...
 687                                              $tempdata = $this->recordInfo($table,abs($pid_value),'pid');
 688                                              $fieldArray['pid']=$tempdata['pid'];
 689                                          }
 690                                      }
 691                                  }
 692                              }
 693                              $theRealPid = $fieldArray['pid'];
 694  
 695                                  // Now, check if we may insert records on this pid.
 696                              if ($theRealPid>=0)    {
 697                                  $recordAccess = $this->checkRecordInsertAccess($table,$theRealPid);        // Checks if records can be inserted on this $pid.
 698                                  if ($recordAccess)    {
 699                                      $this->addDefaultPermittedLanguageIfNotSet($table,$incomingFieldArray);
 700                                      $recordAccess = $this->BE_USER->recordEditAccessInternals($table,$incomingFieldArray,TRUE);
 701                                      if (!$recordAccess)        {
 702                                          $this->newlog("recordEditAccessInternals() check failed. [".$this->BE_USER->errorMsg."]",1);
 703                                      } elseif(!$this->bypassWorkspaceRestrictions)    {
 704                                              // Workspace related processing:
 705                                          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
 706                                              if ($res<0)    {
 707                                                  $recordAccess = FALSE;
 708                                                  $this->newlog('Stage for versioning root point and users access level did not allow for editing',1);
 709                                              }
 710                                          } else {    // So, if no live records were allowed, we have to create a new version of this record:
 711                                              if ($TCA[$table]['ctrl']['versioningWS'])    {
 712                                                  $createNewVersion = TRUE;
 713                                              } else {
 714                                                  $recordAccess = FALSE;
 715                                                  $this->newlog('Record could not be created in this workspace in this branch',1);
 716                                              }
 717                                          }
 718                                      }
 719                                  }
 720                              } else {
 721                                  debug('Internal ERROR: pid should not be less than zero!');
 722                              }
 723                              $status = 'new';                        // Yes new record, change $record_status to 'insert'
 724                          } else {    // Nope... $id is a number
 725                              $fieldArray = array();
 726                              $recordAccess = $this->checkRecordUpdateAccess($table,$id);
 727                              if (!$recordAccess)        {
 728                                  $propArr = $this->getRecordProperties($table,$id);
 729                                  $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']);
 730                              } else {    // Next check of the record permissions (internals)
 731                                  $recordAccess = $this->BE_USER->recordEditAccessInternals($table,$id);
 732                                  if (!$recordAccess)        {
 733                                      $propArr = $this->getRecordProperties($table,$id);
 734                                      $this->newlog("recordEditAccessInternals() check failed. [".$this->BE_USER->errorMsg."]",1);
 735                                  } else {    // Here we fetch the PID of the record that we point to...
 736                                      $tempdata = $this->recordInfo($table,$id,'pid'.($TCA[$table]['ctrl']['versioningWS']?',t3ver_wsid,t3ver_stage':''));
 737                                      $theRealPid = $tempdata['pid'];
 738  
 739                                          // Prepare the reset of the rejected flag if set:
 740                                      if ($TCA[$table]['ctrl']['versioningWS'] && $tempdata['t3ver_stage']<0)    {
 741                                          $resetRejected = TRUE;
 742                                      }
 743  
 744                                          // Checking access in case of offline workspace:
 745                                      if (!$this->bypassWorkspaceRestrictions && $errorCode = $this->BE_USER->workspaceCannotEditRecord($table,$tempdata))    {
 746                                          $recordAccess = FALSE;        // Versioning is required and it must be offline version!
 747  
 748                                              // 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.
 749                                          if ($this->BE_USER->workspaceAllowAutoCreation($table,$id,$theRealPid))    {
 750                                              $tce = t3lib_div::makeInstance('t3lib_TCEmain');
 751                                              $tce->stripslashes_values = 0;
 752  
 753                                                  // Setting up command for creating a new version of the record:
 754                                              $cmd = array();
 755                                              $cmd[$table][$id]['version'] = array(
 756                                                  'action' => 'new',
 757                                                  'treeLevels' => -1,    // Default is to create a version of the individual records...
 758                                                  'label' => 'Auto-created for WS #'.$this->BE_USER->workspace
 759                                              );
 760                                              $tce->start(array(),$cmd);
 761                                              $tce->process_cmdmap();
 762                                              $this->errorLog = array_merge($this->errorLog,$tce->errorLog);
 763  
 764                                              if ($tce->copyMappingArray[$table][$id])    {
 765                                                  $this->uploadedFileArray[$table][$tce->copyMappingArray[$table][$id]] = $this->uploadedFileArray[$table][$id];
 766                                                  $id = $this->autoVersionIdMap[$table][$id] = $tce->copyMappingArray[$table][$id];
 767                                                  $recordAccess = TRUE;
 768                                                  $this->autoVersioningUpdate = TRUE;
 769                                              } else $this->newlog("Could not be edited in offline workspace in the branch where found (failure state: '".$errorCode."'). Auto-creation of version failed!",1);
 770                                          } else $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);
 771                                      }
 772                                  }
 773                              }
 774                              $status = 'update';    // the default is 'update'
 775                          }
 776  
 777                              // If access was granted above, proceed to create or update record:
 778                          if ($recordAccess)    {
 779  
 780                              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.
 781                              $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
 782                              if ($status=='new' && $table=='pages' && is_array($TSConfig['permissions.']))    {
 783                                  $fieldArray = $this->setTSconfigPermissions($fieldArray,$TSConfig['permissions.']);
 784                              }
 785                              if ($createNewVersion)    {
 786                                  $newVersion_placeholderFieldArray = $fieldArray;
 787                              }
 788  
 789                                  // Processing of all fields in incomingFieldArray and setting them in $fieldArray
 790                              $fieldArray = $this->fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$theRealPid,$status,$tscPID);
 791  
 792                                  // NOTICE! All manipulation beyond this point bypasses both "excludeFields" AND possible "MM" relations / file uploads to field!
 793  
 794                                  // Forcing some values unto field array:
 795                              $fieldArray = $this->overrideFieldArray($table,$fieldArray);    // NOTICE: This overriding is potentially dangerous; permissions per field is not checked!!!
 796                              if ($createNewVersion)    {
 797                                  $newVersion_placeholderFieldArray = $this->overrideFieldArray($table,$newVersion_placeholderFieldArray);
 798                              }
 799  
 800                                  // Setting system fields
 801                              if ($status=='new')    {
 802                                  if ($TCA[$table]['ctrl']['crdate'])    {
 803                                      $fieldArray[$TCA[$table]['ctrl']['crdate']]=time();
 804                                      if ($createNewVersion)    $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['crdate']]=time();
 805                                  }
 806                                  if ($TCA[$table]['ctrl']['cruser_id'])    {
 807                                      $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid;
 808                                      if ($createNewVersion)    $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid;
 809                                  }
 810                              } elseif ($this->checkSimilar) {    // Removing fields which are equal to the current value:
 811                                  $fieldArray = $this->compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray);
 812                              }
 813                              if ($TCA[$table]['ctrl']['tstamp'] && count($fieldArray))    {
 814                                  $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
 815                                  if ($createNewVersion)    $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
 816                              }
 817                              if ($resetRejected)    {
 818                                  $fieldArray['t3ver_stage'] = 0;
 819                              }
 820  
 821                                  // Hook: processDatamap_postProcessFieldArray
 822                              foreach($hookObjectsArr as $hookObj)    {
 823                                  if (method_exists($hookObj, 'processDatamap_postProcessFieldArray')) {
 824                                      $hookObj->processDatamap_postProcessFieldArray($status, $table, $id, $fieldArray, $this);
 825                                  }
 826                              }
 827  
 828                                  // Performing insert/update. If fieldArray has been unset by some userfunction (see hook above), don't do anything
 829                                  // Kasper: Unsetting the fieldArray is dangerous; MM relations might be saved already and files could have been uploaded that are now "lost"
 830                              if (is_array($fieldArray)) {
 831                                  if ($status=='new')    {
 832                                      if ($createNewVersion)    {    // This creates a new version of the record with online placeholder and offline version
 833                                          $versioningType = $table==='pages' ? $this->BE_USER->workspaceVersioningTypeGetClosest(t3lib_div::intInRange($TYPO3_CONF_VARS['BE']['newPagesVersioningType'],-1,1)) : -1;
 834                                          if ($this->BE_USER->workspaceVersioningTypeAccess($versioningType))    {
 835                                              $newVersion_placeholderFieldArray['t3ver_label'] = 'INITIAL PLACEHOLDER';
 836                                              $newVersion_placeholderFieldArray['t3ver_state'] = 1;    // Setting placeholder state value for temporary record
 837                                              $newVersion_placeholderFieldArray['t3ver_wsid'] = $this->BE_USER->workspace;    // Setting workspace - only so display of place holders can filter out those from other workspaces.
 838                                              $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['label']] = '[PLACEHOLDER, WS#'.$this->BE_USER->workspace.']';
 839                                              $this->insertDB($table,$id,$newVersion_placeholderFieldArray,FALSE);    // Saving placeholder as 'original'
 840  
 841                                                  // For the actual new offline version, set versioning values to point to placeholder:
 842                                              $fieldArray['pid'] = -1;
 843                                              $fieldArray['t3ver_oid'] = $this->substNEWwithIDs[$id];
 844                                              $fieldArray['t3ver_id'] = 1;
 845                                              $fieldArray['t3ver_state'] = -1;    // Setting placeholder state value for version (so it can know it is currently a new version...)
 846                                              $fieldArray['t3ver_label'] = 'First draft version';
 847                                              $fieldArray['t3ver_wsid'] = $this->BE_USER->workspace;
 848                                              if ($table==='pages') {        // Swap mode set to "branch" so we can build branches for pages.
 849                                                  $fieldArray['t3ver_swapmode'] = $versioningType;
 850                                              }
 851                                              $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!
 852                                              if ($phShadowId)    {
 853                                                  $this->placeholderShadowing($table,$phShadowId);
 854                                              }
 855                                          } else $this->newlog('Versioning type "'.$versioningType.'" was not allowed, so could not create new record.',1);
 856                                      } else {
 857                                          $this->insertDB($table,$id,$fieldArray,FALSE,$incomingFieldArray['uid']);
 858                                      }
 859                                  } else {
 860                                      $this->updateDB($table,$id,$fieldArray);
 861                                      $this->placeholderShadowing($table,$id);
 862                                  }
 863                              }
 864  
 865                                  /*
 866                                   * Hook: processDatamap_afterDatabaseOperations
 867                                   *
 868                                   * Note: When using the hook after INSERT operations, you will only get the temporary NEW... id passed to your hook as $id,
 869                                   *         but you can easily translate it to the real uid of the inserted record using the $this->substNEWwithIDs array.
 870                                   */
 871                                  $this->hook_processDatamap_afterDatabaseOperations($hookObjectsArr, $status, $table, $id, $fieldArray);
 872                          }    // if ($recordAccess)    {
 873                      }    // if (is_array($incomingFieldArray))    {
 874                  }
 875              }
 876          }
 877  
 878              // Process the stack of relations to remap/correct
 879          $this->processRemapStack();
 880  
 881          $this->dbAnalysisStoreExec();
 882          $this->removeRegisteredFiles();
 883      }
 884  
 885      /**
 886       * Fix shadowing of data in case we are editing a offline version of a live "New" placeholder record:
 887       *
 888       * @param    string        Table name
 889       * @param    integer        Record uid
 890       * @return    void
 891       */
 892  	function placeholderShadowing($table,$id)    {
 893          global $TCA;
 894  
 895          t3lib_div::loadTCA($table);
 896          if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'*'))    {
 897              if ((int)$liveRec['t3ver_state']===1)    {
 898                  $justStoredRecord = t3lib_BEfunc::getRecord($table,$id);
 899                  $newRecord = array();
 900  
 901                  $shadowCols = $TCA[$table]['ctrl']['shadowColumnsForNewPlaceholders'];
 902                  $shadowCols.= ','.$TCA[$table]['ctrl']['languageField'];
 903                  $shadowCols.= ','.$TCA[$table]['ctrl']['transOrigPointerField'];
 904                  $shadowCols.= ','.$TCA[$table]['ctrl']['type'];
 905                  $shadowCols.= ','.$TCA[$table]['ctrl']['label'];
 906  
 907                  $shadowColumns = array_unique(t3lib_div::trimExplode(',', $shadowCols,1));
 908                  foreach($shadowColumns as $fieldName)    {
 909                      if (strcmp($justStoredRecord[$fieldName],$liveRec[$fieldName]) && isset($TCA[$table]['columns'][$fieldName]) && $fieldName!=='uid' && $fieldName!=='pid')    {
 910                          $newRecord[$fieldName] = $justStoredRecord[$fieldName];
 911                      }
 912                  }
 913  
 914                  if (count($newRecord))    {
 915                      $this->newlog('Shadowing done on fields '.implode(',',array_keys($newRecord)).' in Placeholder record '.$table.':'.$liveRec['uid'].' (offline version UID='.$id.')');
 916                      $this->updateDB($table,$liveRec['uid'],$newRecord);
 917                  }
 918              }
 919          }
 920      }
 921  
 922      /**
 923       * Filling in the field array
 924       * $this->exclude_array is used to filter fields if needed.
 925       *
 926       * @param    string        Table name
 927       * @param    [type]        $id: ...
 928       * @param    array        Default values, Preset $fieldArray with 'pid' maybe (pid and uid will be not be overridden anyway)
 929       * @param    array        $incomingFieldArray is which fields/values you want to set. There are processed and put into $fieldArray if OK
 930       * @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.
 931       * @param    string        $status = 'new' or 'update'
 932       * @param    [type]        $tscPID: ...
 933       * @return    [type]        ...
 934       */
 935  	function fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$realPid,$status,$tscPID)    {
 936          global $TCA;
 937  
 938              // Initialize:
 939          t3lib_div::loadTCA($table);
 940          $originalLanguageRecord = NULL;
 941          $originalLanguage_diffStorage = NULL;
 942          $diffStorageFlag = FALSE;
 943  
 944              // Setting 'currentRecord' and 'checkValueRecord':
 945          if (strstr($id,'NEW'))    {
 946              $currentRecord = $checkValueRecord = $fieldArray;    // must have the 'current' array - not the values after processing below...
 947  
 948                  // IF $incomingFieldArray is an array, overlay it.
 949                  // 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...
 950              if (is_array($incomingFieldArray) && is_array($checkValueRecord))    {
 951                  $checkValueRecord = t3lib_div::array_merge_recursive_overrule($checkValueRecord, $incomingFieldArray);
 952              }
 953          } else {
 954              $currentRecord = $checkValueRecord = $this->recordInfo($table,$id,'*');    // We must use the current values as basis for this!
 955  
 956              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.
 957  
 958                  // Get original language record if available:
 959              if (is_array($currentRecord)
 960                      && $TCA[$table]['ctrl']['transOrigDiffSourceField']
 961                      && $TCA[$table]['ctrl']['languageField']
 962                      && $currentRecord[$TCA[$table]['ctrl']['languageField']] > 0
 963                      && $TCA[$table]['ctrl']['transOrigPointerField']
 964                      && intval($currentRecord[$TCA[$table]['ctrl']['transOrigPointerField']]) > 0)    {
 965  
 966                  $lookUpTable = $TCA[$table]['ctrl']['transOrigPointerTable'] ? $TCA[$table]['ctrl']['transOrigPointerTable'] : $table;
 967                  $originalLanguageRecord = $this->recordInfo($lookUpTable,$currentRecord[$TCA[$table]['ctrl']['transOrigPointerField']],'*');
 968                  t3lib_BEfunc::workspaceOL($lookUpTable,$originalLanguageRecord);
 969                  $originalLanguage_diffStorage = unserialize($currentRecord[$TCA[$table]['ctrl']['transOrigDiffSourceField']]);
 970              }
 971          }
 972          $this->checkValue_currentRecord = $checkValueRecord;
 973  
 974              /*
 975                  In the following all incoming value-fields are tested:
 976                  - Are the user allowed to change the field?
 977                  - Is the field uid/pid (which are already set)
 978                  - perms-fields for pages-table, then do special things...
 979                  - If the field is nothing of the above and the field is configured in TCA, the fieldvalues are evaluated by ->checkValue
 980  
 981                  If everything is OK, the field is entered into $fieldArray[]
 982              */
 983          foreach($incomingFieldArray as $field => $fieldValue)    {
 984              if (!in_array($table.'-'.$field, $this->exclude_array) && !$this->data_disableFields[$table][$id][$field])    {    // The field must be editable.
 985  
 986                      // Checking if a value for language can be changed:
 987                  $languageDeny = $TCA[$table]['ctrl']['languageField'] && !strcmp($TCA[$table]['ctrl']['languageField'], $field) && !$this->BE_USER->checkLanguageAccess($fieldValue);
 988  
 989                  if (!$languageDeny)    {
 990                          // Stripping slashes - will probably be removed the day $this->stripslashes_values is removed as an option...
 991                      if ($this->stripslashes_values)    {
 992                          if (is_array($fieldValue))    {
 993                              t3lib_div::stripSlashesOnArray($fieldValue);
 994                          } else $fieldValue = stripslashes($fieldValue);
 995                      }
 996  
 997                      switch ($field)    {
 998                          case 'uid':
 999                          case 'pid':
1000                              // Nothing happens, already set
1001                          break;
1002                          case 'perms_userid':
1003                          case 'perms_groupid':
1004                          case 'perms_user':
1005                          case 'perms_group':
1006                          case 'perms_everybody':
1007                                  // Permissions can be edited by the owner or the administrator
1008                              if ($table=='pages' && ($this->admin || $status=='new' || $this->pageInfo($id,'perms_userid')==$this->userid) )    {
1009                                  $value=intval($fieldValue);
1010                                  switch($field)    {
1011                                      case 'perms_userid':
1012                                          $fieldArray[$field]=$value;
1013                                      break;
1014                                      case 'perms_groupid':
1015                                          $fieldArray[$field]=$value;
1016                                      break;
1017                                      default:
1018                                          if ($value>=0 && $value<pow(2,5))    {
1019                                              $fieldArray[$field]=$value;
1020                                          }
1021                                      break;
1022                                  }
1023                              }
1024                          break;
1025                          case 't3ver_oid':
1026                          case 't3ver_id':
1027                          case 't3ver_wsid':
1028                          case 't3ver_state':
1029                          case 't3ver_swapmode':
1030                          case 't3ver_count':
1031                          case 't3ver_stage':
1032                          case 't3ver_tstamp':
1033                              // t3ver_label is not here because it CAN be edited as a regular field!
1034                          break;
1035                          default:
1036                              if (isset($TCA[$table]['columns'][$field]))    {
1037                                      // Evaluating the value.
1038                                  $res = $this->checkValue($table,$field,$fieldValue,$id,$status,$realPid,$tscPID);
1039                                  if (isset($res['value']))    {
1040                                      $fieldArray[$field]=$res['value'];
1041  
1042                                          // Add the value of the original record to the diff-storage content:
1043                                      if ($TCA[$table]['ctrl']['transOrigDiffSourceField'])    {
1044                                          $originalLanguage_diffStorage[$field] = $originalLanguageRecord[$field];
1045                                          $diffStorageFlag = TRUE;
1046                                      }
1047                                  }
1048                              } elseif ($TCA[$table]['ctrl']['origUid']===$field) {    // Allow value for original UID to pass by...
1049                                  $fieldArray[$field] = $fieldValue;
1050                              }
1051                          break;
1052                      }
1053                  }    // Checking language.
1054              }    // Check exclude fields / disabled fields...
1055          }
1056  
1057              // Add diff-storage information:
1058          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...
1059               $fieldArray[$TCA[$table]['ctrl']['transOrigDiffSourceField']] = serialize($originalLanguage_diffStorage);
1060          }
1061  
1062              // Checking for RTE-transformations of fields:
1063          $types_fieldConfig = t3lib_BEfunc::getTCAtypes($table,$currentRecord);
1064          $theTypeString = t3lib_BEfunc::getTCAtypeValue($table,$currentRecord);
1065          if (is_array($types_fieldConfig))    {
1066              reset($types_fieldConfig);
1067              while(list(,$vconf) = each($types_fieldConfig))    {
1068                      // Write file configuration:
1069                  $eFile = t3lib_parsehtml_proc::evalWriteFile($vconf['spec']['static_write'],array_merge($currentRecord,$fieldArray));    // inserted array_merge($currentRecord,$fieldArray) 170502
1070  
1071                      // RTE transformations:
1072                  if (!$this->dontProcessTransformations)    {
1073                      if (isset($fieldArray[$vconf['field']]))    {
1074                              // Look for transformation flag:
1075                          switch((string)$incomingFieldArray['_TRANSFORM_'.$vconf['field']])    {
1076                              case 'RTE':
1077                                  $RTEsetup = $this->BE_USER->getTSConfig('RTE',t3lib_BEfunc::getPagesTSconfig($tscPID));
1078                                  $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'],$table,$vconf['field'],$theTypeString);
1079  
1080                                      // Set alternative relative path for RTE images/links:
1081                                  $RTErelPath = is_array($eFile) ? dirname($eFile['relEditFile']) : '';
1082  
1083                                      // Get RTE object, draw form and set flag:
1084                                  $RTEobj = &t3lib_BEfunc::RTEgetObj();
1085                                  if (is_object($RTEobj))    {
1086                                      $fieldArray[$vconf['field']] = $RTEobj->transformContent('db',$fieldArray[$vconf['field']],$table,$vconf['field'],$currentRecord,$vconf['spec'],$thisConfig,$RTErelPath,$currentRecord['pid']);
1087                                  } else {
1088                                      debug('NO RTE OBJECT FOUND!');
1089                                  }
1090                              break;
1091                          }
1092                      }
1093                  }
1094  
1095                      // Write file configuration:
1096                  if (is_array($eFile))    {
1097                      $mixedRec = array_merge($currentRecord,$fieldArray);
1098                      $SW_fileContent = t3lib_div::getUrl($eFile['editFile']);
1099                      $parseHTML = t3lib_div::makeInstance('t3lib_parsehtml_proc');
1100                      $parseHTML->init('','');
1101  
1102                      $eFileMarker = $eFile['markerField']&&trim($mixedRec[$eFile['markerField']]) ? trim($mixedRec[$eFile['markerField']]) : '###TYPO3_STATICFILE_EDIT###';
1103                      $insertContent = str_replace($eFileMarker,'',$mixedRec[$eFile['contentField']]);    // must replace the marker if present in content!
1104  
1105                      $SW_fileNewContent = $parseHTML->substituteSubpart($SW_fileContent, $eFileMarker, chr(10).$insertContent.chr(10), 1, 1);
1106                      t3lib_div::writeFile($eFile['editFile'],$SW_fileNewContent);
1107  
1108                          // Write status:
1109                      if (!strstr($id,'NEW') && $eFile['statusField'])    {
1110                          $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
1111                              $table,
1112                              'uid='.intval($id),
1113                              array(
1114                                  $eFile['statusField'] => $eFile['relEditFile'].' updated '.date('d-m-Y H:i:s').', bytes '.strlen($mixedRec[$eFile['contentField']])
1115                              )
1116                          );
1117                      }
1118                  } elseif ($eFile && is_string($eFile))    {
1119                      $this->log($table,$id,2,0,1,"Write-file error: '%s'",13,array($eFile),$realPid);
1120                  }
1121              }
1122          }
1123              // Return fieldArray
1124          return $fieldArray;
1125      }
1126  
1127  
1128  
1129  
1130  
1131  
1132  
1133  
1134  
1135  
1136  
1137  
1138      /*********************************************
1139       *
1140       * Evaluation of input values
1141       *
1142       ********************************************/
1143  
1144      /**
1145       * Evaluates a value according to $table/$field settings.
1146       * This function is for real database fields - NOT FlexForm "pseudo" fields.
1147       * 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() )
1148       *
1149       * @param    string        Table name
1150       * @param    string        Field name
1151       * @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.
1152       * @param    string        The record-uid, mainly - but not exclusively - used for logging
1153       * @param    string        'update' or 'new' flag
1154       * @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.
1155       * @param    integer        $tscPID
1156       * @return    array        Returns the evaluated $value as key "value" in this array. Can be checked with isset($res['value']) ...
1157       */
1158  	function checkValue($table,$field,$value,$id,$status,$realPid,$tscPID)    {
1159          global $TCA, $PAGES_TYPES;
1160          t3lib_div::loadTCA($table);
1161  
1162          $res = Array();    // result array
1163          $recFID = $table.':'.$id.':'.$field;
1164  
1165              // Processing special case of field pages.doktype
1166          if ($table=='pages' && $field=='doktype')    {
1167                  // If the user may not use this specific doktype, we issue a warning
1168              if (! ($this->admin || t3lib_div::inList($this->BE_USER->groupData['pagetypes_select'],$value)))    {
1169                  $propArr = $this->getRecordProperties($table,$id);
1170                  $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']);
1171                  return $res;
1172              };
1173              if ($status=='update')    {
1174                      // This checks 1) if we should check for disallowed tables and 2) if there are records from disallowed tables on the current page
1175                  $onlyAllowedTables = isset($PAGES_TYPES[$value]['onlyAllowedTables']) ? $PAGES_TYPES[$value]['onlyAllowedTables'] : $PAGES_TYPES['default']['onlyAllowedTables'];
1176                  if ($onlyAllowedTables)    {
1177                      $theWrongTables = $this->doesPageHaveUnallowedTables($id,$value);
1178                      if ($theWrongTables)    {
1179                          $propArr = $this->getRecordProperties($table,$id);
1180                          $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']);
1181                          return $res;
1182                      }
1183                  }
1184              }
1185          }
1186  
1187              // Get current value:
1188          $curValueRec = $this->recordInfo($table,$id,$field);
1189          $curValue = $curValueRec[$field];
1190  
1191              // Getting config for the field
1192          $tcaFieldConf = $TCA[$table]['columns'][$field]['config'];
1193  
1194              // Preform processing:
1195          $res = $this->checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$this->uploadedFileArray[$table][$id][$field],$tscPID);
1196  
1197          return $res;
1198      }
1199  
1200      /**
1201       * Branches out evaluation of a field value based on its type as configured in TCA
1202       * Can be called for FlexForm pseudo fields as well, BUT must not have $field set if so.
1203       *
1204       * @param    array        The result array. The processed value (if any!) is set in the "value" key.
1205       * @param    string        The value to set.
1206       * @param    array        Field configuration from TCA
1207       * @param    string        Table name
1208       * @param    integer        Return UID
1209       * @param    [type]        $curValue: ...
1210       * @param    [type]        $status: ...
1211       * @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.
1212       * @param    [type]        $recFID: ...
1213       * @param    string        Field name. Must NOT be set if the call is for a flexform field (since flexforms are not allowed within flexforms).
1214       * @param    [type]        $uploadedFiles: ...
1215       * @param    [type]        $tscPID: ...
1216       * @return    array        Returns the evaluated $value as key "value" in this array.
1217       */
1218  	function checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$uploadedFiles,$tscPID)    {
1219  
1220          $PP = array($table,$id,$curValue,$status,$realPid,$recFID,$tscPID);
1221  
1222          switch ($tcaFieldConf['type']) {
1223              case 'text':
1224              case 'passthrough':
1225              case 'user':
1226                  $res['value'] = $value;
1227              break;
1228              case 'input':
1229                  $res = $this->checkValue_input($res,$value,$tcaFieldConf,$PP,$field);
1230              break;
1231              case 'check':
1232                  $res = $this->checkValue_check($res,$value,$tcaFieldConf,$PP);
1233              break;
1234              case 'radio':
1235                  $res = $this->checkValue_radio($res,$value,$tcaFieldConf,$PP);
1236              break;
1237              case 'group':
1238              case 'select':
1239                  $res = $this->checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field);
1240              break;
1241              case 'inline':
1242                  $res = $this->checkValue_inline($res,$value,$tcaFieldConf,$PP,$field);
1243                  break;
1244              case 'flex':
1245                  if ($field)    {    // FlexForms are only allowed for real fields.
1246                      $res = $this->checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field);
1247                  }
1248              break;
1249              default:
1250                  #debug(array($tcaFieldConf,$res,$value),'NON existing field type:');
1251              break;
1252          }
1253  
1254          return $res;
1255      }
1256  
1257      /**
1258       * Evaluate "input" type values.
1259       *
1260       * @param    array        The result array. The processed value (if any!) is set in the "value" key.
1261       * @param    string        The value to set.
1262       * @param    array        Field configuration from TCA
1263       * @param    array        Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1264       * @param    string        Field name
1265       * @return    array        Modified $res array
1266       */
1267  	function checkValue_input($res,$value,$tcaFieldConf,$PP,$field='')    {
1268          list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1269  
1270              // Secures the string-length to be less than max. Will probably make problems with multi-byte strings!
1271          if (intval($tcaFieldConf['max'])>0)    {$value = substr($value,0,intval($tcaFieldConf['max']));}
1272  
1273              // Checking range of value:
1274          if ($tcaFieldConf['range'] && $value!=$tcaFieldConf['checkbox'])    {    // If value is not set to the allowed checkbox-value then it is checked against the ranges
1275              if (isset($tcaFieldConf['range']['upper'])&&$value>$tcaFieldConf['range']['upper'])    {$value=$tcaFieldConf['range']['upper'];}
1276              if (isset($tcaFieldConf['range']['lower'])&&$value<$tcaFieldConf['range']['lower'])    {$value=$tcaFieldConf['range']['lower'];}
1277          }
1278  
1279              // Process evaluation settings:
1280          $evalCodesArray = t3lib_div::trimExplode(',',$tcaFieldConf['eval'],1);
1281          $res = $this->checkValue_input_Eval($value,$evalCodesArray,$tcaFieldConf['is_in']);
1282  
1283              // Process UNIQUE settings:
1284          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...
1285              if ($res['value'] && in_array('uniqueInPid',$evalCodesArray))    {
1286                  $res['value'] = $this->getUnique($table,$field,$res['value'],$id,$realPid);
1287              }
1288              if ($res['value'] && in_array('unique',$evalCodesArray))    {
1289                  $res['value'] = $this->getUnique($table,$field,$res['value'],$id);
1290              }
1291          }
1292  
1293          return $res;
1294      }
1295  
1296      /**
1297       * Evaluates 'check' type values.
1298       *
1299       * @param    array        The result array. The processed value (if any!) is set in the 'value' key.
1300       * @param    string        The value to set.
1301       * @param    array        Field configuration from TCA
1302       * @param    array        Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1303       * @return    array        Modified $res array
1304       */
1305  	function checkValue_check($res,$value,$tcaFieldConf,$PP)    {
1306          list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1307  
1308          $itemC = count($tcaFieldConf['items']);
1309          if (!$itemC)    {$itemC=1;}
1310          $maxV = pow(2,$itemC);
1311  
1312          if ($value<0)    {$value=0;}
1313          if ($value>$maxV)    {$value=$maxV;}
1314          $res['value'] = $value;
1315  
1316          return $res;
1317      }
1318  
1319      /**
1320       * Evaluates 'radio' type values.
1321       *
1322       * @param    array        The result array. The processed value (if any!) is set in the 'value' key.
1323       * @param    string        The value to set.
1324       * @param    array        Field configuration from TCA
1325       * @param    array        Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1326       * @return    array        Modified $res array
1327       */
1328  	function checkValue_radio($res,$value,$tcaFieldConf,$PP)    {
1329          list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1330  
1331          if (is_array($tcaFieldConf['items']))    {
1332              foreach($tcaFieldConf['items'] as $set)    {
1333                  if (!strcmp($set[1],$value))    {
1334                      $res['value'] = $value;
1335                      break;
1336                  }
1337              }
1338          }
1339  
1340          return $res;
1341      }
1342  
1343      /**
1344       * Evaluates 'group' or 'select' type values.
1345       *
1346       * @param    array        The result array. The processed value (if any!) is set in the 'value' key.
1347       * @param    string        The value to set.
1348       * @param    array        Field configuration from TCA
1349       * @param    array        Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1350       * @param    [type]        $uploadedFiles: ...
1351       * @param    string        Field name
1352       * @return    array        Modified $res array
1353       */
1354  	function checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field)    {
1355  
1356          list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1357  
1358              // Detecting if value sent is an array and if so, implode it around a comma:
1359          if (is_array($value))    {
1360              $value = implode(',',$value);
1361          }
1362  
1363              // 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.
1364              // Anyways, this should NOT disturb anything else:
1365          $value = $this->convNumEntityToByteValue($value);
1366  
1367              // When values are sent as group or select they come as comma-separated values which are exploded by this function:
1368          $valueArray = $this->checkValue_group_select_explodeSelectGroupValue($value);
1369  
1370              // If not multiple is set, then remove duplicates:
1371          if (!$tcaFieldConf['multiple'])    {
1372              $valueArray = array_unique($valueArray);
1373          }
1374  
1375              // If an exclusive key is found, discard all others:
1376          if ($tcaFieldConf['type']=='select' && $tcaFieldConf['exclusiveKeys'])    {
1377              $exclusiveKeys = t3lib_div::trimExplode(',', $tcaFieldConf['exclusiveKeys']);
1378              foreach($valueArray as $kk => $vv)    {
1379                  if (in_array($vv, $exclusiveKeys))    {    // $vv is the item key!
1380                      $valueArray = Array($kk => $vv);
1381                      break;
1382                  }
1383              }
1384          }
1385  
1386          // 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?)
1387          // NOTE!!! Must check max-items of files before the later check because that check would just leave out filenames if there are too many!!
1388  
1389              // Checking for select / authMode, removing elements from $valueArray if any of them is not allowed!
1390          if ($tcaFieldConf['type']=='select' && $tcaFieldConf['authMode'])    {
1391              $preCount = count($valueArray);
1392              foreach($valueArray as $kk => $vv)    {
1393                  if (!$this->BE_USER->checkAuthMode($table,$field,$vv,$tcaFieldConf['authMode']))    {
1394                      unset($valueArray[$kk]);
1395                  }
1396              }
1397  
1398                  // 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.
1399              if ($preCount && !count($valueArray))    {
1400                  return array();
1401              }
1402          }
1403  
1404              // For group types:
1405          if ($tcaFieldConf['type']=='group')    {
1406              switch($tcaFieldConf['internal_type'])    {
1407                  case 'file':
1408                      $valueArray = $this->checkValue_group_select_file(
1409                          $valueArray,
1410                          $tcaFieldConf,
1411                          $curValue,
1412                          $uploadedFiles,
1413                          $status,
1414                          $table,
1415                          $id,
1416                          $recFID
1417                      );
1418                  break;
1419                  case 'db':
1420                      $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'group', $table);
1421                  break;
1422              }
1423          }
1424              // For select types which has a foreign table attached:
1425          if ($tcaFieldConf['type']=='select' && $tcaFieldConf['foreign_table'])    {
1426                  // check, if there is a NEW... id in the value, that should be substituded later
1427              if (strpos($value, 'NEW') !== false) {
1428                  $this->remapStackRecords[$table][$id] = array('remapStackIndex' => count($this->remapStack));
1429                  $this->remapStack[] = array(
1430                      'func' => 'checkValue_group_select_processDBdata',
1431                      'args' => array($valueArray,$tcaFieldConf,$id,$status,'select',$table),
1432                      'pos' => array('valueArray' => 0, 'tcaFieldConf' => 1, 'id' => 2, 'table' => 5),
1433                      'field' => $field
1434                  );
1435                  $unsetResult = true;
1436              } else {
1437                  $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'select', $table);
1438              }
1439          }
1440  
1441          if (!$unsetResult) {
1442              $newVal=$this->checkValue_checkMax($tcaFieldConf, $valueArray);
1443              $res['value'] = implode(',',$newVal);
1444          } else {
1445              unset($res['value']);
1446          }
1447  
1448          return $res;
1449      }
1450  
1451      /**
1452       * Handling files for group/select function
1453       *
1454       * @param    array        Array of incoming file references. Keys are numeric, values are files (basically, this is the exploded list of incoming files)
1455       * @param    array        Configuration array from TCA of the field
1456       * @param    string        Current value of the field
1457       * @param    array        Array of uploaded files, if any
1458       * @param    string        Status ("update" or ?)
1459       * @param    string        tablename of record
1460       * @param    integer        UID of record
1461       * @param    string        Field identifier ([table:uid:field:....more for flexforms?]
1462       * @return    array        Modified value array
1463       * @see checkValue_group_select()
1464       */
1465  	function checkValue_group_select_file($valueArray,$tcaFieldConf,$curValue,$uploadedFileArray,$status,$table,$id,$recFID)    {
1466  
1467          if (!$this->bypassFileHandling)    {    // If filehandling should NOT be bypassed, do processing:
1468  
1469                  // If any files are uploaded, add them to value array
1470              if (is_array($uploadedFileArray) &&
1471                  $uploadedFileArray['name'] &&
1472                  strcmp($uploadedFileArray['tmp_name'],'none'))    {
1473                      $valueArray[]=$uploadedFileArray['tmp_name'];
1474                      $this->alternativeFileName[$uploadedFileArray['tmp_name']] = $uploadedFileArray['name'];
1475              }
1476  
1477                  // Creating fileFunc object.
1478              if (!$this->fileFunc)    {
1479                  $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions');
1480                  $this->include_filefunctions=1;
1481              }
1482                  // Setting permitted extensions.
1483              $all_files = Array();
1484              $all_files['webspace']['allow'] = $tcaFieldConf['allowed'];
1485              $all_files['webspace']['deny'] = $tcaFieldConf['disallowed'] ? $tcaFieldConf['disallowed'] : '*';
1486              $all_files['ftpspace'] = $all_files['webspace'];
1487              $this->fileFunc->init('', $all_files);
1488          }
1489  
1490              // If there is an upload folder defined:
1491          if ($tcaFieldConf['uploadfolder'])    {
1492              if (!$this->bypassFileHandling)    {    // If filehandling should NOT be bypassed, do processing:
1493                      // For logging..
1494                  $propArr = $this->getRecordProperties($table,$id);
1495  
1496                      // Get destrination path:
1497                  $dest = $this->destPathFromUploadFolder($tcaFieldConf['uploadfolder']);
1498  
1499                      // If we are updating:
1500                  if ($status=='update')    {
1501  
1502                          // Traverse the input values and convert to absolute filenames in case the update happens to an autoVersionized record.
1503                          // 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!
1504                          // 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_.
1505                          // 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.
1506                          // Illustration of the problem comes here:
1507                          // 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.
1508                          // The versions uid is 456 and the file is copied to "logo_01.gif". But the form data that we sents was based on uid 123 and hence contains the filename "logo.gif" from the original.
1509                          // 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.
1510                          // 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.
1511                      if ($this->autoVersioningUpdate===TRUE)    {
1512                          foreach($valueArray as $key => $theFile)    {
1513                              if ($theFile===basename($theFile))    {    // If it is an already attached file...
1514                                  $valueArray[$key] = PATH_site.$tcaFieldConf['uploadfolder'].'/'.$theFile;
1515                              }
1516                          }
1517                      }
1518  
1519                          // Finding the CURRENT files listed, either from MM or from the current record.
1520                      $theFileValues=array();
1521                      if ($tcaFieldConf['MM'])    {    // If MM relations for the files also!
1522                          $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
1523                          $dbAnalysis->start('','files',$tcaFieldConf['MM'],$id);
1524                          reset($dbAnalysis->itemArray);
1525                          while (list($somekey,$someval)=each($dbAnalysis->itemArray))    {
1526                              if ($someval['id'])    {
1527                                  $theFileValues[]=$someval['id'];
1528                              }
1529                          }
1530                      } else {
1531                          $theFileValues=t3lib_div::trimExplode(',',$curValue,1);
1532                      }
1533  
1534                          // DELETE files: If existing files were found, traverse those and register files for deletion which has been removed:
1535                      if (count($theFileValues))    {
1536                              // 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!)
1537                          foreach($valueArray as $key => $theFile)    {
1538                              if ($theFile && !strstr(t3lib_div::fixWindowsFilePath($theFile),'/'))    {
1539                                  $theFileValues = t3lib_div::removeArrayEntryByValue($theFileValues,$theFile);
1540                              }
1541                          }
1542  
1543                              // This array contains the filenames in the uploadfolder that should be deleted:
1544                          foreach($theFileValues as $key => $theFile)    {
1545                              $theFile = trim($theFile);
1546                              if (@is_file($dest.'/'.$theFile))    {
1547                                  $this->removeFilesStore[]=$dest.'/'.$theFile;
1548                              } elseif ($theFile) {
1549                                  $this->log($table,$id,5,0,1,"Could not delete file '%s' (does not exist). (%s)",10,array($dest.'/'.$theFile, $recFID),$propArr['event_pid']);
1550                              }
1551                          }
1552                      }
1553                  }
1554  
1555                      // Traverse the submitted values:
1556                  foreach($valueArray as $key => $theFile)    {
1557                          // 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)
1558                      if (strstr(t3lib_div::fixWindowsFilePath($theFile),'/'))    {
1559                              // Init:
1560                          $maxSize = intval($tcaFieldConf['max_size']);
1561                          $cmd='';
1562                          $theDestFile='';        // Must be cleared. Else a faulty fileref may be inserted if the below code returns an error!
1563  
1564                              // Check various things before copying file:
1565                          if (@is_dir($dest) && (@is_file($theFile) || @is_uploaded_file($theFile)))    {        // File and destination must exist
1566  
1567                                  // Finding size. For safe_mode we have to rely on the size in the upload array if the file is uploaded.
1568                              if (is_uploaded_file($theFile) && $theFile==$uploadedFileArray['tmp_name'])    {
1569                                  $fileSize = $uploadedFileArray['size'];
1570                              } else {
1571                                  $fileSize = filesize($theFile);
1572                              }
1573  
1574                              if (!$maxSize || $fileSize<=($maxSize*1024))    {    // Check file size:
1575                                      // Prepare filename:
1576                                  $theEndFileName = isset($this->alternativeFileName[$theFile]) ? $this->alternativeFileName[$theFile] : $theFile;
1577                                  $fI = t3lib_div::split_fileref($theEndFileName);
1578  
1579                                      // Check for allowed extension:
1580                                  if ($this->fileFunc->checkIfAllowed($fI['fileext'], $dest, $theEndFileName)) {
1581                                      $theDestFile = $this->fileFunc->getUniqueName($this->fileFunc->cleanFileName($fI['file']), $dest);
1582  
1583                                          // If we have a unique destination filename, then write the file:
1584                                      if ($theDestFile)    {
1585                                          t3lib_div::upload_copy_move($theFile,$theDestFile);
1586                                          $this->copiedFileMap[$theFile] = $theDestFile;
1587                                          clearstatcache();
1588                                          if (!@is_file($theDestFile))    $this->log($table,$id,5,0,1,"Copying file '%s' failed!: The destination path (%s) may be write protected. Please make it write enabled!. (%s)",16,array($theFile, dirname($theDestFile), $recFID),$propArr['event_pid']);
1589                                      } else $this->log($table,$id,5,0,1,"Copying file '%s' failed!: No destination file (%s) possible!. (%s)",11,array($theFile, $theDestFile, $recFID),$propArr['event_pid']);
1590                                  } else $this->log($table,$id,5,0,1,"Fileextension '%s' not allowed. (%s)",12,array($fI['fileext'], $recFID),$propArr['event_pid']);
1591                              } else $this->log($table,$id,5,0,1,"Filesize (%s) of file '%s' exceeds limit (%s). (%s)",13,array(t3lib_div::formatSize($fileSize),$theFile,t3lib_div::formatSize($maxSize*1024),$recFID),$propArr['event_pid']);
1592                          } else $this->log($table,$id,5,0,1,'The destination (%s) or the source file (%s) does not exist. (%s)',14,array($dest, $theFile, $recFID),$propArr['event_pid']);
1593  
1594                              // If the destination file was created, we will set the new filename in the value array, otherwise unset the entry in the value array!
1595                          if (@is_file($theDestFile))    {
1596                              $info = t3lib_div::split_fileref($theDestFile);
1597                              $valueArray[$key]=$info['file']; // The value is set to the new filename
1598                          } else {
1599                              unset($valueArray[$key]);    // The value is set to the new filename
1600                          }
1601                      }
1602                  }
1603              }
1604  
1605                  // 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!
1606              if ($tcaFieldConf['MM'])    {
1607                  $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
1608                  $dbAnalysis->tableArray['files']=array();    // dummy
1609  
1610                  reset($valueArray);
1611                  while (list($key,$theFile)=each($valueArray))    {
1612                          // explode files
1613                          $dbAnalysis->itemArray[]['id']=$theFile;
1614                  }
1615                  if ($status=='update')    {
1616                      $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,0);
1617                  } else {
1618                      $this->dbAnalysisStore[] = array($dbAnalysis, $tcaFieldConf['MM'], $id, 0);    // This will be traversed later to execute the actions
1619                  }
1620                  $valueArray = $dbAnalysis->countItems();
1621              }
1622          }
1623  
1624          return $valueArray;
1625      }
1626  
1627      /**
1628       * Evaluates 'flex' type values.
1629       *
1630       * @param    array        The result array. The processed value (if any!) is set in the 'value' key.
1631       * @param    string        The value to set.
1632       * @param    array        Field configuration from TCA
1633       * @param    array        Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1634       * @param    array        Uploaded files for the field
1635       * @param    array        Current record array.
1636       * @param    string        Field name
1637       * @return    array        Modified $res array
1638       */
1639  	function checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field)    {
1640          list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1641  
1642          if (is_array($value))    {
1643  
1644                  // This value is necessary for flex form processing to happen on flexform fields in page records when they are copied.
1645                  // 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.
1646              $newRecordPidValue = $status=='new' ? $realPid : 0;
1647  
1648                  // Get current value array:
1649              $dataStructArray = t3lib_BEfunc::getFlexFormDS($tcaFieldConf,$this->checkValue_currentRecord,$table,'',TRUE,$newRecordPidValue);
1650  
1651              $currentValueArray = t3lib_div::xml2array($curValue);
1652              if (!is_array($currentValueArray))    $currentValueArray = array();
1653              if (is_array($currentValueArray['meta']['currentLangId']))        unset($currentValueArray['meta']['currentLangId']);    // Remove all old meta for languages...
1654  
1655                  // Evaluation of input values:
1656              $value['data'] = $this->checkValue_flex_procInData($value['data'],$currentValueArray['data'],$uploadedFiles['data'],$dataStructArray,$PP);
1657  
1658                  // Create XML and convert charsets from input value:
1659              $xmlValue = $this->checkValue_flexArray2Xml($value,TRUE);
1660  
1661                  // If we wanted to set UTF fixed:
1662              // $storeInCharset='utf-8';
1663              // $currentCharset=$GLOBALS['LANG']->charSet;
1664              // $xmlValue = $GLOBALS['LANG']->csConvObj->conv($xmlValue,$currentCharset,$storeInCharset,1);
1665              $storeInCharset=$GLOBALS['LANG']->charSet;
1666  
1667                  // Merge them together IF they are both arrays:
1668                  // 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).
1669              if (is_array($currentValueArray))    {
1670                  $arrValue = t3lib_div::xml2array($xmlValue);
1671                  $arrValue = t3lib_div::array_merge_recursive_overrule($currentValueArray,$arrValue);
1672                  $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1673              }
1674  
1675                  // Temporary fix to delete flex form elements:
1676              $deleteCMDs = t3lib_div::_GP('_DELETE_FLEX_FORMdata');
1677              if (is_array($deleteCMDs[$table][$id][$field]['data']))    {
1678                  $arrValue = t3lib_div::xml2array($xmlValue);
1679                  $this->_DELETE_FLEX_FORMdata($arrValue['data'],$deleteCMDs[$table][$id][$field]['data']);
1680                  $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1681              }
1682  
1683                  // Temporary fix to move flex form elements up:
1684              $moveCMDs = t3lib_div::_GP('_MOVEUP_FLEX_FORMdata');
1685              if (is_array($moveCMDs[$table][$id][$field]['data']))    {
1686                  $arrValue = t3lib_div::xml2array($xmlValue);
1687                  $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'up');
1688                  $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1689              }
1690  
1691                  // Temporary fix to move flex form elements down:
1692              $moveCMDs = t3lib_div::_GP('_MOVEDOWN_FLEX_FORMdata');
1693              if (is_array($moveCMDs[$table][$id][$field]['data']))    {
1694                  $arrValue = t3lib_div::xml2array($xmlValue);
1695                  $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'down');
1696                  $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1697              }
1698  
1699                  // Create the value XML:
1700              $res['value']='';
1701              $res['value'].=$xmlValue;
1702          } else {    // Passthrough...:
1703              $res['value']=$value;
1704          }
1705  
1706          return $res;
1707      }
1708  
1709      /**
1710       * Converts an array to FlexForm XML
1711       *
1712       * @param    array        Array with FlexForm data
1713       * @param    boolean        If set, the XML prologue is returned as well.
1714       * @return    string        Input array converted to XML
1715       */
1716  	function checkValue_flexArray2Xml($array, $addPrologue=FALSE)    {
1717          $flexObj = t3lib_div::makeInstance('t3lib_flexformtools');
1718          return $flexObj->flexArray2Xml($array, $addPrologue);
1719      }
1720  
1721      /**
1722       * Deletes a flex form element
1723       *
1724       * @param    array        &$valueArrayToRemoveFrom: by reference
1725       * @param    array        $deleteCMDS: ...     *
1726       * @return    void
1727       */
1728  	function _DELETE_FLEX_FORMdata(&$valueArrayToRemoveFrom,$deleteCMDS)    {
1729          if (is_array($valueArrayToRemoveFrom) && is_array($deleteCMDS))    {
1730              foreach($deleteCMDS as $key => $value)    {
1731                  if (is_array($deleteCMDS[$key]))    {
1732                      $this->_DELETE_FLEX_FORMdata($valueArrayToRemoveFrom[$key],$deleteCMDS[$key]);
1733                  } else {
1734                      unset($valueArrayToRemoveFrom[$key]);
1735                  }
1736              }
1737          }
1738      }
1739  
1740      /**
1741       * Deletes a flex form element
1742       *
1743       * TODO: Like _DELETE_FLEX_FORMdata, this is only a temporary solution!
1744       *
1745       * @param    array        &$valueArrayToMoveIn: by reference
1746       * @param    array        $moveCMDS: ...     *
1747       * @param    string        $direction: 'up' or 'down'
1748       * @return    void
1749       */
1750  	function _MOVE_FLEX_FORMdata(&$valueArrayToMoveIn, $moveCMDS, $direction)    {
1751          if (is_array($valueArrayToMoveIn) && is_array($moveCMDS))    {
1752  
1753                  // Only execute the first move command:
1754              list ($key, $value) = each ($moveCMDS);
1755  
1756              if (is_array($moveCMDS[$key]))    {
1757                  $this->_MOVE_FLEX_FORMdata($valueArrayToMoveIn[$key],$moveCMDS[$key], $direction);
1758              } else {
1759                  switch ($direction) {
1760                      case 'up':
1761                          if ($key > 1) {
1762                              $tmpArr = $valueArrayToMoveIn[$key];
1763                              $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key-1];
1764                              $valueArrayToMoveIn[$key-1] = $tmpArr;
1765                          }
1766                      break;
1767                      case 'down':
1768                          if ($key < count($valueArrayToMoveIn)) {
1769                              $tmpArr = $valueArrayToMoveIn[$key];
1770                              $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key+1];
1771                              $valueArrayToMoveIn[$key+1] = $tmpArr;
1772                          }
1773                      break;
1774                  }
1775              }
1776          }
1777      }
1778  
1779      /**
1780       * Evaluates 'inline' type values.
1781       * (partly copied from the select_group function on this issue)
1782       *
1783       * @param    array        The result array. The processed value (if any!) is set in the 'value' key.
1784       * @param    string        The value to set.
1785       * @param    array        Field configuration from TCA
1786       * @param    array        Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1787       * @param    string        Field name
1788       * @return    array        Modified $res array
1789       */
1790  	function checkValue_inline($res,$value,$tcaFieldConf,$PP,$field)    {
1791          list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1792  
1793          if (!$tcaFieldConf['foreign_table'])    {
1794              return false;    // Fatal error, inline fields should always have a foreign_table defined
1795          }
1796  
1797              // When values are sent they come as comma-separated values which are exploded by this function:
1798          $valueArray = t3lib_div::trimExplode(',', $value);
1799  
1800              // Remove duplicates: (should not be needed)
1801          $valueArray = array_unique($valueArray);
1802  
1803              // Example for received data:
1804              // $value = 45,NEW4555fdf59d154,12,123
1805              // We need to decide whether we use the stack or can save the relation directly.
1806          if(strpos($value, 'NEW') !== false || !t3lib_div::testInt($id)) {
1807              $this->remapStackRecords[$table][$id] = array('remapStackIndex' => count($this->remapStack));
1808              $this->remapStack[] = array(
1809                  'func' => 'checkValue_group_select_processDBdata',
1810                  'args' => array($valueArray,$tcaFieldConf,$id,$status,'inline',$table),
1811                  'pos' => array('valueArray' => 0, 'tcaFieldConf' => 1, 'id' => 2, 'table' => 5),
1812                  'field' => $field
1813              );
1814              unset($res['value']);
1815          } elseif($value || t3lib_div::testInt($id)) {
1816              $newValueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'inline', $table);
1817                  // Checking that the number of items is correct
1818              $newVal = $this->checkValue_checkMax($tcaFieldConf, $newValueArray);
1819              $res['value'] = implode(',',$newVal);
1820          }
1821  
1822          return $res;
1823      }
1824  
1825      /**
1826       * Checks if a fields has more items than defined via TCA in maxitems.
1827       * If there are more items than allowd, the item list is truncated to the defined number.
1828       *
1829       * @param    array        $tcaFieldConf: Field configuration from TCA
1830       * @param    array        $valueArray: Current value array of items
1831       * @return    array        The truncated value array of items
1832       */
1833  	function checkValue_checkMax($tcaFieldConf, $valueArray) {
1834          // 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...
1835  
1836          $valueArrayC = count($valueArray);
1837  
1838              // 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.
1839          $maxI = isset($tcaFieldConf['maxitems']) ? intval($tcaFieldConf['maxitems']):1;
1840          if ($valueArrayC > $maxI)    {$valueArrayC=$maxI;}    // Checking for not too many elements
1841  
1842              // Dumping array to list
1843          $newVal=array();
1844          foreach($valueArray as $nextVal)    {
1845              if ($valueArrayC==0)    {break;}
1846              $valueArrayC--;
1847              $newVal[]=$nextVal;
1848          }
1849  
1850          return $newVal;
1851      }
1852  
1853  
1854  
1855  
1856  
1857  
1858  
1859  
1860  
1861  
1862  
1863  
1864  
1865  
1866  
1867  
1868  
1869      /*********************************************
1870       *
1871       * Helper functions for evaluation functions.
1872       *
1873       ********************************************/
1874  
1875      /**
1876       * Gets a unique value for $table/$id/$field based on $value
1877       *
1878       * @param    string        Table name
1879       * @param    string        Field name for which $value must be unique
1880       * @param    string        Value string.
1881       * @param    integer        UID to filter out in the lookup (the record itself...)
1882       * @param    integer        If set, the value will be unique for this PID
1883       * @return    string        Modified value (if not-unique). Will be the value appended with a number (until 100, then the function just breaks).
1884       */
1885  	function getUnique($table,$field,$value,$id,$newPid=0)    {
1886          global $TCA;
1887  
1888              // Initialize:
1889          t3lib_div::loadTCA($table);
1890          $whereAdd='';
1891          $newValue='';
1892          if (intval($newPid))    { $whereAdd.=' AND pid='.intval($newPid); } else { $whereAdd.=' AND pid>=0'; }    // "AND pid>=0" for versioning
1893          $whereAdd.=$this->deleteClause($table);
1894  
1895              // If the field is configured in TCA, proceed:
1896          if (is_array($TCA[$table]) && is_array($TCA[$table]['columns'][$field]))    {
1897  
1898                  // Look for a record which might already have the value:
1899              $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $table).' AND uid!='.intval($id).$whereAdd);
1900              $counter = 0;
1901  
1902                  // For as long as records with the test-value existing, try again (with incremented numbers appended).
1903              while ($GLOBALS['TYPO3_DB']->sql_num_rows($res))    {
1904                  $newValue = $value.$counter;
1905                  $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($newValue, $table).' AND uid!='.intval($id).$whereAdd);
1906                  $counter++;
1907                  if ($counter>100)    { break; }    // At "100" it will give up and accept a duplicate - should probably be fixed to a small hash string instead...!
1908              }
1909                  // If the new value is there:
1910              $value = strlen($newValue) ? $newValue : $value;
1911          }
1912          return $value;
1913      }
1914  
1915      /**
1916       * Evaluation of 'input'-type values based on 'eval' list
1917       *
1918       * @param    string        Value to evaluate
1919       * @param    array        Array of evaluations to traverse.
1920       * @param    string        Is-in string
1921       * @return    string        Modified $value
1922       */
1923  	function checkValue_input_Eval($value,$evalArray,$is_in)    {
1924          $res = Array();
1925          $newValue = $value;
1926          $set = true;
1927  
1928          foreach($evalArray as $func)    {
1929              switch($func)    {
1930                  case 'int':
1931                  case 'year':
1932                  case 'date':
1933                  case 'datetime':
1934                  case 'time':
1935                  case 'timesec':
1936                      $value = intval($value);
1937                  break;
1938                  case 'double2':
1939                      $theDec = 0;
1940                      for ($a=strlen($value); $a>0; $a--)    {
1941                          if (substr($value,$a-1,1)=='.' || substr($value,$a-1,1)==',')    {
1942                              $theDec = substr($value,$a);
1943                              $value = substr($value,0,$a-1);
1944                              break;
1945                          }
1946                      }
1947                      $theDec = ereg_replace('[^0-9]','',$theDec).'00';
1948                      $value = intval(str_replace(' ','',$value)).'.'.substr($theDec,0,2);
1949                  break;
1950                  case 'md5':
1951                      if (strlen($value)!=32){$set=false;}
1952                  break;
1953                  case 'trim':
1954                      $value = trim($value);
1955                  break;
1956                  case 'upper':
1957                      $value = strtoupper($value);
1958  #                    $value = strtr($value, '', '');    // WILL make trouble with other charsets than ISO-8859-1, so what do we do here? PHP-function which can handle this for other charsets? Currently the browsers JavaScript will fix it.
1959                  break;
1960                  case 'lower':
1961                      $value = strtolower($value);
1962  #                    $value = strtr($value, '', '');    // WILL make trouble with other charsets than ISO-8859-1, so what do we do here? PHP-function which can handle this for other charsets? Currently the browsers JavaScript will fix it.
1963                  break;
1964                  case 'required':
1965                      if (!$value)    {$set=0;}
1966                  break;
1967                  case 'is_in':
1968                      $c=strlen($value);
1969                      if ($c)    {
1970                          $newVal = '';
1971                          for ($a=0;$a<$c;$a++)    {
1972                              $char = substr($value,$a,1);
1973                              if (strstr($is_in,$char))    {
1974                                  $newVal.=$char;
1975                              }
1976                          }
1977                          $value = $newVal;
1978                      }
1979                  break;
1980                  case 'nospace':
1981                      $value = str_replace(' ','',$value);
1982                  break;
1983                  case 'alpha':
1984                      $value = ereg_replace('[^a-zA-Z]','',$value);
1985                  break;
1986                  case 'num':
1987                      $value = ereg_replace('[^0-9]','',$value);
1988                  break;
1989                  case 'alphanum':
1990                      $value = ereg_replace('[^a-zA-Z0-9]','',$value);
1991                  break;
1992                  case 'alphanum_x':
1993                      $value = ereg_replace('[^a-zA-Z0-9_-]','',$value);
1994                  break;
1995                  default:
1996                      if (substr($func, 0, 3) == 'tx_')    {
1997                          $evalObj = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func].':&'.$func);
1998                          if (is_object($evalObj) && method_exists($evalObj, 'evaluateFieldValue'))    {
1999                              $value = $evalObj->evaluateFieldValue($value, $is_in, $set);
2000                          }
2001                      }
2002                  break;
2003              }
2004          }
2005          if ($set)    {$res['value'] = $value;}
2006          return $res;
2007      }
2008  
2009      /**
2010       * Returns data for group/db and select fields
2011       *
2012       * @param    array        Current value array
2013       * @param    array        TCA field config
2014       * @param    integer        Record id, used for look-up of MM relations (local_uid)
2015       * @param    string        Status string ('update' or 'new')
2016       * @param    string        The type, either 'select', 'group' or 'inline'
2017       * @param    string        Table name, needs to be passed to t3lib_loadDBGroup
2018       * @return    array        Modified value array
2019       */
2020  	function checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,$type,$currentTable)    {
2021          $tables = $type=='group'?$tcaFieldConf['allowed']:$tcaFieldConf['foreign_table'].','.$tcaFieldConf['neg_foreign_table'];
2022          $prep = $type=='group'?$tcaFieldConf['prepend_tname']:$tcaFieldConf['neg_foreign_table'];
2023  
2024          $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
2025          $dbAnalysis->registerNonTableValues=$tcaFieldConf['allowNonIdValues'] ? 1 : 0;
2026          $dbAnalysis->start(implode(',',$valueArray),$tables, '', 0, $currentTable, $tcaFieldConf);
2027  
2028          if ($tcaFieldConf['MM'])    {
2029              if ($status=='update')    {
2030                  $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,$prep);
2031              } else {
2032                  $this->dbAnalysisStore[] = array($dbAnalysis,$tcaFieldConf['MM'],$id,$prep);    // This will be traversed later to execute the actions
2033              }
2034              $valueArray = $dbAnalysis->countItems();
2035          } elseif ($type == 'inline') {
2036              if ($tcaFieldConf['foreign_field']) {
2037                      // if the record was imported, sorting was also imported, so skip this
2038                  $skipSorting = $this->callFromImpExp ? true : false;
2039                      // update record in intermediate table (sorting & pointer uid to parent record)
2040                  $dbAnalysis->writeForeignField($tcaFieldConf, $id, 0, $skipSorting);
2041                  $valueArray = $dbAnalysis->countItems();
2042              } else {
2043                  $valueArray = $dbAnalysis->getValueArray($prep);
2044                  if ($prep) {
2045                          // @TODO: Do we want to support relations to multiple tables in Comma Separated Lists?
2046                      $valueArray = $dbAnalysis->convertPosNeg($valueArray,$tcaFieldConf['foreign_table'],$tcaFieldConf['neg_foreign_table']);
2047                  }
2048              }
2049          } else {
2050              $valueArray = $dbAnalysis->getValueArray($prep);
2051              if ($type=='select' && $prep)    {
2052                  $valueArray = $dbAnalysis->convertPosNeg($valueArray,$tcaFieldConf['foreign_table'],$tcaFieldConf['neg_foreign_table']);
2053              }
2054          }
2055  
2056              // 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.
2057          return $valueArray;
2058      }
2059  
2060      /**
2061       * Explodes the $value, which is a list of files/uids (group select)
2062       *
2063       * @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.
2064       * @return    array        The value array.
2065       */
2066      function checkValue_group_select_explodeSelectGroupValue($value)    {
2067          $valueArray = t3lib_div::trimExplode(',',$value,1);
2068          reset($valueArray);
2069          while(list($key,$newVal)=each($valueArray))    {
2070              $temp=explode('|',$newVal,2);
2071              $valueArray[$key] = str_replace(',','',str_replace('|','',rawurldecode($temp[0])));
2072          }
2073          return $valueArray;
2074      }
2075  
2076      /**
2077       * Starts the processing the input data for flexforms. This will traverse all sheets / languages and for each it will traverse the sub-structure.
2078       * See checkValue_flex_procInData_travDS() for more details.
2079       * 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
2080       *
2081       * @param    array        The 'data' part of the INPUT flexform data
2082       * @param    array        The 'data' part of the CURRENT flexform data
2083       * @param    array        The uploaded files for the 'data' part of the INPUT flexform data
2084       * @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.
2085       * @param    array        A set of parameters to pass through for the calling of the evaluation functions
2086       * @param    string        Optional call back function, see checkValue_flex_procInData_travDS()  DEPRICATED, use class.t3lib_flexformtools.php instead for traversal!
2087       * @return    array        The modified 'data' part.
2088       * @see checkValue_flex_procInData_travDS()
2089       */
2090  	function checkValue_flex_procInData($dataPart,$dataPart_current,$uploadedFiles,$dataStructArray,$pParams,$callBackFunc='')    {
2091  #debug(array($dataPart,$dataPart_current,$dataStructArray));
2092          if (is_array($dataPart))    {
2093              foreach($dataPart as $sKey => $sheetDef)    {
2094                  list ($dataStruct,$actualSheet) = t3lib_div::resolveSheetDefInDS($dataStructArray,$sKey);
2095  #debug(array($dataStruct,$actualSheet,$sheetDef,$actualSheet,$sKey));
2096                  if (is_array($dataStruct) && $actualSheet==$sKey && is_array($sheetDef))    {
2097                      foreach($sheetDef as $lKey => $lData)    {
2098                          $this->checkValue_flex_procInData_travDS(
2099                              $dataPart[$sKey][$lKey],
2100                              $dataPart_current[$sKey][$lKey],
2101                              $uploadedFiles[$sKey][$lKey],
2102                              $dataStruct['ROOT']['el'],
2103                              $pParams,
2104                              $callBackFunc,
2105                              $sKey.'/'.$lKey.'/'
2106                          );
2107                      }
2108                  }
2109              }
2110          }
2111  
2112          return $dataPart;
2113      }
2114  
2115      /**
2116       * Processing of the sheet/language data array
2117       * 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.
2118       *
2119       * @param    array        New values (those being processed): Multidimensional Data array for sheet/language, passed by reference!
2120       * @param    array        Current values: Multidimensional Data array. May be empty array() if not needed (for callBackFunctions)
2121       * @param    array        Uploaded files array for sheet/language. May be empty array() if not needed (for callBackFunctions)
2122       * @param    array        Data structure which fits the data array
2123       * @param    array        A set of parameters to pass through for the calling of the evaluation functions / call back function
2124       * @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.
2125       * @param    [type]        $structurePath: ...
2126       * @return    void
2127       * @see checkValue_flex_procInData()
2128       */
2129  	function checkValue_flex_procInData_travDS(&$dataValues,$dataValues_current,$uploadedFiles,$DSelements,$pParams,$callBackFunc,$structurePath)    {
2130          if (is_array($DSelements))    {
2131  
2132                  // For each DS element:
2133              foreach($DSelements as $key => $dsConf)    {
2134  
2135                          // Array/Section:
2136                  if ($DSelements[$key]['type']=='array')    {
2137                      if (is_array($dataValues[$key]['el']))    {
2138                          if ($DSelements[$key]['section'])    {
2139                              foreach($dataValues[$key]['el'] as $ik => $el)    {
2140                                  $theKey = key($el);
2141  
2142                                      // It may happen that an element exists in $dataValues but is missing in $dataValues_current. In this case, just skip the element...
2143                                  $elExists = is_array($dataValues_current[$key]['el'][$ik]);
2144  
2145                                  if ($elExists && is_array($dataValues[$key]['el'][$ik][$theKey]['el']))    {
2146                                      $this->checkValue_flex_procInData_travDS(
2147                                              $dataValues[$key]['el'][$ik][$theKey]['el'],
2148                                              $dataValues_current[$key]['el'][$ik][$theKey]['el'],
2149                                              $uploadedFiles[$key]['el'][$ik][$theKey]['el'],
2150                                              $DSelements[$key]['el'][$theKey]['el'],
2151                                              $pParams,
2152                                              $callBackFunc,
2153                                              $structurePath.$key.'/el/'.$ik.'/'.$theKey.'/el/'
2154                                          );
2155                                  }
2156                              }
2157                          } else {
2158                              if (!isset($dataValues[$key]['el']))    $dataValues[$key]['el']=array();
2159                              $this->checkValue_flex_procInData_travDS(
2160                                      $dataValues[$key]['el'],
2161                                      $dataValues_current[$key]['el'],
2162                                      $uploadedFiles[$key]['el'],
2163                                      $DSelements[$key]['el'],
2164                                      $pParams,
2165                                      $callBackFunc,
2166                                      $structurePath.$key.'/el/'
2167                                  );
2168                          }
2169                      }
2170                  } else {
2171                      if (is_array($dsConf['TCEforms']['config']) && is_array($dataValues[$key]))    {
2172                          foreach($dataValues[$key] as $vKey => $data)    {
2173  
2174                              if ($callBackFunc)    {
2175                                  if (is_object($this->callBackObj))    {
2176                                      $res = $this->callBackObj->$callBackFunc(
2177                                                  $pParams,
2178                                                  $dsConf['TCEforms']['config'],
2179                                                  $dataValues[$key][$vKey],
2180                                                  $dataValues_current[$key][$vKey],
2181                                                  $uploadedFiles[$key][$vKey],
2182                                                  $structurePath.$key.'/'.$vKey.'/'
2183                                              );
2184                                  } else {
2185                                      $res = $this->$callBackFunc(
2186                                                  $pParams,
2187                                                  $dsConf['TCEforms']['config'],
2188                                                  $dataValues[$key][$vKey],
2189                                                  $dataValues_current[$key][$vKey],
2190                                                  $uploadedFiles[$key][$vKey]
2191                                              );
2192                                  }
2193                              } else {    // Default
2194                                  list($CVtable,$CVid,$CVcurValue,$CVstatus,$CVrealPid,$CVrecFID,$CVtscPID) = $pParams;
2195  
2196                                  $res = $this->checkValue_SW(
2197                                              array(),
2198                                              $dataValues[$key][$vKey],
2199                                              $dsConf['TCEforms']['config'],
2200                                              $CVtable,
2201                                              $CVid,
2202                                              $dataValues_current[$key][$vKey],
2203                                              $CVstatus,
2204                                              $CVrealPid,
2205                                              $CVrecFID,
2206                                              '',
2207                                              $uploadedFiles[$key][$vKey],
2208                                              array(),
2209                                              $CVtscPID
2210                                          );
2211  
2212                                      // Look for RTE transformation of field:
2213                                  if ($dataValues[$key]['_TRANSFORM_'.$vKey] == 'RTE' && !$this->dontProcessTransformations)    {
2214  
2215                                          // Unsetting trigger field - we absolutely don't want that into the data storage!
2216                                      unset($dataValues[$key]['_TRANSFORM_'.$vKey]);
2217  
2218                                      if (isset($res['value']))    {
2219  
2220                                              // Calculating/Retrieving some values here:
2221                                          list(,,$recFieldName) = explode(':', $CVrecFID);
2222                                          $theTypeString = t3lib_BEfunc::getTCAtypeValue($CVtable,$this->checkValue_currentRecord);
2223                                          $specConf = t3lib_BEfunc::getSpecConfParts('',$dsConf['TCEforms']['defaultExtras']);
2224  
2225                                              // Find, thisConfig:
2226                                          $RTEsetup = $this->BE_USER->getTSConfig('RTE',t3lib_BEfunc::getPagesTSconfig($CVtscPID));
2227                                          $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'],$CVtable,$recFieldName,$theTypeString);
2228  
2229                                              // Get RTE object, draw form and set flag:
2230                                          $RTEobj = &t3lib_BEfunc::RTEgetObj();
2231                                          if (is_object($RTEobj))    {
2232                                              $res['value'] = $RTEobj->transformContent('db',$res['value'],$CVtable,$recFieldName,$this->checkValue_currentRecord,$specConf,$thisConfig,'',$CVrealPid);
2233                                          } else {
2234                                              debug('NO RTE OBJECT FOUND!');
2235                                          }
2236                                      }
2237                                  }
2238                              }
2239  
2240                                  // Adding the value:
2241                              if (isset($res['value']))    {
2242                                  $dataValues[$key][$vKey] = $res['value'];
2243                              }
2244                          }
2245                      }
2246                  }
2247              }
2248          }
2249      }
2250  
2251  
2252  
2253  
2254  
2255  
2256  
2257  
2258  
2259  
2260  
2261  
2262  
2263  
2264  
2265  
2266  
2267      /*********************************************
2268       *
2269       * PROCESSING COMMANDS
2270       *
2271       ********************************************/
2272  
2273      /**
2274       * Processing the cmd-array
2275       * See "TYPO3 Core API" for a description of the options.
2276       *
2277       * @return    void
2278       */
2279  	function process_cmdmap() {
2280          global $TCA, $TYPO3_CONF_VARS;
2281  
2282              // Editing frozen:
2283          if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze'])    {
2284              $this->newlog('All editing in this workspace has been frozen!',1);
2285              return FALSE;
2286          }
2287  
2288              // Hook initialization:
2289          $hookObjectsArr = array();
2290          if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'])) {
2291              foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'] as $classRef) {
2292                  $hookObjectsArr[] = &t3lib_div::getUserObj($classRef);
2293              }
2294          }
2295  
2296              // Traverse command map:
2297          reset($this->cmdmap);
2298          while(list($table,) = each($this->cmdmap))    {
2299  
2300                  // Check if the table may be modified!
2301              $modifyAccessList = $this->checkModifyAccessList($table);
2302              if (!$modifyAccessList)    {
2303                  $id = 0;
2304                  $this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table));
2305              }    // FIXME: $id not set here (Comment added by Sebastian Kurfuerst)
2306  
2307                  // Check basic permissions and circumstances:
2308              if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->cmdmap[$table]) && $modifyAccessList)    {
2309  
2310                      // Traverse the command map:
2311                  foreach($this->cmdmap[$table] as $id => $incomingCmdArray)    {
2312                      if (is_array($incomingCmdArray))    {    // have found a command.
2313  
2314                              // Get command and value (notice, only one command is observed at a time!):
2315                          reset($incomingCmdArray);
2316                          $command = key($incomingCmdArray);
2317                          $value = current($incomingCmdArray);
2318  
2319                          foreach($hookObjectsArr as $hookObj) {
2320                              if (method_exists($hookObj, 'processCmdmap_preProcess')) {
2321                                  $hookObj->processCmdmap_preProcess($command, $table, $id, $value, $this);
2322                              }
2323                          }
2324  
2325                              // Init copyMapping array:
2326                          $this->copyMappingArray = Array();        // Must clear this array before call from here to those functions: Contains mapping information between new and old id numbers.
2327  
2328                              // Branch, based on command
2329                          switch ($command)    {
2330                              case 'move':
2331                                  $this->moveRecord($table,$id,$value);
2332                              break;
2333                              case 'copy':
2334                                  if ($table === 'pages')    {
2335                                      $this->copyPages($id,$value);
2336                                  } else {
2337                                      $this->copyRecord($table,$id,$value,1);
2338                                  }
2339                              break;
2340                              case 'localize':
2341                                  $this->localize($table,$id,$value);
2342                              break;
2343                              case 'version':
2344                                  switch ((string)$value['action'])    {
2345                                      case 'new':
2346                                          $versionizeTree = t3lib_div::intInRange(!isset($value['treeLevels'])?-1:$value['treeLevels'],-1,100);
2347                                          if ($table == 'pages' && $versionizeTree>=0)    {
2348                                              $this->versionizePages($id,$value['label'],$versionizeTree);
2349                                          } else {
2350                                              $this->versionizeRecord($table,$id,$value['label']);
2351                                          }
2352                                      break;
2353                                      case 'swap':
2354                                          $this->version_swap($table,$id,$value['swapWith'],$value['swapIntoWS']);
2355                                      break;
2356                                      case 'clearWSID':
2357                                          $this->version_clearWSID($table,$id);
2358                                      break;
2359                                      case 'setStage':
2360                                          $idList = t3lib_div::trimExplode(',',$id,1);
2361                                          foreach($idList as $id)    {
2362                                              $this->version_setStage($table,$id,$value['stageId'],$value['comment']?$value['comment']:$this->generalComment);
2363                                          }
2364                                      break;
2365                                  }
2366                              break;
2367                              case 'delete':
2368                                  $this->deleteAction($table, $id);
2369                              break;
2370                              case 'undelete':
2371                                  $this->undeleteRecord($table, $id);
2372                              break;
2373                          }
2374  
2375                          foreach($hookObjectsArr as $hookObj) {
2376                              if (method_exists($hookObj, 'processCmdmap_postProcess')) {
2377                                  $hookObj->processCmdmap_postProcess($command, $table, $id, $value, $this);
2378                              }
2379                          }
2380  
2381                              // Merging the copy-array info together for remapping purposes.
2382                          $this->copyMappingArray_merged= t3lib_div::array_merge_recursive_overrule($this->copyMappingArray_merged,$this->copyMappingArray);
2383                      }
2384                  }
2385              }
2386          }
2387  
2388              // Finally, before exit, check if there are ID references to remap. This might be the case if versioning or copying has taken place!
2389          $this->remapListedDBRecords();
2390      }
2391  
2392  
2393  
2394  
2395  
2396  
2397  
2398  
2399  
2400  
2401  
2402      /*********************************************
2403       *
2404       * Cmd: Copying
2405       *
2406       ********************************************/
2407  
2408      /**
2409       * Copying a single record
2410       *
2411       * @param    string        Element table
2412       * @param    integer        Element UID
2413       * @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
2414       * @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
2415       * @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!
2416       * @param    string        Commalist of fields to exclude from the copy process (might get default values)
2417       * @return    integer        ID of new record, if any
2418       */
2419  	function copyRecord($table,$uid,$destPid,$first=0,$overrideValues=array(),$excludeFields='')    {
2420          global $TCA;
2421  
2422          $uid = $origUid = intval($uid);
2423              // Only copy if the table is defined in TCA, a uid is given and the record wasn't copied before:
2424          if ($TCA[$table] && $uid && !$this->isRecordCopied($table, $uid))    {
2425              t3lib_div::loadTCA($table);
2426  /*
2427                  // 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)
2428              if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid'))    {
2429                  $uid = $lookForLiveVersion['uid'];
2430              }
2431                  // Get workspace version of the source record, if any: Then we will copy workspace version instead:
2432              if ($WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid'))    {
2433                  $uid = $WSversion['uid'];
2434              }
2435                  // 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.
2436  */
2437              if ($this->doesRecordExist($table,$uid,'show'))    {        // This checks if the record can be selected which is all that a copy action requires.
2438                  $data = Array();
2439  
2440                  $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));
2441  
2442                  // $row = $this->recordInfo($table,$uid,'*');
2443                  $row = t3lib_BEfunc::getRecordWSOL($table,$uid);    // So it copies (and localized) content from workspace...
2444                  if (is_array($row))    {
2445  
2446                          // Initializing:
2447                      $theNewID = uniqid('NEW');
2448                      $enableField = isset($TCA[$table]['ctrl']['enablecolumns']) ? $TCA[$table]['ctrl']['enablecolumns']['disabled'] : '';
2449                      $headerField = $TCA[$table]['ctrl']['label'];
2450  
2451                          // Getting default data:
2452                      $defaultData = $this->newFieldArray($table);
2453  
2454                          // Getting "copy-after" fields if applicable:
2455                          // 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.
2456                      $copyAfterFields = $destPid<0 ? $this->fixCopyAfterDuplFields($table,$uid,abs($destPid),0) : array();
2457  
2458                          // Page TSconfig related:
2459                      $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...
2460                      $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
2461                      $tE = $this->getTableEntries($table,$TSConfig);
2462  
2463                          // Traverse ALL fields of the selected record:
2464                      foreach($row as $field => $value)    {
2465                          if (!in_array($field,$nonFields))    {
2466  
2467                                  // Get TCA configuration for the field:
2468                              $conf = $TCA[$table]['columns'][$field]['config'];
2469  
2470                                  // Preparation/Processing of the value:
2471                              if ($field=='pid')    {    // "pid" is hardcoded of course:
2472                                  $value = $destPid;
2473                              } elseif (isset($overrideValues[$field]))    {    // Override value...
2474                                  $value = $overrideValues[$field];
2475                              } elseif (isset($copyAfterFields[$field]))    {    // Copy-after value if available:
2476                                  $value = $copyAfterFields[$field];
2477                              } elseif ($TCA[$table]['ctrl']['setToDefaultOnCopy'] && t3lib_div::inList($TCA[$table]['ctrl']['setToDefaultOnCopy'],$field))    {    // Revert to default for some fields:
2478                                  $value = $defaultData[$field];
2479                              } else {
2480                                      // Hide at copy may override:
2481                                  if ($first && $field==$enableField && $TCA[$table]['ctrl']['hideAtCopy'] && !$this->neverHideAtCopy && !$tE['disableHideAtCopy'])    {
2482                                      $value=1;
2483                                  }
2484                                      // Prepend label on copy:
2485                                  if ($first && $field==$headerField && $TCA[$table]['ctrl']['prependAtCopy'] && !$tE['disablePrependAtCopy'])    {
2486                                      $value = $this->getCopyHeader($table,$this->resolvePid($table,$destPid),$field,$this->clearPrefixFromValue($table,$value),0);
2487                                  }
2488                                      // Processing based on the TCA config field type (files, references, flexforms...)
2489                                  $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf,$tscPID);
2490                              }
2491  
2492                                  // Add value to array.
2493                              $data[$table][$theNewID][$field] = $value;
2494                          }
2495                      }
2496  
2497                          // Overriding values:
2498                      if ($TCA[$table]['ctrl']['editlock'])    {
2499                          $data[$table][$theNewID][$TCA[$table]['ctrl']['editlock']] = 0;
2500                      }
2501  
2502                          // Setting original UID:
2503                      if ($TCA[$table]['ctrl']['origUid'])    {
2504                          $data[$table][$theNewID][$TCA[$table]['ctrl']['origUid']] = $uid;
2505                      }
2506  
2507                          // Do the copy by simply submitting the array through TCEmain:
2508                      $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain');
2509                      $copyTCE->stripslashes_values = 0;
2510                      $copyTCE->copyTree = $this->copyTree;
2511                      $copyTCE->cachedTSconfig = $this->cachedTSconfig;    // Copy forth the cached TSconfig
2512                      $copyTCE->dontProcessTransformations=1;        // Transformations should NOT be carried out during copy
2513  
2514                      $copyTCE->start($data,'',$this->BE_USER);
2515                      $copyTCE->process_datamap();
2516  
2517                          // Getting the new UID:
2518                      $theNewSQLID = $copyTCE->substNEWwithIDs[$theNewID];
2519                      if ($theNewSQLID)    {
2520                          $this->copyMappingArray[$table][$origUid] = $theNewSQLID;
2521                      }
2522  
2523                          // Copy back the cached TSconfig
2524                      $this->cachedTSconfig = $copyTCE->cachedTSconfig;
2525                      $this->errorLog = array_merge($this->errorLog,$copyTCE->errorLog);
2526                      unset($copyTCE);
2527  
2528                      return $theNewSQLID;
2529                  } else $this->log($table,$uid,3,0,1,'Attempt to copy record that did not exist!');
2530              } else $this->log($table,$uid,3,0,1,'Attempt to copy record without permission');
2531          }
2532      }
2533  
2534      /**
2535       * Copying pages
2536       * Main function for copying pages.
2537       *
2538       * @param    integer        Page UID to copy
2539       * @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
2540       * @return    void
2541       */
2542  	function copyPages($uid,$destPid)    {
2543  
2544              // Initialize:
2545          $uid = intval($uid);
2546          $destPid = intval($destPid);
2547  
2548              // Finding list of tables to copy.
2549          $copyTablesArray = $this->admin ? $this->compileAdminTables() : explode(',',$this->BE_USER->groupData['tables_modify']);    // These are the tables, the user may modify
2550          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
2551              foreach($copyTablesArray as $k => $table)    {
2552                  if (!$table || !t3lib_div::inList($this->copyWhichTables.',pages',$table))    {    // pages are always going...
2553                      unset($copyTablesArray[$k]);
2554                  }
2555              }
2556          }
2557          $copyTablesArray = array_unique($copyTablesArray);
2558  
2559              // Begin to copy pages if we're allowed to:
2560          if ($this->admin || in_array('pages',$copyTablesArray))    {
2561  
2562                  // Copy this page we're on. And set first-flag (this will trigger that the record is hidden if that is configured)!
2563              $theNewRootID = $this->copySpecificPage($uid,$destPid,$copyTablesArray,1);
2564  
2565                  // If we're going to copy recursively...:
2566              if ($theNewRootID && $this->copyTree)    {
2567  
2568                      // Get ALL subpages to copy (read-permissions are respected!):
2569                  $CPtable = $this->int_pageTreeInfo(Array(), $uid, intval($this->copyTree), $theNewRootID);
2570  
2571                      // Now copying the subpages:
2572                  foreach($CPtable as $thePageUid => $thePagePid)    {
2573                      $newPid = $this->copyMappingArray['pages'][$thePagePid];
2574                      if (isset($newPid))    {
2575                          $this->copySpecificPage($thePageUid,$newPid,$copyTablesArray);
2576                      } else {
2577                          $this->log('pages',$uid,5,0,1,'Something went wrong during copying branch');
2578                          break;
2579                      }
2580                  }
2581              }    // else the page was not copied. Too bad...
2582          } else {
2583              $this->log('pages',$uid,5,0,1,'Attempt to copy page without permission to this table');
2584          }
2585      }
2586  
2587      /**
2588       * Copying a single page ($uid) to $destPid and all tables in the array copyTablesArray.
2589       *
2590       * @param    integer        Page uid
2591       * @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
2592       * @param    array        Table on pages to copy along with the page.
2593       * @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
2594       * @return    integer        The id of the new page, if applicable.
2595       */
2596  	function copySpecificPage($uid,$destPid,$copyTablesArray,$first=0)    {
2597          global $TCA;
2598  
2599              // Copy the page itself:
2600          $theNewRootID = $this->copyRecord('pages',$uid,$destPid,$first);
2601  
2602              // If a new page was created upon the copy operation we will proceed with all the tables ON that page:
2603          if ($theNewRootID)    {
2604              foreach($copyTablesArray as $table)    {
2605                  if ($table && is_array($TCA[$table]) && $table!='pages')    {    // all records under the page is copied.
2606                      $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table), '', ($TCA[$table]['ctrl']['sortby'] ? $TCA[$table]['ctrl']['sortby'].' DESC' : ''));
2607                      while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))    {
2608                          $this->copyRecord($table,$row['uid'], $theNewRootID);    // Copying each of the underlying records...
2609                      }
2610                  }
2611              }
2612              return $theNewRootID;
2613          }
2614      }
2615  
2616      /**
2617       * Copying records, but makes a "raw" copy of a record.
2618       * Basically the only thing observed is field processing like the copying of files and correction of ids. All other fields are 1-1 copied.
2619       * 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.
2620       * 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!?
2621       * This function is used to create new versions of a record.
2622       * 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.
2623       *
2624       * @param    string        Element table
2625       * @param    integer        Element UID
2626       * @param    integer        Element PID (real PID, not checked)
2627       * @param    array        Override array - must NOT contain any fields not in the table!
2628       * @return    integer        Returns the new ID of the record (if applicable)
2629       */
2630  	function copyRecord_raw($table,$uid,$pid,$overrideArray=array())    {
2631          global $TCA;
2632  
2633          $uid = intval($uid);
2634              // Only copy if the table is defined in TCA, a uid is given and the record wasn't copied before:
2635          if ($TCA[$table] && $uid && !$this->isRecordCopied($table, $uid))    {
2636              t3lib_div::loadTCA($table);
2637              if ($this->doesRecordExist($table,$uid,'show'))    {
2638  
2639                      // Set up fields which should not be processed. They are still written - just passed through no-questions-asked!
2640                  $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');
2641  
2642                      // Select main record:
2643                  $row = $this->recordInfo($table,$uid,'*');
2644                  if (is_array($row))    {
2645  
2646                          // Merge in override array.
2647                      $row = array_merge($row,$overrideArray);
2648  
2649                          // Traverse ALL fields of the selected record:
2650                      foreach($row as $field => $value)    {
2651                          if (!in_array($field,$nonFields))    {
2652  
2653                                  // Get TCA configuration for the field:
2654                              $conf = $TCA[$table]['columns'][$field]['config'];
2655                              if (is_array($conf))    {
2656                                      // Processing based on the TCA config field type (files, references, flexforms...)
2657                                  $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf,$pid);
2658                              }
2659  
2660                                  // Add value to array.
2661                              $row[$field] = $value;
2662                          }
2663                      }
2664  
2665                          // Force versioning related fields:
2666                      $row['pid'] = $pid;
2667  
2668                          // Setting original UID:
2669                      if ($TCA[$table]['ctrl']['origUid'])    {
2670                          $row[$TCA[$table]['ctrl']['origUid']] = $uid;
2671                      }
2672  
2673                          // Do the copy by internal function
2674                      $theNewSQLID = $this->insertNewCopyVersion($table,$row,$pid);
2675                      if ($theNewSQLID)    {
2676                          $this->dbAnalysisStoreExec();
2677                          $this->dbAnalysisStore = array();
2678                          return $this->copyMappingArray[$table][$uid] = $theNewSQLID;
2679                      }
2680                  } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record that did not exist!');
2681              } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record without copy permission');
2682          }
2683      }
2684  
2685      /**
2686       * Copies all records from tables in $copyTablesArray from page with $old_pid to page with $new_pid
2687       * Uses raw-copy for the operation (meant for versioning!)
2688       *
2689       * @param    integer        Current page id.
2690       * @param    integer        New page id
2691       * @param    array        Array of tables from which to copy
2692       * @return    void
2693       * @see versionizePages()
2694       */
2695  	function rawCopyPageContent($old_pid,$new_pid,$copyTablesArray)    {
2696          global $TCA;
2697  
2698          if ($new_pid)    {
2699              foreach($copyTablesArray as $table)    {
2700                  if ($table && is_array($TCA[$table]) && $table!='pages')    {    // all records under the page is copied.
2701                      $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($old_pid).$this->deleteClause($table));
2702                      while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))    {
2703                          $this->copyRecord_raw($table,$row['uid'],$new_pid);    // Copying each of the underlying records (method RAW)
2704                      }
2705                  }
2706              }
2707          }
2708      }
2709  
2710      /**
2711       * Inserts a record in the database, passing TCA configuration values through checkValue() but otherwise does NOTHING and checks nothing regarding permissions.
2712       * 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...
2713       *
2714       * @param    string        Table name
2715       * @param    array        Field array to insert as a record
2716       * @param    integer        The value of PID field.  -1 is indication that we are creating a new version!
2717       * @return    integer        Returns the new ID of the record (if applicable)
2718       */
2719  	function insertNewCopyVersion($table,$fieldArray,$realPid)    {
2720          global $TCA;
2721  
2722          $id = uniqid('NEW');
2723  
2724              // $fieldArray is set as current record.
2725              // 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...
2726          $this->checkValue_currentRecord = $fieldArray;
2727  
2728              // Traverse record and input-process each value:
2729          foreach($fieldArray as $field => $fieldValue)    {
2730              if (isset($TCA[$table]['columns'][$field]))    {
2731                  // Evaluating the value.
2732                  $res = $this->checkValue($table,$field,$fieldValue,$id,'new',$realPid,0);
2733                  if (isset($res['value']))    {
2734                      $fieldArray[$field] = $res['value'];
2735                  }
2736              }
2737          }
2738  
2739              // System fields being set:
2740          if ($TCA[$table]['ctrl']['crdate'])    {
2741              $fieldArray[$TCA[$table]['ctrl']['crdate']]=time();
2742          }
2743          if ($TCA[$table]['ctrl']['cruser_id'])    {
2744              $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid;
2745          }
2746          if ($TCA[$table]['ctrl']['tstamp'])    {
2747              $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
2748          }
2749  
2750              // Finally, insert record:
2751          $this->insertDB($table,$id,$fieldArray, TRUE);
2752  
2753              // Return new id:
2754          return $this->substNEWwithIDs[$id];
2755      }
2756  
2757      /**
2758       * Processing/Preparing content for copyRecord() function
2759       *
2760       * @param    string        Table name
2761       * @param    integer        Record uid
2762       * @param    string        Field name being processed
2763       * @param    string        Input value to be processed.
2764       * @param    array        Record array
2765       * @param    array        TCA field configuration
2766       * @param    integer        Real page id (pid) the record is copied to
2767       * @return    mixed        Processed value. Normally a string/integer, but can be an array for flexforms!
2768       * @access private
2769       * @see copyRecord()
2770       */
2771  	function copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf,$realDestPid)    {
2772          global $TCA;
2773  
2774              // 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)
2775          $value = $this->copyRecord_procFilesRefs($conf, $uid, $value);
2776          $inlineSubType = $this->getInlineFieldType($conf);
2777  
2778              // Register if there are references to take care of or MM is used on an inline field (no change to value):
2779          if ($this->isReferenceField($conf) || $inlineSubType == 'mm')    {
2780              $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table'];
2781              $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
2782              if ($conf['MM'])    {
2783                  $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
2784                  $dbAnalysis->start('', $allowedTables, $conf['MM'], $uid, $table, $conf);
2785                  $value = implode(',',$dbAnalysis->getValueArray($prependName));
2786              }
2787              if ($value)    {    // Setting the value in this array will notify the remapListedDBRecords() function that this field MAY need references to be corrected
2788                  $this->registerDBList[$table][$uid][$field] = $value;
2789              }
2790  
2791              // If another inline subtype is used (comma-separated-values or the foreign_field property):
2792          } elseif ($inlineSubType !== false) {
2793              $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
2794              $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
2795  
2796                  // walk through the items, copy them and remember the new id
2797              foreach ($dbAnalysis->itemArray as $k => $v) {
2798                      // Take the real positive integer if available:
2799                  if (t3lib_div::testInt($realDestPid) && $realDestPid >= 0) {
2800                      $childDestPid = $realDestPid;
2801                      // If the $realDestPid is not a valid integer or negative (e.g. workspaces):
2802                      // @TODO: Check again concerning workspaces
2803                  } else {
2804                      $childDestPid = -$v['id'];
2805                  }
2806  
2807                      // If the current field is set on a page record, update the pid of related child records:
2808                  if ($table == 'pages') {
2809                      $this->registerDBPids[$v['table']][$v['id']] = $uid;
2810                      // If the current field has ancestors that have a field on a page record, update the pid of related child records:
2811                  } elseif (isset($this->registerDBPids[$table][$uid])) {
2812                      $this->registerDBPids[$v['table']][$v['id']] = $this->registerDBPids[$table][$uid];
2813                  }
2814  
2815                  $newId = $this->copyRecord($v['table'], $v['id'], $childDestPid);
2816                  $dbAnalysis->itemArray[$k]['id'] = $newId;
2817              }
2818  
2819                  // store the new values, we will set up the uids for the subtype later on
2820              $value = implode(',',$dbAnalysis->getValueArray());
2821              $this->registerDBList[$table][$uid][$field] = $value;
2822          }
2823  
2824              // 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())
2825          if ($conf['type']=='flex')    {
2826  
2827                  // Get current value array:
2828              $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $row, $table);
2829              $currentValueArray = t3lib_div::xml2array($value);
2830  
2831                  // Traversing the XML structure, processing files:
2832              if (is_array($currentValueArray))    {
2833                  $currentValueArray['data'] = $this->checkValue_flex_procInData(
2834                              $currentValueArray['data'],
2835                              array(),    // Not used.
2836                              array(),    // Not used.
2837                              $dataStructArray,
2838                              array($table,$uid,$field),    // Parameters.
2839                              'copyRecord_flexFormCallBack'
2840                          );
2841                  $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.
2842              }
2843          }
2844  
2845          return $value;
2846      }
2847  
2848      /**
2849       * Callback function for traversing the FlexForm structure in relation to creating copied files of file relations inside of flex form structures.
2850       *
2851       * @param    array        Array of parameters in num-indexes: table, uid, field
2852       * @param    array        TCA field configuration (from Data Structure XML)
2853       * @param    string        The value of the flexForm field
2854       * @param    string        Not used.
2855       * @param    string        Not used.
2856       * @return    array        Result array with key "value" containing the value of the processing.
2857       * @see copyRecord(), checkValue_flex_procInData_travDS()
2858       */
2859  	function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)    {
2860  
2861              // Extract parameters:
2862          list($table, $uid, $field) = $pParams;
2863  
2864              // Process references and files, currently that means only the files, prepending absolute paths:
2865          $dataValue = $this->copyRecord_procFilesRefs($dsConf, $uid, $dataValue);
2866  
2867              // If references are set for this field, set flag so they can be corrected later (in ->remapListedDBRecords())
2868          if ($this->isReferenceField($dsConf) && strlen($dataValue)) {
2869              $this->registerDBList[$table][$uid][$field] = 'FlexForm_reference';
2870          }
2871  
2872              // Return
2873          return array('value' => $dataValue);
2874      }
2875  
2876      /**
2877       * Modifying a field value for any situation regarding files/references:
2878       * For attached files: take current filenames and prepend absolute paths so they get copied.
2879       * For DB references: Nothing done.
2880       *
2881       * @param    array        TCE field config
2882       * @param    integer        Record UID
2883       * @param    string        Field value (eg. list of files)
2884       * @return    string        The (possibly modified) value
2885       * @see copyRecord(), copyRecord_flexFormCallBack()
2886       */
2887  	function copyRecord_procFilesRefs($conf, $uid, $value)    {
2888  
2889              // Prepend absolute paths to files:
2890          if ($conf['type']=='group' && $conf['internal_type']=='file')    {
2891  
2892                  // Get an array with files as values:
2893              if ($conf['MM'])    {
2894                  $theFileValues = array();
2895  
2896                  $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
2897                  $dbAnalysis->start('', 'files', $conf['MM'], $uid);
2898  
2899                  foreach($dbAnalysis->itemArray as $somekey => $someval)    {
2900                      if ($someval['id'])    {
2901                          $theFileValues[] = $someval['id'];
2902                      }
2903                  }
2904              } else {
2905                  $theFileValues = t3lib_div::trimExplode(',',$value,1);
2906              }
2907  
2908                  // Traverse this array of files:
2909              $uploadFolder = $conf['uploadfolder'];
2910              $dest = $this->destPathFromUploadFolder($uploadFolder);
2911              $newValue = array();
2912  
2913              foreach($theFileValues as $file)    {
2914                  if (trim($file))    {
2915                      $realFile = $dest.'/'.trim($file);
2916                      if (@is_file($realFile))    {
2917                          $newValue[] = $realFile;
2918                      }
2919                  }
2920              }
2921  
2922                  // 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...)
2923              $value = implode(',',$newValue);
2924          }
2925  
2926              // Return the new value:
2927          return $value;
2928      }
2929  
2930  
2931  
2932  
2933  
2934  
2935  
2936  
2937  
2938  
2939  
2940  
2941  
2942      /*********************************************
2943       *
2944       * Cmd: Moving, Localizing
2945       *
2946       ********************************************/
2947  
2948      /**
2949       * Moving single records
2950       *
2951       * @param    string        Table name to move
2952       * @param    integer        Record uid to move
2953       * @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
2954       * @return    void
2955       */
2956  	function moveRecord($table,$uid,$destPid)    {
2957          global $TCA, $TYPO3_CONF_VARS;
2958  
2959          if ($TCA[$table])    {
2960  
2961                  // Prepare user defined objects (if any) for hooks which extend this function:
2962              $hookObjectsArr = array();
2963              if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'])) {
2964                  foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'] as $classRef) {
2965                      $hookObjectsArr[] = &t3lib_div::getUserObj($classRef);
2966                  }
2967              }
2968  
2969                  // 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)
2970              if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid'))    {
2971                  $uid = $lookForLiveVersion['uid'];
2972              }
2973  
2974                  // Get workspace version of the source record, if any:
2975              $WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid');
2976  
2977                  // Initialize:
2978              $sortRow = $TCA[$table]['ctrl']['sortby'];
2979              $destPid = intval($destPid);
2980              $origDestPid = $destPid;
2981  
2982              $propArr = $this->getRecordProperties($table,$uid);    // Get this before we change the pid (for logging)
2983              $moveRec = $this->getRecordProperties($table,$uid,TRUE);
2984              $resolvedPid = $this->resolvePid($table,$destPid);    // This is the actual pid of the moving to destination
2985  
2986                  // 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.
2987                  // 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.
2988              if ($table!='pages' || $resolvedPid==$moveRec['pid'])    {
2989                  $mayMoveAccess = $this->checkRecordUpdateAccess($table,$uid);    // Edit rights for the record...
2990              } else {
2991                  $mayMoveAccess = $this->doesRecordExist($table,$uid,'delete');
2992              }
2993  
2994                  // 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
2995              if ($table!='pages' || $resolvedPid!=$moveRec['pid'])    {
2996                  $mayInsertAccess = $this->checkRecordInsertAccess($table,$resolvedPid,4);    // Insert rights for the record...
2997              } else {
2998                  $mayInsertAccess = $this->checkRecordUpdateAccess($table,$uid);
2999              }
3000  
3001                  // Check workspace permissions:
3002              $workspaceAccessBlocked = array();
3003              $recIsNewVersion = !strcmp($moveRec['_ORIG_pid'],'') && (int)$moveRec['t3ver_state']==1;    // Element was an online version AND it was in "New state" so it can be moved...
3004              $destRes = $this->BE_USER->workspaceAllowLiveRecordsInPID($resolvedPid,$table);
3005                  // Workspace source check:
3006              if ($errorCode = $this->BE_USER->workspaceCannotEditRecord($table, $WSversion['uid'] ? $WSversion['uid'] : $uid))    {
3007                  $workspaceAccessBlocked['src1']='Record could not be edited in workspace: '.$errorCode.' ';
3008              } else {
3009                  if (!$recIsNewVersion && $this->BE_USER->workspaceAllowLiveRecordsInPID($moveRec['pid'],$table)<=0)    {
3010                      $workspaceAccessBlocked['src2']='Could not remove record from table "'.$table.'" from its page "'.$moveRec['pid'].'" ';
3011                  }
3012              }
3013                  // Workspace destination check:
3014              if (!($destRes>0 || ($recIsNewVersion && !$destRes)))    {    // All records can be inserted if $destRes is greater than zero. Only new versions can be inserted if $destRes is false. NO RECORDS can be inserted if $destRes is negative which indicates a stage not allowed for use.
3015                  $workspaceAccessBlocked['dest1']='Could not insert record from table "'.$table.'" in destination PID "'.$resolvedPid.'" ';
3016              } elseif ($destRes==1 && $WSversion['uid'])    {
3017                  $workspaceAccessBlocked['dest2']='Could not insert other versions in destination PID ';
3018              }
3019  
3020                  // 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...)
3021              if (($destPid<0 && !$sortRow) || $destPid>=0)    {    // $destPid>=0 because we must correct pid in case of versioning "page" types.
3022                  $destPid = $resolvedPid;
3023              }
3024  
3025                  // Timestamp field:
3026              $updateFields = array();
3027              if ($TCA[$table]['ctrl']['tstamp'])    {
3028                  $updateFields[$TCA[$table]['ctrl']['tstamp']] = time();
3029              }
3030  
3031                      // If moving is allowed, begin the processing:
3032              if (!count($workspaceAccessBlocked))    {
3033                  if ($mayMoveAccess)    {
3034                      if ($destPid>=0)    {    // insert as first element on page (where uid = $destPid)
3035                          if ($mayInsertAccess)    {
3036                              if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid))    {
3037                                  $this->clear_cache($table,$uid);    // clear cache before moving
3038  
3039                                  $updateFields['pid'] = $destPid;    // Setting PID
3040  
3041                                      // table is sorted by 'sortby'
3042                                  if ($sortRow)    {
3043                                      $sortNumber = $this->getSortNumber($table,$uid,$destPid);
3044                                      $updateFields[$sortRow] = $sortNumber;
3045                                  }
3046  
3047                                      // check for child records that have also to be moved
3048                                  $this->moveRecord_procFields($table,$uid,$destPid);
3049                                      // Create query for update:
3050                                  $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
3051  
3052                                      // Call post processing hooks:
3053                                  foreach($hookObjectsArr as $hookObj) {
3054                                      if (method_exists($hookObj, 'moveRecord_firstElementPostProcess')) {
3055                                          $hookObj->moveRecord_firstElementPostProcess($table, $uid, $destPid, $moveRec, $updateFields, $this);
3056                                      }
3057                                  }
3058  
3059                                      // Logging...
3060                                  $newPropArr = $this->getRecordProperties($table,$uid);
3061                                  $oldpagePropArr = $this->getRecordProperties('pages',$propArr['pid']);
3062                                  $newpagePropArr = $this->getRecordProperties('pages',$destPid);
3063  
3064                                  if ($destPid!=$propArr['pid'])    {
3065                                      $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
3066                                      $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
3067                                  } else {
3068                                      $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
3069                                  }
3070                                  $this->clear_cache($table,$uid);    // clear cache after moving
3071                                  $this->fixUniqueInPid($table,$uid);
3072                                      // fixCopyAfterDuplFields
3073                                  if ($origDestPid<0)    {$this->fixCopyAfterDuplFields($table,$uid,abs($origDestPid),1);}    // 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.
3074                              } else {
3075                                  $destPropArr = $this->getRecordProperties('pages',$destPid);
3076                                  $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']);
3077                              }
3078                          }
3079                      } else {    // Put after another record
3080                          if ($sortRow)    {    // table is being sorted
3081                              $sortInfo = $this->getSortNumber($table,$uid,$destPid);
3082                              $destPid = $sortInfo['pid'];    // Setting the destPid to the new pid of the record.
3083                              if (is_array($sortInfo))    {    // If not an array, there was an error (which is already logged)
3084                                  if ($mayInsertAccess)    {
3085                                      if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid))    {
3086                                          $this->clear_cache($table,$uid);    // clear cache before moving
3087  
3088                                              // We now update the pid and sortnumber
3089                                          $updateFields['pid'] = $destPid;
3090                                          $updateFields[$sortRow] = $sortInfo['sortNumber'];
3091  
3092                                              // check for child records that have also to be moved
3093                                          $this->moveRecord_procFields($table,$uid,$destPid);
3094                                              // Create query for update:
3095                                          $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
3096  
3097                                              // Call post processing hooks:
3098                                          foreach($hookObjectsArr as $hookObj) {
3099                                              if (method_exists($hookObj, 'moveRecord_afterAnotherElementPostProcess')) {
3100                                                  $hookObj->moveRecord_afterAnotherElementPostProcess($table, $uid, $destPid, $origDestPid, $moveRec, $updateFields, $this);
3101                                              }
3102                                          }
3103  
3104                                              // Logging...
3105                                          $newPropArr = $this->getRecordProperties($table,$uid);
3106                                          $oldpagePropArr = $this->getRecordProperties('pages',$propArr['pid']);
3107                                          if ($destPid!=$propArr['pid'])    {
3108                                              $newpagePropArr = $this->getRecordProperties('pages',$destPid);
3109                                              $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
3110                                              $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
3111                                          } else {
3112                                              $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
3113                                          }
3114  
3115                                              // clear cache after moving
3116                                          $this->clear_cache($table,$uid);
3117  
3118                                              // fixUniqueInPid
3119                                          $this->fixUniqueInPid($table,$uid);
3120  
3121                                              // fixCopyAfterDuplFields
3122                                          if ($origDestPid<0)    {$this->fixCopyAfterDuplFields($table,$uid,abs($origDestPid),1);}
3123                                      } else {
3124                                          $destPropArr = $this->getRecordProperties('pages',$destPid);
3125                                          $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']);
3126                                      }
3127                                  }
3128                              }
3129                          } else {
3130                              $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']);
3131                          }
3132                      }
3133                  } else {
3134                      $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']);
3135                  }
3136              } else {
3137                  $this->newlog("Move attempt failed due to workspace restrictions: ".implode(' ',$workspaceAccessBlocked),1);
3138              }
3139          }
3140      }
3141  
3142      /**
3143       * Walk through all fields of the moved record and look for children of e.g. the inline type.
3144       * If child records are found, they are also move to the new $destPid.
3145       *
3146       * @param    string        $table: Record Table
3147       * @param    string        $uid: Record UID
3148       * @param    string        $destPid: Position to move to
3149       * @return    [type]        ...
3150       */
3151  	function moveRecord_procFields($table,$uid,$destPid) {
3152          t3lib_div::loadTCA($table);
3153          $conf = $GLOBALS['TCA'][$table]['columns'];
3154          $row = t3lib_BEfunc::getRecordWSOL($table,$uid);
3155          foreach ($row as $field => $value) {
3156              $this->moveRecord_procBasedOnFieldType($table,$uid,$destPid,$field,$value,$conf[$field]['config']);
3157          }
3158      }
3159  
3160      /**
3161       * Move child records depending on the field type of the parent record.
3162       *
3163       * @param    string        $table: Record Table
3164       * @param    string        $uid: Record UID
3165       * @param    string        $destPid: Position to move to
3166       * @param    string        $field: Record field
3167       * @param    string        $value: Record field value
3168       * @param    array        $conf: TCA configuration on current field
3169       * @return    [type]        ...
3170       */
3171  	function moveRecord_procBasedOnFieldType($table,$uid,$destPid,$field,$value,$conf) {
3172          $moveTable = '';
3173          $moveIds = array();
3174  
3175          if ($conf['type'] == 'inline')    {
3176              $foreign_table = $conf['foreign_table'];
3177  
3178              if ($foreign_table) {
3179                  $inlineType = $this->getInlineFieldType($conf);
3180                  if ($inlineType == 'list' || $inlineType == 'field') {
3181                      $moveTable = $foreign_table;
3182                      $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
3183                      $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
3184                  }
3185              }
3186          }
3187  
3188              // move the records
3189          if (isset($dbAnalysis)) {
3190              foreach ($dbAnalysis->itemArray as $v) {
3191                  $this->moveRecord($v['table'],$v['id'],$destPid);
3192              }
3193          }
3194      }
3195  
3196      /**
3197       * Localizes a record to another system language
3198       * In reality it only works if transOrigPointerTable is not set. For "pages" the implementation is hardcoded
3199       *
3200       * @param    string        Table name
3201       * @param    integer        Record uid (to be localized)
3202       * @param    integer        Language ID (from sys_language table)
3203       * @return    void
3204       */
3205  	function localize($table,$uid,$language)    {
3206          global $TCA;
3207  
3208          $uid = intval($uid);
3209  
3210          if ($TCA[$table] && $uid)    {
3211              t3lib_div::loadTCA($table);
3212  
3213              if (($TCA[$table]['ctrl']['languageField'] && $TCA[$table]['ctrl']['transOrigPointerField'] && !$TCA[$table]['ctrl']['transOrigPointerTable']) || $table==='pages')    {
3214                  if ($langRec = t3lib_BEfunc::getRecord('sys_language',intval($language),'uid,title'))    {
3215                      if ($this->doesRecordExist($table,$uid,'show'))    {
3216  
3217                          $row = t3lib_BEfunc::getRecordWSOL($table,$uid);    // Getting workspace overlay if possible - this will localize versions in workspace if any
3218                          if (is_array($row))    {
3219                              if ($row[$TCA[$table]['ctrl']['languageField']] <= 0 || $table==='pages')    {
3220                                  if ($row[$TCA[$table]['ctrl']['transOrigPointerField']] == 0 || $table==='pages')    {
3221                                      if ($table==='pages')    {
3222                                          $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']));
3223                                          $Ttable = 'pages_language_overlay';
3224                                          t3lib_div::loadTCA($Ttable);
3225                                      } else {
3226                                          $pass = !t3lib_BEfunc::getRecordsByField($table,$TCA[$table]['ctrl']['transOrigPointerField'],$uid,'AND pid='.intval($row['pid']).' AND '.$TCA[$table]['ctrl']['languageField'].'='.intval($langRec['uid']));
3227                                          $Ttable = $table;
3228                                      }
3229  
3230                                      if ($pass)    {
3231  
3232                                              // Initialize:
3233                                          $overrideValues = array();
3234                                          $excludeFields = array();
3235  
3236                                              // Set override values:
3237                                          $overrideValues[$TCA[$Ttable]['ctrl']['languageField']] = $langRec['uid'];
3238                                          $overrideValues[$TCA[$Ttable]['ctrl']['transOrigPointerField']] = $uid;
3239  
3240                                              // Set exclude Fields:
3241                                          foreach($TCA[$Ttable]['columns'] as $fN => $fCfg)    {
3242                                              if ($fCfg['l10n_mode']=='prefixLangTitle')    {    // Check if we are just prefixing:
3243                                                  if (($fCfg['config']['type']=='text' || $fCfg['config']['type']=='input') && strlen($row[$fN]))    {
3244                                                      list($tscPID) = t3lib_BEfunc::getTSCpid($table,$uid,'');
3245                                                      $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
3246  
3247                                                      if (isset($TSConfig['translateToMessage']) && strlen($TSConfig['translateToMessage']))    {
3248                                                          $translateToMsg = @sprintf($TSConfig['translateToMessage'], $langRec['title']);
3249                                                      }
3250                                                      if (!strlen($translateToMsg))    {
3251                                                          $translateToMsg = 'Translate to '.$langRec['title'].':';
3252                                                      }
3253  
3254                                                      $overrideValues[$fN] = '['.$translateToMsg.'] '.$row[$fN];
3255                                                  }
3256                                              } 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)
3257                                                  $excludeFields[] = $fN;
3258                                              }
3259                                          }
3260  
3261                                          if ($Ttable === $table)    {
3262  
3263                                                  // Execute the copy:
3264                                              $this->copyRecord($table,$uid,-$uid,1,$overrideValues,implode(',',$excludeFields));
3265                                          } else {
3266  
3267                                                  // Create new record:
3268                                              $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain');
3269                                              $copyTCE->stripslashes_values = 0;
3270                                              $copyTCE->cachedTSconfig = $this->cachedTSconfig;    // Copy forth the cached TSconfig
3271                                              $copyTCE->dontProcessTransformations=1;        // Transformations should NOT be carried out during copy
3272  
3273                                              $copyTCE->start(array($Ttable=>array('NEW'=>$overrideValues)),'',$this->BE_USER);
3274                                              $copyTCE->process_datamap();
3275  
3276                                                  // Getting the new UID as if it had been copied:
3277                                              $theNewSQLID = $copyTCE->substNEWwithIDs['NEW'];
3278                                              if ($theNewSQLID)    {
3279                                                      // 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"
3280                                                  $this->copyMappingArray[$Ttable][$uid] = $theNewSQLID;
3281                                              }
3282                                          }
3283                                      } else $this->newlog('Localization failed; There already was a localization for this language of the record!',1);
3284                                  } else $this->newlog('Localization failed; Source record contained a reference to an original default record (which is strange)!',1);
3285                              } else $this->newlog('Localization failed; Source record had another language than "Default" or "All" defined!',1);
3286                          } else $this->newlog('Attempt to localize record that did not exist!',1);
3287                      } else $this->newlog('Attempt to localize record without permission',1);
3288                  } else $this->newlog('Sys language UID "'.$language.'" not found valid!',1);
3289              } else $this->newlog('Localization failed; "languageField" and "transOrigPointerField" must be defined for the table!',1);
3290          }
3291      }
3292  
3293  
3294  
3295  
3296  
3297  
3298  
3299  
3300  
3301  
3302  
3303  
3304  
3305  
3306      /*********************************************
3307       *
3308       * Cmd: Deleting
3309       *
3310       ********************************************/
3311  
3312      /**
3313       * Delete a single record
3314       *
3315       * @param    string        Table name
3316       * @param    integer        Record UID
3317       * @return    void
3318       */
3319  	function deleteAction($table, $id)    {
3320          global $TCA;
3321  
3322          $delRec = t3lib_BEfunc::getRecord($table, $id);
3323  
3324          if (is_array($delRec))    {    // Record asked to be deleted was found:
3325  
3326                  // For Live version, try if there is a workspace version because if so, rather "delete" that instead
3327              if ($delRec['pid']!=-1)    {    // Look, if record is an offline version, then delete directly:
3328                  if ($wsVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $id))    {
3329                      $delRec = $wsVersion;
3330                      $id = $delRec['uid'];
3331                  }
3332              }
3333  
3334              if ($delRec['pid']==-1)    {    // Look, if record is an offline version, then delete directly:
3335                  if ($TCA[$table]['ctrl']['versioningWS'])    {
3336                      if ($this->BE_USER->workspace==0 || (int)$delRec['t3ver_wsid']==$this->BE_USER->workspace)    {    // In Live workspace, delete any. In other workspaces there must be match.
3337                          $liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'uid,t3ver_state');
3338  
3339                          if ($delRec['t3ver_wsid']==0 || (int)$liveRec['t3ver_state']!==1)    {    // Delete those in WS 0 + if their live records state was not "Placeholder".
3340                              $this->deleteEl($table, $id);
3341                          } else {    // If live record was placeholder, rather clear it from workspace (because it clears both version and placeholder).
3342                              $this->version_clearWSID($table,$id);
3343                          }
3344                      } else $this->newlog('Tried to delete record from another workspace',1);
3345                  } else $this->newlog('Versioning not enabled for record with PID = -1!',2);
3346              } elseif ($res = $this->BE_USER->workspaceAllowLiveRecordsInPID($delRec['pid'], $table))    {    // Look, if record is "online" or in a versionized branch, then delete directly.
3347                  if ($res>0)    {
3348                      $this->deleteEl($table, $id);
3349                  } else $this->newlog('Stage of root point did not allow for deletion',1);
3350              } else {
3351                  // Otherwise, try to delete by versionization:
3352                  $this->versionizeRecord($table,$id,'DELETED!',TRUE);
3353              }
3354          }
3355      }
3356  
3357      /**
3358       * Delete element from any table
3359       *
3360       * @param    string        Table name
3361       * @param    integer        Record UID
3362       * @param    boolean        Flag: If $noRecordCheck is set, then the function does not check permission to delete record
3363       * @param    boolean        If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
3364       * @return    void
3365       */
3366  	function deleteEl($table, $uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE)    {
3367          if ($table == 'pages')    {
3368              $this->deletePages($uid, $noRecordCheck, $forceHardDelete);
3369          } else {
3370              $this->deleteVersionsForRecord($table,$uid,$forceHardDelete);
3371              $this->deleteRecord($table, $uid, $noRecordCheck, $forceHardDelete);
3372          }
3373      }
3374  
3375      /**
3376       * Delete versions for element from any table
3377       *
3378       * @param    string        Table name
3379       * @param    integer        Record UID
3380       * @param    boolean        If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
3381       * @return    void
3382       */
3383  	function deleteVersionsForRecord($table, $uid, $forceHardDelete)    {
3384          $versions = t3lib_BEfunc::selectVersionsOfRecord($table, $uid, 'uid,pid');
3385          if (is_array($versions))    {
3386              foreach($versions as $verRec)    {
3387                  if (!$verRec['_CURRENT_VERSION'])    {
3388                      if ($table == 'pages')    {
3389                          $this->deletePages($verRec['uid'], TRUE, $forceHardDelete);
3390                      } else {
3391                          $this->deleteRecord($table, $verRec['uid'], TRUE, $forceHardDelete);
3392                      }
3393                  }
3394              }
3395          }
3396      }
3397  
3398      /**
3399       * Undelete a single record
3400       *
3401       * @param    string        Table name
3402       * @param    integer        Record UID
3403       * @return    void
3404       */
3405  	function undeleteRecord($table,$uid)    {
3406          $this->deleteRecord($table,$uid,TRUE,FALSE,TRUE);
3407      }
3408  
3409      /**
3410       * Deleting/Undeleting a record
3411       * This function may not be used to delete pages-records unless the underlying records are already deleted
3412       * Deletes a record regardless of versioning state (live or offline, doesn't matter, the uid decides)
3413       * If both $noRecordCheck and $forceHardDelete are set it could even delete a "deleted"-flagged record!
3414       *
3415       * @param    string        Table name
3416       * @param    integer        Record UID
3417       * @param    boolean        Flag: If $noRecordCheck is set, then the function does not check permission to delete record
3418       * @param    boolean        If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
3419       * @param    boolean        If TRUE, the "deleted" flag is set to 0 again and thus, the item is undeleted.
3420       * @return    void
3421       */
3422  	function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE,$undeleteRecord=FALSE)    {
3423          global $TCA;
3424  
3425          $uid = intval($uid);
3426          if ($TCA[$table] && $uid)    {
3427              if ($noRecordCheck || $this->doesRecordExist($table,$uid,'delete'))    {
3428                  $this->clear_cache($table,$uid);    // clear cache before deleting the record, else the correct page cannot be identified by clear_cache
3429  
3430                  $propArr = $this->getRecordProperties($table, $uid);
3431                  $pagePropArr = $this->getRecordProperties('pages', $propArr['pid']);
3432  
3433                  $deleteRow = $TCA[$table]['ctrl']['delete'];
3434                  if ($deleteRow && !$forceHardDelete)    {
3435                      $value = $undeleteRecord ? 0 : 1;
3436                      $updateFields = array(
3437                          $deleteRow => $value
3438                      );
3439  
3440                      if ($TCA[$table]['ctrl']['tstamp']) {
3441                          $updateFields[$TCA[$table]['ctrl']['tstamp']] = time();
3442                      }
3443  
3444                          // If the table is sorted, then the sorting number is set very high
3445                      if ($TCA[$table]['ctrl']['sortby'] && !$undeleteRecord)    {
3446                          $updateFields[$TCA[$table]['ctrl']['sortby']] = 1000000000;
3447                      }
3448  
3449                          // before (un-)deleting this record, check for child records or references
3450                      $this->deleteRecord_procFields($table, $uid, $undeleteRecord);
3451                      $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
3452                  } else {
3453  
3454                          // Fetches all fields with flexforms and look for files to delete:
3455                      t3lib_div::loadTCA($table);
3456                      foreach($TCA[$table]['columns'] as $fieldName => $cfg)    {
3457                          $conf = $cfg['config'];
3458  
3459                          switch($conf['type'])    {
3460                              case 'flex':
3461                                  $flexObj = t3lib_div::makeInstance('t3lib_flexformtools');
3462                                  $flexObj->traverseFlexFormXMLData($table,$fieldName,t3lib_BEfunc::getRecordRaw($table,'uid='.intval($uid)),$this,'deleteRecord_flexFormCallBack');
3463                              break;
3464                          }
3465                      }
3466  
3467                          // Fetches all fields that holds references to files
3468                      $fileFieldArr = $this->extFileFields($table);
3469                      if (count($fileFieldArr))    {
3470                          $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',',$fileFieldArr), $table, 'uid='.intval($uid));
3471                          if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))    {
3472                              $fArray = $fileFieldArr;
3473                              foreach($fArray as $theField)    {    // MISSING: Support for MM file relations!
3474                                  $this->extFileFunctions($table,$theField,$row[$theField],'deleteAll');        // This deletes files that belonged to this record.
3475                              }
3476                          } else {
3477                              $this->log($table,$uid,3,0,100,'Delete: Zero rows in result when trying to read filenames from record which should be deleted');
3478                          }
3479                      }
3480  
3481                          // Delete the hard way...:
3482                      $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($uid));
3483                  }
3484  
3485                  $state = $undeleteRecord ? 1 : 3;    // 1 means insert, 3 means delete
3486                  if (!$GLOBALS['TYPO3_DB']->sql_error())    {
3487                      if ($forceHardDelete) {
3488                          $message = "Record '%s' (%s) was deleted unrecoverable from page '%s' (%s)";
3489                      }
3490                      else {
3491                          $message = $state == 1 ?
3492                              "Record '%s' (%s) was restored on page '%s' (%s)" :
3493                              "Record '%s' (%s) was deleted from page '%s' (%s)";
3494                      }
3495                      $this->log($table, $uid, $state, 0, 0,
3496                                  $message, 0,
3497                                  array(
3498                                      $propArr['header'],
3499                                      $table.':'.$uid,
3500                                      $pagePropArr['header'],
3501                                      $propArr['pid']
3502                                      ),
3503                                  $propArr['pid']);
3504  
3505                  } else {
3506                      $this->log($table,$uid,$state,0,100,$GLOBALS['TYPO3_DB']->sql_error());
3507                  }
3508  
3509                      // Update reference index:
3510                  $this->updateRefIndex($table,$uid);
3511  
3512                      // if there are entries in the updateRefIndexStack
3513                  if (is_array($this->updateRefIndexStack[$table]) && is_array($this->updateRefIndexStack[$table][$uid])) {
3514                      while ($args = array_pop($this->updateRefIndexStack[$table][$uid])) {
3515                              // $args[0]: table, $args[1]: uid
3516                          $this->updateRefIndex($args[0], $args[1]);
3517                      }
3518                      unset($this->updateRefIndexStack[$table][$uid]);
3519                  }
3520  
3521              } else $this->log($table,$uid,3,0,1,'Attempt to delete record without delete-permissions');
3522          }
3523      }
3524  
3525      /**
3526       * Call back function for deleting file relations for flexform fields in records which are being completely deleted.
3527       *
3528       * @param    [type]        $dsArr: ...
3529       * @param    [type]        $dataValue: ...
3530       * @param    [type]        $PA: ...
3531       * @param    [type]        $structurePath: ...
3532       * @param    [type]        $pObj: ...
3533       * @return    [type]        ...
3534       */
3535  	function deleteRecord_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj)    {
3536  
3537              // Use reference index object to find files in fields:
3538          $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
3539          $files = $refIndexObj->getRelations_procFiles($dataValue, $dsArr['TCEforms']['config'], $PA['uid']);
3540  
3541              // Traverse files and delete them:
3542          if (is_array($files))    {
3543              foreach($files as $dat)    {
3544                  if (@is_file($dat['ID_absFile']))    {
3545                      unlink ($dat['ID_absFile']);
3546  #echo 'DELETE FlexFormFile:'.$dat['ID_absFile'].chr(10);
3547                  } else {
3548                      $this->log($table,0,3,0,100,"Delete: Referenced file '".$dat['ID_absFile']."' that was supposed to be deleted together with it's record didn't exist");
3549                  }
3550              }
3551          }
3552      }
3553  
3554      /**
3555       * Used to delete page because it will check for branch below pages and unallowed tables on the page as well.
3556       *
3557       * @param    integer        Page id
3558       * @param    boolean        If TRUE, pages are not checked for permission.
3559       * @param    boolean        If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
3560       * @return    void
3561       */
3562  	function deletePages($uid,$force=FALSE,$forceHardDelete=FALSE)    {
3563              // Getting list of pages to delete:
3564          if ($force)    {
3565              $brExist = $this->doesBranchExist('',$uid,0,1);        // returns the branch WITHOUT permission checks (0 secures that)
3566              $res = t3lib_div::trimExplode(',',$brExist.$uid,1);
3567          } else {
3568              $res = $this->canDeletePage($uid);
3569          }
3570  
3571              // Perform deletion if not error:
3572          if (is_array($res))    {
3573              foreach($res as $deleteId)    {
3574                  $this->deleteSpecificPage($deleteId,$forceHardDelete);
3575              }
3576          } else {
3577              $this->newlog($res,1);
3578          }
3579      }
3580  
3581      /**
3582       * Delete a page and all records on it.
3583       *
3584       * @param    integer        Page id
3585       * @param    boolean        If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
3586       * @return    void
3587       * @access private
3588       * @see deletePages()
3589       */
3590  	function deleteSpecificPage($uid,$forceHardDelete=FALSE)    {
3591          global $TCA;
3592          reset ($TCA);
3593          $uid = intval($uid);
3594          if ($uid)    {
3595              while (list($table)=each($TCA))    {
3596                  if ($table!='pages')    {
3597                      $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table));
3598                      while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))    {
3599                          $this->deleteVersionsForRecord($table,$row['uid'],$forceHardDelete);
3600                          $this->deleteRecord($table,$row['uid'], TRUE, $forceHardDelete);
3601                      }
3602                  }
3603              }
3604              $this->deleteVersionsForRecord('pages',$uid,$forceHardDelete);
3605              $this->deleteRecord('pages',$uid, TRUE, $forceHardDelete);
3606          }
3607      }
3608  
3609      /**
3610       * Used to evaluate if a page can be deleted
3611       *
3612       * @param    integer        Page id
3613       * @return    mixed        If array: List of page uids to traverse and delete (means OK), if string: error code.
3614       */
3615  	function canDeletePage($uid)    {
3616          if ($this->doesRecordExist('pages',$uid,'delete'))    {    // If we may at all delete this page
3617              if ($this->deleteTree)    {
3618                  $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1);    // returns the branch
3619                  if ($brExist != -1)    {    // Checks if we had permissions
3620                      if ($this->noRecordsFromUnallowedTables($brExist.$uid))    {
3621                          return t3lib_div::trimExplode(',',$brExist.$uid,1);
3622                      } else return 'Attempt to delete records from disallowed tables';
3623                  } else return 'Attempt to delete pages in branch without permissions';
3624              } else {
3625                  $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1);    // returns the branch
3626                  if ($brExist == '')    {    // Checks if branch exists
3627                      if ($this->noRecordsFromUnallowedTables($uid))    {
3628                          return array($uid);
3629                      } else return 'Attempt to delete records from disallowed tables';
3630                  } else return 'Attempt to delete page which has subpages';
3631              }
3632          } else return 'Attempt to delete page without permissions';
3633      }
3634  
3635      /**
3636       * Returns true if record CANNOT be deleted, otherwise false. Used to check before the versioning API allows a record to be marked for deletion.
3637       *
3638       * @param    string        Record Table
3639       * @param    integer        Record UID
3640       * @return    string        Returns a string IF there is an error (error string explaining). FALSE means record can be deleted
3641       */
3642  	function cannotDeleteRecord($table,$id)    {
3643          if ($table==='pages')    {
3644              $res = $this->canDeletePage($id);
3645              return is_array($res) ? FALSE : $res;
3646          } else {
3647              return $this->doesRecordExist($table,$id,'delete') ? FALSE : 'No permission to delete record';
3648          }
3649      }
3650  
3651      /**
3652       * Beford a record is deleted, check if it has references such as inline type or MM references.
3653       * If so, set these child records also to be deleted.
3654       *
3655       * @param    string        $table: Record Table
3656       * @param    string        $uid: Record UID
3657       * @param    boolean        $undeleteRecord: If a record should be undeleted (e.g. from history/undo)
3658       * @return    void
3659       * @see     deleteRecord()
3660       */
3661  	function deleteRecord_procFields($table, $uid, $undeleteRecord = false) {
3662          t3lib_div::loadTCA($table);
3663          $conf = $GLOBALS['TCA'][$table]['columns'];
3664          $row = t3lib_BEfunc::getRecord($table, $uid, '*', '', false);
3665  
3666          foreach ($row as $field => $value) {
3667              $this->deleteRecord_procBasedOnFieldType($table, $uid, $field, $value, $conf[$field]['config'], $undeleteRecord);
3668          }
3669      }
3670  
3671      /**
3672       * Process fields of a record to be deleted and search for special handling, like
3673       * inline type, MM records, etc.
3674       *
3675       * @param    string        $table: Record Table
3676       * @param    string        $uid: Record UID
3677       * @param    string        $field: Record field
3678       * @param    string        $value: Record field value
3679       * @param    array        $conf: TCA configuration on current field
3680       * @param    boolean        $undeleteRecord: If a record should be undeleted (e.g. from history/undo)
3681       * @return    void
3682       * @see     deleteRecord()
3683       */
3684  	function deleteRecord_procBasedOnFieldType($table, $uid, $field, $value, $conf, $undeleteRecord = false) {
3685          if ($conf['type'] == 'inline')    {
3686              $foreign_table = $conf['foreign_table'];
3687  
3688              if ($foreign_table) {
3689                  $inlineType = $this->getInlineFieldType($conf);
3690                  if ($inlineType == 'list' || $inlineType == 'field') {
3691                      $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
3692                      $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf);
3693                      $dbAnalysis->undeleteRecord = true;
3694  
3695                          // walk through the items and remove them
3696                      foreach ($dbAnalysis->itemArray as $v) {
3697                          if (!$undeleteRecord)    {
3698                              $this->deleteAction($v['table'], $v['id']);
3699                          } else {
3700                              $this->undeleteRecord($v['table'], $v['id']);
3701                          }
3702                      }
3703                  }
3704              }
3705  
3706              // no delete action but calls to updateRefIndex *AFTER* this record was deleted
3707          } elseif ($this->isReferenceField($conf)) {
3708              $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table'];
3709              $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
3710  
3711              $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
3712              $dbAnalysis->start($value, $allowedTables, $conf['MM'], $uid, $table, $conf);
3713  
3714              foreach ($dbAnalysis->itemArray as $v) {
3715                  $this->updateRefIndexStack[$table][$uid][] = array($v['table'], $v['id']);
3716              }
3717          }
3718      }
3719  
3720  
3721  
3722  
3723  
3724  
3725  
3726  
3727      /*********************************************
3728       *
3729       * Cmd: Versioning
3730       *
3731       ********************************************/
3732  
3733      /**
3734       * Creates a new version of a record
3735       * (Requires support in the table)
3736       *
3737       * @param    string        Table name
3738       * @param    integer        Record uid to versionize
3739       * @param    string        Version label
3740       * @param    boolean        If true, the version is created to delete the record.
3741       * @param    integer        Indicating "treeLevel" - or versioning type - "element" (-1), "page" (0) or "branch" (>=1)
3742       * @return    integer        Returns the id of the new version (if any)
3743       * @see copyRecord()
3744       */
3745  	function versionizeRecord($table,$id,$label,$delete=FALSE,$versionizeTree=-1)    {
3746          global $TCA;
3747  
3748          $id = intval($id);
3749  
3750          if ($TCA[$table] && $TCA[$table]['ctrl']['versioningWS'] && $id>0)    {
3751              if ($this->doesRecordExist($table,$id,'show'))    {
3752                  if ($this->BE_USER->workspaceVersioningTypeAccess($versionizeTree))    {
3753  
3754                          // Select main record:
3755                      $row = $this->recordInfo($table,$id,'pid,t3ver_id');
3756                      if (is_array($row))    {
3757                          if ($row['pid']>=0)    {    // record must be online record
3758                              if (!$delete || !$this->cannotDeleteRecord($table,$id)) {
3759  
3760                                      // Look for next version number:
3761                                  $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
3762                                      't3ver_id',
3763                                      $table,
3764                                      '(t3ver_oid='.$id.' OR uid='.$id.')'.$this->deleteClause($table),
3765                                      '',
3766                                      't3ver_id DESC',
3767                                      '1'
3768                                  );
3769                                  list($highestVerNumber) = $GLOBALS['TYPO3_DB']->sql_fetch_row($res);
3770  
3771                                      // Look for version number of the current:
3772                                  $subVer = $row['t3ver_id'].'.'.($highestVerNumber+1);
3773  
3774                                      // Set up the values to override when making a raw-copy:
3775                                  $overrideArray = array(
3776                                      't3ver_id' => $highestVerNumber+1,
3777                                      't3ver_oid' => $id,
3778                                      't3ver_label' => ($label ? $label : $subVer.' / '.date('d-m-Y H:m:s')),
3779                                      't3ver_wsid' => $this->BE_USER->workspace,
3780                                      't3ver_state' => $delete ? 2 : 0,
3781                                      't3ver_count' => 0,
3782                                      't3ver_stage' => 0,
3783                                      't3ver_tstamp' => 0
3784                                  );
3785                                  if ($TCA[$table]['ctrl']['editlock'])    {
3786                                      $overrideArray[$TCA[$table]['ctrl']['editlock']] = 0;
3787                                  }
3788                                  if ($table==='pages')    {
3789                                      $overrideArray['t3ver_swapmode'] = $versionizeTree;
3790                                  }
3791  
3792                                      // Checking if the record already has a version in the current workspace of the backend user
3793                                  $workspaceCheck = TRUE;
3794                                  if ($this->BE_USER->workspace!==0)    {
3795                                          // Look for version already in workspace:
3796                                      $workspaceCheck = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace,$table,$id,'uid') ? FALSE : TRUE;
3797                                  }
3798  
3799                                  if ($workspaceCheck)    {
3800  
3801                                          // Create raw-copy and return result:
3802                                      return $this->copyRecord_raw($table,$id,-1,$overrideArray);
3803                                  } else $this->newlog('Record you wanted to versionize was already a version in the workspace (wsid='.$this->BE_USER->workspace.')!',1);
3804                              } else $this->newlog('Record cannot be deleted: '.$this->cannotDeleteRecord($table,$id),1);
3805                          } else $this->newlog('Record you wanted to versionize was already a version in archive (pid=-1)!',1);
3806                      } else $this->newlog('Record you wanted to versionize did not exist!',1);
3807                  } else $this->newlog('The versioning type '.$versionizeTree.' mode you requested was not allowed',1);
3808              } else $this->newlog('You didnt have correct permissions to make a new version (copy) of this record "'.$table.'" / '.$id,1);
3809          } else $this->newlog('Versioning is not supported for this table "'.$table.'" / '.$id,1);
3810      }
3811  
3812      /**
3813       * Creates a new version of a page including content and possible subpages.
3814       *
3815       * @param    integer        Page uid to create new version of.
3816       * @param    string        Version label
3817       * @param    integer        Indicating "treeLevel" - "page" (0) or "branch" (>=1) ["element" type must call versionizeRecord() directly]
3818       * @return    void
3819       * @see copyPages()
3820       */
3821  	function versionizePages($uid,$label,$versionizeTree)    {
3822          global $TCA;
3823  
3824          $uid = intval($uid);
3825          $brExist = $this->doesBranchExist('',$uid,$this->pMap['show'],1);    // returns the branch
3826  
3827          if ($brExist != -1)    {    // Checks if we had permissions
3828  
3829                  // Finding list of tables ALLOWED to be copied
3830              $allowedTablesArray = $this->admin ? $this->compileAdminTables() : explode(',',$this->BE_USER->groupData['tables_modify']);    // These are the tables, the user may modify
3831              $allowedTablesArray = $this->compileAdminTables();    // These are ALL tables because a new version should be ALL of them regardless of permission of the user executing the request.
3832  
3833                  // Make list of tables that should come along with a new version of the page:
3834              $verTablesArray = array();
3835              $allTables = array_keys($TCA);
3836              foreach($allTables as $tN)    {
3837                  if ($tN!='pages' && ($versionizeTree>0 || $TCA[$tN]['ctrl']['versioning_followPages']) && ($this->admin || in_array($tN, $allowedTablesArray)))    {
3838                      $verTablesArray[] = $tN;
3839                  }
3840              }
3841  
3842                  // Begin to copy pages if we're allowed to:
3843              if ($this->admin || in_array('pages',$allowedTablesArray))    {
3844                  if ($this->BE_USER->workspaceVersioningTypeAccess($versionizeTree))    {
3845                          // Versionize this page:
3846                      $theNewRootID = $this->versionizeRecord('pages',$uid,$label,FALSE,$versionizeTree);
3847                      if ($theNewRootID)    {
3848                          $this->rawCopyPageContent($uid,$theNewRootID,$verTablesArray);
3849  
3850                              // If we're going to copy recursively...:
3851                          if ($versionizeTree>0)    {
3852  
3853                                  // Get ALL subpages to copy (read permissions respected - they should NOT be...):
3854                              $CPtable = $this->int_pageTreeInfo(Array(), $uid, intval($versionizeTree), $theNewRootID);
3855  
3856                                  // Now copying the subpages:
3857                              foreach($CPtable as $thePageUid => $thePagePid)    {
3858                                  $newPid = $this->copyMappingArray['pages'][$thePagePid];
3859                                  if (isset($newPid))    {
3860                                      $theNewRootID = $this->copyRecord_raw('pages',$thePageUid,$newPid);
3861                                      $this->rawCopyPageContent($thePageUid,$theNewRootID,$verTablesArray);
3862                                  } else {
3863                                      $this->newlog('Something went wrong during copying branch (for versioning)',1);
3864                                      break;
3865                                  }
3866                              }
3867                          }    // else the page was not copied. Too bad...
3868                      } else $this->newlog('The root version could not be created!',1);
3869                  } else $this->newlog('Versioning type "'.$versionizeTree.'" was not allowed in workspace',1);
3870              } else $this->newlog('Attempt to versionize page without permission to this table',1);
3871          } else $this->newlog('Could not read all subpages to versionize.',1);
3872      }
3873  
3874      /**
3875       * Swapping versions of a record
3876       * Version from archive (future/past, called "swap version") will get the uid of the "t3ver_oid", the official element with uid = "t3ver_oid" will get the new versions old uid. PIDs are swapped also
3877       *
3878       * @param    string        Table name
3879       * @param    integer        UID of the online record to swap
3880       * @param    integer        UID of the archived version to swap with!
3881       * @param    boolean        If set, swaps online into workspace instead of publishing out of workspace.
3882       * @return    void
3883       */
3884  	function version_swap($table,$id,$swapWith,$swapIntoWS=0)    {
3885          global $TCA;
3886  
3887          /*
3888          Version ID swapping principles:
3889              - Version from archive (future/past, called "swap version") will get the uid of the "t3ver_oid", the official element with uid = "t3ver_oid" will get the new versions old uid. PIDs are swapped also
3890  
3891              uid        pid            uid        t3ver_oid    pid
3892          1:    13        123     -->    -13        247            123        (Original has negated UID, and sets t3ver_oid to the final UID (which is nice to know for recovery). PID is unchanged at this point)
3893          2:    247        -1     -->    13        13            123        (Swap version gets original UID, correct t3ver_oid (not required for online version) and is moved to the final PID (123))
3894          3:    -13        123     -->    247        13            -1        (Original gets the swap versions old UID, has t3ver_oid set correctly (important) and the ver. repository PID set right.)
3895  
3896              13 is online UID,
3897              247 is specific versions UID
3898              123 is the PID of the original record
3899              -1 is the versioning repository PID
3900  
3901              Recovery Process:
3902                  Search for negative UID (here "-13"):
3903                      YES: Step 1 completed, but at least step 3 didn't.
3904                          Search for the negativ UIDs positive (here: "13")
3905                              YES: Step 2 completed: Rollback: "t3ver_oid" of the -uid record shows the original UID of the swap record. Use that to change back UID and pid to -1. After that, proceed with recovery for step 1 (see below)
3906                              NO: Only Step 1 completed! Rollback: Just change uid "-13" to "13" and "t3ver_oid" to "13" (not important)
3907                      NO: No problems.
3908          */
3909  
3910              // First, check if we may actually edit the online record
3911          if ($this->checkRecordUpdateAccess($table,$id))    {
3912  
3913                  // Select the two versions:
3914              $curVersion = t3lib_BEfunc::getRecord($table,$id,'*');
3915              $swapVersion = t3lib_BEfunc::getRecord($table,$swapWith,'*');
3916  
3917              if (is_array($curVersion) && is_array($swapVersion))    {
3918                  if ($this->BE_USER->workspacePublishAccess($swapVersion['t3ver_wsid']))    {
3919                      $wsAccess = $this->BE_USER->checkWorkspace($swapVersion['t3ver_wsid']);
3920                      if ($swapVersion['t3ver_wsid']<=0 || !($wsAccess['publish_access']&1) || (int)$swapVersion['t3ver_stage']===10)    {
3921                          if ($this->doesRecordExist($table,$swapWith,'show') && $this->checkRecordUpdateAccess($table,$swapWith)) {
3922                              if (!$swapIntoWS || $this->BE_USER->workspaceSwapAccess())    {
3923  
3924                                      // Check if the swapWith record really IS a version of the original!
3925                                  if ((int)$swapVersion['pid']==-1 && (int)$curVersion['pid']>=0 && !strcmp($swapVersion['t3ver_oid'],$id))    {
3926  
3927                                          // Lock file name:
3928                                      $lockFileName = PATH_site.'typo3temp/swap_locking/'.$table.':'.$id.'.ser';
3929  
3930                                      if (!@is_file($lockFileName))    {
3931  
3932                                              // Write lock-file:
3933                                          t3lib_div::writeFileToTypo3tempDir($lockFileName,serialize(array(
3934                                              'tstamp'=>time(),
3935                                              'user'=>$GLOBALS['BE_USER']->user['username'],
3936                                              'curVersion'=>$curVersion,
3937                                              'swapVersion'=>$swapVersion
3938                                          )));
3939  
3940                                              // Find fields to keep
3941                                          $keepFields = $this->getUniqueFields($table);
3942                                          if ($TCA[$table]['ctrl']['sortby'])    {
3943                                              $keepFields[] = $TCA[$table]['ctrl']['sortby'];
3944                                          }
3945  
3946                                              // Swap "keepfields"
3947                                          foreach($keepFields as $fN)    {
3948                                              $tmp = $swapVersion[$fN];
3949                                              $swapVersion[$fN] = $curVersion[$fN];
3950                                              $curVersion[$fN] = $tmp;
3951                                          }
3952  
3953                                              // Preserve states:
3954                                          $t3ver_state = array();
3955                                          $t3ver_state['swapVersion'] = $swapVersion['t3ver_state'];
3956                                          $t3ver_state['curVersion'] = $curVersion['t3ver_state'];
3957  
3958                                              // Modify offline version to become online:
3959                                          $tmp_wsid = $swapVersion['t3ver_wsid'];
3960                                          unset($swapVersion['uid']);
3961                                          $swapVersion['pid'] = intval($curVersion['pid']);    // Set pid for ONLINE
3962                                          $swapVersion['t3ver_oid'] = intval($id);
3963                                          $swapVersion['t3ver_wsid'] = $swapIntoWS ? intval($curVersion['t3ver_wsid']) : 0;
3964                                          $swapVersion['t3ver_tstamp'] = time();
3965                                          $swapVersion['t3ver_stage'] = $swapVersion['t3ver_state'] = 0;
3966  
3967                                              // Modify online version to become offline:
3968                                          unset($curVersion['uid']);
3969                                          $curVersion['pid'] = -1;    // Set pid for OFFLINE
3970                                          $curVersion['t3ver_oid'] = intval($id);
3971                                          $curVersion['t3ver_wsid'] = $swapIntoWS ? intval($tmp_wsid) : 0;
3972                                          $curVersion['t3ver_tstamp'] = time();
3973                                          $curVersion['t3ver_count'] = $curVersion['t3ver_count']+1;    // Increment lifecycle counter
3974                                          $curVersion['t3ver_stage'] = $curVersion['t3ver_state'] = 0;
3975  
3976                                          if ($table==='pages') {        // Keeping the swapmode state
3977                                                  $curVersion['t3ver_swapmode'] = $swapVersion['t3ver_swapmode'];
3978                                          }
3979  
3980                                              // Execute swapping:
3981                                          $sqlErrors = array();
3982                                          $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($id),$swapVersion);
3983                                          if ($GLOBALS['TYPO3_DB']->sql_error())        {
3984                                              $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error();
3985                                          } else {
3986                                              $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($swapWith),$curVersion);
3987                                              if ($GLOBALS['TYPO3_DB']->sql_error())    {
3988                                                  $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error();
3989                                              } else {
3990                                                  unlink($lockFileName);
3991                                              }
3992                                          }
3993  
3994                                          if (!count($sqlErrors))    {
3995  
3996                                                  // Checking for delete:
3997                                              if ($t3ver_state['swapVersion']==2)    {
3998                                                  $this->deleteEl($table,$id,TRUE);    // Force delete
3999                                              }
4000  
4001                                              $this->newlog('Swapping successful for table "'.$table.'" uid '.$id.'=>'.$swapWith);
4002  
4003                                                  // Update reference index:
4004                                              $this->updateRefIndex($table,$id);
4005                                              $this->updateRefIndex($table,$swapWith);
4006  
4007                                                  // SWAPPING pids for subrecords:
4008                                              if ($table=='pages' && $swapVersion['t3ver_swapmode']>=0)    {
4009  
4010                                                      // Collect table names that should be copied along with the tables:
4011                                                  foreach($TCA as $tN => $tCfg)    {
4012                                                      if ($swapVersion['t3ver_swapmode']>0 || $TCA[$tN]['ctrl']['versioning_followPages'])    {    // For "Branch" publishing swap ALL, otherwise for "page" publishing, swap only "versioning_followPages" tables
4013                                                          $temporaryPid = -($id+1000000);
4014  
4015                                                          $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($id),array('pid'=>$temporaryPid));
4016                                                          if ($GLOBALS['TYPO3_DB']->sql_error())    $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error();
4017  
4018                                                          $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($swapWith),array('pid'=>$id));
4019                                                          if ($GLOBALS['TYPO3_DB']->sql_error())    $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error();
4020  
4021                                                          $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($temporaryPid),array('pid'=>$swapWith));
4022                                                          if ($GLOBALS['TYPO3_DB']->sql_error())    $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error();
4023  
4024                                                          if (count($sqlErrors))    {
4025                                                              $this->newlog('During Swapping: SQL errors happend: '.implode('; ',$sqlErrors),2);
4026                                                          }
4027                                                      }
4028                                                  }
4029                                              }
4030                                                  // Clear cache:
4031                                              $this->clear_cache($table,$id);
4032  
4033                                                  // Checking for "new-placeholder" and if found, delete it (BUT FIRST after swapping!):
4034                                              if ($t3ver_state['curVersion']==1)    {
4035                                                  $this->deleteEl($table, $swapWith, TRUE, TRUE);     // For delete + completely delete!
4036                                              }
4037                                          } else $this->newlog('During Swapping: SQL errors happend: '.implode('; ',$sqlErrors),2);
4038                                      } else $this->newlog('A swapping lock file was present. Either another swap process is already running or a previous swap process failed. Ask your administrator to handle the situation.',2);
4039                                  } else $this->newlog('In swap version, either pid was not -1 or the t3ver_oid didn\'t match the id of the online version as it must!',2);
4040                              } else $this->newlog('Workspace #'.$swapVersion['t3ver_wsid'].' does not support swapping.',1);
4041                          } else $this->newlog('You cannot publish a record you do not have edit and show permissions for',1);
4042                      } else $this->newlog('Records in workspace #'.$swapVersion['t3ver_wsid'].' can only be published when in "Publish" stage.',1);
4043                  } else $this->newlog('User could not publish records from workspace #'.$swapVersion['t3ver_wsid'],1);
4044              } else $this->newlog('Error: Either online or swap version could not be selected!',2);
4045          } else $this->newlog('Error: You cannot swap versions for a record you do not have access to edit!',1);
4046      }
4047  
4048      /**
4049       * Release version from this workspace (and into "Live" workspace but as an offline version).
4050       *
4051       * @param    string        Table name
4052       * @param    integer        Record UID
4053       * @return    void
4054       */
4055  	function version_clearWSID($table,$id)    {
4056          if ($errorCode = $this->BE_USER->workspaceCannotEditOfflineVersion($table, $id))    {
4057              $this->newlog('Attempt to reset workspace for record failed: '.$errorCode,1);
4058          } elseif ($this->checkRecordUpdateAccess($table,$id)) {
4059              if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'uid,t3ver_state'))    {
4060                      // Clear workspace ID:
4061                  $sArray = array();
4062                  $sArray['t3ver_wsid'] = 0;
4063                  $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($id),$sArray);
4064  
4065                      // Clear workspace ID for live version AND DELETE IT as well because it is a new record!
4066                  if ((int)$liveRec['t3ver_state']===1)    {
4067                      $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($liveRec['uid']),$sArray);
4068                      $this->deleteEl($table, $liveRec['uid'], TRUE);    // THIS assumes that the record was placeholder ONLY for ONE record (namely $id)
4069                  }
4070  
4071                      // If "deleted" flag is set for the version that got released it doesn't make sense to keep that "placeholder" anymore and we delete it completly.
4072                  $wsRec = t3lib_BEfunc::getRecord($table,$id);
4073                  if ((int)$wsRec['t3ver_state']===2)    {
4074                      $this->deleteEl($table, $id, TRUE, TRUE);
4075                  }
4076              }
4077          } else $this->newlog('Attempt to reset workspace for record failed because you do not have edit access',1);
4078      }
4079  
4080      /**
4081       * Setting stage of record
4082       *
4083       * @param    string        Table name
4084       * @param    integer        Record UID
4085       * @param    integer        Stage ID to set
4086       * @param    string        Comment that goes into log
4087       * @return    void
4088       */
4089  	function version_setStage($table,$id,$stageId,$comment='')    {
4090          if ($errorCode = $this->BE_USER->workspaceCannotEditOfflineVersion($table, $id))    {
4091              $this->newlog('Attempt to set stage for record failed: '.$errorCode,1);
4092          } elseif ($this->checkRecordUpdateAccess($table,$id)) {
4093              $stat = $this->BE_USER->checkWorkspaceCurrent();
4094              if (t3lib_div::inList('admin,online,offline,reviewer,owner', $stat['_ACCESS']) || ($stageId<=1 && $stat['_ACCESS']==='member'))    {
4095  
4096                      // Set stage of record:
4097                  $sArray = array();
4098                  $sArray['t3ver_stage'] = $stageId;
4099                  $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($id), $sArray);
4100                  $this->newlog('Stage for record was changed to '.$stageId.'. Comment was: "'.substr($comment,0,100).'"');
4101  // TEMPORARY, except 6-30 as action/detail number which is observed elsewhere!
4102  $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stage'=>$stageId));
4103  
4104                  if ((int)$stat['stagechg_notification']>0)    {
4105                      $this->notifyStageChange($stat,$stageId,$table,$id,$comment);
4106                  }
4107              } else $this->newlog('The member user tried to set a stage value "'.$stageId.'" that was not allowed',1);
4108          } else $this->newlog('Attempt to set stage for record failed because you do not have edit access',1);
4109      }
4110  
4111  
4112  
4113  
4114  
4115  
4116  
4117  
4118  
4119  
4120  
4121  
4122  
4123      /*********************************************
4124       *
4125       * Cmd: Helper functions
4126       *
4127       ********************************************/
4128  
4129      /**
4130       * Processes the fields with references as registered during the copy process. This includes all FlexForm fields which had references.
4131       *
4132       * @return    void
4133       */
4134  	function remapListedDBRecords()    {
4135          global $TCA;
4136  
4137          if (count($this->registerDBList))    {
4138              reset($this->registerDBList);
4139              while(list($table,$records)=each($this->registerDBList))    {
4140                  t3lib_div::loadTCA($table);
4141                  reset($records);
4142                  while(list($uid,$fields)=each($records))    {
4143                      $newData = array();
4144                      $theUidToUpdate = $this->copyMappingArray_merged[$table][$uid];
4145                      $theUidToUpdate_saveTo = t3lib_BEfunc::wsMapId($table,$theUidToUpdate);
4146  
4147                      foreach($fields as $fieldName => $value)    {
4148                          $conf = $TCA[$table]['columns'][$fieldName]['config'];
4149  
4150                          switch($conf['type'])    {
4151                              case 'group':
4152                              case 'select':
4153                                  $vArray = $this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate, $table);
4154                                  if (is_array($vArray))    {
4155                                      $newData[$fieldName] = implode(',',$vArray);
4156                                  }
4157                              break;
4158                              case 'flex':
4159                                  if ($value=='FlexForm_reference')    {
4160                                      $origRecordRow = $this->recordInfo($table,$theUidToUpdate,'*');    // This will fetch the new row for the element
4161  
4162                                      if (is_array($origRecordRow))    {
4163                                          t3lib_BEfunc::workspaceOL($table,$origRecordRow);
4164  
4165                                              // Get current data structure and value array:
4166                                          $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $origRecordRow, $table);
4167                                          $currentValueArray = t3lib_div::xml2array($origRecordRow[$fieldName]);
4168  
4169                                              // Do recursive processing of the XML data:
4170                                          $currentValueArray['data'] = $this->checkValue_flex_procInData(
4171                                                      $currentValueArray['data'],
4172                                                      array(),    // Not used.
4173                                                      array(),    // Not used.
4174                                                      $dataStructArray,
4175                                                      array($table,$theUidToUpdate,$fieldName),    // Parameters.
4176                                                      'remapListedDBRecords_flexFormCallBack'
4177                                                  );
4178  
4179                                              // The return value should be compiled back into XML, ready to insert directly in the field (as we call updateDB() directly later):
4180                                          if (is_array($currentValueArray['data']))    {
4181                                              $newData[$fieldName] =
4182                                                  $this->checkValue_flexArray2Xml($currentValueArray,TRUE);
4183                                          }
4184                                      }
4185                                  }
4186                              break;
4187                              case 'inline':
4188                                  $this->remapListedDBRecords_procInline($conf, $value, $uid, $table);
4189                              break;
4190                              default:
4191                                  debug('Field type should not appear here: '. $conf['type']);
4192                              break;
4193                          }
4194                      }
4195  
4196                      if (count($newData))    {    // If any fields were changed, those fields are updated!
4197                          $this->updateDB($table,$theUidToUpdate_saveTo,$newData);
4198                      }
4199                  }
4200              }
4201          }
4202      }
4203  
4204      /**
4205       * Callback function for traversing the FlexForm structure in relation to creating copied files of file relations inside of flex form structures.
4206       *
4207       * @param    array        Set of parameters in numeric array: table, uid, field
4208       * @param    array        TCA config for field (from Data Structure of course)
4209       * @param    string        Field value (from FlexForm XML)
4210       * @param    string        Not used
4211       * @param    string        Not used
4212       * @return    array        Array where the "value" key carries the value.
4213       * @see checkValue_flex_procInData_travDS(), remapListedDBRecords()
4214       */
4215  	function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)    {
4216  
4217              // Extract parameters:
4218          list($table,$uid,$field)    = $pParams;
4219  
4220              // If references are set for this field, set flag so they can be corrected later:
4221          if ($this->isReferenceField($dsConf) && strlen($dataValue)) {
4222              $vArray = $this->remapListedDBRecords_procDBRefs($dsConf, $dataValue, $uid, $table);
4223              if (is_array($vArray))    {
4224                  $dataValue = implode(',',$vArray);
4225              }
4226          }
4227  
4228              // Return
4229          return array('value' => $dataValue);
4230      }
4231  
4232      /**
4233       * Performs remapping of old UID values to NEW uid values for a DB reference field.
4234       *
4235       * @param    array        TCA field config
4236       * @param    string        Field value
4237       * @param    integer        UID of local record (for MM relations - might need to change if support for FlexForms should be done!)
4238       * @param    string        Table name
4239       * @return    array        Returns array of items ready to implode for field content.
4240       * @see remapListedDBRecords()
4241       */
4242  	function remapListedDBRecords_procDBRefs($conf, $value, $MM_localUid, $table)    {
4243  
4244              // Initialize variables
4245          $set = FALSE;    // Will be set true if an upgrade should be done...
4246          $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table'];        // Allowed tables for references.
4247          $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : '';    // Table name to prepend the UID
4248          $dontRemapTables = t3lib_div::trimExplode(',',$conf['dontRemapTablesOnCopy'],1);    // Which tables that should possibly not be remapped
4249  
4250              // Convert value to list of references:
4251          $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
4252          $dbAnalysis->registerNonTableValues = ($conf['type']=='select' && $conf['allowNonIdValues']) ? 1 : 0;
4253          $dbAnalysis->start($value, $allowedTables, $conf['MM'], $MM_localUid, $table, $conf);
4254  
4255              // Traverse those references and map IDs:
4256          foreach($dbAnalysis->itemArray as $k => $v)    {
4257              $mapID = $this->copyMappingArray_merged[$v['table']][$v['id']];
4258              if ($mapID && !in_array($v['table'],$dontRemapTables))    {
4259                  $dbAnalysis->itemArray[$k]['id'] = $mapID;
4260                  $set = TRUE;
4261              }
4262          }
4263  
4264              // If a change has been done, set the new value(s)
4265          if ($set)    {
4266              if ($conf['MM'])    {
4267  // FIXME $theUidToUpdate is undefined
4268                  $dbAnalysis->writeMM($conf['MM'], $theUidToUpdate, $prependName);
4269              } else {
4270                  $vArray = $dbAnalysis->getValueArray($prependName);
4271                  if ($conf['type']=='select')    {
4272                      $vArray = $dbAnalysis->convertPosNeg($vArray, $conf['foreign_table'], $conf['neg_foreign_table']);
4273                  }
4274                  return $vArray;
4275              }
4276          }
4277      }
4278  
4279      /**
4280       * Performs remapping of old UID values to NEW uid values for a inline field.
4281       *
4282       * @param    array        $conf: TCA field config
4283       * @param    string        $value: Field value
4284       * @param    integer        $uid: The uid of the ORIGINAL record
4285       * @param    string        $table: Table name
4286       * @return    void
4287       */
4288  	function remapListedDBRecords_procInline($conf, $value, $uid, $table) {
4289          $theUidToUpdate = $this->copyMappingArray_merged[$table][$uid];
4290  
4291          if ($conf['foreign_table']) {
4292              $inlineType = $this->getInlineFieldType($conf);
4293  
4294              if ($inlineType == 'mm') {
4295                  $this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate, $table);
4296  
4297              } elseif ($inlineType !== false) {
4298                  $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
4299                  $dbAnalysis->start($value, $conf['foreign_table'], '', 0, $table, $conf);
4300  
4301                      // If the current field is set on a page record, update the pid of related child records:
4302                  if ($table == 'pages') {
4303                      $thePidToUpdate = $theUidToUpdate;
4304                      // If the current field has ancestors that have a field on a page record, update the pid of related child records:
4305                  } elseif (isset($this->registerDBPids[$table][$uid])) {
4306                      $thePidToUpdate = $this->registerDBPids[$table][$uid];
4307                      $thePidToUpdate = $this->copyMappingArray_merged['pages'][$thePidToUpdate];
4308                  }
4309  
4310                      // Update child records if using pointer fields ('foreign_field'):
4311                  if ($inlineType == 'field') {
4312                      $dbAnalysis->writeForeignField($conf, $uid, $theUidToUpdate);
4313                  }
4314  
4315                      // Update child records if change to pid is required:
4316                  if ($thePidToUpdate) {
4317                      $updateValues = array('pid' => $thePidToUpdate);
4318                      foreach ($dbAnalysis->itemArray as $v) {
4319                          if ($v['id'] && $v['table']) {
4320                              $GLOBALS['TYPO3_DB']->exec_UPDATEquery($v['table'], 'uid='.intval($v['id']), $updateValues);
4321                          }
4322                      }
4323                  }
4324              }
4325          }
4326      }
4327  
4328      /**
4329       * Processes the $this->remapStack at the end of copying, inserting, etc. actions.
4330       * The remapStack takes care about the correct mapping of new and old uids in case of relational data.
4331       *
4332       * @return    void
4333       */
4334  	function processRemapStack() {
4335          if(is_array($this->remapStack)) {
4336              foreach($this->remapStack as $remapAction) {
4337                      // If no position index for the arguments was set, skip this remap action:
4338                  if (!is_array($remapAction['pos'])) continue;
4339  
4340                      // Load values from the argument array in remapAction:
4341                  $field = $remapAction['field'];
4342                  $id = $remapAction['args'][$remapAction['pos']['id']];
4343                  $rawId = $id;
4344                  $table = $remapAction['args'][$remapAction['pos']['table']];
4345                  $valueArray = $remapAction['args'][$remapAction['pos']['valueArray']];
4346                  $tcaFieldConf = $remapAction['args'][$remapAction['pos']['tcaFieldConf']];
4347  
4348                      // Replace NEW... IDs with real uids:
4349                  if(strpos($id, 'NEW') !== false) {
4350                      $id = $this->substNEWwithIDs[$id];
4351                      $remapAction['args'][$remapAction['pos']['id']] = $id;
4352                  }
4353  
4354                      // Replace relations to NEW...-IDs in values:
4355                  if(is_array($valueArray)) {
4356                      $foreign_table = $tcaFieldConf['foreign_table'];
4357                      foreach($valueArray as $key => $value) {
4358                          if(strpos($value, 'NEW') !== false) {
4359                                  // fetch the proper uid as integer for the NEW...-ID
4360                              $valueArray[$key] = $this->substNEWwithIDs[$value];
4361                                  // Set a hint that this was a new child record:
4362                              $this->newRelatedIDs[$foreign_table][] = $valueArray[$key];
4363                          }
4364                      }
4365                      $remapAction['args'][$remapAction['pos']['valueArray']] = $valueArray;
4366                  }
4367  
4368                      // Process the arguments with the defined function:
4369                  $remapAction['args'][$remapAction['pos']['valueArray']] = call_user_func_array(
4370                      array($this, $remapAction['func']),
4371                      $remapAction['args']
4372                  );
4373  
4374                      // @TODO: Add option to disable count-field
4375                  $newVal = $this->checkValue_checkMax($tcaFieldConf, $remapAction['args'][$remapAction['pos']['valueArray']]);
4376                  $this->updateDB($table,$id,array($field => implode(',', $newVal)));
4377  
4378                      // Process waiting Hook: processDatamap_afterDatabaseOperations:
4379                  if (isset($this->remapStackRecords[$table][$rawId]['processDatamap_afterDatabaseOperations'])) {
4380                      $hookArgs = $this->remapStackRecords[$table][$rawId]['processDatamap_afterDatabaseOperations'];
4381                          // Update field with remapped data:
4382                      $hookArgs['fieldArray'][$field] = implode(',', $newVal);
4383                          // Process waiting hook objects:
4384                      $hookObjectsArr = $hookArgs['hookObjectsArr'];
4385                      foreach($hookObjectsArr as $hookObj)    {
4386                          if (method_exists($hookObj, 'processDatamap_afterDatabaseOperations')) {
4387                              $hookObj->processDatamap_afterDatabaseOperations($hookArgs['status'], $table, $rawId, $hookArgs['fieldArray'], $this);
4388                          }
4389                      }
4390                  }
4391              }
4392          }
4393              // Reset:
4394          $this->remapStack = array();
4395          $this->remapStackRecords = array();
4396      }
4397  
4398  
4399  
4400  
4401  
4402  
4403  
4404  
4405      /*****************************
4406       *
4407       * Access control / Checking functions
4408       *
4409       *****************************/
4410  
4411      /**
4412       * Checking group modify_table access list
4413       *
4414       * @param    string        Table name
4415       * @return    boolean        Returns true if the user has general access to modify the $table
4416       */
4417  	function checkModifyAccessList($table)    {
4418          $res = ($this->admin || (!$this->tableAdminOnly($table) && t3lib_div::inList($this->BE_USER->groupData['tables_modify'],$table)));
4419          return $res;
4420      }
4421  
4422      /**
4423       * Checking if a record with uid $id from $table is in the BE_USERS webmounts which is required for editing etc.
4424       *
4425       * @param    string        Table name
4426       * @param    integer        UID of record
4427       * @return    boolean        Returns true if OK. Cached results.
4428       */
4429  	function isRecordInWebMount($table,$id)    {
4430          if (!isset($this->isRecordInWebMount_Cache[$table.':'.$id]))    {
4431              $recP=$this->getRecordProperties($table,$id);
4432              $this->isRecordInWebMount_Cache[$table.':'.$id]=$this->isInWebMount($recP['event_pid']);
4433          }
4434          return $this->isRecordInWebMount_Cache[$table.':'.$id];
4435      }
4436  
4437      /**
4438       * Checks if the input page ID is in the BE_USER webmounts
4439       *
4440       * @param    integer        Page ID to check
4441       * @return    boolean        True if OK. Cached results.
4442       */
4443  	function isInWebMount($pid)    {
4444          if (!isset($this->isInWebMount_Cache[$pid]))    {
4445              $this->isInWebMount_Cache[$pid]=$this->BE_USER->isInWebMount($pid);
4446          }
4447          return $this->isInWebMount_Cache[$pid];
4448      }
4449  
4450      /**
4451       * Checks if user may update a record with uid=$id from $table
4452       *
4453       * @param    string        Record table
4454       * @param    integer        Record UID
4455       * @return    boolean        Returns true if the user may update the record given by $table and $id
4456       */
4457  	function checkRecordUpdateAccess($table,$id)    {
4458          global $TCA;
4459          $res = 0;
4460          if ($TCA[$table] && intval($id)>0)    {
4461              if (isset($this->recUpdateAccessCache[$table][$id]))    {    // If information is cached, return it
4462                  return $this->recUpdateAccessCache[$table][$id];
4463                  // Check if record exists and 1) if 'pages' the page may be edited, 2) if page-content the page allows for editing
4464              } elseif ($this->doesRecordExist($table,$id,'edit'))    {
4465                  $res = 1;
4466              }
4467              $this->recUpdateAccessCache[$table][$id]=$res;    // Cache the result
4468          }
4469          return $res;
4470      }
4471  
4472      /**
4473       * Checks if user may insert a record from $insertTable on $pid
4474       * Does not check for workspace, use BE_USER->workspaceAllowLiveRecordsInPID for this in addition to this function call.
4475       *
4476       * @param    string        Tablename to check
4477       * @param    integer        Integer PID
4478       * @param    integer        For logging: Action number.
4479       * @return    boolean        Returns true if the user may insert a record from table $insertTable on page $pid
4480       */
4481  	function checkRecordInsertAccess($insertTable,$pid,$action=1)    {
4482          global $TCA;
4483  
4484          $res = 0;
4485          $pid = intval($pid);
4486          if ($pid>=0)    {
4487              if (isset($this->recInsertAccessCache[$insertTable][$pid]))    {    // If information is cached, return it
4488                  return $this->recInsertAccessCache[$insertTable][$pid];
4489              } else {
4490                      // 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
4491                  if ( (!$pid && $this->admin) || $this->doesRecordExist('pages',$pid,($insertTable=='pages'?$this->pMap['new']:$this->pMap['editcontent'])) )    {        // Check permissions
4492                      if ($this->isTableAllowedForThisPage($pid, $insertTable))    {
4493                          $res = 1;
4494                          $this->recInsertAccessCache[$insertTable][$pid]=$res;    // Cache the result
4495                      } else {
4496                          $propArr = $this->getRecordProperties('pages',$pid);
4497                          $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']);
4498                      }
4499                  } else {
4500                      $propArr = $this->getRecordProperties('pages',$pid);
4501                      $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']);
4502                  }
4503              }
4504          }
4505          return $res;
4506      }
4507  
4508      /**
4509       * 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.
4510       *
4511       * @param    integer        Page id for which to check, including 0 (zero) if checking for page tree root.
4512       * @param    string        Table name to check
4513       * @return    boolean        True if OK
4514       */
4515  	function isTableAllowedForThisPage($page_uid, $checkTable)    {
4516          global $TCA, $PAGES_TYPES;
4517          $page_uid = intval($page_uid);
4518  
4519              // 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.
4520          if (($TCA[$checkTable]['ctrl']['rootLevel'] xor !$page_uid) && $TCA[$checkTable]['ctrl']['rootLevel']!=-1 && $checkTable!='pages')    {
4521              return false;
4522          }
4523  
4524              // Check root-level
4525          if (!$page_uid)    {
4526              if ($this->admin)    {
4527                  return true;
4528              }
4529          } else {
4530                  // Check non-root-level
4531              $doktype = $this->pageInfo($page_uid,'doktype');
4532              $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables'];
4533              $allowedArray = t3lib_div::trimExplode(',',$allowedTableList,1);
4534              if (strstr($allowedTableList,'*') || in_array($checkTable,$allowedArray))    {        // If all tables or the table is listed as a allowed type, return true
4535                  return true;
4536              }
4537          }
4538      }
4539  
4540      /**
4541       * Checks if record can be selected based on given permission criteria
4542       *
4543       * @param    string        Record table name
4544       * @param    integer        Record UID
4545       * @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
4546       * @return    boolean        Returns true if the record given by $table, $id and $perms can be selected
4547       */
4548  	function doesRecordExist($table,$id,$perms)    {
4549          global $TCA;
4550  
4551          if ($this->bypassAccessCheckForRecords)    {
4552              return is_array(t3lib_BEfunc::getRecordRaw($table,'uid='.intval($id),'uid'));
4553          }
4554  
4555          $res = 0;
4556          $id = intval($id);
4557  
4558              // Processing the incoming $perms (from possible string to integer that can be AND'ed)
4559          if (!t3lib_div::testInt($perms))    {
4560              if ($table!='pages')    {
4561                  switch($perms)    {
4562                      case 'edit':
4563                      case 'delete':
4564                      case 'new':
4565                          $perms = 'editcontent';        // This holds it all in case the record is not page!!
4566                      break;
4567                  }
4568              }
4569              $perms = intval($this->pMap[$perms]);
4570          } else {
4571              $perms = intval($perms);
4572          }
4573  
4574          if (!$perms)    {die('Internal ERROR: no permissions to check for non-admin user.');}
4575  
4576              // For all tables: Check if record exists:
4577          if (is_array($TCA[$table]) && $id>0 && ($this->isRecordInWebMount($table,$id) || $this->admin))    {
4578              if ($table != 'pages')    {
4579  
4580                      // Find record without checking page:
4581                  $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid', $table, 'uid='.intval($id).$this->deleteClause($table));    // THIS SHOULD CHECK FOR editlock I think!
4582                  $output = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres);
4583                  t3lib_BEfunc::fixVersioningPid($table,$output,TRUE);
4584  
4585                      // If record found, check page as well:
4586                  if (is_array($output))    {
4587  
4588                          // Looking up the page for record:
4589                      $mres = $this->doesRecordExist_pageLookUp($output['pid'], $perms);
4590                      $pageRec = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres);
4591                          // 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):
4592                      if (is_array($pageRec) || (!$output['pid'] && $this->admin))    {
4593                          return TRUE;
4594                      }
4595                  }
4596                  return FALSE;
4597              } else {
4598                  $mres = $this->doesRecordExist_pageLookUp($id, $perms);
4599                  return $GLOBALS['TYPO3_DB']->sql_num_rows($mres);
4600              }
4601          }
4602      }
4603  
4604      /**
4605       * Looks up a page based on permissions.
4606       *
4607       * @param    integer        Page id
4608       * @param    integer        Permission integer
4609       * @return    pointer        MySQL result pointer (from exec_SELECTquery())
4610       * @access private
4611       * @see doesRecordExist()
4612       */
4613  	function doesRecordExist_pageLookUp($id, $perms)    {
4614          global $TCA;
4615  
4616          return $GLOBALS['TYPO3_DB']->exec_SELECTquery(
4617              'uid',
4618              'pages',
4619              'uid='.intval($id).
4620                  $this->deleteClause('pages').
4621                  ($perms && !$this->admin ? ' AND '.$this->BE_USER->getPagePermsClause($perms) : '').
4622                  (!$this->admin && $TCA['pages']['ctrl']['editlock'] && ($perms & (2+4+16)) ? ' AND '.$TCA['pages']['ctrl']['editlock'].'=0':'')    // admin users don't need check
4623          );
4624      }
4625  
4626      /**
4627       * Checks if a whole branch of pages exists
4628       *
4629       * Tests the branch under $pid (like doesRecordExist). It doesn't test the page with $pid as uid. Use doesRecordExist() for this purpose
4630       * Returns an ID-list or "" if OK. Else -1 which means that somewhere there was no permission (eg. to delete).
4631       * 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
4632       *
4633       * @param    string        List of page uids, this is added to and outputted in the end
4634       * @param    integer        Page ID to select subpages from.
4635       * @param    integer        Perms integer to check each page record for.
4636       * @param    boolean        Recursion flag: If set, it will go out through the branch.
4637       * @return    string        List of integers in branch
4638       */
4639  	function doesBranchExist($inList,$pid,$perms,$recurse)    {
4640          global $TCA;
4641          $pid = intval($pid);
4642          $perms = intval($perms);
4643  
4644          if ($pid>=0)    {
4645              $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
4646                  'uid, perms_userid, perms_groupid, perms_user, perms_group, perms_everybody',
4647                  'pages',
4648                  'pid='.intval($pid).$this->deleteClause('pages'),
4649                  '',
4650                  'sorting'
4651              );
4652              while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))    {
4653                  if ($this->admin || $this->BE_USER->doesUserHaveAccess($row,$perms))    {    // IF admin, then it's OK
4654                      $inList.=$row['uid'].',';
4655                      if ($recurse)    {    // Follow the subpages recursively...
4656                          $inList = $this->doesBranchExist($inList, $row['uid'], $perms, $recurse);
4657                          if ($inList == -1)    {return -1;}        // No permissions somewhere in the branch
4658                      }
4659                  } else {
4660                      return -1;        // No permissions
4661                  }
4662              }
4663          }
4664          return $inList;
4665      }
4666  
4667      /**
4668       * Checks if the $table is readOnly
4669       *
4670       * @param    string        Table name
4671       * @return    boolean        True, if readonly
4672       */
4673  	function tableReadOnly($table)    {
4674              // returns true if table is readonly
4675          global $TCA;
4676          return ($TCA[$table]['ctrl']['readOnly'] ? 1 : 0);
4677      }
4678  
4679      /**
4680       * Checks if the $table is only editable by admin-users
4681       *
4682       * @param    string        Table name
4683       * @return    boolean        True, if readonly
4684       */
4685  	function tableAdminOnly($table)    {
4686              // returns true if table is admin-only
4687          global $TCA;
4688          return ($TCA[$table]['ctrl']['adminOnly'] ? 1 : 0);
4689      }
4690  
4691      /**
4692       * Checks if piage $id is a uid in the rootline from page id, $dest
4693       * Used when moving a page
4694       *
4695       * @param    integer        Destination Page ID to test
4696       * @param    integer        Page ID to test for presence inside Destination
4697       * @return    boolean        Returns false if ID is inside destination (including equal to)
4698       */
4699  	function destNotInsideSelf($dest,$id)    {
4700          $loopCheck = 100;
4701          $dest = intval($dest);
4702          $id = intval($id);
4703  
4704          if ($dest==$id)    {
4705              return FALSE;
4706          }
4707  
4708          while ($dest!=0 && $loopCheck>0)    {
4709              $loopCheck--;
4710              $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid, uid, t3ver_oid,t3ver_wsid', 'pages', 'uid='.intval($dest).$this->deleteClause('pages'));
4711              if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))    {
4712                  t3lib_BEfunc::fixVersioningPid('pages',$row);
4713                  if ($row['pid']==$id)    {
4714                      return FALSE;
4715                  } else {
4716                      $dest = $row['pid'];
4717                  }
4718              } else {
4719                  return FALSE;
4720              }
4721          }
4722          return TRUE;
4723      }
4724  
4725      /**
4726       * 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
4727       * Will also generate this list for admin-users so they must be check for before calling the function
4728       *
4729       * @return    array        Array of [table]-[field] pairs to exclude from editing.
4730       */
4731  	function getExcludeListArray()    {
4732          global $TCA;
4733  
4734          $list = array();
4735          reset($TCA);
4736          while (list($table)=each($TCA))    {
4737              t3lib_div::loadTCA($table);
4738              while (list($field,$config)=each($TCA[$table]['columns']))    {
4739                  if ($config['exclude'] && !t3lib_div::inList($this->BE_USER->groupData['non_exclude_fields'],$table.':'.$field))    {
4740                      $list[]=$table.'-'.$field;
4741                  }
4742              }
4743          }
4744          return $list;
4745      }
4746  
4747      /**
4748       * Checks if there are records on a page from tables that are not allowed
4749       *
4750       * @param    integer        Page ID
4751       * @param    integer        Page doktype
4752       * @return    array        Returns a list of the tables that are 'present' on the page but not allowed with the page_uid/doktype
4753       */
4754  	function doesPageHaveUnallowedTables($page_uid,$doktype)    {
4755          global $TCA, $PAGES_TYPES;
4756  
4757          $page_uid = intval($page_uid);
4758          if (!$page_uid)    {
4759              return FALSE;     // Not a number. Probably a new page
4760          }
4761  
4762          $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables'];
4763          $allowedArray = t3lib_div::trimExplode(',',$allowedTableList,1);
4764          if (strstr($allowedTableList,'*'))    {    // If all tables is OK the return true
4765              return FALSE;    // OK...
4766          }
4767  
4768          reset ($TCA);
4769          $tableList = array();
4770          while (list($table)=each($TCA))    {
4771              if (!in_array($table,$allowedArray))    {    // If the table is not in the allowed list, check if there are records...
4772                  $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid='.intval($page_uid));
4773                  $count = $GLOBALS['TYPO3_DB']->sql_fetch_row($mres);
4774                  if ($count[0])    {
4775                      $tableList[]=$table;
4776                  }
4777              }
4778          }
4779          return implode(',',$tableList);
4780      }
4781  
4782  
4783  
4784  
4785  
4786  
4787  
4788  
4789      /*****************************
4790       *
4791       * Information lookup
4792       *
4793       *****************************/
4794  
4795      /**
4796       * Returns the value of the $field from page $id
4797       * 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!
4798       *
4799       * @param    integer        Page uid
4800       * @param    string        Field name for which to return value
4801       * @return    string        Value of the field. Result is cached in $this->pageCache[$id][$field] and returned from there next time!
4802       */
4803  	function pageInfo($id,$field)    {
4804          if (!isset($this->pageCache[$id]))    {
4805              $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'pages', 'uid='.intval($id));
4806              if ($GLOBALS['TYPO3_DB']->sql_num_rows($res))    {
4807                  $this->pageCache[$id] = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
4808              }
4809              $GLOBALS['TYPO3_DB']->sql_free_result($res);
4810          }
4811          return $this->pageCache[$id][$field];
4812      }
4813  
4814      /**
4815       * Returns the row of a record given by $table and $id and $fieldList (list of fields, may be '*')
4816       * NOTICE: No check for deleted or access!
4817       *
4818       * @param    string        Table name
4819       * @param    integer        UID of the record from $table
4820       * @param    string        Field list for the SELECT query, eg. "*" or "uid,pid,..."
4821       * @return    mixed        Returns the selected record on success, otherwise false.
4822       */
4823  	function recordInfo($table,$id,$fieldList)    {
4824          global $TCA;
4825          if (is_array($TCA[$table]))    {
4826              $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fieldList, $table, 'uid='.intval($id));
4827              if ($GLOBALS['TYPO3_DB']->sql_num_rows($res))    {
4828                  $result = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
4829                  $GLOBALS['TYPO3_DB']->sql_free_result($res);
4830                  return $result;
4831              }
4832          }
4833      }
4834  
4835      /**
4836       * Returns an array with record properties, like header and pid
4837       * No check for deleted or access is done!
4838       * For versionized records, pid is resolved to its live versions pid.
4839       * Used for loggin
4840       *
4841       * @param    string        Table name
4842       * @param    integer        Uid of record
4843       * @param    boolean        If set, no workspace overlay is performed
4844       * @return    array        Properties of record
4845       */
4846  	function getRecordProperties($table,$id,$noWSOL=FALSE)    {
4847          $row = ($table=='pages' && !$id) ? array('title'=>'[root-level]', 'uid' => 0, 'pid' => 0) :$this->recordInfo($table,$id,'*');
4848          if (!$noWSOL)    {
4849              t3lib_BEfunc::workspaceOL($table,$row);
4850          }
4851          t3lib_BEfunc::fixVersioningPid($table,$row);
4852          return $this->getRecordPropertiesFromRow($table,$row);
4853      }
4854  
4855      /**
4856       * Returns an array with record properties, like header and pid, based on the row
4857       *
4858       * @param    string        Table name
4859       * @param    array        Input row
4860       * @return    array        Output array
4861       */
4862  	function getRecordPropertiesFromRow($table,$row)    {
4863          global $TCA;
4864          if ($TCA[$table])    {
4865              $out = array(
4866                  'header' => $row[$TCA[$table]['ctrl']['label']],
4867                  'pid' => $row['pid'],
4868                  'event_pid' => ($table=='pages'?$row['uid']:$row['pid']),
4869                  't3ver_state' => $TCA[$table]['ctrl']['versioningWS'] ? $row['t3ver_state'] : '',
4870                  '_ORIG_pid' => $row['_ORIG_pid']
4871              );
4872              return $out;
4873          }
4874      }
4875  
4876  
4877  
4878  
4879  
4880  
4881  
4882  
4883  
4884  
4885  
4886  
4887  
4888  
4889  
4890      /*********************************************
4891       *
4892       * Storing data to Database Layer
4893       *
4894       ********************************************/
4895  
4896      /**
4897       * Update database record
4898       * Does not check permissions but expects them to be verified on beforehand
4899       *
4900       * @param    string        Record table name
4901       * @param    integer        Record uid
4902       * @param    array        Array of field=>value pairs to insert. FIELDS MUST MATCH the database FIELDS. No check is done.
4903       * @return    void
4904       */
4905  	function updateDB($table,$id,$fieldArray)    {
4906          global $TCA;
4907  
4908          if (is_array($fieldArray) && is_array($TCA[$table]) && intval($id))    {
4909              unset($fieldArray['uid']);    // Do NOT update the UID field, ever!
4910  
4911              if (count($fieldArray))    {
4912  
4913                      // Execute the UPDATE query:
4914                  $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($id), $fieldArray);
4915  
4916                      // If succees, do...:
4917                  if (!$GLOBALS['TYPO3_DB']->sql_error())    {
4918  
4919                      if ($this->checkStoredRecords)    {
4920                          $newRow = $this->checkStoredRecord($table,$id,$fieldArray,2);
4921                      }
4922  
4923                          // Update reference index:
4924                      $this->updateRefIndex($table,$id);
4925  
4926                          // Set log entry:
4927                      $propArr = $this->getRecordPropertiesFromRow($table,$newRow);
4928                      $theLogId = $this->log($table,$id,2,$propArr['pid'],0,"Record '%s' (%s) was updated.",10,array($propArr['header'],$table.':'.$id),$propArr['event_pid']);
4929  
4930                          // Set History data:
4931                      $this->setHistory($table,$id,$theLogId);
4932  
4933                          // Clear cache for relevant pages:
4934                      $this->clear_cache($table,$id);
4935  
4936                          // Unset the pageCache for the id if table was page.
4937                      if ($table=='pages')    unset($this->pageCache[$id]);
4938                  } else {
4939                      $this->log($table,$id,2,0,2,"SQL error: '%s' (%s)",12,array($GLOBALS['TYPO3_DB']->sql_error(),$table.':'.$id));
4940                  }
4941              }
4942          }
4943      }
4944  
4945      /**
4946       * Insert into database
4947       * Does not check permissions but expects them to be verified on beforehand
4948       *
4949       * @param    string        Record table name
4950       * @param    string        "NEW...." uid string
4951       * @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!
4952       * @param    boolean        Set to true if new version is created.
4953       * @param    integer        Suggested UID value for the inserted record. See the array $this->suggestedInsertUids; Admin-only feature
4954       * @param    boolean        If true, the ->substNEWwithIDs array is not updated. Only useful in very rare circumstances!
4955       * @return    integer        Returns ID on success.
4956       */
4957  	function insertDB($table,$id,$fieldArray,$newVersion=FALSE,$suggestedUid=0,$dontSetNewIdIndex=FALSE)    {
4958          global $TCA;
4959  
4960          if (is_array($fieldArray) && is_array($TCA[$table]) && isset($fieldArray['pid']))    {
4961              unset($fieldArray['uid']);    // Do NOT insert the UID field, ever!
4962  
4963              if (count($fieldArray))    {
4964  
4965                      // Check for "suggestedUid".
4966                      // This feature is used by the import functionality to force a new record to have a certain UID value.
4967                      // This is only recommended for use when the destination server is a passive mirrow of another server.
4968                      // As a security measure this feature is available only for Admin Users (for now)
4969                  $suggestedUid = intval($suggestedUid);
4970                  if ($this->BE_USER->isAdmin() && $suggestedUid && $this->suggestedInsertUids[$table.':'.$suggestedUid])    {
4971                          // When the value of ->suggestedInsertUids[...] is "DELETE" it will try to remove the previous record
4972                      if ($this->suggestedInsertUids[$table.':'.$suggestedUid]==='DELETE')    {
4973                              // DELETE:
4974                          $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($suggestedUid));
4975                      }
4976                      $fieldArray['uid'] = $suggestedUid;
4977                  }
4978  
4979                      // Execute the INSERT query:
4980                  $GLOBALS['TYPO3_DB']->exec_INSERTquery($table, $fieldArray);
4981  
4982                      // If succees, do...:
4983                  if (!$GLOBALS['TYPO3_DB']->sql_error())    {
4984  
4985                          // Set mapping for NEW... -> real uid:
4986                      $NEW_id = $id;        // the NEW_id now holds the 'NEW....' -id
4987                      $id = $GLOBALS['TYPO3_DB']->sql_insert_id();
4988                      if (!$dontSetNewIdIndex)    {
4989                          $this->substNEWwithIDs[$NEW_id] = $id;
4990                          $this->substNEWwithIDs_table[$NEW_id] = $table;
4991                      }
4992  
4993                          // Checking the record is properly saved and writing to log
4994                      if ($this->checkStoredRecords)    {
4995                          $newRow = $this->checkStoredRecord($table,$id,$fieldArray,1);
4996                      }
4997  
4998                          // Update reference index:
4999                      $this->updateRefIndex($table,$id);
5000  
5001                      if ($newVersion)    {
5002                          $this->log($table,$id,1,0,0,"New version created of table '%s', uid '%s'",10,array($table,$fieldArray['t3ver_oid']),$newRow['pid'],$NEW_id);
5003                      } else {
5004                          $propArr = $this->getRecordPropertiesFromRow($table,$newRow);
5005                          $page_propArr = $this->getRecordProperties('pages',$propArr['pid']);
5006                          $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);
5007  
5008                              // Clear cache for relavant pages:
5009                          $this->clear_cache($table,$id);
5010                      }
5011  
5012                      return $id;
5013                  } else {
5014                      $this->log($table,$id,1,0,2,"SQL error: '%s' (%s)",12,array($GLOBALS['TYPO3_DB']->sql_error(),$table.':'.$id));
5015                  }
5016              }
5017          }
5018      }
5019  
5020      /**
5021       * Checking stored record to see if the written values are properly updated.
5022       *
5023       * @param    string        Record table name
5024       * @param    integer        Record uid
5025       * @param    array        Array of field=>value pairs to insert/update
5026       * @param    string        Action, for logging only.
5027       * @return    array        Selected row
5028       * @see insertDB(), updateDB()
5029       */
5030  	function checkStoredRecord($table,$id,$fieldArray,$action)    {
5031          global $TCA;
5032  
5033          $id = intval($id);
5034          if (is_array($TCA[$table]) && $id)    {
5035              $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid='.intval($id));
5036              if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))    {
5037                  // Traverse array of values that was inserted into the database and compare with the actually stored value:
5038                  $errorString = array();
5039                  foreach($fieldArray as $key => $value)    {
5040                      if ($this->checkStoredRecords_loose && !$value && !$row[$key])    {
5041                          // Nothing...
5042                      } elseif (strcmp($value,$row[$key]))    {
5043                          $errorString[] = $key;
5044                      }
5045                  }
5046  
5047                      // Set log message if there were fields with unmatching values:
5048                  if (count($errorString))    {
5049                      $this->log($table,$id,$action,0,102,'These fields are not properly updated in database: ('.implode(',',$errorString).') Probably value mismatch with fieldtype.');
5050                  }
5051  
5052                      // Return selected rows:
5053                  return $row;
5054              }
5055              $GLOBALS['TYPO3_DB']->sql_free_result($res);
5056          }
5057      }
5058  
5059      /**
5060       * Setting sys_history record, based on content previously set in $this->historyRecords[$table.':'.$id] (by compareFieldArrayWithCurrentAndUnset())
5061       *
5062       * @param    string        Table name
5063       * @param    integer        Record ID
5064       * @param    integer        Log entry ID, important for linking between log and history views
5065       * @return    void
5066       */
5067  	function setHistory($table,$id,$logId)    {
5068          if (isset($this->historyRecords[$table.':'.$id]))    {
5069  
5070                  // Initialize settings:
5071              list($tscPID) = t3lib_BEfunc::getTSCpid($table,$id,'');
5072              $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
5073  
5074              $tE = $this->getTableEntries($table,$TSConfig);
5075              $maxAgeSeconds = 60*60*24*(strcmp($tE['history.']['maxAgeDays'],'') ? t3lib_div::intInRange($tE['history.']['maxAgeDays'],0,365) : 30);    // one month
5076  
5077                  // Garbage collect old entries:
5078              $this->clearHistory($maxAgeSeconds, $table);
5079  
5080                  // Set history data:
5081              $fields_values = array();
5082              $fields_values['history_data'] = serialize($this->historyRecords[$table.':'.$id]);
5083              $fields_values['fieldlist'] = implode(',',array_keys($this->historyRecords[$table.':'.$id]['newRecord']));
5084              $fields_values['tstamp'] = time();
5085              $fields_values['tablename'] = $table;
5086              $fields_values['recuid'] = $id;
5087              $fields_values['sys_log_uid'] = $logId;
5088  
5089              $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_history', $fields_values);
5090          }
5091      }
5092  
5093      /**
5094       * Clearing sys_history table from older entries that are expired.
5095       *
5096       * @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.
5097       * @param    string        table where the history should be cleared
5098       * @return    void
5099       */
5100  	function clearHistory($maxAgeSeconds=604800,$table)    {
5101          $tstampLimit = $maxAgeSeconds ? time()-$maxAgeSeconds : 0;
5102  
5103          $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_history', 'tstamp<'.intval($tstampLimit).' AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history'));
5104          }
5105  
5106      /**
5107       * Update Reference Index (sys_refindex) for a record
5108       * Should be called any almost any update to a record which could affect references inside the record.
5109       *
5110       * @param    string        Table name
5111       * @param    integer        Record UID
5112       * @return    void
5113       */
5114  	function updateRefIndex($table,$id)    {
5115          $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
5116          $result = $refIndexObj->updateRefIndexTable($table,$id);
5117      }
5118  
5119  
5120  
5121  
5122  
5123  
5124  
5125  
5126  
5127  
5128  
5129  
5130  
5131      /*********************************************
5132       *
5133       * Misc functions
5134       *
5135       ********************************************/
5136  
5137      /**
5138       * Returning sorting number for tables with a "sortby" column
5139       * Using when new records are created and existing records are moved around.
5140       *
5141       * @param    string        Table name
5142       * @param    integer        Uid of record to find sorting number for. May be zero in case of new.
5143       * @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)
5144       * @return    mixed        Returns integer if PID is >=0, otherwise an array with PID and sorting number. Possibly false in case of error.
5145       */
5146  	function getSortNumber($table,$uid,$pid)    {
5147          global $TCA;
5148          if ($TCA[$table] && $TCA[$table]['ctrl']['sortby'])    {
5149              $sortRow = $TCA[$table]['ctrl']['sortby'];
5150              if ($pid>=0)    {    // Sorting number is in the top
5151                  $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
5152                  if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))    {    // There was an element
5153                      if ($row['uid']==$uid)    {    // The top record was the record it self, so we return its current sortnumber
5154                          return $row[$sortRow];
5155                      }
5156                      if ($row[$sortRow] < 1) {    // If the pages sortingnumber < 1 we must resort the records under this pid
5157                          $this->resorting($table,$pid,$sortRow,0);
5158                          return $this->sortIntervals;    // First sorting number after resorting
5159                      } else {
5160                          return floor($row[$sortRow]/2);    // Sorting number between current top element and zero
5161                      }
5162                  } else {    // No pages, so we choose the default value as sorting-number
5163                      return $this->sortIntervals;    // First sorting number if no elements.
5164                  }
5165              } else {    // Sorting number is inside the list
5166                  $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
5167                  if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res))    {    // There was a record
5168  
5169                          // 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.
5170                      if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$row['uid'],$sortRow.',pid,uid'))    {
5171                          $row =     $lookForLiveVersion;
5172                      }
5173  
5174                          // If the record happends to be it self
5175                      if ($row['uid']==$uid)    {
5176                          $sortNumber = $row[$sortRow];
5177                      } else {
5178                          $subres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
5179                                          $sortRow.',pid,uid',
5180                                          $table,
5181                                          'pid='.intval($row['pid']).' AND '.$sortRow.'>='.intval($row[$sortRow]).$this->deleteClause($table),
5182                                          '',
5183                                          $sortRow.' ASC',
5184                                          '2'
5185                                      );        // Fetches the next record in order to calculate the in between sortNumber
5186                          if ($GLOBALS['TYPO3_DB']->sql_num_rows($subres)==2)    {    // There was a record afterwards
5187                              $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres);                // Forward to the second result...
5188                              $subrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres);    // There was a record afterwards
5189                              $sortNumber = $row[$sortRow]+ floor(($subrow[$sortRow]-$row[$sortRow])/2);    // The sortNumber is found in between these values
5190                              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
5191                                  $sortNumber = $this->resorting($table,$row['pid'],$sortRow,  $row['uid']);    // By this special param, resorting reserves and returns the sortnumber after the uid
5192                              }
5193                          } else {    // If after the last record in the list, we just add the sortInterval to the last sortvalue
5194                              $sortNumber = $row[$sortRow]+$this->sortIntervals;
5195                          }
5196                      }
5197                      return Array('pid' => $row['pid'], 'sortNumber' => $sortNumber);
5198                  } else {
5199                      $propArr = $this->getRecordProperties($table,$uid);
5200                      $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...
5201                      return false;    // There MUST be a page or else this cannot work
5202                  }
5203              }
5204          }
5205      }
5206  
5207      /**
5208       * Resorts a table.
5209       * Used internally by getSortNumber()
5210       *
5211       * @param    string        Table name
5212       * @param    integer        Pid in which to resort records.
5213       * @param    string        Sorting row
5214       * @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
5215       * @return    integer        If $return_SortNumber_After_This_Uid is set, will contain usable sorting number after that record if found (otherwise 0)
5216       * @access private
5217       * @see getSortNumber()
5218       */
5219  	function resorting($table,$pid,$sortRow, $return_SortNumber_After_This_Uid) {
5220          global $TCA;
5221          if ($TCA[$table] && $sortRow && $TCA[$table]['ctrl']['sortby']==$sortRow)    {
5222              $returnVal = 0;
5223              $intervals = $this->sortIntervals;
5224              $i = $intervals*2;
5225  
5226              $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($pid).$this->deleteClause($table), '', $sortRow.' ASC');
5227              while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
5228                  $uid=intval($row['uid']);
5229                  if ($uid)    {
5230                      $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), array($sortRow=>$i));
5231                      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
5232                          $i = $i+$intervals;
5233                          $returnVal=$i;
5234                      }
5235                  } else {die ('Fatal ERROR!! No Uid at resorting.');}
5236                  $i = $i+$intervals;
5237              }
5238              return $returnVal;
5239          }
5240      }
5241  
5242      /**
5243       * Setting up perms_* fields in $fieldArray based on TSconfig input
5244       * Used for new pages
5245       *
5246       * @param    array        Field Array, returned with modifications
5247       * @param    array        TSconfig properties
5248       * @return    array        Modified Field Array
5249       */
5250  	function setTSconfigPermissions($fieldArray,$TSConfig_p)    {
5251          if (strcmp($TSConfig_p['userid'],''))    $fieldArray['perms_userid']=intval($TSConfig_p['userid']);
5252          if (strcmp($TSConfig_p['groupid'],''))    $fieldArray['perms_groupid']=intval($TSConfig_p['groupid']);
5253          if (strcmp($TSConfig_p['user'],''))            $fieldArray['perms_user']=t3lib_div::testInt($TSConfig_p['user']) ? $TSConfig_p['user'] : $this->assemblePermissions($TSConfig_p['user']);
5254          if (strcmp($TSConfig_p['group'],''))        $fieldArray['perms_group']=t3lib_div::testInt($TSConfig_p['group']) ? $TSConfig_p['group'] : $this->assemblePermissions($TSConfig_p['group']);
5255          if (strcmp($TSConfig_p['everybody'],''))    $fieldArray['perms_everybody']=t3lib_div::testInt($TSConfig_p['everybody']) ? $TSConfig_p['everybody'] : $this->assemblePermissions($TSConfig_p['everybody']);
5256  
5257          return $fieldArray;
5258      }
5259  
5260      /**
5261       * 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.
5262       * Used for new records and during copy operations for defaults
5263       *
5264       * @param    string        Table name for which to set default values.
5265       * @return    array        Array with default values.
5266       */
5267  	function newFieldArray($table)    {
5268          global $TCA;
5269  
5270          t3lib_div::loadTCA($table);
5271          $fieldArray=Array();
5272          if (is_array($TCA[$table]['columns']))    {
5273              reset ($TCA[$table]['columns']);
5274              while (list($field,$content)=each($TCA[$table]['columns']))    {
5275                  if (isset($this->defaultValues[$table][$field]))    {
5276                      $fieldArray[$field] = $this->defaultValues[$table][$field];
5277                  } elseif (isset($content['config']['default']))    {
5278                      $fieldArray[$field] = $content['config']['default'];
5279                  }
5280              }
5281          }
5282          if ($table==='pages')    {        // Set default permissions for a page.
5283              $fieldArray['perms_userid'] = $this->userid;
5284              $fieldArray['perms_groupid'] = intval($this->BE_USER->firstMainGroup);
5285              $fieldArray['perms_user'] = $this->assemblePermissions($this->defaultPermissions['user']);
5286              $fieldArray['perms_group'] = $this->assemblePermissions($this->defaultPermissions['group']);
5287              $fieldArray['perms_everybody'] = $this->assemblePermissions($this->defaultPermissions['everybody']);
5288          }
5289          return $fieldArray;
5290      }
5291  
5292      /**
5293       * 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.
5294       *
5295       * @param    string        Table name
5296       * @param    array        Incoming array (passed by reference)
5297       * @return    void
5298       */
5299  	function addDefaultPermittedLanguageIfNotSet($table,&$incomingFieldArray)    {
5300          global $TCA;
5301  
5302              // Checking languages:
5303          if ($TCA[$table]['ctrl']['languageField'])    {
5304              if (!isset($incomingFieldArray[$TCA[$table]['ctrl']['languageField']]))    {    // Language field must be found in input row - otherwise it does not make sense.
5305                  $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)));
5306                  foreach($rows as $r)    {
5307                      if ($this->BE_USER->checkLanguageAccess($r['uid']))        {
5308                          $incomingFieldArray[$TCA[$table]['ctrl']['languageField']] = $r['uid'];
5309                          break;
5310                      }
5311                  }
5312              }
5313          }
5314      }
5315  
5316      /**
5317       * Returns the $data array from $table overridden in the fields defined in ->overrideValues.
5318       *
5319       * @param    string        Table name
5320       * @param    array        Data array with fields from table. These will be overlaid with values in $this->overrideValues[$table]
5321       * @return    array        Data array, processed.
5322       */
5323  	function overrideFieldArray($table,$data)    {
5324          if (is_array($this->overrideValues[$table]))    {
5325              $data = array_merge($data,$this->overrideValues[$table]);
5326          }
5327          return $data;
5328      }
5329  
5330      /**
5331       * Compares the incoming field array with the current record and unsets all fields which are the same.
5332       * Used for existing records being updated
5333       *
5334       * @param    string        Record table name
5335       * @param    integer        Record uid
5336       * @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!
5337       * @return    array        Returns $fieldArray. If the returned array is empty, then the record should not be updated!
5338       */
5339  	function compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray)    {
5340  
5341              // Fetch the original record:
5342          $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid='.intval($id));
5343          $currentRecord = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
5344  
5345              // If the current record exists (which it should...), begin comparison:
5346          if (is_array($currentRecord))    {
5347  
5348                  // Read all field types:
5349              $c = 0;
5350              $cRecTypes = array();
5351              foreach($currentRecord as $col => $val)    {
5352                  $cRecTypes[$col] = $GLOBALS['TYPO3_DB']->sql_field_type($res,$c);
5353                  $c++;
5354              }
5355  
5356                  // Free result:
5357              $GLOBALS['TYPO3_DB']->sql_free_result($res);
5358  
5359                  // Unset the fields which are similar:
5360              foreach($fieldArray as $col => $val)    {
5361                  if (
5362                          !strcmp($val,$currentRecord[$col]) ||    // Unset fields which matched exactly.
5363                          ($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.
5364                      )    {
5365                      unset($fieldArray[$col]);
5366                  } else {
5367                      $this->historyRecords[$table.':'.$id]['oldRecord'][$col] = $currentRecord[$col];
5368                      $this->historyRecords[$table.':'.$id]['newRecord'][$col] = $fieldArray[$col];
5369                  }
5370              }
5371          } else {    // If the current record does not exist this is an error anyways and we just return an empty array here.
5372              $fieldArray = array();
5373          }
5374  
5375          return $fieldArray;
5376      }
5377  
5378      /**
5379       * Calculates the bitvalue of the permissions given in a string, comma-sep
5380       *
5381       * @param    string        List of pMap strings
5382       * @return    integer        Integer mask
5383       * @see setTSconfigPermissions(), newFieldArray()
5384       */
5385  	function assemblePermissions($string)    {
5386          $keyArr = t3lib_div::trimExplode(',',$string,1);
5387          $value=0;
5388          while(list(,$key)=each($keyArr))    {
5389              if ($key && isset($this->pMap[$key]))    {
5390                  $value |= $this->pMap[$key];
5391              }
5392          }
5393          return $value;
5394      }
5395  
5396      /**
5397       * Returns the $input string without a comma in the end
5398       *
5399       * @param    string        Input string
5400       * @return    string        Output string with any comma in the end removed, if any.
5401       */
5402  	function rmComma($input)    {
5403          return ereg_replace(',$','',$input);
5404      }
5405  
5406      /**
5407       * Converts a HTML entity (like &#123;) to the character '123'
5408       *
5409       * @param    string        Input string
5410       * @return    string        Output string
5411       */
5412  	function convNumEntityToByteValue($input)    {
5413          $token = md5(microtime());
5414          $parts = explode($token,ereg_replace('(&#([0-9]+);)',$token.'\2'.$token,$input));
5415  
5416          foreach($parts as $k => $v)    {
5417              if ($k%2)    {
5418                  $v = intval($v);
5419                  if ($v > 32)    {    // Just to make sure that control bytes are not converted.
5420                      $parts[$k] =chr(intval($v));
5421                  }
5422              }
5423          }
5424  
5425          return implode('',$parts);
5426      }
5427  
5428      /**
5429       * Returns absolute destination path for the uploadfolder, $folder
5430       *
5431       * @param    string        Upload folder name, relative to PATH_site
5432       * @return    string        Input string prefixed with PATH_site
5433       */
5434  	function destPathFromUploadFolder($folder)    {
5435          return PATH_site.$folder;
5436      }
5437  
5438      /**
5439       * Returns delete-clause for the $table
5440       *
5441       * @param    string        Table name
5442       * @return    string        Delete clause
5443       */
5444  	function deleteClause($table)    {
5445              // Returns the proper delete-clause if any for a table from TCA
5446          global $TCA;
5447          if ($TCA[$table]['ctrl']['delete'])    {
5448              return ' AND '.$table.'.'.$TCA[$table]['ctrl']['delete'].'=0';
5449          } else {
5450              return '';
5451          }
5452      }
5453  
5454      /**
5455       * Return TSconfig for a page id
5456       *
5457       * @param    integer        Page id (PID) from which to get configuration.
5458       * @return    array        TSconfig array, if any
5459       */
5460  	function getTCEMAIN_TSconfig($tscPID)    {
5461          if (!isset($this->cachedTSconfig[$tscPID]))    {
5462              $this->cachedTSconfig[$tscPID] = $this->BE_USER->getTSConfig('TCEMAIN',t3lib_BEfunc::getPagesTSconfig($tscPID));
5463          }
5464          return $this->cachedTSconfig[$tscPID]['properties'];
5465      }
5466  
5467      /**
5468       * Extract entries from TSconfig for a specific table. This will merge specific and default configuration together.
5469       *
5470       * @param    string        Table name
5471       * @param    array        TSconfig for page
5472       * @return    array        TSconfig merged
5473       * @see getTCEMAIN_TSconfig()
5474       */
5475  	function getTableEntries($table,$TSconfig)    {
5476          $tA = is_array($TSconfig['table.'][$table.'.']) ? $TSconfig['table.'][$table.'.'] : array();;
5477          $dA = is_array($TSconfig['default.']) ? $TSconfig['default.'] : array();
5478          return t3lib_div::array_merge_recursive_overrule($dA,$tA);
5479      }
5480  
5481      /**
5482       * Returns the pid of a record from $table with $uid
5483       *
5484       * @param    string        Table name
5485       * @param    integer        Record uid
5486       * @return    integer        PID value (unless the record did not exist in which case FALSE)
5487       */
5488  	function getPID($table,$uid)    {
5489          $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', $table, 'uid='.intval($uid));
5490          if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp))    {
5491              return $row['pid'];
5492          }
5493      }
5494  
5495      /**
5496       * Executing dbAnalysisStore
5497       * This will save MM relations for new records but is executed after records are created because we need to know the ID of them
5498       *
5499       * @return    void
5500       */
5501  	function dbAnalysisStoreExec()    {
5502          reset($this->dbAnalysisStore);
5503          while(list($k,$v)=each($this->dbAnalysisStore))    {
5504              $id = $this->substNEWwithIDs[$v[2]];
5505              if ($id)    {
5506                  $v[2] = $id;
5507                  $v[0]->writeMM($v[1],$v[2],$v[3]);
5508              }
5509          }
5510      }
5511  
5512      /**
5513       * Removing files registered for removal before exit
5514       *
5515       * @return    void
5516       */
5517  	function removeRegisteredFiles()    {
5518          reset($this->removeFilesStore);
5519          while(list($k,$v)=each($this->removeFilesStore))    {
5520              unlink($v);
5521          }
5522      }
5523  
5524      /**
5525       * Unlink (delete) typo3conf/temp_CACHED_*.php cache files
5526       *
5527       * @return    integer        The number of files deleted
5528       */
5529  	function removeCacheFiles()    {
5530          return t3lib_extMgm::removeCacheFiles();
5531      }
5532  
5533      /**
5534       * Returns array, $CPtable, of pages under the $pid going down to $counter levels.
5535       * Selecting ONLY pages which the user has read-access to!
5536       *
5537       * @param    array        Accumulation of page uid=>pid pairs in branch of $pid
5538       * @param    integer        Page ID for which to find subpages
5539       * @param    integer        Number of levels to go down.
5540       * @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!
5541       * @return    array        Return array.
5542       */
5543  	function int_pageTreeInfo($CPtable,$pid,$counter, $rootID)    {
5544          if ($counter)    {
5545              $addW =  !$this->admin ? ' AND '.$this->BE_USER->getPagePermsClause($this->pMap['show']) : '';
5546              $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', 'pid='.intval($pid).$this->deleteClause('pages').$addW, '', 'sorting DESC');
5547              while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres))    {
5548                  if ($row['uid']!=$rootID)    {
5549                      $CPtable[$row['uid']] = $pid;
5550                      if ($counter-1)    {    // If the uid is NOT the rootID of the copyaction and if we are supposed to walk further down
5551                          $CPtable = $this->int_pageTreeInfo($CPtable,$row['uid'],$counter-1, $rootID);
5552                      }
5553                  }
5554              }
5555          }
5556          return $CPtable;
5557      }
5558  
5559      /**
5560       * List of all tables (those administrators has access to = array_keys of $TCA)
5561       *
5562       * @return    array        Array of all TCA table names
5563       */
5564  	function compileAdminTables()    {
5565          global $TCA;
5566          reset ($TCA);
5567          $listArr = array();
5568          while (list($table)=each($TCA))    {
5569              $listArr[]=$table;
5570          }
5571          return $listArr;
5572      }
5573  
5574      /**
5575       * Checks if any uniqueInPid eval input fields are in the record and if so, they are re-written to be correct.
5576       *
5577       * @param    string        Table name
5578       * @param    integer        Record UID
5579       * @return    void
5580       */
5581  	function fixUniqueInPid($table,$uid)    {
5582          global $TCA;
5583          if ($TCA[$table])    {
5584              t3lib_div::loadTCA($table);
5585              reset ($TCA[$table]['columns']);
5586              $curData=$this->recordInfo($table,$uid,'*');
5587              $newData=array();
5588              while (list($field,$conf)=each($TCA[$table]['columns']))    {
5589                  if ($conf['config']['type']=='input')    {
5590                      $evalCodesArray = t3lib_div::trimExplode(',',$conf['config']['eval'],1);
5591                      if (in_array('uniqueInPid',$evalCodesArray))    {
5592                          $newV = $this->getUnique($table,$field,$curData[$field],$uid,$curData['pid']);
5593                          if (strcmp($newV,$curData[$field]))    {
5594                              $newData[$field]=$newV;
5595                          }
5596                      }
5597                  }
5598              }
5599                  // IF there are changed fields, then update the database
5600              if (count($newData))    {
5601                  $this->updateDB($table,$uid,$newData);
5602              }
5603          }
5604      }
5605  
5606      /**
5607       * When er record is copied you can specify fields from the previous record which should be copied into the new one
5608       * 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)
5609       *
5610       * @param    string        Table name
5611       * @param    integer        Record UID
5612       * @param    integer        UID of previous record
5613       * @param    boolean        If set, updates the record
5614       * @param    array        Input array. If fields are already specified AND $update is not set, values are not set in output array.
5615       * @return    array        Output array (For when the copying operation needs to get the information instead of updating the info)
5616       */
5617  	function fixCopyAfterDuplFields($table,$uid,$prevUid,$update, $newData=array())    {
5618          global $TCA;
5619          if ($TCA[$table] && $TCA[$table]['ctrl']['copyAfterDuplFields'])    {
5620              t3lib_div::loadTCA($table);
5621              $prevData=$this->recordInfo($table,$prevUid,'*');
5622              $theFields = t3lib_div::trimExplode(',',$TCA[$table]['ctrl']['copyAfterDuplFields'],1);
5623              reset($theFields);
5624              while(list(,$field)=each($theFields))    {
5625                  if ($TCA[$table]['columns'][$field] && ($update || !isset($newData[$field])))    {
5626                      $newData[$field]=$prevData[$field];
5627                  }
5628              }
5629              if ($update && count($newData))    {
5630                  $this->updateDB($table,$uid,$newData);
5631              }
5632          }
5633          return $newData;
5634      }
5635  
5636      /**
5637       * Returns all fieldnames from a table which are a list of files
5638       *
5639       * @param    string        Table name
5640       * @return    array        Array of fieldnames that are either "group" or "file" types.
5641       */
5642  	function extFileFields($table)    {
5643          global $TCA;
5644          $listArr=array();
5645          t3lib_div::loadTCA($table);
5646          if ($TCA[$table]['columns'])    {
5647              reset($TCA[$table]['columns']);
5648              while (list($field,$configArr)=each($TCA[$table]['columns']))    {
5649                  if ($configArr['config']['type']=='group' && $configArr['config']['internal_type']=='file')    {
5650                      $listArr[]=$field;
5651                  }
5652              }
5653          }
5654          return $listArr;
5655      }
5656  
5657      /**
5658       * Returns all fieldnames from a table which have the unique evaluation type set.
5659       *
5660       * @param    string        Table name
5661       * @return    array        Array of fieldnames
5662       */
5663  	function getUniqueFields($table)    {
5664          global $TCA;
5665  
5666          $listArr=array();
5667          t3lib_div::loadTCA($table);
5668          if ($TCA[$table]['columns'])    {
5669              reset($TCA[$table]['columns']);
5670              while (list($field,$configArr)=each($TCA[$table]['columns']))    {
5671                  if ($configArr['config']['type']==='input')    {
5672                      $evalCodesArray = t3lib_div::trimExplode(',',$configArr['config']['eval'],1);
5673                      if (in_array('uniqueInPid',$evalCodesArray) || in_array('unique',$evalCodesArray))    {
5674                          $listArr[]=$field;
5675                      }
5676                  }
5677              }
5678          }
5679          return $listArr;
5680      }
5681  
5682      /**
5683       * Returns true if the TCA/columns field type is a DB reference field
5684       *
5685       * @param    array        config array for TCA/columns field
5686       * @return    boolean        True if DB reference field (group/db or select with foreign-table)
5687       */
5688  	function isReferenceField($conf)    {
5689          return ($conf['type']=='group' && $conf['internal_type']=='db') ||    ($conf['type']=='select' && $conf['foreign_table']);
5690      }
5691  
5692      /**
5693       * Returns the subtype as a string of an inline field.
5694       * If it's not a inline field at all, it returns false.
5695       *
5696       * @param    array        config array for TCA/columns field
5697       * @return    mixed        string: inline subtype (field|mm|list), boolean: false
5698       */
5699  	function getInlineFieldType($conf) {
5700          if ($conf['type'] == 'inline' && $conf['foreign_table']) {
5701              if ($conf['foreign_field'])
5702                  return 'field';        // the reference to the parent is stored in a pointer field in the child record
5703              elseif ($conf['MM'])
5704                  return 'mm';        // regular MM intermediate table is used to store data
5705              else
5706                  return 'list';        // an item list (separated by comma) is stored (like select type is doing)
5707          }
5708          return false;
5709      }
5710  
5711      /**
5712       * Get modified header for a copied record
5713       *
5714       * @param    string        Table name
5715       * @param    integer        PID value in which other records to test might be
5716       * @param    string        Field name to get header value for.
5717       * @param    string        Current field value
5718       * @param    integer        Counter (number of recursions)
5719       * @param    string        Previous title we checked for (in previous recursion)
5720       * @return    string        The field value, possibly appended with a "copy label"
5721       */
5722  	function getCopyHeader($table,$pid,$field,$value,$count,$prevTitle='')    {
5723          global $TCA;
5724  
5725              // Set title value to check for:
5726          if ($count)    {
5727              $checkTitle = $value.rtrim(' '.sprintf($this->prependLabel($table),$count));
5728          }    else {
5729              $checkTitle = $value;
5730          }
5731  
5732              // Do check:
5733          if ($prevTitle != $checkTitle || $count<100)    {
5734              $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($pid).' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($checkTitle, $table).$this->deleteClause($table), '', '', '1');
5735              if ($GLOBALS['TYPO3_DB']->sql_num_rows($res))    {
5736                  return $this->getCopyHeader($table,$pid,$field,$value,$count+1,$checkTitle);
5737              }
5738          }
5739  
5740              // Default is to just return the current input title if no other was returned before:
5741          return $checkTitle;
5742      }
5743  
5744      /**
5745       * Return "copy" label for a table. Although the name is "prepend" it actually APPENDs the label (after ...)
5746       *
5747       * @param    string        Table name
5748       * @return    string        Label to append, containing "%s" for the number
5749       * @see getCopyHeader()
5750       */
5751  	function prependLabel($table)    {
5752          global $TCA;
5753          if (is_object($GLOBALS['LANG']))    {
5754              $label = $GLOBALS['LANG']->sL($TCA[$table]['ctrl']['prependAtCopy']);
5755          } else {
5756              list($label) = explode('|',$TCA[$table]['ctrl']['prependAtCopy']);
5757          }
5758          return $label;
5759      }
5760  
5761      /**
5762       * Get the final pid based on $table and $pid ($destPid type... pos/neg)
5763       *
5764       * @param    string        Table name
5765       * @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!
5766       * @return    integer
5767       */
5768  	function resolvePid($table,$pid)    {
5769          global $TCA;
5770          $pid = intval($pid);
5771          if ($pid < 0)    {
5772              $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', $table, 'uid='.abs($pid));
5773              $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
5774  
5775                  // 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.
5776              if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,abs($pid),'pid'))    {
5777                  $row = $lookForLiveVersion;
5778              }
5779  
5780              $pid = intval($row['pid']);
5781          } 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.
5782              if ($WSdestPage = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, 'pages', $pid, 'uid,t3ver_swapmode'))    {    // Looks for workspace version of page.
5783                  if ($WSdestPage['t3ver_swapmode']==0)    {    // if swapmode is zero, then change pid value.
5784                      $pid = $WSdestPage['uid'];
5785                  }
5786              }
5787          }
5788          return $pid;
5789      }
5790  
5791      /**
5792       * Removes the prependAtCopy prefix on values
5793       *
5794       * @param    string        Table name
5795       * @param    string        The value to fix
5796       * @return    string        Clean name
5797       */
5798  	function clearPrefixFromValue($table,$value)    {
5799          global $TCA;
5800          $regex = sprintf(quotemeta($this->prependLabel($table)),'[0-9]*').'$';
5801          return @ereg_replace($regex,'',$value);
5802      }
5803  
5804      /**
5805       * File functions on external file references. eg. deleting files when deleting record
5806       *
5807       * @param    string        Table name
5808       * @param    string        Field name
5809       * @param    string        List of files to work on from field
5810       * @param    string        Function, eg. "deleteAll" which will delete all files listed.
5811       * @return    void
5812       */
5813  	function extFileFunctions($table,$field,$filelist,$func)    {
5814          global $TCA;
5815          t3lib_div::loadTCA($table);
5816          $uploadFolder = $TCA[$table]['columns'][$field]['config']['uploadfolder'];
5817          if ($uploadFolder && trim($filelist))    {
5818              $uploadPath = $this->destPathFromUploadFolder($uploadFolder);
5819              $fileArray = explode(',',$filelist);
5820              while (list(,$theFile)=each($fileArray))    {
5821                  $theFile=trim($theFile);
5822                  if ($theFile)    {
5823                      switch($func)    {
5824                          case 'deleteAll':
5825                              if (@is_file($uploadPath.'/'.$theFile))    {
5826                                  unlink ($uploadPath.'/'.$theFile);
5827                              } else {
5828                                  $this->log($table,0,3,0,100,"Delete: Referenced file that was supposed to be deleted together with it's record didn't exist");
5829                              }
5830                          break;
5831                      }
5832                  }
5833              }
5834          }
5835      }
5836  
5837      /**
5838       * Used by the deleteFunctions to check if there are records from disallowed tables under the pages to be deleted.
5839       *
5840       * @param    string        List of page integers
5841       * @return    boolean        Return true, if permission granted
5842       */
5843  	function noRecordsFromUnallowedTables($inList)    {
5844          global $TCA;
5845          reset ($TCA);
5846          $inList = trim($this->rmComma(trim($inList)));
5847          if ($inList && !$this->admin)    {
5848              while (list($table) = each($TCA))    {
5849                  $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid IN ('.$inList.')'.t3lib_BEfunc::deleteClause($table));
5850                  $count = $GLOBALS['TYPO3_DB']->sql_fetch_row($mres);
5851                  if ($count[0] && ($this->tableReadOnly($table) || !$this->checkModifyAccessList($table)))    {
5852                      return FALSE;
5853                  }
5854              }
5855          }
5856          return TRUE;
5857      }
5858  
5859      /**
5860       * Send an email notification to users in workspace
5861       *
5862       * @param    array        Workspace access array (from t3lib_userauthgroup::checkWorkspace())
5863       * @param    integer        New Stage number: 0 = editing, 1= just ready for review, 10 = ready for publication, -1 = rejected!
5864       * @param    string        Table name of element
5865       * @param    integer        Record uid of element
5866       * @param    string        User comment sent along with action
5867       * @return    void
5868       */
5869  	function notifyStageChange($stat,$stageId,$table,$id,$comment)    {
5870          $workspaceRec = t3lib_BEfunc::getRecord('sys_workspace', $stat['uid']);
5871  
5872          if (is_array($workspaceRec))    {
5873  
5874                  // Compile label:
5875              switch((int)$stageId)    {
5876                  case 1:
5877                      $newStage = 'Ready for review';
5878                  break;
5879                  case 10:
5880                      $newStage = 'Ready for publishing';
5881                  break;
5882                  case -1:
5883                      $newStage = 'Element was rejected!';
5884                  break;
5885                  case 0:
5886                      $newStage = 'Rejected element was noticed and edited';
5887                  break;
5888                  default:
5889                      $newStage = 'Unknown state change!?';
5890                  break;
5891              }
5892  
5893                  // Compile list of recipients:
5894              $emails = array();
5895              switch((int)$stat['stagechg_notification'])    {
5896                  case 1:
5897                      switch((int)$stageId)    {
5898                          case 1:
5899                              $emails = $this->notifyStageChange_getEmails($workspaceRec['reviewers']);
5900                          break;
5901                          case 10:
5902                              $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE);
5903                          break;
5904                          case -1:
5905                              $emails = $this->notifyStageChange_getEmails($workspaceRec['reviewers']);
5906                              $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['members']));
5907                          break;
5908                          case 0:
5909                              $emails = $this->notifyStageChange_getEmails($workspaceRec['members']);
5910                          break;
5911                          default:
5912                              $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE);
5913                          break;
5914                      }
5915                  break;
5916                  case 10:
5917                      $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE);
5918                      $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['reviewers']));
5919                      $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['members']));
5920                  break;
5921              }
5922              $emails = array_unique($emails);
5923  
5924                  // Send email:
5925              if (count($emails))    {
5926                  $message = sprintf('
5927  At the TYPO3 site "%s" (%s)
5928  in workspace "%s" (#%s)
5929  the stage has changed for the element "%s":
5930  
5931  ==> %s
5932  
5933  User Comment:
5934  "%s"
5935  
5936  State was change by %s (username: %s)
5937                  ',
5938                  $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
5939                  t3lib_div::getIndpEnv('TYPO3_SITE_URL').TYPO3_mainDir,
5940                  $workspaceRec['title'],
5941                  $workspaceRec['uid'],
5942                  $table.':'.$id,
5943                  $newStage,
5944                  $comment,
5945                  $this->BE_USER->user['realName'],
5946                  $this->BE_USER->user['username']);
5947  
5948                  t3lib_div::plainMailEncoded(
5949                      implode(',',$emails),
5950                      'TYPO3 Workspace Note: Stage Change for '.$table.':'.$id,
5951                      trim($message)
5952                  );
5953              }
5954          }
5955      }
5956  
5957      /**
5958       * Return emails addresses of be_users from input list.
5959       *
5960       * @param    string        List of backend users, on the form "be_users_10,be_users_2" or "10,2" in case noTablePrefix is set.
5961       * @param    boolean        If set, the input list are integers and not strings.
5962       * @return    array        Array of emails
5963       */
5964  	function notifyStageChange_getEmails($listOfUsers,$noTablePrefix=FALSE)    {
5965          $users = t3lib_div::trimExplode(',',$listOfUsers,1);
5966          $emails = array();
5967          foreach($users as $userIdent)    {
5968              if ($noTablePrefix)    {
5969                  $id = intval($userIdent);
5970              } else {
5971                  list($table,$id) = t3lib_div::revExplode('_',$userIdent,2);
5972              }
5973              if ($table==='be_users' || $noTablePrefix)    {
5974                  if ($userRecord = t3lib_BEfunc::getRecord('be_users', $id, 'email'))    {
5975                      if (strlen(trim($userRecord['email'])))    {
5976                          $emails[$id] = $userRecord['email'];
5977                      }
5978                  }
5979              }
5980          }
5981          return $emails;
5982      }
5983  
5984      /**
5985       * Determine if a record was copied or if a record is the result of a copy action.
5986       *
5987       * @param    string        $table: The tablename of the record
5988       * @param    integer        $uid: The uid of the record
5989       * @return    boolean        Returns true if the record is copied or is the result of a copy action
5990       */
5991  	function isRecordCopied($table, $uid) {
5992              // If the record was copied:
5993          if (isset($this->copyMappingArray[$table][$uid])) {
5994              return true;
5995              // If the record is the result of a copy action:
5996          } elseif (isset($this->copyMappingArray[$table]) && in_array($uid, array_values($this->copyMappingArray[$table]))) {
5997              return true;
5998          }
5999          return false;
6000      }
6001  
6002  
6003  
6004  
6005  
6006  
6007  
6008  
6009  
6010  
6011  
6012  
6013      /******************************
6014       *
6015       * Clearing cache
6016       *
6017       ******************************/
6018  
6019      /**
6020       * Clearing the cache based on a page being updated
6021       * If the $table is 'pages' then cache is cleared for all pages on the same level (and subsequent?)
6022       * Else just clear the cache for the parent page of the record.
6023       *
6024       * @param    string        Table name of record that was just updated.
6025       * @param    integer        UID of updated / inserted record
6026       * @return    void
6027       */
6028  	function clear_cache($table,$uid) {
6029          global $TCA, $TYPO3_CONF_VARS;
6030  
6031          $uid = intval($uid);
6032          if (is_array($TCA[$table]) && $uid > 0)    {
6033  
6034                  // Get Page TSconfig relavant:
6035              list($tscPID) = t3lib_BEfunc::getTSCpid($table,$uid,'');
6036              $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
6037  
6038              if (!$TSConfig['clearCache_disable'])    {
6039  
6040                      // If table is "pages":
6041                  if (t3lib_extMgm::isLoaded('cms'))    {
6042                      $list_cache = array();
6043                      if ($table=='pages')    {
6044  
6045                              // Builds list of pages on the SAME level as this page (siblings)
6046                          $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
6047                                          'A.pid AS pid, B.uid AS uid',
6048                                          'pages A, pages B',
6049                                          'A.uid='.intval($uid).' AND B.pid=A.pid AND B.deleted=0'
6050                                      );
6051  
6052                          $pid_tmp = 0;
6053                          while ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) {
6054                              $list_cache[] = $row_tmp['uid'];
6055                              $pid_tmp = $row_tmp['pid'];
6056  
6057                                  // Add children as well:
6058                              if ($TSConfig['clearCache_pageSiblingChildren'])    {
6059                                  $res_tmp2 = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
6060                                                  'uid',
6061                                                  'pages',
6062                                                  'pid='.intval($row_tmp['uid']).' AND deleted=0'
6063                                              );
6064                                  while ($row_tmp2 = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp2))    {
6065                                      $list_cache[] = $row_tmp2['uid'];
6066                                  }
6067                              }
6068                          }
6069  
6070                              // Finally, add the parent page as well:
6071                          $list_cache[] = $pid_tmp;
6072  
6073                              // Add grand-parent as well:
6074                          if ($TSConfig['clearCache_pageGrandParent'])    {
6075                              $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
6076                                              'pid',
6077                                              'pages',
6078                                              'uid='.intval($pid_tmp)
6079                                          );
6080                              if ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp))    {
6081                                  $list_cache[] = $row_tmp['pid'];
6082                              }
6083                          }
6084                      } else {    // For other tables than "pages", delete cache for the records "parent page".
6085                          $list_cache[] = intval($this->getPID($table,$uid));
6086                      }
6087  
6088                          // Call pre-processing function for clearing of cache for page ids:
6089                      if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval']))    {
6090                          foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName)    {
6091                              $_params = array('pageIdArray' => &$list_cache, 'table' => $table, 'uid' => $uid, 'functionID' => 'clear_cache()');
6092                                  // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array!
6093                              t3lib_div::callUserFunction($funcName,$_params,$this);
6094                          }
6095                      }
6096  
6097                          // Delete cache for selected pages:
6098                      if (is_array($list_cache))    {
6099                          $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')');
6100                          $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection', 'page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')');
6101                      }
6102                  }
6103              }
6104  
6105                  // Clear cache for pages entered in TSconfig:
6106              if ($TSConfig['clearCacheCmd'])    {
6107                  $Commands = t3lib_div::trimExplode(',',strtolower($TSConfig['clearCacheCmd']),1);
6108                  $Commands = array_unique($Commands);
6109                  foreach($Commands as $cmdPart)    {
6110                      $this->clear_cacheCmd($cmdPart);
6111                  }
6112              }
6113  
6114                  // Call post processing function for clear-cache:
6115              global $TYPO3_CONF_VARS;
6116              if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc']))    {
6117  // FIXME $uid_page is undefined
6118                  $_params = array('table' => $table,'uid' => $uid,'uid_page' => $uid_page,'TSConfig' => $TSConfig);
6119                  foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef)    {
6120                      t3lib_div::callUserFunction($_funcRef,$_params,$this);
6121                  }
6122              }
6123          }
6124      }
6125  
6126      /**
6127       * Clears the cache based on a command, $cacheCmd
6128       *
6129       * $cacheCmd='pages':    Clears cache for all pages. Requires admin-flag to be set for BE_USER
6130       * $cacheCmd='all':        Clears all cache_tables. This is necessary if templates are updated. Requires admin-flag to be set for BE_USER
6131       * $cacheCmd=[integer]:        Clears cache for the page pointed to by $cacheCmd (an integer).
6132       *
6133       * Can call a list of post processing functions as defined in $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] (num array with values being the function references, called by t3lib_div::callUserFunction())
6134       *
6135       * @param    string        The cache comment, see above description.
6136       * @return    void
6137       */
6138  	function clear_cacheCmd($cacheCmd)    {
6139          global $TYPO3_CONF_VARS;
6140  
6141              // Clear cache for either ALL pages or ALL tables!
6142          switch($cacheCmd)    {
6143              case 'pages':
6144                  if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.pages'))    {
6145                      $this->internal_clearPageCache();
6146                  }
6147              break;
6148              case 'all':
6149                  if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.all'))    {
6150                      if (t3lib_extMgm::isLoaded('cms'))    {
6151                          $this->internal_clearPageCache();
6152                          $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection','');
6153                      }
6154                      $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_hash','');
6155  
6156                          // Clearing additional cache tables:
6157                      if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables']))    {
6158                          foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'] as $tableName)    {
6159                              if (!ereg('[^[:alnum:]_]',$tableName) && substr($tableName,-5)=='cache')    {
6160                                  $GLOBALS['TYPO3_DB']->exec_DELETEquery($tableName,'');
6161                              } else {
6162                                  die('Fatal Error: Trying to flush table "'.$tableName.'" with "Clear All Cache"');
6163                              }
6164                          }
6165                      }
6166                  }
6167              break;
6168              case 'temp_CACHED':
6169                  if ($this->admin && $TYPO3_CONF_VARS['EXT']['extCache'])    {
6170                      $this->removeCacheFiles();
6171                  }
6172              break;
6173          }
6174  
6175              // Clear cache for a page ID!
6176          if (t3lib_div::testInt($cacheCmd))    {
6177              if (t3lib_extMgm::isLoaded('cms'))    {
6178  
6179                  $list_cache = array($cacheCmd);
6180  
6181                      // Call pre-processing function for clearing of cache for page ids:
6182                  if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval']))    {
6183                      foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName)    {
6184                          $_params = array('pageIdArray' => &$list_cache, 'cacheCmd' => $cacheCmd, 'functionID' => 'clear_cacheCmd()');
6185                              // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array!
6186                          t3lib_div::callUserFunction($funcName,$_params,$this);
6187                      }
6188                  }
6189  
6190                      // Delete cache for selected pages:
6191                  if (is_array($list_cache))    {
6192                      $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')');
6193                      $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!
6194                  }
6195              }
6196          }
6197  
6198              // Call post processing function for clear-cache:
6199          if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc']))    {
6200              $_params = array('cacheCmd'=>$cacheCmd);
6201              foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef)    {
6202                  t3lib_div::callUserFunction($_funcRef,$_params,$this);
6203              }
6204          }
6205      }
6206  
6207  
6208  
6209  
6210  
6211  
6212  
6213  
6214  
6215  
6216  
6217  
6218  
6219  
6220      /*****************************
6221       *
6222       * Logging
6223       *
6224       *****************************/
6225  
6226      /**
6227       * Logging actions from TCEmain
6228       *
6229       * @param    string        Table name the log entry is concerned with. Blank if NA
6230       * @param    integer        Record UID. Zero if NA
6231       * @param    integer        Action number: 0=No category, 1=new record, 2=update record, 3= delete record, 4= move record, 5= Check/evaluate
6232       * @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
6233       * @param    integer        The severity: 0 = message, 1 = error, 2 = System Error, 3 = security notice (admin)
6234       * @param    string        Default error message in english
6235       * @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
6236       * @param    array        Array with special information that may go into $details by '%s' marks / sprintf() when the log is shown
6237       * @param    integer        The page_uid (pid) where the event occurred. Used to select log-content for specific pages.
6238       * @param    string        NEW id for new records
6239       * @return    integer        Log entry UID
6240       * @see    class.t3lib_userauthgroup.php
6241       */
6242  	function log($table,$recuid,$action,$recpid,$error,$details,$details_nr=-1,$data=array(),$event_pid=-1,$NEWid='') {
6243          if ($this->enableLogging)    {
6244              $type=1;    // Type value for tce_db.php
6245              if (!$this->storeLogMessages)    {$details='';}
6246              if ($error>0)    $this->errorLog[] = '['.$type.'.'.$action.'.'.$details_nr.']: '.$details;
6247              return $this->BE_USER->writelog($type,$action,$error,$details_nr,$details,$data,$table,$recuid,$recpid,$event_pid,$NEWid);
6248          }
6249      }
6250  
6251      /**
6252       * Simple logging function meant to be used when logging messages is not yet fixed.
6253       *
6254       * @param    string        Message string
6255       * @param    integer        Error code, see log()
6256       * @return    integer        Log entry UID
6257       * @see log()
6258       */
6259  	function newlog($message, $error=0)    {
6260          return $this->log('',0,0,0,$error,$message,-1);
6261      }
6262  
6263      /**
6264       * Print log error messages from the operations of this script instance
6265       *
6266       * @param    string        Redirect URL (for creating link in message)
6267       * @return    void        (Will exit on error)
6268       */
6269  	function printLogErrorMessages($redirect)    {
6270  
6271          $res_log = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
6272                      '*',
6273                      'sys_log',
6274                      'type=1 AND userid='.intval($this->BE_USER->user['uid']).' AND tstamp='.intval($GLOBALS['EXEC_TIME']).'    AND error!=0'
6275                  );
6276          $errorJS = array();
6277          while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_log)) {
6278              $log_data = unserialize($row['log_data']);
6279              $errorJS[] = $row['error'].': '.sprintf($row['details'], $log_data[0],$log_data[1],$log_data[2],$log_data[3],$log_data[4]);
6280          }
6281  
6282          if (count($errorJS))    {
6283              $error_doc = t3lib_div::makeInstance('template');
6284              $error_doc->backPath = $GLOBALS['BACK_PATH'];
6285  
6286              $content.= $error_doc->startPage('tce_db.php Error output');
6287  
6288              $lines[] = '
6289                      <tr class="bgColor5">
6290                          <td colspan="2" align="center"><strong>Errors:</strong></td>
6291                      </tr>';
6292  
6293              foreach($errorJS as $line)    {
6294                  $lines[] = '
6295                      <tr class="bgColor4">
6296                          <td valign="top"><img'.t3lib_iconWorks::skinImg($error_doc->backPath,'gfx/icon_fatalerror.gif','width="18" height="16"').' alt="" /></td>
6297                          <td>'.htmlspecialchars($line).'</td>
6298                      </tr>';
6299              }
6300  
6301              $lines[] = '
6302                      <tr>
6303                          <td colspan="2" align="center"><br />'.
6304                          '<form action=""><input type="submit" value="Continue" onclick="'.htmlspecialchars('window.location.href=\''.$redirect.'\';return false;').'"></form>'.
6305                          '</td>
6306                      </tr>';
6307  
6308              $content.= '
6309                  <br/><br/>
6310                  <table border="0" cellpadding="1" cellspacing="1" width="300" align="center">
6311                      '.implode('',$lines).'
6312                  </table>';
6313  
6314              $content.= $error_doc->endPage();
6315              echo $content;
6316              exit;
6317          }
6318      }
6319  
6320      /*****************************
6321       *
6322       * Internal (do not use outside Core!)
6323       *
6324       *****************************/
6325  
6326      /**
6327       * Clears page cache. Takes into account file cache.
6328       *
6329       * @return    void
6330       */
6331  	function internal_clearPageCache() {
6332          if (t3lib_extMgm::isLoaded('cms'))    {
6333              $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','');
6334              if ($GLOBALS['TYPO3_CONF_VARS']['FE']['pageCacheToExternalFiles']) {
6335                  $cacheDir = PATH_site.'typo3temp/cache_pages';
6336                      // TODO: Replace with t3lib_div::rmdir() when available
6337                  if (@is_dir($cacheDir) && false !== ($topDir = @opendir($cacheDir))) {
6338                      while (false !== ($dir = @readdir($topDir))) {
6339                          $curDirName = $cacheDir . '/' . $dir;
6340                          if (@is_dir($curDirName) && $curDirName{0} != '.' && false !== ($curDir = @opendir($curDirName))) {
6341                              while (false != ($fname = @readdir($curDir))) {
6342                                  $curFName = $curDirName . '/' . $fname;
6343                                  if (@is_file($curFName)) {
6344                                      @unlink($curFName);
6345                                  }
6346                              }
6347                              closedir($curDir);
6348                              @rmdir($curDirName);
6349                          }
6350                      }
6351                      closedir($topDir);
6352                  }
6353              }
6354          }
6355      }
6356  }
6357  
6358  
6359  
6360  if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tcemain.php'])    {
6361      include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tcemain.php']);
6362  }
6363  ?>


Généré le : Sun Nov 25 17:13:16 2007 par Balluche grâce à PHPXref 0.7
  Clicky Web Analytics