| [ Index ] |
|
Code source de Typo3 4.1.3 |
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 '{' 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 {) 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 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
| Généré le : Sun Nov 25 17:13:16 2007 | par Balluche grâce à PHPXref 0.7 |
|