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