* Merging all changes from TYPO3_4-0 branch back into HEAD
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_tcemain.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2006 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Contains the TYPO3 Core Engine
29 *
30 * $Id$
31 * Revised for TYPO3 3.9 October 2005 by Kasper Skaarhoj
32 *
33 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
34 */
35 /**
36 * [CLASS/FUNCTION INDEX of SCRIPT]
37 *
38 *
39 *
40 * 229: class t3lib_TCEmain
41 * 348: function start($data,$cmd,$altUserObject='')
42 * 387: function setMirror($mirror)
43 * 412: function setDefaultsFromUserTS($userTS)
44 * 435: function process_uploads($postFiles)
45 * 473: function process_uploads_traverseArray(&$outputArr,$inputArr,$keyToSet)
46 *
47 * SECTION: PROCESSING DATA
48 * 509: function process_datamap()
49 * 815: function placeholderShadowing($table,$id)
50 * 851: function fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$realPid,$status,$tscPID)
51 *
52 * SECTION: Evaluation of input values
53 * 1072: function checkValue($table,$field,$value,$id,$status,$realPid,$tscPID)
54 * 1132: function checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$uploadedFiles,$tscPID)
55 * 1178: function checkValue_input($res,$value,$tcaFieldConf,$PP,$field='')
56 * 1216: function checkValue_check($res,$value,$tcaFieldConf,$PP)
57 * 1239: function checkValue_radio($res,$value,$tcaFieldConf,$PP)
58 * 1265: function checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field)
59 * 1366: function checkValue_group_select_file($valueArray,$tcaFieldConf,$curValue,$uploadedFileArray,$status,$table,$id,$recFID)
60 * 1536: function checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field)
61 * 1609: function checkValue_flexArray2Xml($array)
62 * 1621: function _DELETE_FLEX_FORMdata(&$valueArrayToRemoveFrom,$deleteCMDS)
63 * 1643: function _MOVE_FLEX_FORMdata(&$valueArrayToMoveIn, $moveCMDS, $direction)
64 *
65 * SECTION: Helper functions for evaluation functions.
66 * 1705: function getUnique($table,$field,$value,$id,$newPid=0)
67 * 1743: function checkValue_input_Eval($value,$evalArray,$is_in)
68 * 1839: function checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,$type)
69 * 1872: function checkValue_group_select_explodeSelectGroupValue($value)
70 * 1896: function checkValue_flex_procInData($dataPart,$dataPart_current,$uploadedFiles,$dataStructArray,$pParams,$callBackFunc='')
71 * 1935: function checkValue_flex_procInData_travDS(&$dataValues,$dataValues_current,$uploadedFiles,$DSelements,$pParams,$callBackFunc,$structurePath)
72 *
73 * SECTION: PROCESSING COMMANDS
74 * 2081: function process_cmdmap()
75 *
76 * SECTION: Cmd: Copying
77 * 2221: function copyRecord($table,$uid,$destPid,$first=0,$overrideValues=array(),$excludeFields='')
78 * 2343: function copyPages($uid,$destPid)
79 * 2397: function copySpecificPage($uid,$destPid,$copyTablesArray,$first=0)
80 * 2431: function copyRecord_raw($table,$uid,$pid,$overrideArray=array())
81 * 2495: function rawCopyPageContent($old_pid,$new_pid,$copyTablesArray)
82 * 2519: function insertNewCopyVersion($table,$fieldArray,$realPid)
83 * 2570: function copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf)
84 * 2626: function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)
85 * 2654: function copyRecord_procFilesRefs($conf, $uid, $value)
86 *
87 * SECTION: Cmd: Moving, Localizing
88 * 2723: function moveRecord($table,$uid,$destPid)
89 * 2910: function localize($table,$uid,$language)
90 *
91 * SECTION: Cmd: Deleting
92 * 2994: function deleteAction($table, $id)
93 * 3041: function deleteEl($table, $uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE)
94 * 3056: function undeleteRecord($table,$uid)
95 * 3073: function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE,$undeleteRecord=FALSE)
96 * 3159: function deletePages($uid,$force=FALSE,$forceHardDelete=FALSE)
97 * 3187: function deleteSpecificPage($uid,$forceHardDelete=FALSE)
98 * 3210: function canDeletePage($uid)
99 * 3237: function cannotDeleteRecord($table,$id)
100 *
101 * SECTION: Cmd: Versioning
102 * 3275: function versionizeRecord($table,$id,$label,$delete=FALSE,$versionizeTree=-1)
103 * 3351: function versionizePages($uid,$label,$versionizeTree)
104 * 3414: function version_swap($table,$id,$swapWith,$swapIntoWS=0)
105 * 3585: function version_clearWSID($table,$id)
106 * 3619: function version_setStage($table,$id,$stageId,$comment='')
107 *
108 * SECTION: Cmd: Helper functions
109 * 3664: function remapListedDBRecords()
110 * 3741: function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2)
111 * 3767: function remapListedDBRecords_procDBRefs($conf, $value, $MM_localUid)
112 *
113 * SECTION: Access control / Checking functions
114 * 3832: function checkModifyAccessList($table)
115 * 3844: function isRecordInWebMount($table,$id)
116 * 3858: function isInWebMount($pid)
117 * 3872: function checkRecordUpdateAccess($table,$id)
118 * 3896: function checkRecordInsertAccess($insertTable,$pid,$action=1)
119 * 3930: function isTableAllowedForThisPage($page_uid, $checkTable)
120 * 3963: function doesRecordExist($table,$id,$perms)
121 * 4024: function doesRecordExist_pageLookUp($id, $perms)
122 * 4050: function doesBranchExist($inList,$pid,$perms,$recurse)
123 * 4084: function tableReadOnly($table)
124 * 4096: function tableAdminOnly($table)
125 * 4110: function destNotInsideSelf($dest,$id)
126 * 4142: function getExcludeListArray()
127 * 4165: function doesPageHaveUnallowedTables($page_uid,$doktype)
128 *
129 * SECTION: Information lookup
130 * 4214: function pageInfo($id,$field)
131 * 4234: function recordInfo($table,$id,$fieldList)
132 * 4255: function getRecordProperties($table,$id,$noWSOL=FALSE)
133 * 4271: function getRecordPropertiesFromRow($table,$row)
134 *
135 * SECTION: Storing data to Database Layer
136 * 4314: function updateDB($table,$id,$fieldArray)
137 * 4366: function insertDB($table,$id,$fieldArray,$newVersion=FALSE,$suggestedUid=0,$dontSetNewIdIndex=FALSE)
138 * 4439: function checkStoredRecord($table,$id,$fieldArray,$action)
139 * 4476: function setHistory($table,$id,$logId)
140 * 4509: function clearHistory($maxAgeSeconds=604800,$table)
141 * 4523: function updateRefIndex($table,$id)
142 *
143 * SECTION: Misc functions
144 * 4555: function getSortNumber($table,$uid,$pid)
145 * 4628: function resorting($table,$pid,$sortRow, $return_SortNumber_After_This_Uid)
146 * 4659: function setTSconfigPermissions($fieldArray,$TSConfig_p)
147 * 4676: function newFieldArray($table)
148 * 4708: function addDefaultPermittedLanguageIfNotSet($table,&$incomingFieldArray)
149 * 4732: function overrideFieldArray($table,$data)
150 * 4748: function compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray)
151 * 4794: function assemblePermissions($string)
152 * 4811: function rmComma($input)
153 * 4821: function convNumEntityToByteValue($input)
154 * 4843: function destPathFromUploadFolder($folder)
155 * 4853: function deleteClause($table)
156 * 4869: function getTCEMAIN_TSconfig($tscPID)
157 * 4884: function getTableEntries($table,$TSconfig)
158 * 4897: function getPID($table,$uid)
159 * 4910: function dbAnalysisStoreExec()
160 * 4926: function removeRegisteredFiles()
161 * 4938: function removeCacheFiles()
162 * 4952: function int_pageTreeInfo($CPtable,$pid,$counter, $rootID)
163 * 4973: function compileAdminTables()
164 * 4990: function fixUniqueInPid($table,$uid)
165 * 5026: function fixCopyAfterDuplFields($table,$uid,$prevUid,$update, $newData=array())
166 * 5051: function extFileFields($table)
167 * 5072: function getUniqueFields($table)
168 * 5097: function isReferenceField($conf)
169 * 5112: function getCopyHeader($table,$pid,$field,$value,$count,$prevTitle='')
170 * 5141: function prependLabel($table)
171 * 5158: function resolvePid($table,$pid)
172 * 5188: function clearPrefixFromValue($table,$value)
173 * 5203: function extFileFunctions($table,$field,$filelist,$func)
174 * 5233: function noRecordsFromUnallowedTables($inList)
175 * 5259: function notifyStageChange($stat,$stageId,$table,$id,$comment)
176 * 5354: function notifyStageChange_getEmails($listOfUsers,$noTablePrefix=FALSE)
177 *
178 * SECTION: Clearing cache
179 * 5400: function clear_cache($table,$uid)
180 * 5510: function clear_cacheCmd($cacheCmd)
181 *
182 * SECTION: Logging
183 * 5616: function log($table,$recuid,$action,$recpid,$error,$details,$details_nr=-1,$data=array(),$event_pid=-1,$NEWid='')
184 * 5633: function newlog($message, $error=0)
185 * 5643: function printLogErrorMessages($redirect)
186 *
187 * TOTAL FUNCTIONS: 115
188 * (This index is automatically created/updated by the extension "extdeveval")
189 *
190 */
191
192
193
194
195 // *******************************
196 // Including necessary libraries
197 // *******************************
198 require_once (PATH_t3lib.'class.t3lib_loaddbgroup.php');
199 require_once (PATH_t3lib.'class.t3lib_parsehtml_proc.php');
200 require_once (PATH_t3lib.'class.t3lib_stdgraphic.php');
201 require_once (PATH_t3lib.'class.t3lib_basicfilefunc.php');
202 require_once (PATH_t3lib."class.t3lib_refindex.php");
203 require_once (PATH_t3lib.'class.t3lib_flexformtools.php');
204
205
206
207
208
209
210
211
212
213
214
215 /**
216 * This is the TYPO3 Core Engine class for manipulation of the database
217 * This class is used by eg. the tce_db.php script which provides an the interface for POST forms to this class.
218 *
219 * Dependencies:
220 * - $GLOBALS['TCA'] must exist
221 * - $GLOBALS['LANG'] (languageobject) may be preferred, but not fatal.
222 *
223 * tce_db.php for further comments and SYNTAX! Also see document 'TYPO3 Core API' for details.
224 *
225 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
226 * @package TYPO3
227 * @subpackage t3lib
228 */
229 class t3lib_TCEmain {
230
231
232 // *********************
233 // Public variables you can configure before using the class:
234 // *********************
235
236 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.
237 var $enableLogging = TRUE; // Boolean: If true, actions are logged to sys_log.
238 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.
239 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.
240 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!
241 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
242 var $checkStoredRecords_loose = TRUE; // Boolean: If set, values '' and 0 will equal each other when the stored records are checked.
243 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
244 var $neverHideAtCopy = FALSE; // Boolean. If set, then the 'hideAtCopy' flag for tables will be ignored.
245 var $dontProcessTransformations = FALSE; // Boolean: If set, then transformations are NOT performed on the input.
246 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!
247
248 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)
249 var $generalComment = ''; // General comment, eg. for staging in workspaces.
250
251 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
252
253 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)
254 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!
255 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.
256 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.
257 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)
258
259 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.
260
261
262
263
264 // *********************
265 // Internal variables (mapping arrays) which can be used (read-only) from outside
266 // *********************
267 var $autoVersionIdMap = Array(); // Contains mapping of auto-versionized records.
268 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
269 var $substNEWwithIDs_table = Array(); // Like $substNEWwithIDs, but where each old "NEW..." id is mapped to the table it was from.
270 var $copyMappingArray_merged = Array(); // This array is the sum of all copying operations in this class. May be READ from outside, thus partly public.
271 var $copiedFileMap = Array(); // A map between input file name and final destination for files being attached to records.
272 var $errorLog = Array(); // Errors are collected in this variable.
273
274
275
276 // *********************
277 // Internal Variables, do not touch.
278 // *********************
279
280 // Variables set in init() function:
281 var $BE_USER; // The user-object the script uses. If not set from outside, this is set to the current global $BE_USER.
282 var $userid; // will be set to uid of be_user executing this script
283 var $username; // will be set to username of be_user executing this script
284 var $admin; // will be set if user is admin
285
286 var $defaultPermissions = array( // Can be overridden from $TYPO3_CONF_VARS
287 'user' => 'show,edit,delete,new,editcontent',
288 'group' => 'show,edit,new,editcontent',
289 'everybody' => ''
290 );
291
292 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.
293 var $datamap = Array(); // Set with incoming data array
294 var $cmdmap = Array(); // Set with incoming cmd array
295
296 // Internal static:
297 var $pMap = Array( // Permission mapping
298 'show' => 1, // 1st bit
299 'edit' => 2, // 2nd bit
300 'delete' => 4, // 3rd bit
301 'new' => 8, // 4th bit
302 'editcontent' => 16 // 5th bit
303 );
304 var $sortIntervals = 256; // Integer: The interval between sorting numbers used with tables with a 'sorting' field defined. Min 1
305
306 // Internal caching arrays
307 var $recUpdateAccessCache = Array(); // Used by function checkRecordUpdateAccess() to store whether a record is updateable or not.
308 var $recInsertAccessCache = Array(); // User by function checkRecordInsertAccess() to store whether a record can be inserted on a page id
309 var $isRecordInWebMount_Cache=array(); // Caching array for check of whether records are in a webmount
310 var $isInWebMount_Cache=array(); // Caching array for page ids in webmounts
311 var $cachedTSconfig = array(); // Caching for collecting TSconfig for page ids
312 var $pageCache = Array(); // Used for caching page records in pageInfo()
313 var $checkWorkspaceCache = Array(); // Array caching workspace access for BE_USER
314
315 // Other arrays:
316 var $dbAnalysisStore=array(); // For accumulation of MM relations that must be written after new records are created.
317 var $removeFilesStore=array(); // For accumulation of files which must be deleted after processing of all input content
318 var $uploadedFileArray = array(); // Uploaded files, set by process_uploads()
319 var $registerDBList=array(); // Used for tracking references that might need correction after operations
320 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.
321
322 // Various
323 var $fileFunc; // For "singleTon" file-manipulation object
324 var $checkValue_currentRecord=array(); // Set to "currentRecord" during checking of values.
325 var $autoVersioningUpdate = FALSE; // A signal flag used to tell file processing that autoversioning has happend and hence certain action should be applied.
326
327
328
329
330
331
332
333
334
335
336
337
338 /**
339 * Initializing.
340 * For details, see 'TYPO3 Core API' document.
341 * This function does not start the processing of data, but merely initializes the object
342 *
343 * @param array Data to be modified or inserted in the database
344 * @param array Commands to copy, move, delete, localize, versionize records.
345 * @param object An alternative userobject you can set instead of the default, which is $GLOBALS['BE_USER']
346 * @return void
347 */
348 function start($data,$cmd,$altUserObject='') {
349
350 // Initializing BE_USER
351 $this->BE_USER = is_object($altUserObject) ? $altUserObject : $GLOBALS['BE_USER'];
352 $this->userid = $this->BE_USER->user['uid'];
353 $this->username = $this->BE_USER->user['username'];
354 $this->admin = $this->BE_USER->user['admin'];
355
356 if ($GLOBALS['BE_USER']->uc['recursiveDelete']) {
357 $this->deleteTree = 1;
358 }
359
360 // Initializing default permissions for pages
361 $defaultPermissions = $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPermissions'];
362 if (isset($defaultPermissions['user'])) {$this->defaultPermissions['user'] = $defaultPermissions['user'];}
363 if (isset($defaultPermissions['group'])) {$this->defaultPermissions['group'] = $defaultPermissions['group'];}
364 if (isset($defaultPermissions['everybody'])) {$this->defaultPermissions['everybody'] = $defaultPermissions['everybody'];}
365
366 // generates the excludelist, based on TCA/exclude-flag and non_exclude_fields for the user:
367 $this->exclude_array = $this->admin ? array() : $this->getExcludeListArray();
368
369 // Setting the data and cmd arrays
370 if (is_array($data)) {
371 reset($data);
372 $this->datamap = $data;
373 }
374 if (is_array($cmd)) {
375 reset($cmd);
376 $this->cmdmap = $cmd;
377 }
378 }
379
380 /**
381 * Function that can mirror input values in datamap-array to other uid numbers.
382 * 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]
383 *
384 * @param array This array has the syntax $mirror[table_name][uid] = [list of uids to copy data-value TO!]
385 * @return void
386 */
387 function setMirror($mirror) {
388 if (is_array($mirror)) {
389 reset($mirror);
390 while(list($table,$uid_array)=each($mirror)) {
391 if (isset($this->datamap[$table])) {
392 reset($uid_array);
393 while (list($id,$uidList) = each($uid_array)) {
394 if (isset($this->datamap[$table][$id])) {
395 $theIdsInArray = t3lib_div::trimExplode(',',$uidList,1);
396 while(list(,$copyToUid)=each($theIdsInArray)) {
397 $this->datamap[$table][$copyToUid] = $this->datamap[$table][$id];
398 }
399 }
400 }
401 }
402 }
403 }
404 }
405
406 /**
407 * Initializes default values coming from User TSconfig
408 *
409 * @param array User TSconfig array
410 * @return void
411 */
412 function setDefaultsFromUserTS($userTS) {
413 global $TCA;
414 if (is_array($userTS)) {
415 foreach($userTS as $k => $v) {
416 $k = substr($k,0,-1);
417 if ($k && is_array($v) && isset($TCA[$k])) {
418 if (is_array($this->defaultValues[$k])) {
419 $this->defaultValues[$k] = array_merge($this->defaultValues[$k],$v);
420 } else {
421 $this->defaultValues[$k] = $v;
422 }
423 }
424 }
425 }
426 }
427
428 /**
429 * Processing of uploaded files.
430 * 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.
431 *
432 * @param array $_FILES array
433 * @return void
434 */
435 function process_uploads($postFiles) {
436
437 if (is_array($postFiles)) {
438
439 // Editing frozen:
440 if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze']) {
441 $this->newlog('All editing in this workspace has been frozen!',1);
442 return FALSE;
443 }
444
445 reset($postFiles);
446 $subA = current($postFiles);
447 if (is_array($subA)) {
448 if (is_array($subA['name']) && is_array($subA['type']) && is_array($subA['tmp_name']) && is_array($subA['size'])) {
449 // Initialize the uploadedFilesArray:
450 $this->uploadedFileArray=array();
451
452 // For each entry:
453 foreach($subA as $key => $values) {
454 $this->process_uploads_traverseArray($this->uploadedFileArray,$values,$key);
455 }
456 } else {
457 $this->uploadedFileArray=$subA;
458 }
459 }
460 }
461 }
462
463 /**
464 * Traverse the upload array if needed to rearrange values.
465 *
466 * @param array $this->uploadedFileArray passed by reference
467 * @param array Input array ($_FILES parts)
468 * @param string The current $_FILES array key to set on the outermost level.
469 * @return void
470 * @access private
471 * @see process_uploads()
472 */
473 function process_uploads_traverseArray(&$outputArr,$inputArr,$keyToSet) {
474 if (is_array($inputArr)) {
475 foreach($inputArr as $key => $value) {
476 $this->process_uploads_traverseArray($outputArr[$key],$inputArr[$key],$keyToSet);
477 }
478 } else {
479 $outputArr[$keyToSet]=$inputArr;
480 }
481 }
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497 /*********************************************
498 *
499 * PROCESSING DATA
500 *
501 *********************************************/
502
503 /**
504 * Processing the data-array
505 * Call this function to process the data-array set by start()
506 *
507 * @return void
508 */
509 function process_datamap() {
510 global $TCA, $TYPO3_CONF_VARS;
511
512 // Editing frozen:
513 if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze']) {
514 $this->newlog('All editing in this workspace has been frozen!',1);
515 return FALSE;
516 }
517
518 // First prepare user defined objects (if any) for hooks which extend this function:
519 $hookObjectsArr = array();
520 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'])) {
521 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'] as $classRef) {
522 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef);
523 }
524 }
525
526 // 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.
527 $orderOfTables = Array();
528 if (isset($this->datamap['pages'])) { // Set pages first.
529 $orderOfTables[]='pages';
530 }
531 reset($this->datamap);
532 while (list($table,) = each($this->datamap)) {
533 if ($table!='pages') {
534 $orderOfTables[]=$table;
535 }
536 }
537
538 // Process the tables...
539 foreach($orderOfTables as $table) {
540 /* Check if
541 - table is set in $TCA,
542 - table is NOT readOnly
543 - the table is set with content in the data-array (if not, there's nothing to process...)
544 - permissions for tableaccess OK
545 */
546 $modifyAccessList = $this->checkModifyAccessList($table);
547 if (!$modifyAccessList) {
548 $id = 0;
549 $this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table));
550 }
551 if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->datamap[$table]) && $modifyAccessList) {
552 if ($this->reverseOrder) {
553 $this->datamap[$table] = array_reverse($this->datamap[$table], 1);
554 }
555
556 // For each record from the table, do:
557 // $id is the record uid, may be a string if new records...
558 // $incomingFieldArray is the array of fields
559 foreach($this->datamap[$table] as $id => $incomingFieldArray) {
560 if (is_array($incomingFieldArray)) {
561
562 // Hook: processDatamap_preProcessIncomingFieldArray
563 foreach($hookObjectsArr as $hookObj) {
564 if (method_exists($hookObj, 'processDatamap_preProcessFieldArray')) {
565 $hookObj->processDatamap_preProcessFieldArray($incomingFieldArray, $table, $id, $this);
566 }
567 }
568
569 // ******************************
570 // Checking access to the record
571 // ******************************
572 $createNewVersion = FALSE;
573 $recordAccess = FALSE;
574 $old_pid_value = '';
575 $resetRejected = FALSE;
576 $this->autoVersioningUpdate = FALSE;
577
578 if (!t3lib_div::testInt($id)) { // Is it a new record? (Then Id is a string)
579 $fieldArray = $this->newFieldArray($table); // Get a fieldArray with default values
580 if (isset($incomingFieldArray['pid'])) { // A pid must be set for new records.
581 // $value = the pid
582 $pid_value = $incomingFieldArray['pid'];
583
584 // Checking and finding numerical pid, it may be a string-reference to another value
585 $OK = 1;
586 if (strstr($pid_value,'NEW')) { // If a NEW... id
587 if (substr($pid_value,0,1)=='-') {$negFlag=-1;$pid_value=substr($pid_value,1);} else {$negFlag=1;}
588 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.
589 $old_pid_value = $pid_value;
590 $pid_value=intval($negFlag*$this->substNEWwithIDs[$pid_value]);
591 } else {$OK = 0;} // If not found in the substArray we must stop the process...
592 } 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.
593 if ($WSdestPage = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, 'pages', $pid_value, 'uid,t3ver_swapmode')) { // Looks for workspace version of page.
594 if ($WSdestPage['t3ver_swapmode']==0) { // if swapmode is zero, then change pid value.
595 $pid_value = $WSdestPage['uid'];
596 }
597 }
598 }
599 $pid_value = intval($pid_value);
600
601 // The $pid_value is now the numerical pid at this point
602 if ($OK) {
603 $sortRow = $TCA[$table]['ctrl']['sortby'];
604 if ($pid_value>=0) { // Points to a page on which to insert the element, possibly in the top of the page
605 if ($sortRow) { // If this table is sorted we better find the top sorting number
606 $fieldArray[$sortRow] = $this->getSortNumber($table,0,$pid_value);
607 }
608 $fieldArray['pid'] = $pid_value; // The numerical pid is inserted in the data array
609 } else { // points to another record before ifself
610 if ($sortRow) { // If this table is sorted we better find the top sorting number
611 $tempArray=$this->getSortNumber($table,0,$pid_value); // Because $pid_value is < 0, getSortNumber returns an array
612 $fieldArray['pid'] = $tempArray['pid'];
613 $fieldArray[$sortRow] = $tempArray['sortNumber'];
614 } else { // Here we fetch the PID of the record that we point to...
615 $tempdata = $this->recordInfo($table,abs($pid_value),'pid');
616 $fieldArray['pid']=$tempdata['pid'];
617 }
618 }
619 }
620 }
621 $theRealPid = $fieldArray['pid'];
622
623 // Now, check if we may insert records on this pid.
624 if ($theRealPid>=0) {
625 $recordAccess = $this->checkRecordInsertAccess($table,$theRealPid); // Checks if records can be inserted on this $pid.
626 if ($recordAccess) {
627 $this->addDefaultPermittedLanguageIfNotSet($table,$incomingFieldArray);
628 $recordAccess = $this->BE_USER->recordEditAccessInternals($table,$incomingFieldArray,TRUE);
629 if (!$recordAccess) {
630 $this->newlog("recordEditAccessInternals() check failed. [".$this->BE_USER->errorMsg."]",1);
631 } elseif(!$this->bypassWorkspaceRestrictions) {
632 // Workspace related processing:
633 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
634 if ($res<0) {
635 $recordAccess = FALSE;
636 $this->newlog('Stage for versioning root point and users access level did not allow for editing',1);
637 }
638 } else { // So, if no live records were allowed, we have to create a new version of this record:
639 if ($TCA[$table]['ctrl']['versioningWS']) {
640 $createNewVersion = TRUE;
641 } else {
642 $recordAccess = FALSE;
643 $this->newlog('Record could not be created in this workspace in this branch',1);
644 }
645 }
646 }
647 }
648 } else {
649 debug('Internal ERROR: pid should not be less than zero!');
650 }
651 $status = 'new'; // Yes new record, change $record_status to 'insert'
652 } else { // Nope... $id is a number
653 $fieldArray = array();
654 $recordAccess = $this->checkRecordUpdateAccess($table,$id);
655 if (!$recordAccess) {
656 $propArr = $this->getRecordProperties($table,$id);
657 $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']);
658 } else { // Next check of the record permissions (internals)
659 $recordAccess = $this->BE_USER->recordEditAccessInternals($table,$id);
660 if (!$recordAccess) {
661 $propArr = $this->getRecordProperties($table,$id);
662 $this->newlog("recordEditAccessInternals() check failed. [".$this->BE_USER->errorMsg."]",1);
663 } else { // Here we fetch the PID of the record that we point to...
664 $tempdata = $this->recordInfo($table,$id,'pid'.($TCA[$table]['ctrl']['versioningWS']?',t3ver_wsid,t3ver_stage':''));
665 $theRealPid = $tempdata['pid'];
666
667 // Prepare the reset of the rejected flag if set:
668 if ($TCA[$table]['ctrl']['versioningWS'] && $tempdata['t3ver_stage']<0) {
669 $resetRejected = TRUE;
670 }
671
672 // Checking access in case of offline workspace:
673 if (!$this->bypassWorkspaceRestrictions && $errorCode = $this->BE_USER->workspaceCannotEditRecord($table,$tempdata)) {
674 $recordAccess = FALSE; // Versioning is required and it must be offline version!
675
676 // 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.
677 if ($this->BE_USER->workspaceAllowAutoCreation($table,$id,$theRealPid)) {
678 $tce = t3lib_div::makeInstance('t3lib_TCEmain');
679 $tce->stripslashes_values = 0;
680
681 // Setting up command for creating a new version of the record:
682 $cmd = array();
683 $cmd[$table][$id]['version'] = array(
684 'action' => 'new',
685 'treeLevels' => -1, // Default is to create a version of the individual records...
686 'label' => 'Auto-created for WS #'.$this->BE_USER->workspace
687 );
688 $tce->start(array(),$cmd);
689 $tce->process_cmdmap();
690 $this->errorLog = array_merge($this->errorLog,$tce->errorLog);
691
692 if ($tce->copyMappingArray[$table][$id]) {
693 $this->uploadedFileArray[$table][$tce->copyMappingArray[$table][$id]] = $this->uploadedFileArray[$table][$id];
694 $id = $this->autoVersionIdMap[$table][$id] = $tce->copyMappingArray[$table][$id];
695 $recordAccess = TRUE;
696 $this->autoVersioningUpdate = TRUE;
697 } else $this->newlog("Could not be edited in offline workspace in the branch where found (failure state: '".$errorCode."'). Auto-creation of version failed!",1);
698 } else $this->newlog("Could not be edited in offline workspace in the branch where found (failure state: '".$errorCode."'). Auto-creation of version not allowed in workspace!",1);
699 }
700 }
701 }
702 $status = 'update'; // the default is 'update'
703 }
704
705 // If access was granted above, proceed to create or update record:
706 if ($recordAccess) {
707
708 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.
709 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
710 if ($status=='new' && $table=='pages' && is_array($TSConfig['permissions.'])) {
711 $fieldArray = $this->setTSconfigPermissions($fieldArray,$TSConfig['permissions.']);
712 }
713 if ($createNewVersion) {
714 $newVersion_placeholderFieldArray = $fieldArray;
715 }
716
717 // Processing of all fields in incomingFieldArray and setting them in $fieldArray
718 $fieldArray = $this->fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$theRealPid,$status,$tscPID);
719
720 // NOTICE! All manipulation beyond this point bypasses both "excludeFields" AND possible "MM" relations / file uploads to field!
721
722 // Forcing some values unto field array:
723 $fieldArray = $this->overrideFieldArray($table,$fieldArray); // NOTICE: This overriding is potentially dangerous; permissions per field is not checked!!!
724 if ($createNewVersion) {
725 $newVersion_placeholderFieldArray = $this->overrideFieldArray($table,$newVersion_placeholderFieldArray);
726 }
727
728 // Setting system fields
729 if ($status=='new') {
730 if ($TCA[$table]['ctrl']['crdate']) {
731 $fieldArray[$TCA[$table]['ctrl']['crdate']]=time();
732 if ($createNewVersion) $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['crdate']]=time();
733 }
734 if ($TCA[$table]['ctrl']['cruser_id']) {
735 $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid;
736 if ($createNewVersion) $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid;
737 }
738 } elseif ($this->checkSimilar) { // Removing fields which are equal to the current value:
739 $fieldArray = $this->compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray);
740 }
741 if ($TCA[$table]['ctrl']['tstamp'] && count($fieldArray)) {
742 $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
743 if ($createNewVersion) $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
744 }
745 if ($resetRejected) {
746 $fieldArray['t3ver_stage'] = 0;
747 }
748
749 // Hook: processDatamap_postProcessFieldArray
750 foreach($hookObjectsArr as $hookObj) {
751 if (method_exists($hookObj, 'processDatamap_postProcessFieldArray')) {
752 $hookObj->processDatamap_postProcessFieldArray($status, $table, $id, $fieldArray, $this);
753 }
754 }
755
756 // Performing insert/update. If fieldArray has been unset by some userfunction (see hook above), don't do anything
757 // Kasper: Unsetting the fieldArray is dangerous; MM relations might be saved already and files could have been uploaded that are now "lost"
758 if (is_array($fieldArray)) {
759 if ($status=='new') {
760 if ($createNewVersion) { // This creates a new version of the record with online placeholder and offline version
761 $versioningType = $table==='pages' ? $this->BE_USER->workspaceVersioningTypeGetClosest(t3lib_div::intInRange($TYPO3_CONF_VARS['BE']['newPagesVersioningType'],-1,1)) : -1;
762 if ($this->BE_USER->workspaceVersioningTypeAccess($versioningType)) {
763 $newVersion_placeholderFieldArray['t3ver_label'] = 'INITIAL PLACEHOLDER';
764 $newVersion_placeholderFieldArray['t3ver_state'] = 1; // Setting placeholder state value for temporary record
765 $newVersion_placeholderFieldArray['t3ver_wsid'] = $this->BE_USER->workspace; // Setting workspace - only so display of place holders can filter out those from other workspaces.
766 $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['label']] = '[PLACEHOLDER, WS#'.$this->BE_USER->workspace.']';
767 $this->insertDB($table,$id,$newVersion_placeholderFieldArray,FALSE); // Saving placeholder as 'original'
768
769 // For the actual new offline version, set versioning values to point to placeholder:
770 $fieldArray['pid'] = -1;
771 $fieldArray['t3ver_oid'] = $this->substNEWwithIDs[$id];
772 $fieldArray['t3ver_id'] = 1;
773 $fieldArray['t3ver_state'] = -1; // Setting placeholder state value for version (so it can know it is currently a new version...)
774 $fieldArray['t3ver_label'] = 'First draft version';
775 $fieldArray['t3ver_wsid'] = $this->BE_USER->workspace;
776 if ($table==='pages') { // Swap mode set to "branch" so we can build branches for pages.
777 $fieldArray['t3ver_swapmode'] = $versioningType;
778 }
779 $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!
780 if ($phShadowId) {
781 $this->placeholderShadowing($table,$phShadowId);
782 }
783 } else $this->newlog('Versioning type "'.$versioningType.'" was not allowed, so could not create new record.',1);
784 } else {
785 $this->insertDB($table,$id,$fieldArray,FALSE,$incomingFieldArray['uid']);
786 }
787 } else {
788 $this->updateDB($table,$id,$fieldArray);
789 $this->placeholderShadowing($table,$id);
790 }
791 }
792
793 // Hook: processDatamap_afterDatabaseOperations
794 foreach($hookObjectsArr as $hookObj) {
795 if (method_exists($hookObj, 'processDatamap_afterDatabaseOperations')) {
796 $hookObj->processDatamap_afterDatabaseOperations($status, $table, $id, $fieldArray, $this);
797 }
798 }
799 } // if ($recordAccess) {
800 } // if (is_array($incomingFieldArray)) {
801 }
802 }
803 }
804 $this->dbAnalysisStoreExec();
805 $this->removeRegisteredFiles();
806 }
807
808 /**
809 * Fix shadowing of data in case we are editing a offline version of a live "New" placeholder record:
810 *
811 * @param string Table name
812 * @param integer Record uid
813 * @return void
814 */
815 function placeholderShadowing($table,$id) {
816 global $TCA;
817
818 if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'*')) {
819 if ((int)$liveRec['t3ver_state']===1) {
820 $justStoredRecord = t3lib_BEfunc::getRecord($table,$id);
821 $newRecord = array();
822
823 $shadowColumns = t3lib_div::trimExplode(',', $TCA[$table]['ctrl']['shadowColumnsForNewPlaceholders'],1);
824 foreach($shadowColumns as $fieldName) {
825 if (strcmp($justStoredRecord[$fieldName],$liveRec[$fieldName])) {
826 $newRecord[$fieldName] = $justStoredRecord[$fieldName];
827 }
828 }
829
830 if (count($newRecord)) {
831 $this->newlog('Shadowing done on fields '.implode(',',array_keys($newRecord)).' in Placeholder record '.$table.':'.$liveRec['uid'].' (offline version UID='.$id.')');
832 $this->updateDB($table,$liveRec['uid'],$newRecord);
833 }
834 }
835 }
836 }
837
838 /**
839 * Filling in the field array
840 * $this->exclude_array is used to filter fields if needed.
841 *
842 * @param string Table name
843 * @param [type] $id: ...
844 * @param array Default values, Preset $fieldArray with 'pid' maybe (pid and uid will be not be overridden anyway)
845 * @param array $incomingFieldArray is which fields/values you want to set. There are processed and put into $fieldArray if OK
846 * @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.
847 * @param string $status = 'new' or 'update'
848 * @param [type] $tscPID: ...
849 * @return [type] ...
850 */
851 function fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$realPid,$status,$tscPID) {
852 global $TCA;
853
854 // Initialize:
855 t3lib_div::loadTCA($table);
856 $originalLanguageRecord = NULL;
857 $originalLanguage_diffStorage = NULL;
858 $diffStorageFlag = FALSE;
859
860 // Setting 'currentRecord' and 'checkValueRecord':
861 if (strstr($id,'NEW')) {
862 $currentRecord = $checkValueRecord = $fieldArray; // must have the 'current' array - not the values after processing below...
863
864 // IF $incomingFieldArray is an array, overlay it.
865 // 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...
866 if (is_array($incomingFieldArray) && is_array($checkValueRecord)) {
867 $checkValueRecord = t3lib_div::array_merge_recursive_overrule($checkValueRecord, $incomingFieldArray);
868 }
869 } else {
870 $currentRecord = $checkValueRecord = $this->recordInfo($table,$id,'*'); // We must use the current values as basis for this!
871
872 // Get original language record if available:
873 if (is_array($currentRecord)
874 && $TCA[$table]['ctrl']['transOrigDiffSourceField']
875 && $TCA[$table]['ctrl']['languageField']
876 && $currentRecord[$TCA[$table]['ctrl']['languageField']] > 0
877 && $TCA[$table]['ctrl']['transOrigPointerField']
878 && intval($currentRecord[$TCA[$table]['ctrl']['transOrigPointerField']]) > 0) {
879
880 $lookUpTable = $TCA[$table]['ctrl']['transOrigPointerTable'] ? $TCA[$table]['ctrl']['transOrigPointerTable'] : $table;
881 $originalLanguageRecord = $this->recordInfo($lookUpTable,$currentRecord[$TCA[$table]['ctrl']['transOrigPointerField']],'*');
882 t3lib_BEfunc::workspaceOL($lookUpTable,$originalLanguageRecord);
883 $originalLanguage_diffStorage = unserialize($currentRecord[$TCA[$table]['ctrl']['transOrigDiffSourceField']]);
884 }
885 }
886 $this->checkValue_currentRecord = $checkValueRecord;
887
888 /*
889 In the following all incoming value-fields are tested:
890 - Are the user allowed to change the field?
891 - Is the field uid/pid (which are already set)
892 - perms-fields for pages-table, then do special things...
893 - If the field is nothing of the above and the field is configured in TCA, the fieldvalues are evaluated by ->checkValue
894
895 If everything is OK, the field is entered into $fieldArray[]
896 */
897 foreach($incomingFieldArray as $field => $fieldValue) {
898 if (!in_array($table.'-'.$field, $this->exclude_array) && !$this->data_disableFields[$table][$id][$field]) { // The field must be editable.
899
900 // Checking if a value for language can be changed:
901 $languageDeny = $TCA[$table]['ctrl']['languageField'] && !strcmp($TCA[$table]['ctrl']['languageField'], $field) && !$this->BE_USER->checkLanguageAccess($fieldValue);
902
903 if (!$languageDeny) {
904 // Stripping slashes - will probably be removed the day $this->stripslashes_values is removed as an option...
905 if ($this->stripslashes_values) {
906 if (is_array($fieldValue)) {
907 t3lib_div::stripSlashesOnArray($fieldValue);
908 } else $fieldValue = stripslashes($fieldValue);
909 }
910
911 switch ($field) {
912 case 'uid':
913 case 'pid':
914 // Nothing happens, already set
915 break;
916 case 'perms_userid':
917 case 'perms_groupid':
918 case 'perms_user':
919 case 'perms_group':
920 case 'perms_everybody':
921 // Permissions can be edited by the owner or the administrator
922 if ($table=='pages' && ($this->admin || $status=='new' || $this->pageInfo($id,'perms_userid')==$this->userid) ) {
923 $value=intval($fieldValue);
924 switch($field) {
925 case 'perms_userid':
926 $fieldArray[$field]=$value;
927 break;
928 case 'perms_groupid':
929 $fieldArray[$field]=$value;
930 break;
931 default:
932 if ($value>=0 && $value<pow(2,5)) {
933 $fieldArray[$field]=$value;
934 }
935 break;
936 }
937 }
938 break;
939 case 't3ver_oid':
940 case 't3ver_id':
941 case 't3ver_wsid':
942 case 't3ver_state':
943 case 't3ver_swapmode':
944 case 't3ver_count':
945 case 't3ver_stage':
946 case 't3ver_tstamp':
947 // t3ver_label is not here because it CAN be edited as a regular field!
948 break;
949 default:
950 if (isset($TCA[$table]['columns'][$field])) {
951 // Evaluating the value.
952 $res = $this->checkValue($table,$field,$fieldValue,$id,$status,$realPid,$tscPID);
953 if (isset($res['value'])) {
954 $fieldArray[$field]=$res['value'];
955
956 // Add the value of the original record to the diff-storage content:
957 if ($TCA[$table]['ctrl']['transOrigDiffSourceField']) {
958 $originalLanguage_diffStorage[$field] = $originalLanguageRecord[$field];
959 $diffStorageFlag = TRUE;
960 }
961 }
962 } elseif ($TCA[$table]['ctrl']['origUid']===$field) { // Allow value for original UID to pass by...
963 $fieldArray[$field] = $fieldValue;
964 }
965 break;
966 }
967 } // Checking language.
968 } // Check exclude fields / disabled fields...
969 }
970
971 // Add diff-storage information:
972 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...
973 $fieldArray[$TCA[$table]['ctrl']['transOrigDiffSourceField']] = serialize($originalLanguage_diffStorage);
974 }
975
976 // Checking for RTE-transformations of fields:
977 $types_fieldConfig = t3lib_BEfunc::getTCAtypes($table,$currentRecord);
978 $theTypeString = t3lib_BEfunc::getTCAtypeValue($table,$currentRecord);
979 if (is_array($types_fieldConfig)) {
980 reset($types_fieldConfig);
981 while(list(,$vconf) = each($types_fieldConfig)) {
982 // Write file configuration:
983 $eFile = t3lib_parsehtml_proc::evalWriteFile($vconf['spec']['static_write'],array_merge($currentRecord,$fieldArray)); // inserted array_merge($currentRecord,$fieldArray) 170502
984
985 // RTE transformations:
986 if (!$this->dontProcessTransformations) {
987 if (isset($fieldArray[$vconf['field']])) {
988 // Look for transformation flag:
989 switch((string)$incomingFieldArray['_TRANSFORM_'.$vconf['field']]) {
990 case 'RTE':
991 $RTEsetup = $this->BE_USER->getTSConfig('RTE',t3lib_BEfunc::getPagesTSconfig($tscPID));
992 $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'],$table,$vconf['field'],$theTypeString);
993
994 // Set alternative relative path for RTE images/links:
995 $RTErelPath = is_array($eFile) ? dirname($eFile['relEditFile']) : '';
996
997 // Get RTE object, draw form and set flag:
998 $RTEobj = &t3lib_BEfunc::RTEgetObj();
999 if (is_object($RTEobj)) {
1000 $fieldArray[$vconf['field']] = $RTEobj->transformContent('db',$fieldArray[$vconf['field']],$table,$vconf['field'],$currentRecord,$vconf['spec'],$thisConfig,$RTErelPath,$currentRecord['pid']);
1001 } else {
1002 debug('NO RTE OBJECT FOUND!');
1003 }
1004 break;
1005 }
1006 }
1007 }
1008
1009 // Write file configuration:
1010 if (is_array($eFile)) {
1011 $mixedRec = array_merge($currentRecord,$fieldArray);
1012 $SW_fileContent = t3lib_div::getUrl($eFile['editFile']);
1013 $parseHTML = t3lib_div::makeInstance('t3lib_parsehtml_proc');
1014 $parseHTML->init('','');
1015
1016 $eFileMarker = $eFile['markerField']&&trim($mixedRec[$eFile['markerField']]) ? trim($mixedRec[$eFile['markerField']]) : '###TYPO3_STATICFILE_EDIT###';
1017 $insertContent = str_replace($eFileMarker,'',$mixedRec[$eFile['contentField']]); // must replace the marker if present in content!
1018
1019 $SW_fileNewContent = $parseHTML->substituteSubpart($SW_fileContent, $eFileMarker, chr(10).$insertContent.chr(10), 1, 1);
1020 t3lib_div::writeFile($eFile['editFile'],$SW_fileNewContent);
1021
1022 // Write status:
1023 if (!strstr($id,'NEW') && $eFile['statusField']) {
1024 $GLOBALS['TYPO3_DB']->exec_UPDATEquery(
1025 $table,
1026 'uid='.intval($id),
1027 array(
1028 $eFile['statusField'] => $eFile['relEditFile'].' updated '.date('d-m-Y H:i:s').', bytes '.strlen($mixedRec[$eFile['contentField']])
1029 )
1030 );
1031 }
1032 } elseif ($eFile && is_string($eFile)) {
1033 $this->log($table,$id,2,0,1,"Write-file error: '%s'",13,array($eFile),$realPid);
1034 }
1035 }
1036 }
1037 // Return fieldArray
1038 return $fieldArray;
1039 }
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052 /*********************************************
1053 *
1054 * Evaluation of input values
1055 *
1056 ********************************************/
1057
1058 /**
1059 * Evaluates a value according to $table/$field settings.
1060 * This function is for real database fields - NOT FlexForm "pseudo" fields.
1061 * 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() )
1062 *
1063 * @param string Table name
1064 * @param string Field name
1065 * @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.
1066 * @param string The record-uid, mainly - but not exclusively - used for logging
1067 * @param string 'update' or 'new' flag
1068 * @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.
1069 * @param integer $tscPID
1070 * @return array Returns the evaluated $value as key "value" in this array. Can be checked with isset($res['value']) ...
1071 */
1072 function checkValue($table,$field,$value,$id,$status,$realPid,$tscPID) {
1073 global $TCA, $PAGES_TYPES;
1074 t3lib_div::loadTCA($table);
1075
1076 $res = Array(); // result array
1077 $recFID = $table.':'.$id.':'.$field;
1078
1079 // Processing special case of field pages.doktype
1080 if ($table=='pages' && $field=='doktype') {
1081 // If the user may not use this specific doktype, we issue a warning
1082 if (! ($this->admin || t3lib_div::inList($this->BE_USER->groupData['pagetypes_select'],$value))) {
1083 $propArr = $this->getRecordProperties($table,$id);
1084 $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']);
1085 return $res;
1086 };
1087 if ($status=='update') {
1088 // This checks 1) if we should check for disallowed tables and 2) if there are records from disallowed tables on the current page
1089 $onlyAllowedTables = isset($PAGES_TYPES[$value]['onlyAllowedTables']) ? $PAGES_TYPES[$value]['onlyAllowedTables'] : $PAGES_TYPES['default']['onlyAllowedTables'];
1090 if ($onlyAllowedTables) {
1091 $theWrongTables = $this->doesPageHaveUnallowedTables($id,$value);
1092 if ($theWrongTables) {
1093 $propArr = $this->getRecordProperties($table,$id);
1094 $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']);
1095 return $res;
1096 }
1097 }
1098 }
1099 }
1100
1101 // Get current value:
1102 $curValueRec = $this->recordInfo($table,$id,$field);
1103 $curValue = $curValueRec[$field];
1104
1105 // Getting config for the field
1106 $tcaFieldConf = $TCA[$table]['columns'][$field]['config'];
1107
1108 // Preform processing:
1109 $res = $this->checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$this->uploadedFileArray[$table][$id][$field],$tscPID);
1110
1111 return $res;
1112 }
1113
1114 /**
1115 * Branches out evaluation of a field value based on its type as configured in TCA
1116 * Can be called for FlexForm pseudo fields as well, BUT must not have $field set if so.
1117 *
1118 * @param array The result array. The processed value (if any!) is set in the "value" key.
1119 * @param string The value to set.
1120 * @param array Field configuration from TCA
1121 * @param string Table name
1122 * @param integer Return UID
1123 * @param [type] $curValue: ...
1124 * @param [type] $status: ...
1125 * @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.
1126 * @param [type] $recFID: ...
1127 * @param string Field name. Must NOT be set if the call is for a flexform field (since flexforms are not allowed within flexforms).
1128 * @param [type] $uploadedFiles: ...
1129 * @param [type] $tscPID: ...
1130 * @return array Returns the evaluated $value as key "value" in this array.
1131 */
1132 function checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$uploadedFiles,$tscPID) {
1133
1134 $PP = array($table,$id,$curValue,$status,$realPid,$recFID,$tscPID);
1135
1136 switch ($tcaFieldConf['type']) {
1137 case 'text':
1138 case 'passthrough':
1139 case 'user':
1140 $res['value'] = $value;
1141 break;
1142 case 'input':
1143 $res = $this->checkValue_input($res,$value,$tcaFieldConf,$PP,$field);
1144 break;
1145 case 'check':
1146 $res = $this->checkValue_check($res,$value,$tcaFieldConf,$PP);
1147 break;
1148 case 'radio':
1149 $res = $this->checkValue_radio($res,$value,$tcaFieldConf,$PP);
1150 break;
1151 case 'group':
1152 case 'select':
1153 $res = $this->checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field);
1154 break;
1155 case 'flex':
1156 if ($field) { // FlexForms are only allowed for real fields.
1157 $res = $this->checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field);
1158 }
1159 break;
1160 default:
1161 #debug(array($tcaFieldConf,$res,$value),'NON existing field type:');
1162 break;
1163 }
1164
1165 return $res;
1166 }
1167
1168 /**
1169 * Evaluate "input" type values.
1170 *
1171 * @param array The result array. The processed value (if any!) is set in the "value" key.
1172 * @param string The value to set.
1173 * @param array Field configuration from TCA
1174 * @param array Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1175 * @param string Field name
1176 * @return array Modified $res array
1177 */
1178 function checkValue_input($res,$value,$tcaFieldConf,$PP,$field='') {
1179 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1180
1181 // Secures the string-length to be less than max. Will probably make problems with multi-byte strings!
1182 if (intval($tcaFieldConf['max'])>0) {$value = substr($value,0,intval($tcaFieldConf['max']));}
1183
1184 // Checking range of value:
1185 if ($tcaFieldConf['range'] && $value!=$tcaFieldConf['checkbox']) { // If value is not set to the allowed checkbox-value then it is checked against the ranges
1186 if (isset($tcaFieldConf['range']['upper'])&&$value>$tcaFieldConf['range']['upper']) {$value=$tcaFieldConf['range']['upper'];}
1187 if (isset($tcaFieldConf['range']['lower'])&&$value<$tcaFieldConf['range']['lower']) {$value=$tcaFieldConf['range']['lower'];}
1188 }
1189
1190 // Process evaluation settings:
1191 $evalCodesArray = t3lib_div::trimExplode(',',$tcaFieldConf['eval'],1);
1192 $res = $this->checkValue_input_Eval($value,$evalCodesArray,$tcaFieldConf['is_in']);
1193
1194 // Process UNIQUE settings:
1195 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...
1196 if ($res['value'] && in_array('uniqueInPid',$evalCodesArray)) {
1197 $res['value'] = $this->getUnique($table,$field,$res['value'],$id,$realPid);
1198 }
1199 if ($res['value'] && in_array('unique',$evalCodesArray)) {
1200 $res['value'] = $this->getUnique($table,$field,$res['value'],$id);
1201 }
1202 }
1203
1204 return $res;
1205 }
1206
1207 /**
1208 * Evaluates 'check' type values.
1209 *
1210 * @param array The result array. The processed value (if any!) is set in the 'value' key.
1211 * @param string The value to set.
1212 * @param array Field configuration from TCA
1213 * @param array Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1214 * @return array Modified $res array
1215 */
1216 function checkValue_check($res,$value,$tcaFieldConf,$PP) {
1217 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1218
1219 $itemC = count($tcaFieldConf['items']);
1220 if (!$itemC) {$itemC=1;}
1221 $maxV = pow(2,$itemC);
1222
1223 if ($value<0) {$value=0;}
1224 if ($value>$maxV) {$value=$maxV;}
1225 $res['value'] = $value;
1226
1227 return $res;
1228 }
1229
1230 /**
1231 * Evaluates 'radio' type values.
1232 *
1233 * @param array The result array. The processed value (if any!) is set in the 'value' key.
1234 * @param string The value to set.
1235 * @param array Field configuration from TCA
1236 * @param array Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1237 * @return array Modified $res array
1238 */
1239 function checkValue_radio($res,$value,$tcaFieldConf,$PP) {
1240 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1241
1242 if (is_array($tcaFieldConf['items'])) {
1243 foreach($tcaFieldConf['items'] as $set) {
1244 if (!strcmp($set[1],$value)) {
1245 $res['value'] = $value;
1246 break;
1247 }
1248 }
1249 }
1250
1251 return $res;
1252 }
1253
1254 /**
1255 * Evaluates 'group' or 'select' type values.
1256 *
1257 * @param array The result array. The processed value (if any!) is set in the 'value' key.
1258 * @param string The value to set.
1259 * @param array Field configuration from TCA
1260 * @param array Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1261 * @param [type] $uploadedFiles: ...
1262 * @param string Field name
1263 * @return array Modified $res array
1264 */
1265 function checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field) {
1266
1267 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1268
1269 // Detecting if value send is an array and if so, implode it around a comma:
1270 if (is_array($value)) {
1271 $value = implode(',',$value);
1272 }
1273
1274 // This converts all occurencies of '&#123;' to the byte 123 in the string - this is needed in very rare cases where filenames with special characters (like ???, umlaud etc) gets sent to the server as HTML entities instead of bytes. The error is done only by MSIE, not Mozilla and Opera.
1275 // Anyways, this should NOT disturb anything else:
1276 $value = $this->convNumEntityToByteValue($value);
1277
1278 // When values are send as group or select they come as comma-separated values which are exploded by this function:
1279 $valueArray = $this->checkValue_group_select_explodeSelectGroupValue($value);
1280
1281 // If not multiple is set, then remove duplicates:
1282 if (!$tcaFieldConf['multiple']) {
1283 $valueArray = array_unique($valueArray);
1284 }
1285
1286 // This could be a good spot for parsing the array through a validation-function which checks if the values are allright (except that database references are not in their final form - but that is the point, isn't it?)
1287 // NOTE!!! Must check max-items of files before the later check because that check would just leave out filenames if there are too many!!
1288
1289 // Checking for select / authMode, removing elements from $valueArray if any of them is not allowed!
1290 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['authMode']) {
1291 $preCount = count($valueArray);
1292 foreach($valueArray as $kk => $vv) {
1293 if (!$this->BE_USER->checkAuthMode($table,$field,$vv,$tcaFieldConf['authMode'])) {
1294 unset($valueArray[$kk]);
1295 }
1296 }
1297
1298 // 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.
1299 if ($preCount && !count($valueArray)) {
1300 return array();
1301 }
1302 }
1303
1304 // For group types:
1305 if ($tcaFieldConf['type']=='group') {
1306 switch($tcaFieldConf['internal_type']) {
1307 case 'file':
1308 $valueArray = $this->checkValue_group_select_file(
1309 $valueArray,
1310 $tcaFieldConf,
1311 $curValue,
1312 $uploadedFiles,
1313 $status,
1314 $table,
1315 $id,
1316 $recFID
1317 );
1318 break;
1319 case 'db':
1320 $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'group');
1321 break;
1322 }
1323 }
1324 // For select types which has a foreign table attached:
1325 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['foreign_table']) {
1326 $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'select');
1327 }
1328
1329 // 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...
1330
1331 // Checking the number of items, that it is correct.
1332 // If files, there MUST NOT be too many files in the list at this point, so check that prior to this code.
1333 $valueArrayC = count($valueArray);
1334 $minI = isset($tcaFieldConf['minitems']) ? intval($tcaFieldConf['minitems']):0;
1335
1336 // 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.
1337 $maxI = isset($tcaFieldConf['maxitems']) ? intval($tcaFieldConf['maxitems']):1;
1338 if ($valueArrayC > $maxI) {$valueArrayC=$maxI;} // Checking for not too many elements
1339
1340 // Dumping array to list
1341 $newVal=array();
1342 foreach($valueArray as $nextVal) {
1343 if ($valueArrayC==0) {break;}
1344 $valueArrayC--;
1345 $newVal[]=$nextVal;
1346 }
1347 $res['value'] = implode(',',$newVal);
1348
1349 return $res;
1350 }
1351
1352 /**
1353 * Handling files for group/select function
1354 *
1355 * @param [type] $valueArray: ...
1356 * @param [type] $tcaFieldConf: ...
1357 * @param [type] $curValue: ...
1358 * @param [type] $uploadedFileArray: ...
1359 * @param [type] $status: ...
1360 * @param [type] $table: ...
1361 * @param [type] $id: ...
1362 * @param [type] $recFID: ...
1363 * @return array Modified value array
1364 * @see checkValue_group_select()
1365 */
1366 function checkValue_group_select_file($valueArray,$tcaFieldConf,$curValue,$uploadedFileArray,$status,$table,$id,$recFID) {
1367
1368 // If any files are uploaded:
1369 if (is_array($uploadedFileArray) &&
1370 $uploadedFileArray['name'] &&
1371 strcmp($uploadedFileArray['tmp_name'],'none')) {
1372 $valueArray[]=$uploadedFileArray['tmp_name'];
1373 $this->alternativeFileName[$uploadedFileArray['tmp_name']] = $uploadedFileArray['name'];
1374 }
1375
1376 // Creating fileFunc object.
1377 if (!$this->fileFunc) {
1378 $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions');
1379 $this->include_filefunctions=1;
1380 }
1381 // Setting permitted extensions.
1382 $all_files = Array();
1383 $all_files['webspace']['allow'] = $tcaFieldConf['allowed'];
1384 $all_files['webspace']['deny'] = $tcaFieldConf['disallowed'] ? $tcaFieldConf['disallowed'] : '*';
1385 $all_files['ftpspace'] = $all_files['webspace'];
1386 $this->fileFunc->init('', $all_files);
1387
1388 // If there is an upload folder defined:
1389 if ($tcaFieldConf['uploadfolder']) {
1390 // For logging..
1391 $propArr = $this->getRecordProperties($table,$id);
1392
1393 // Get destrination path:
1394 $dest = $this->destPathFromUploadFolder($tcaFieldConf['uploadfolder']);
1395
1396 // If we are updating:
1397 if ($status=='update') {
1398
1399 // Traverse the input values and convert to absolute filenames in case the update happens to an autoVersionized record.
1400 // 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!
1401 // 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_.
1402 // 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.
1403 // Illustration of the problem comes here:
1404 // 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.
1405 // The versions uid is 456 and the file is copied to "logo_01.gif". But the form data that we sents was based on uid 123 and hence contains the filename "logo.gif" from the original.
1406 // 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.
1407 // 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.
1408 if ($this->autoVersioningUpdate===TRUE) {
1409 foreach($valueArray as $key => $theFile) {
1410 if ($theFile===basename($theFile)) {
1411 $valueArray[$key] = PATH_site.$tcaFieldConf['uploadfolder'].'/'.$theFile;
1412 }
1413 }
1414 }
1415
1416 // Finding the CURRENT files listed, either from MM or from the current record.
1417 $theFileValues=array();
1418 if ($tcaFieldConf['MM']) { // If MM relations for the files also!
1419 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
1420 $dbAnalysis->start('','files',$tcaFieldConf['MM'],$id);
1421 reset($dbAnalysis->itemArray);
1422 while (list($somekey,$someval)=each($dbAnalysis->itemArray)) {
1423 if ($someval['id']) {
1424 $theFileValues[]=$someval['id'];
1425 }
1426 }
1427 } else {
1428 $theFileValues=t3lib_div::trimExplode(',',$curValue,1);
1429 }
1430
1431 // DELETE files: If existing files were found, traverse those and register files for deletion which has been removed:
1432 if (count($theFileValues)) {
1433 // 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!)
1434 foreach($valueArray as $key => $theFile) {
1435 if ($theFile && !strstr(t3lib_div::fixWindowsFilePath($theFile),'/')) {
1436 $theFileValues = t3lib_div::removeArrayEntryByValue($theFileValues,$theFile);
1437 }
1438 }
1439
1440 // This array contains the filenames in the uploadfolder that should be deleted:
1441 foreach($theFileValues as $key => $theFile) {
1442 $theFile = trim($theFile);
1443 if (@is_file($dest.'/'.$theFile)) {
1444 $this->removeFilesStore[]=$dest.'/'.$theFile;
1445 } elseif ($theFile) {
1446 $this->log($table,$id,5,0,1,"Could not delete file '%s' (does not exist). (%s)",10,array($dest.'/'.$theFile, $recFID),$propArr['event_pid']);
1447 }
1448 }
1449 }
1450 }
1451
1452 // Traverse the submitted values:
1453 foreach($valueArray as $key => $theFile) {
1454 // 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)
1455 if (strstr(t3lib_div::fixWindowsFilePath($theFile),'/')) {
1456 // Init:
1457 $maxSize = intval($tcaFieldConf['max_size']);
1458 $cmd='';
1459 $theDestFile=''; // Must be cleared. Else a faulty fileref may be inserted if the below code returns an error!! (Change: 22/12/2000)
1460
1461 // Check various things before copying file:
1462 if (@is_dir($dest) && (@is_file($theFile) || @is_uploaded_file($theFile))) { // File and destination must exist
1463
1464 // Finding size. For safe_mode we have to rely on the size in the upload array if the file is uploaded.
1465 if (is_uploaded_file($theFile) && $theFile==$uploadedFileArray['tmp_name']) {
1466 $fileSize = $uploadedFileArray['size'];
1467 } else {
1468 $fileSize = filesize($theFile);
1469 }
1470
1471 if (!$maxSize || $fileSize<=($maxSize*1024)) { // Check file size:
1472 // Prepare filename:
1473 $theEndFileName = isset($this->alternativeFileName[$theFile]) ? $this->alternativeFileName[$theFile] : $theFile;
1474 $fI = t3lib_div::split_fileref($theEndFileName);
1475
1476 // Check for allowed extension:
1477 if ($this->fileFunc->checkIfAllowed($fI['fileext'], $dest, $theEndFileName)) {
1478 $theDestFile = $this->fileFunc->getUniqueName($this->fileFunc->cleanFileName($fI['file']), $dest);
1479
1480 // If we have a unique destination filename, then write the file:
1481 if ($theDestFile) {
1482 t3lib_div::upload_copy_move($theFile,$theDestFile);
1483 $this->copiedFileMap[$theFile] = $theDestFile;
1484 clearstatcache();
1485 if (!@is_file($theDestFile)) $this->log($table,$id,5,0,1,"Copying file '%s' failed!: The destination path (%s) may be write protected. Please make it write enabled!. (%s)",16,array($theFile, dirname($theDestFile), $recFID),$propArr['event_pid']);
1486 } else $this->log($table,$id,5,0,1,"Copying file '%s' failed!: No destination file (%s) possible!. (%s)",11,array($theFile, $theDestFile, $recFID),$propArr['event_pid']);
1487 } else $this->log($table,$id,5,0,1,"Fileextension '%s' not allowed. (%s)",12,array($fI['fileext'], $recFID),$propArr['event_pid']);
1488 } else $this->log($table,$id,5,0,1,"Filesize (%s) of file '%s' exceeds limit (%s). (%s)",13,array(t3lib_div::formatSize($fileSize),$theFile,t3lib_div::formatSize($maxSize*1024),$recFID),$propArr['event_pid']);
1489 } else $this->log($table,$id,5,0,1,'The destination (%s) or the source file (%s) does not exist. (%s)',14,array($dest, $theFile, $recFID),$propArr['event_pid']);
1490
1491 // If the destination file was created, we will set the new filename in the value array, otherwise unset the entry in the value array!
1492 if (@is_file($theDestFile)) {
1493 $info = t3lib_div::split_fileref($theDestFile);
1494 $valueArray[$key]=$info['file']; // The value is set to the new filename
1495 } else {
1496 unset($valueArray[$key]); // The value is set to the new filename
1497 }
1498 }
1499 }
1500
1501 // 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!
1502 if ($tcaFieldConf['MM']) {
1503 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
1504 $dbAnalysis->tableArray['files']=array(); // dummy
1505
1506 reset($valueArray);
1507 while (list($key,$theFile)=each($valueArray)) {
1508 // explode files
1509 $dbAnalysis->itemArray[]['id']=$theFile;
1510 }
1511 if ($status=='update') {
1512 $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,0);
1513 } else {
1514 $this->dbAnalysisStore[] = array($dbAnalysis, $tcaFieldConf['MM'], $id, 0); // This will be traversed later to execute the actions
1515 }
1516 $cc=count($dbAnalysis->itemArray);
1517 $valueArray = array($cc);
1518 }
1519 }
1520
1521 return $valueArray;
1522 }
1523
1524 /**
1525 * Evaluates 'flex' type values.
1526 *
1527 * @param array The result array. The processed value (if any!) is set in the 'value' key.
1528 * @param string The value to set.
1529 * @param array Field configuration from TCA
1530 * @param array Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1531 * @param array Uploaded files for the field
1532 * @param array Current record array.
1533 * @param string Field name
1534 * @return array Modified $res array
1535 */
1536 function checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field) {
1537 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1538
1539 if (is_array($value)) {
1540
1541 // Get current value array:
1542 $dataStructArray = t3lib_BEfunc::getFlexFormDS($tcaFieldConf,$this->checkValue_currentRecord,$table);
1543 #debug($this->checkValue_currentRecord);
1544 $currentValueArray = t3lib_div::xml2array($curValue);
1545 if (!is_array($currentValueArray)) $currentValueArray = array();
1546 if (is_array($currentValueArray['meta']['currentLangId'])) unset($currentValueArray['meta']['currentLangId']); // Remove all old meta for languages...
1547
1548 // Evaluation of input values:
1549 $value['data'] = $this->checkValue_flex_procInData($value['data'],$currentValueArray['data'],$uploadedFiles['data'],$dataStructArray,$PP);
1550
1551 // Create XML and convert charsets from input value:
1552 $xmlValue = $this->checkValue_flexArray2Xml($value,TRUE);
1553
1554 // If we wanted to set UTF fixed:
1555 // $storeInCharset='utf-8';
1556 // $currentCharset=$GLOBALS['LANG']->charSet;
1557 // $xmlValue = $GLOBALS['LANG']->csConvObj->conv($xmlValue,$currentCharset,$storeInCharset,1);
1558 $storeInCharset=$GLOBALS['LANG']->charSet;
1559
1560 // Merge them together IF they are both arrays:
1561 // 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).
1562 if (is_array($currentValueArray)) {
1563 $arrValue = t3lib_div::xml2array($xmlValue);
1564 $arrValue = t3lib_div::array_merge_recursive_overrule($currentValueArray,$arrValue);
1565 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1566 }
1567
1568 // Temporary fix to delete flex form elements:
1569 $deleteCMDs = t3lib_div::_GP('_DELETE_FLEX_FORMdata');
1570 if (is_array($deleteCMDs[$table][$id][$field]['data'])) {
1571 $arrValue = t3lib_div::xml2array($xmlValue);
1572 $this->_DELETE_FLEX_FORMdata($arrValue['data'],$deleteCMDs[$table][$id][$field]['data']);
1573 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1574 }
1575
1576 // Temporary fix to move flex form elements up:
1577 $moveCMDs = t3lib_div::_GP('_MOVEUP_FLEX_FORMdata');
1578 if (is_array($moveCMDs[$table][$id][$field]['data'])) {
1579 $arrValue = t3lib_div::xml2array($xmlValue);
1580 $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'up');
1581 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1582 }
1583
1584 // Temporary fix to move flex form elements down:
1585 $moveCMDs = t3lib_div::_GP('_MOVEDOWN_FLEX_FORMdata');
1586 if (is_array($moveCMDs[$table][$id][$field]['data'])) {
1587 $arrValue = t3lib_div::xml2array($xmlValue);
1588 $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'down');
1589 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1590 }
1591
1592 // Create the value XML:
1593 $res['value']='';
1594 $res['value'].=$xmlValue;
1595 } else { // Passthrough...:
1596 $res['value']=$value;
1597 }
1598
1599 return $res;
1600 }
1601
1602 /**
1603 * Converts an array to FlexForm XML
1604 *
1605 * @param array Array with FlexForm data
1606 * @param boolean If set, the XML prologue is returned as well.
1607 * @return string Input array converted to XML
1608 */
1609 function checkValue_flexArray2Xml($array, $addPrologue=FALSE) {
1610 $flexObj = t3lib_div::makeInstance('t3lib_flexformtools');
1611 return $flexObj->flexArray2Xml($array, $addPrologue);
1612 }
1613
1614 /**
1615 * Deletes a flex form element
1616 *
1617 * @param array &$valueArrayToRemoveFrom: by reference
1618 * @param [type] $deleteCMDS: ... *
1619 * @return void
1620 */
1621 function _DELETE_FLEX_FORMdata(&$valueArrayToRemoveFrom,$deleteCMDS) {
1622 if (is_array($valueArrayToRemoveFrom) && is_array($deleteCMDS)) {
1623 foreach($deleteCMDS as $key => $value) {
1624 if (is_array($deleteCMDS[$key])) {
1625 $this->_DELETE_FLEX_FORMdata($valueArrayToRemoveFrom[$key],$deleteCMDS[$key]);
1626 } else {
1627 unset($valueArrayToRemoveFrom[$key]);
1628 }
1629 }
1630 }
1631 }
1632
1633 /**
1634 * Deletes a flex form element
1635 *
1636 * TODO: Like _DELETE_FLEX_FORMdata, this is only a temporary solution!
1637 *
1638 * @param array &$valueArrayToMoveIn: by reference
1639 * @param [type] $moveCMDS: ... *
1640 * @param string $direction: 'up' or 'down'
1641 * @return void
1642 */
1643 function _MOVE_FLEX_FORMdata(&$valueArrayToMoveIn, $moveCMDS, $direction) {
1644 if (is_array($valueArrayToMoveIn) && is_array($moveCMDS)) {
1645
1646 // Only execute the first move command:
1647 list ($key, $value) = each ($moveCMDS);
1648
1649 if (is_array($moveCMDS[$key])) {
1650 $this->_MOVE_FLEX_FORMdata($valueArrayToMoveIn[$key],$moveCMDS[$key], $direction);
1651 } else {
1652 switch ($direction) {
1653 case 'up':
1654 if ($key > 1) {
1655 $tmpArr = $valueArrayToMoveIn[$key];
1656 $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key-1];
1657 $valueArrayToMoveIn[$key-1] = $tmpArr;
1658 }
1659 break;
1660 case 'down':
1661 if ($key < count($valueArrayToMoveIn)) {
1662 $tmpArr = $valueArrayToMoveIn[$key];
1663 $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key+1];
1664 $valueArrayToMoveIn[$key+1] = $tmpArr;
1665 }
1666 break;
1667 }
1668 }
1669 }
1670 }
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689 /*********************************************
1690 *
1691 * Helper functions for evaluation functions.
1692 *
1693 ********************************************/
1694
1695 /**
1696 * Gets a unique value for $table/$id/$field based on $value
1697 *
1698 * @param string Table name
1699 * @param string Field name for which $value must be unique
1700 * @param string Value string.
1701 * @param integer UID to filter out in the lookup (the record itself...)
1702 * @param integer If set, the value will be unique for this PID
1703 * @return string Modified value (if not-unique). Will be the value appended with a number (until 100, then the function just breaks).
1704 */
1705 function getUnique($table,$field,$value,$id,$newPid=0) {
1706 global $TCA;
1707
1708 // Initialize:
1709 t3lib_div::loadTCA($table);
1710 $whereAdd='';
1711 $newValue='';
1712 if (intval($newPid)) { $whereAdd.=' AND pid='.intval($newPid); } else { $whereAdd.=' AND pid>=0'; } // "AND pid>=0" for versioning
1713 $whereAdd.=$this->deleteClause($table);
1714
1715 // If the field is configured in TCA, proceed:
1716 if (is_array($TCA[$table]) && is_array($TCA[$table]['columns'][$field])) {
1717
1718 // Look for a record which might already have the value:
1719 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $table).' AND uid!='.intval($id).$whereAdd);
1720 $counter = 0;
1721
1722 // For as long as records with the test-value existing, try again (with incremented numbers appended).
1723 while ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) {
1724 $newValue = $value.$counter;
1725 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($newValue, $table).' AND uid!='.intval($id).$whereAdd);
1726 $counter++;
1727 if ($counter>100) { break; } // At "100" it will give up and accept a duplicate - should probably be fixed to a small hash string instead...!
1728 }
1729 // If the new value is there:
1730 $value = strlen($newValue) ? $newValue : $value;
1731 }
1732 return $value;
1733 }
1734
1735 /**
1736 * Evaluation of 'input'-type values based on 'eval' list
1737 *
1738 * @param string Value to evaluate
1739 * @param array Array of evaluations to traverse.
1740 * @param string Is-in string
1741 * @return string Modified $value
1742 */
1743 function checkValue_input_Eval($value,$evalArray,$is_in) {
1744 $res = Array();
1745 $newValue = $value;
1746 $set = true;
1747
1748 foreach($evalArray as $func) {
1749 switch($func) {
1750 case 'int':
1751 case 'year':
1752 case 'date':
1753 case 'datetime':
1754 case 'time':
1755 case 'timesec':
1756 $value = intval($value);
1757 break;
1758 case 'double2':
1759 $theDec = 0;
1760 for ($a=strlen($value); $a>0; $a--) {
1761 if (substr($value,$a-1,1)=='.' || substr($value,$a-1,1)==',') {
1762 $theDec = substr($value,$a);
1763 $value = substr($value,0,$a-1);
1764 break;
1765 }
1766 }
1767 $theDec = ereg_replace('[^0-9]','',$theDec).'00';
1768 $value = intval(str_replace(' ','',$value)).'.'.substr($theDec,0,2);
1769 break;
1770 case 'md5':
1771 if (strlen($value)!=32){$set=false;}
1772 break;
1773 case 'trim':
1774 $value = trim($value);
1775 break;
1776 case 'upper':
1777 $value = strtoupper($value);
1778 # $value = strtr($value, 'áéúíâêûôîæøåäöü', 'ÁÉÚÍÂÊÛÔÎÆØÅÄÖÜ'); // WILL make trouble with other charsets than ISO-8859-1, so what do we do here? PHP-function which can handle this for other charsets? Currently the browsers JavaScript will fix it.
1779 break;
1780 case 'lower':
1781 $value = strtolower($value);
1782 # $value = strtr($value, 'ÁÉÚÍÂÊÛÔÎÆØÅÄÖÜ', 'áéúíâêûôîæøåäöü'); // WILL make trouble with other charsets than ISO-8859-1, so what do we do here? PHP-function which can handle this for other charsets? Currently the browsers JavaScript will fix it.
1783 break;
1784 case 'required':
1785 if (!$value) {$set=0;}
1786 break;
1787 case 'is_in':
1788 $c=strlen($value);
1789 if ($c) {
1790 $newVal = '';
1791 for ($a=0;$a<$c;$a++) {
1792 $char = substr($value,$a,1);
1793 if (strstr($is_in,$char)) {
1794 $newVal.=$char;
1795 }
1796 }
1797 $value = $newVal;
1798 }
1799 break;
1800 case 'nospace':
1801 $value = str_replace(' ','',$value);
1802 break;
1803 case 'alpha':
1804 $value = ereg_replace('[^a-zA-Z]','',$value);
1805 break;
1806 case 'num':
1807 $value = ereg_replace('[^0-9]','',$value);
1808 break;
1809 case 'alphanum':
1810 $value = ereg_replace('[^a-zA-Z0-9]','',$value);
1811 break;
1812 case 'alphanum_x':
1813 $value = ereg_replace('[^a-zA-Z0-9_-]','',$value);
1814 break;
1815 default:
1816 if (substr($func, 0, 3) == 'tx_') {
1817 $evalObj = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func].':&'.$func);
1818 if(is_object($evalObj) && method_exists($evalObj, 'evaluateFieldValue')) {
1819 $value = $evalObj->evaluateFieldValue($value, $is_in, $set);
1820 }
1821 }
1822 break;
1823 }
1824 }
1825 if ($set) {$res['value'] = $value;}
1826 return $res;
1827 }
1828
1829 /**
1830 * Returns data for group/db and select fields
1831 *
1832 * @param array Current value array
1833 * @param array TCA field config
1834 * @param integer Record id, used for look-up of MM relations (local_uid)
1835 * @param string Status string ('update' or 'new')
1836 * @param string The type, either 'select' or 'group'
1837 * @return array Modified value array
1838 */
1839 function checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,$type) {
1840 $tables = $type=='group'?$tcaFieldConf['allowed']:$tcaFieldConf['foreign_table'].','.$tcaFieldConf['neg_foreign_table'];
1841 $prep = $type=='group'?$tcaFieldConf['prepend_tname']:$tcaFieldConf['neg_foreign_table'];
1842
1843 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
1844 $dbAnalysis->registerNonTableValues=$tcaFieldConf['allowNonIdValues'] ? 1 : 0;
1845 $dbAnalysis->start(implode(',',$valueArray),$tables);
1846
1847 if ($tcaFieldConf['MM']) {
1848 if ($status=='update') {
1849 $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,$prep);
1850 } else {
1851 $this->dbAnalysisStore[] = array($dbAnalysis,$tcaFieldConf['MM'],$id,$prep); // This will be traversed later to execute the actions
1852 }
1853 $cc=count($dbAnalysis->itemArray);
1854 $valueArray = array($cc);
1855 } else {
1856 $valueArray = $dbAnalysis->getValueArray($prep);
1857 if ($type=='select' && $prep) {
1858 $valueArray = $dbAnalysis->convertPosNeg($valueArray,$tcaFieldConf['foreign_table'],$tcaFieldConf['neg_foreign_table']);
1859 }
1860 }
1861
1862 // Here we should se if 1) the records exist anymore, 2) which are new and check if the BE_USER has read-access to the new ones.
1863 return $valueArray;
1864 }
1865
1866 /**
1867 * Explodes the $value, which is a list of files/uids (group select)
1868 *
1869 * @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.
1870 * @return array The value array.
1871 */
1872 function checkValue_group_select_explodeSelectGroupValue($value) {
1873 $valueArray = t3lib_div::trimExplode(',',$value,1);
1874 reset($valueArray);
1875 while(list($key,$newVal)=each($valueArray)) {
1876 $temp=explode('|',$newVal,2);
1877 $valueArray[$key] = str_replace(',','',str_replace('|','',rawurldecode($temp[0])));
1878 }
1879 return $valueArray;
1880 }
1881
1882 /**
1883 * Starts the processing the input data for flexforms. This will traverse all sheets / languages and for each it will traverse the sub-structure.
1884 * See checkValue_flex_procInData_travDS() for more details.
1885 * 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
1886 *
1887 * @param array The 'data' part of the INPUT flexform data
1888 * @param array The 'data' part of the CURRENT flexform data
1889 * @param array The uploaded files for the 'data' part of the INPUT flexform data
1890 * @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.
1891 * @param array A set of parameters to pass through for the calling of the evaluation functions
1892 * @param string Optional call back function, see checkValue_flex_procInData_travDS() DEPRICATED, use class.t3lib_flexformtools.php instead for traversal!
1893 * @return array The modified 'data' part.
1894 * @see checkValue_flex_procInData_travDS()
1895 */
1896 function checkValue_flex_procInData($dataPart,$dataPart_current,$uploadedFiles,$dataStructArray,$pParams,$callBackFunc='') {
1897 #debug(array($dataPart,$dataPart_current,$dataStructArray));
1898 if (is_array($dataPart)) {
1899 foreach($dataPart as $sKey => $sheetDef) {
1900 list ($dataStruct,$actualSheet) = t3lib_div::resolveSheetDefInDS($dataStructArray,$sKey);
1901 #debug(array($dataStruct,$actualSheet,$sheetDef,$actualSheet,$sKey));
1902 if (is_array($dataStruct) && $actualSheet==$sKey && is_array($sheetDef)) {
1903 foreach($sheetDef as $lKey => $lData) {
1904 $this->checkValue_flex_procInData_travDS(
1905 $dataPart[$sKey][$lKey],
1906 $dataPart_current[$sKey][$lKey],
1907 $uploadedFiles[$sKey][$lKey],
1908 $dataStruct['ROOT']['el'],
1909 $pParams,
1910 $callBackFunc,
1911 $sKey.'/'.$lKey.'/'
1912 );
1913 }
1914 }
1915 }
1916 }
1917
1918 return $dataPart;
1919 }
1920
1921 /**
1922 * Processing of the sheet/language data array
1923 * 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.
1924 *
1925 * @param array New values (those being processed): Multidimensional Data array for sheet/language, passed by reference!
1926 * @param array Current values: Multidimensional Data array. May be empty array() if not needed (for callBackFunctions)
1927 * @param array Uploaded files array for sheet/language. May be empty array() if not needed (for callBackFunctions)
1928 * @param array Data structure which fits the data array
1929 * @param array A set of parameters to pass through for the calling of the evaluation functions / call back function
1930 * @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.
1931 * @param [type] $structurePath: ...
1932 * @return void
1933 * @see checkValue_flex_procInData()
1934 */
1935 function checkValue_flex_procInData_travDS(&$dataValues,$dataValues_current,$uploadedFiles,$DSelements,$pParams,$callBackFunc,$structurePath) {
1936 if (is_array($DSelements)) {
1937
1938 // For each DS element:
1939 foreach($DSelements as $key => $dsConf) {
1940
1941 // Array/Section:
1942 if ($DSelements[$key]['type']=='array') {
1943 if (is_array($dataValues[$key]['el'])) {
1944 if ($DSelements[$key]['section']) {
1945 foreach($dataValues[$key]['el'] as $ik => $el) {
1946 $theKey = key($el);
1947 if (is_array($dataValues[$key]['el'][$ik][$theKey]['el'])) {
1948 $this->checkValue_flex_procInData_travDS(
1949 $dataValues[$key]['el'][$ik][$theKey]['el'],
1950 $dataValues_current[$key]['el'][$ik][$theKey]['el'],
1951 $uploadedFiles[$key]['el'][$ik][$theKey]['el'],
1952 $DSelements[$key]['el'][$theKey]['el'],
1953 $pParams,
1954 $callBackFunc,
1955 $structurePath.$key.'/el/'.$ik.'/'.$theKey.'/el/'
1956 );
1957 }
1958 }
1959 } else {
1960 if (!isset($dataValues[$key]['el'])) $dataValues[$key]['el']=array();
1961 $this->checkValue_flex_procInData_travDS(
1962 $dataValues[$key]['el'],
1963 $dataValues_current[$key]['el'],
1964 $uploadedFiles[$key]['el'],
1965 $DSelements[$key]['el'],
1966 $pParams,
1967 $callBackFunc,
1968 $structurePath.$key.'/el/'
1969 );
1970 }
1971 }
1972 } else {
1973 if (is_array($dsConf['TCEforms']['config']) && is_array($dataValues[$key])) {
1974 foreach($dataValues[$key] as $vKey => $data) {
1975
1976 if ($callBackFunc) {
1977 if (is_object($this->callBackObj)) {
1978 $res = $this->callBackObj->$callBackFunc(
1979 $pParams,
1980 $dsConf['TCEforms']['config'],
1981 $dataValues[$key][$vKey],
1982 $dataValues_current[$key][$vKey],
1983 $uploadedFiles[$key][$vKey],
1984 $structurePath.$key.'/'.$vKey.'/'
1985 );
1986 } else {
1987 $res = $this->$callBackFunc(
1988 $pParams,
1989 $dsConf['TCEforms']['config'],
1990 $dataValues[$key][$vKey],
1991 $dataValues_current[$key][$vKey],
1992 $uploadedFiles[$key][$vKey]
1993 );
1994 }
1995 } else { // Default
1996 list($CVtable,$CVid,$CVcurValue,$CVstatus,$CVrealPid,$CVrecFID,$CVtscPID) = $pParams;
1997
1998 $res = $this->checkValue_SW(
1999 array(),
2000 $dataValues[$key][$vKey],
2001 $dsConf['TCEforms']['config'],
2002 $CVtable,
2003 $CVid,
2004 $dataValues_current[$key][$vKey],
2005 $CVstatus,
2006 $CVrealPid,
2007 $CVrecFID,
2008 '',
2009 $uploadedFiles[$key][$vKey],
2010 array(),
2011 $CVtscPID
2012 );
2013
2014 // Look for RTE transformation of field:
2015 if ($dataValues[$key]['_TRANSFORM_'.$vKey] == 'RTE' && !$this->dontProcessTransformations) {
2016
2017 // Unsetting trigger field - we absolutely don't want that into the data storage!
2018 unset($dataValues[$key]['_TRANSFORM_'.$vKey]);
2019
2020 if (isset($res['value'])) {
2021
2022 // Calculating/Retrieving some values here:
2023 list(,,$recFieldName) = explode(':', $CVrecFID);
2024 $theTypeString = t3lib_BEfunc::getTCAtypeValue($CVtable,$this->checkValue_currentRecord);
2025 $specConf = t3lib_BEfunc::getSpecConfParts('',$dsConf['TCEforms']['defaultExtras']);
2026
2027 // Find, thisConfig:
2028 $RTEsetup = $this->BE_USER->getTSConfig('RTE',t3lib_BEfunc::getPagesTSconfig($CVtscPID));
2029 $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'],$CVtable,$recFieldName,$theTypeString);
2030
2031 // Get RTE object, draw form and set flag:
2032 $RTEobj = &t3lib_BEfunc::RTEgetObj();
2033 if (is_object($RTEobj)) {
2034 $res['value'] = $RTEobj->transformContent('db',$res['value'],$CVtable,$recFieldName,$this->checkValue_currentRecord,$specConf,$thisConfig,'',$CVrealPid);
2035 } else {
2036 debug('NO RTE OBJECT FOUND!');
2037 }
2038 }
2039 }
2040 }
2041
2042 // Adding the value:
2043 if (isset($res['value'])) {
2044 $dataValues[$key][$vKey] = $res['value'];
2045 }
2046 }
2047 }
2048 }
2049 }
2050 }
2051 }
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069 /*********************************************
2070 *
2071 * PROCESSING COMMANDS
2072 *
2073 ********************************************/
2074
2075 /**
2076 * Processing the cmd-array
2077 * See "TYPO3 Core API" for a description of the options.
2078 *
2079 * @return void
2080 */
2081 function process_cmdmap() {
2082 global $TCA, $TYPO3_CONF_VARS;
2083
2084 // Editing frozen:
2085 if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze']) {
2086 $this->newlog('All editing in this workspace has been frozen!',1);
2087 return FALSE;
2088 }
2089
2090 // Hook initialization:
2091 $hookObjectsArr = array();
2092 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'])) {
2093 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'] as $classRef) {
2094 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef);
2095 }
2096 }
2097
2098 // Traverse command map:
2099 reset($this->cmdmap);
2100 while(list($table,) = each($this->cmdmap)) {
2101
2102 // Check if the table may be modified!
2103 $modifyAccessList = $this->checkModifyAccessList($table);
2104 if (!$modifyAccessList) {
2105 $id = 0;
2106 $this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table));
2107 } // FIXME: $id not set here (Comment added by Sebastian Kurfuerst)
2108
2109 // Check basic permissions and circumstances:
2110 if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->cmdmap[$table]) && $modifyAccessList) {
2111
2112 // Traverse the command map:
2113 foreach($this->cmdmap[$table] as $id => $incomingCmdArray) {
2114 if (is_array($incomingCmdArray)) { // have found a command.
2115
2116 // Get command and value (notice, only one command is observed at a time!):
2117 reset($incomingCmdArray);
2118 $command = key($incomingCmdArray);
2119 $value = current($incomingCmdArray);
2120
2121 foreach($hookObjectsArr as $hookObj) {
2122 if (method_exists($hookObj, 'processCmdmap_preProcess')) {
2123 $hookObj->processCmdmap_preProcess($command, $table, $id, $value, $this);
2124 }
2125 }
2126
2127 // Init copyMapping array:
2128 $this->copyMappingArray = Array(); // Must clear this array before call from here to those functions: Contains mapping information between new and old id numbers.
2129
2130 // Branch, based on command
2131 switch ($command) {
2132 case 'move':
2133 $this->moveRecord($table,$id,$value);
2134 break;
2135 case 'copy':
2136 if ($table === 'pages') {
2137 $this->copyPages($id,$value);
2138 } else {
2139 $this->copyRecord($table,$id,$value,1);
2140 }
2141 break;
2142 case 'localize':
2143 $this->localize($table,$id,$value);
2144 break;
2145 case 'version':
2146 switch ((string)$value['action']) {
2147 case 'new':
2148 $versionizeTree = t3lib_div::intInRange(!isset($value['treeLevels'])?-1:$value['treeLevels'],-1,100);
2149 if ($table == 'pages' && $versionizeTree>=0) {
2150 $this->versionizePages($id,$value['label'],$versionizeTree);
2151 } else {
2152 $this->versionizeRecord($table,$id,$value['label']);
2153 }
2154 break;
2155 case 'swap':
2156 $this->version_swap($table,$id,$value['swapWith'],$value['swapIntoWS']);
2157 break;
2158 case 'clearWSID':
2159 $this->version_clearWSID($table,$id);
2160 break;
2161 case 'setStage':
2162 $idList = t3lib_div::trimExplode(',',$id,1);
2163 foreach($idList as $id) {
2164 $this->version_setStage($table,$id,$value['stageId'],$value['comment']?$value['comment']:$this->generalComment);
2165 }
2166 break;
2167 }
2168 break;
2169 case 'delete':
2170 $this->deleteAction($table, $id);
2171 break;
2172 case 'undelete':
2173 $this->undeleteRecord($table, $id);
2174 break;
2175 }
2176
2177 foreach($hookObjectsArr as $hookObj) {
2178 if (method_exists($hookObj, 'processCmdmap_postProcess')) {
2179 $hookObj->processCmdmap_postProcess($command, $table, $id, $value, $this);
2180 }
2181 }
2182
2183 // Merging the copy-array info together for remapping purposes.
2184 $this->copyMappingArray_merged= t3lib_div::array_merge_recursive_overrule($this->copyMappingArray_merged,$this->copyMappingArray);
2185 }
2186 }
2187 }
2188 }
2189
2190 // Finally, before exit, check if there are ID references to remap. This might be the case if versioning or copying has taken place!
2191 $this->remapListedDBRecords();
2192 }
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204 /*********************************************
2205 *
2206 * Cmd: Copying
2207 *
2208 ********************************************/
2209
2210 /**
2211 * Copying a single record
2212 *
2213 * @param string Element table
2214 * @param integer Element UID
2215 * @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
2216 * @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
2217 * @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!
2218 * @param string Commalist of fields to exclude from the copy process (might get default values)
2219 * @return integer ID of new record, if any
2220 */
2221 function copyRecord($table,$uid,$destPid,$first=0,$overrideValues=array(),$excludeFields='') {
2222 global $TCA;
2223
2224 $uid = $origUid = intval($uid);
2225 if ($TCA[$table] && $uid) {
2226 t3lib_div::loadTCA($table);
2227 /*
2228 // 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)
2229 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid')) {
2230 $uid = $lookForLiveVersion['uid'];
2231 }
2232 // Get workspace version of the source record, if any: Then we will copy workspace version instead:
2233 if ($WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid')) {
2234 $uid = $WSversion['uid'];
2235 }
2236 // 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.
2237 */
2238 if ($this->doesRecordExist($table,$uid,'show')) { // This checks if the record can be selected which is all that a copy action requires.
2239 $data = Array();
2240
2241 $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));
2242
2243 #$row = $this->recordInfo($table,$uid,'*');
2244 $row = t3lib_BEfunc::getRecordWSOL($table,$uid); // So it copies (and localized) content from workspace...
2245 if (is_array($row)) {
2246
2247 // Initializing:
2248 $theNewID = uniqid('NEW');
2249 $enableField = isset($TCA[$table]['ctrl']['enablecolumns']) ? $TCA[$table]['ctrl']['enablecolumns']['disabled'] : '';
2250 $headerField = $TCA[$table]['ctrl']['label'];
2251
2252 // Getting default data:
2253 $defaultData = $this->newFieldArray($table);
2254
2255 // Getting "copy-after" fields if applicable:
2256 // 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.
2257 $copyAfterFields = $destPid<0 ? $this->fixCopyAfterDuplFields($table,$uid,abs($destPid),0) : array();
2258
2259 // Page TSconfig related:
2260 $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...
2261 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
2262 $tE = $this->getTableEntries($table,$TSConfig);
2263
2264 // Traverse ALL fields of the selected record:
2265 foreach($row as $field => $value) {
2266 if (!in_array($field,$nonFields)) {
2267
2268 // Get TCA configuration for the field:
2269 $conf = $TCA[$table]['columns'][$field]['config'];
2270
2271 // Preparation/Processing of the value:
2272 if ($field=='pid') { // "pid" is hardcoded of course:
2273 $value = $destPid;
2274 } elseif (isset($overrideValues[$field])) { // Override value...
2275 $value = $overrideValues[$field];
2276 } elseif (isset($copyAfterFields[$field])) { // Copy-after value if available:
2277 $value = $copyAfterFields[$field];
2278 } elseif ($TCA[$table]['ctrl']['setToDefaultOnCopy'] && t3lib_div::inList($TCA[$table]['ctrl']['setToDefaultOnCopy'],$field)) { // Revert to default for some fields:
2279 $value = $defaultData[$field];
2280 } else {
2281 // Hide at copy may override:
2282 if ($first && $field==$enableField && $TCA[$table]['ctrl']['hideAtCopy'] && !$this->neverHideAtCopy && !$tE['disableHideAtCopy']) {
2283 $value=1;
2284 }
2285 // Prepend label on copy:
2286 if ($first && $field==$headerField && $TCA[$table]['ctrl']['prependAtCopy'] && !$tE['disablePrependAtCopy']) {
2287 $value = $this->getCopyHeader($table,$this->resolvePid($table,$destPid),$field,$this->clearPrefixFromValue($table,$value),0);
2288 }
2289 // Processing based on the TCA config field type (files, references, flexforms...)
2290 $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf);
2291 }
2292
2293 // Add value to array.
2294 $data[$table][$theNewID][$field] = $value;
2295 }
2296 }
2297
2298 // Overriding values:
2299 if ($TCA[$table]['ctrl']['editlock']) {
2300 $data[$table][$theNewID][$TCA[$table]['ctrl']['editlock']] = 0;
2301 }
2302
2303 // Setting original UID:
2304 if ($TCA[$table]['ctrl']['origUid']) {
2305 $data[$table][$theNewID][$TCA[$table]['ctrl']['origUid']] = $uid;
2306 }
2307
2308 // Do the copy by simply submitting the array through TCEmain:
2309 $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain');
2310 $copyTCE->stripslashes_values = 0;
2311 $copyTCE->copyTree = $this->copyTree;
2312 $copyTCE->cachedTSconfig = $this->cachedTSconfig; // Copy forth the cached TSconfig
2313 $copyTCE->dontProcessTransformations=1; // Transformations should NOT be carried out during copy
2314
2315 $copyTCE->start($data,'',$this->BE_USER);
2316 $copyTCE->process_datamap();
2317
2318 // Getting the new UID:
2319 $theNewSQLID = $copyTCE->substNEWwithIDs[$theNewID];
2320 if ($theNewSQLID) {
2321 $this->copyMappingArray[$table][$origUid] = $theNewSQLID;
2322 }
2323
2324 // Copy back the cached TSconfig
2325 $this->cachedTSconfig = $copyTCE->cachedTSconfig;
2326 $this->errorLog = array_merge($this->errorLog,$copyTCE->errorLog);
2327 unset($copyTCE);
2328
2329 return $theNewSQLID;
2330 } else $this->log($table,$uid,3,0,1,'Attempt to copy record that did not exist!');
2331 } else $this->log($table,$uid,3,0,1,'Attempt to copy record without permission');
2332 }
2333 }
2334
2335 /**
2336 * Copying pages
2337 * Main function for copying pages.
2338 *
2339 * @param integer Page UID to copy
2340 * @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
2341 * @return void
2342 */
2343 function copyPages($uid,$destPid) {
2344
2345 // Initialize:
2346 $uid = intval($uid);
2347 $destPid = intval($destPid);
2348
2349 // Finding list of tables to copy.
2350 $copyTablesArray = $this->admin ? $this->compileAdminTables() : explode(',',$this->BE_USER->groupData['tables_modify']); // These are the tables, the user may modify
2351 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
2352 foreach($copyTablesArray as $k => $table) {
2353 if (!$table || !t3lib_div::inList($this->copyWhichTables.',pages',$table)) { // pages are always going...
2354 unset($copyTablesArray[$k]);
2355 }
2356 }
2357 }
2358 $copyTablesArray = array_unique($copyTablesArray);
2359
2360 // Begin to copy pages if we're allowed to:
2361 if ($this->admin || in_array('pages',$copyTablesArray)) {
2362
2363 // Copy this page we're on. And set first-flag (this will trigger that the record is hidden if that is configured)!
2364 $theNewRootID = $this->copySpecificPage($uid,$destPid,$copyTablesArray,1);
2365
2366 // If we're going to copy recursively...:
2367 if ($theNewRootID && $this->copyTree) {
2368
2369 // Get ALL subpages to copy (read-permissions are respected!):
2370 $CPtable = $this->int_pageTreeInfo(Array(), $uid, intval($this->copyTree), $theNewRootID);
2371
2372 // Now copying the subpages:
2373 foreach($CPtable as $thePageUid => $thePagePid) {
2374 $newPid = $this->copyMappingArray['pages'][$thePagePid];
2375 if (isset($newPid)) {
2376 $this->copySpecificPage($thePageUid,$newPid,$copyTablesArray);
2377 } else {
2378 $this->log('pages',$uid,5,0,1,'Something went wrong during copying branch');
2379 break;
2380 }
2381 }
2382 } // else the page was not copied. Too bad...
2383 } else {
2384 $this->log('pages',$uid,5,0,1,'Attempt to copy page without permission to this table');
2385 }
2386 }
2387
2388 /**
2389 * Copying a single page ($uid) to $destPid and all tables in the array copyTablesArray.
2390 *
2391 * @param integer Page uid
2392 * @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
2393 * @param array Table on pages to copy along with the page.
2394 * @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
2395 * @return integer The id of the new page, if applicable.
2396 */
2397 function copySpecificPage($uid,$destPid,$copyTablesArray,$first=0) {
2398 global $TCA;
2399
2400 // Copy the page itself:
2401 $theNewRootID = $this->copyRecord('pages',$uid,$destPid,$first);
2402
2403 // If a new page was created upon the copy operation we will proceed with all the tables ON that page:
2404 if ($theNewRootID) {
2405 foreach($copyTablesArray as $table) {
2406 if ($table && is_array($TCA[$table]) && $table!='pages') { // all records under the page is copied.
2407 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table), '', ($TCA[$table]['ctrl']['sortby'] ? $TCA[$table]['ctrl']['sortby'].' DESC' : ''));
2408 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
2409 $this->copyRecord($table,$row['uid'], $theNewRootID); // Copying each of the underlying records...
2410 }
2411 }
2412 }
2413 return $theNewRootID;
2414 }
2415 }
2416
2417 /**
2418 * Copying records, but makes a "raw" copy of a record.
2419 * Basically the only thing observed is field processing like the copying of files and correction of ids. All other fields are 1-1 copied.
2420 * 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.
2421 * 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!?
2422 * This function is used to create new versions of a record.
2423 * 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.
2424 *
2425 * @param string Element table
2426 * @param integer Element UID
2427 * @param integer Element PID (real PID, not checked)
2428 * @param array Override array - must NOT contain any fields not in the table!
2429 * @return integer Returns the new ID of the record (if applicable)
2430 */
2431 function copyRecord_raw($table,$uid,$pid,$overrideArray=array()) {
2432 global $TCA;
2433
2434 $uid = intval($uid);
2435 if ($TCA[$table] && $uid) {
2436 t3lib_div::loadTCA($table);
2437 if ($this->doesRecordExist($table,$uid,'show')) {
2438
2439 // Set up fields which should not be processed. They are still written - just passed through no-questions-asked!
2440 $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');
2441
2442 // Select main record:
2443 $row = $this->recordInfo($table,$uid,'*');
2444 if (is_array($row)) {
2445
2446 // Merge in override array.
2447 $row = array_merge($row,$overrideArray);
2448
2449 // Traverse ALL fields of the selected record:
2450 foreach($row as $field => $value) {
2451 if (!in_array($field,$nonFields)) {
2452
2453 // Get TCA configuration for the field:
2454 $conf = $TCA[$table]['columns'][$field]['config'];
2455 if (is_array($conf)) {
2456 // Processing based on the TCA config field type (files, references, flexforms...)
2457 $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf);
2458 }
2459
2460 // Add value to array.
2461 $row[$field] = $value;
2462 }
2463 }
2464
2465 // Force versioning related fields:
2466 $row['pid'] = $pid;
2467
2468 // Setting original UID:
2469 if ($TCA[$table]['ctrl']['origUid']) {
2470 $row[$TCA[$table]['ctrl']['origUid']] = $uid;
2471 }
2472
2473 // Do the copy by internal function
2474 $theNewSQLID = $this->insertNewCopyVersion($table,$row,$pid);
2475 if ($theNewSQLID) {
2476 $this->dbAnalysisStoreExec();
2477 $this->dbAnalysisStore = array();
2478 return $this->copyMappingArray[$table][$uid] = $theNewSQLID;
2479 }
2480 } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record that did not exist!');
2481 } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record without copy permission');
2482 }
2483 }
2484
2485 /**
2486 * Copies all records from tables in $copyTablesArray from page with $old_pid to page with $new_pid
2487 * Uses raw-copy for the operation (meant for versioning!)
2488 *
2489 * @param integer Current page id.
2490 * @param integer New page id
2491 * @param array Array of tables from which to copy
2492 * @return void
2493 * @see versionizePages()
2494 */
2495 function rawCopyPageContent($old_pid,$new_pid,$copyTablesArray) {
2496 global $TCA;
2497
2498 if ($new_pid) {
2499 foreach($copyTablesArray as $table) {
2500 if ($table && is_array($TCA[$table]) && $table!='pages') { // all records under the page is copied.
2501 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($old_pid).$this->deleteClause($table));
2502 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
2503 $this->copyRecord_raw($table,$row['uid'],$new_pid); // Copying each of the underlying records (method RAW)
2504 }
2505 }
2506 }
2507 }
2508 }
2509
2510 /**
2511 * Inserts a record in the database, passing TCA configuration values through checkValue() but otherwise does NOTHING and checks nothing regarding permissions.
2512 * 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...
2513 *
2514 * @param string Table name
2515 * @param array Field array to insert as a record
2516 * @param integer The value of PID field. -1 is indication that we are creating a new version!
2517 * @return integer Returns the new ID of the record (if applicable)
2518 */
2519 function insertNewCopyVersion($table,$fieldArray,$realPid) {
2520 global $TCA;
2521
2522 $id = uniqid('NEW');
2523
2524 // $fieldArray is set as current record.
2525 // 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...
2526 $this->checkValue_currentRecord = $fieldArray;
2527
2528 // Traverse record and input-process each value:
2529 foreach($fieldArray as $field => $fieldValue) {
2530 if (isset($TCA[$table]['columns'][$field])) {
2531 // Evaluating the value.
2532 $res = $this->checkValue($table,$field,$fieldValue,$id,'new',$realPid,0);
2533 if (isset($res['value'])) {
2534 $fieldArray[$field] = $res['value'];
2535 }
2536 }
2537 }
2538
2539 // System fields being set:
2540 if ($TCA[$table]['ctrl']['crdate']) {
2541 $fieldArray[$TCA[$table]['ctrl']['crdate']]=time();
2542 }
2543 if ($TCA[$table]['ctrl']['cruser_id']) {
2544 $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid;
2545 }
2546 if ($TCA[$table]['ctrl']['tstamp']) {
2547 $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
2548 }
2549
2550 // Finally, insert record:
2551 $this->insertDB($table,$id,$fieldArray, TRUE);
2552
2553 // Return new id:
2554 return $this->substNEWwithIDs[$id];
2555 }
2556
2557 /**
2558 * Processing/Preparing content for copyRecord() function
2559 *
2560 * @param string Table name
2561 * @param integer Record uid
2562 * @param string Field name being processed
2563 * @param string Input value to be processed.
2564 * @param array Record array
2565 * @param array TCA field configuration
2566 * @return mixed Processed value. Normally a string/integer, but can be an array for flexforms!
2567 * @access private
2568 * @see copyRecord()
2569 */
2570 function copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf) {
2571 global $TCA;
2572
2573 // 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)
2574 $value = $this->copyRecord_procFilesRefs($conf, $uid, $value);
2575
2576
2577 // Register if there are references to take care of (no change to value):
2578 if ($this->isReferenceField($conf)) {
2579 $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table'];
2580 $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
2581 if ($conf['MM']) {
2582 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
2583 $dbAnalysis->start('',$allowedTables,$conf['MM'],$uid);
2584 $value = implode(',',$dbAnalysis->getValueArray($prependName));
2585 }
2586 if ($value) { // Setting the value in this array will notify the remapListedDBRecords() function that this field MAY need references to be corrected
2587 $this->registerDBList[$table][$uid][$field] = $value;
2588 }
2589 }
2590
2591 // 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())
2592 if ($conf['type']=='flex') {
2593
2594 // Get current value array:
2595 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $row, $table);
2596 $currentValueArray = t3lib_div::xml2array($value);
2597
2598 // Traversing the XML structure, processing files:
2599 if (is_array($currentValueArray)) {
2600 $currentValueArray['data'] = $this->checkValue_flex_procInData(
2601 $currentValueArray['data'],
2602 array(), // Not used.
2603 array(), // Not used.
2604 $dataStructArray,
2605 array($table,$uid,$field), // Parameters.
2606 'copyRecord_flexFormCallBack'
2607 );
2608 $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.
2609 }
2610 }
2611
2612 return $value;
2613 }
2614
2615 /**
2616 * Callback function for traversing the FlexForm structure in relation to creating copied files of file relations inside of flex form structures.
2617 *
2618 * @param array Array of parameters in num-indexes: table, uid, field
2619 * @param array TCA field configuration (from Data Structure XML)
2620 * @param string The value of the flexForm field
2621 * @param string Not used.
2622 * @param string Not used.
2623 * @return array Result array with key "value" containing the value of the processing.
2624 * @see copyRecord(), checkValue_flex_procInData_travDS()
2625 */
2626 function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2) {
2627
2628 // Extract parameters:
2629 list($table, $uid, $field) = $pParams;
2630
2631 // Process references and files, currently that means only the files, prepending absolute paths:
2632 $dataValue = $this->copyRecord_procFilesRefs($dsConf, $uid, $dataValue);
2633
2634 // If references are set for this field, set flag so they can be corrected later (in ->remapListedDBRecords())
2635 if ($this->isReferenceField($dsConf) && strlen($dataValue)) {
2636 $this->registerDBList[$table][$uid][$field] = 'FlexForm_reference';
2637 }
2638
2639 // Return
2640 return array('value' => $dataValue);
2641 }
2642
2643 /**
2644 * Modifying a field value for any situation regarding files/references:
2645 * For attached files: take current filenames and prepend absolute paths so they get copied.
2646 * For DB references: Nothing done.
2647 *
2648 * @param array TCE field config
2649 * @param integer Record UID
2650 * @param string Field value (eg. list of files)
2651 * @return string The (possibly modified) value
2652 * @see copyRecord(), copyRecord_flexFormCallBack()
2653 */
2654 function copyRecord_procFilesRefs($conf, $uid, $value) {
2655
2656 // Prepend absolute paths to files:
2657 if ($conf['type']=='group' && $conf['internal_type']=='file') {
2658
2659 // Get an array with files as values:
2660 if ($conf['MM']) {
2661 $theFileValues = array();
2662
2663 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
2664 $dbAnalysis->start('', 'files', $conf['MM'], $uid);
2665
2666 foreach($dbAnalysis->itemArray as $somekey => $someval) {
2667 if ($someval['id']) {
2668 $theFileValues[] = $someval['id'];
2669 }
2670 }
2671 } else {
2672 $theFileValues = t3lib_div::trimExplode(',',$value,1);
2673 }
2674
2675 // Traverse this array of files:
2676 $uploadFolder = $conf['uploadfolder'];
2677 $dest = $this->destPathFromUploadFolder($uploadFolder);
2678 $newValue = array();
2679
2680 foreach($theFileValues as $file) {
2681 if (trim($file)) {
2682 $realFile = $dest.'/'.trim($file);
2683 if (@is_file($realFile)) {
2684 $newValue[] = $realFile;
2685 }
2686 }
2687 }
2688
2689 // 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...)
2690 $value = implode(',',$newValue);
2691 }
2692
2693 // Return the new value:
2694 return $value;
2695 }
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709 /*********************************************
2710 *
2711 * Cmd: Moving, Localizing
2712 *
2713 ********************************************/
2714
2715 /**
2716 * Moving single records
2717 *
2718 * @param string Table name to move
2719 * @param integer Record uid to move
2720 * @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
2721 * @return void
2722 */
2723 function moveRecord($table,$uid,$destPid) {
2724 global $TCA, $TYPO3_CONF_VARS;
2725
2726 if ($TCA[$table]) {
2727
2728 // Prepare user defined objects (if any) for hooks which extend this function:
2729 $hookObjectsArr = array();
2730 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'])) {
2731 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'] as $classRef) {
2732 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef);
2733 }
2734 }
2735
2736 // 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)
2737 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid')) {
2738 $uid = $lookForLiveVersion['uid'];
2739 }
2740
2741 // Get workspace version of the source record, if any:
2742 $WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid');
2743
2744 // Initialize:
2745 $sortRow = $TCA[$table]['ctrl']['sortby'];
2746 $destPid = intval($destPid);
2747 $origDestPid = $destPid;
2748
2749 $propArr = $this->getRecordProperties($table,$uid); // Get this before we change the pid (for logging)
2750 $moveRec = $this->getRecordProperties($table,$uid,TRUE);
2751 $resolvedPid = $this->resolvePid($table,$destPid); // This is the actual pid of the moving to destination
2752
2753 // 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.
2754 // 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.
2755 if ($table!='pages' || $resolvedPid==$moveRec['pid']) {
2756 $mayMoveAccess = $this->checkRecordUpdateAccess($table,$uid); // Edit rights for the record...
2757 } else {
2758 $mayMoveAccess = $this->doesRecordExist($table,$uid,'delete');
2759 }
2760
2761 // 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
2762 if ($table!='pages' || $resolvedPid!=$moveRec['pid']) {
2763 $mayInsertAccess = $this->checkRecordInsertAccess($table,$resolvedPid,4); // Insert rights for the record...
2764 } else {
2765 $mayInsertAccess = $this->checkRecordUpdateAccess($table,$uid);
2766 }
2767
2768 // Check workspace permissions:
2769 $workspaceAccessBlocked = array();
2770 $recIsNewVersion = !strcmp($moveRec['_ORIG_pid'],'') && (int)$moveRec['t3ver_state']==1; // Element was an online version AND it was in "New state" so it can be moved...
2771 $destRes = $this->BE_USER->workspaceAllowLiveRecordsInPID($resolvedPid,$table);
2772 // Workspace source check:
2773 if ($errorCode = $this->BE_USER->workspaceCannotEditRecord($table, $WSversion['uid'] ? $WSversion['uid'] : $uid)) {
2774 $workspaceAccessBlocked['src1']='Record could not be edited in workspace: '.$errorCode.' ';
2775 } else {
2776 if (!$recIsNewVersion && $this->BE_USER->workspaceAllowLiveRecordsInPID($moveRec['pid'],$table)<=0) {
2777 $workspaceAccessBlocked['src2']='Could not remove record from table "'.$table.'" from its page "'.$moveRec['pid'].'" ';
2778 }
2779 }
2780 // Workspace destination check:
2781 if (!($destRes>0 || ($recIsNewVersion && !$destRes))) { // All records can be inserted if $destRes is greater than zero. Only new versions can be inserted if $destRes is false. NO RECORDS can be inserted if $destRes is negative which indicates a stage not allowed for use.
2782 $workspaceAccessBlocked['dest1']='Could not insert record from table "'.$table.'" in destination PID "'.$resolvedPid.'" ';
2783 } elseif ($destRes==1 && $WSversion['uid']) {
2784 $workspaceAccessBlocked['dest2']='Could not insert other versions in destination PID ';
2785 }
2786
2787 // 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...)
2788 if (($destPid<0 && !$sortRow) || $destPid>=0) { // $destPid>=0 because we must correct pid in case of versioning "page" types.
2789 $destPid = $resolvedPid;
2790 }
2791
2792 // Timestamp field:
2793 $updateFields = array();
2794 if ($TCA[$table]['ctrl']['tstamp']) {
2795 $updateFields[$TCA[$table]['ctrl']['tstamp']] = time();
2796 }
2797
2798 // If moving is allowed, begin the processing:
2799 if (!count($workspaceAccessBlocked)) {
2800 if ($mayMoveAccess) {
2801 if ($destPid>=0) { // insert as first element on page (where uid = $destPid)
2802 if ($mayInsertAccess) {
2803 if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid)) {
2804 $this->clear_cache($table,$uid); // clear cache before moving
2805
2806 $updateFields['pid'] = $destPid; // Setting PID
2807
2808 // table is sorted by 'sortby'
2809 if ($sortRow) {
2810 $sortNumber = $this->getSortNumber($table,$uid,$destPid);
2811 $updateFields[$sortRow] = $sortNumber;
2812 }
2813 // Create query for update:
2814 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
2815
2816 // Call post processing hooks:
2817 foreach($hookObjectsArr as $hookObj) {
2818 if (method_exists($hookObj, 'moveRecord_firstElementPostProcess')) {
2819 $hookObj->moveRecord_firstElementPostProcess($table, $uid, $destPid, $moveRec, $updateFields, $this);
2820 }
2821 }
2822
2823 // Logging...
2824 $newPropArr = $this->getRecordProperties($table,$uid);
2825 $oldpagePropArr = $this->getRecordProperties('pages',$propArr['pid']);
2826 $newpagePropArr = $this->getRecordProperties('pages',$destPid);
2827
2828 if ($destPid!=$propArr['pid']) {
2829 $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
2830 $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
2831 } else {
2832 $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
2833 }
2834 $this->clear_cache($table,$uid); // clear cache after moving
2835 $this->fixUniqueInPid($table,$uid);
2836 // fixCopyAfterDuplFields
2837 if ($origDestPid<0) {$this->fixCopyAfterDuplFields($table,$uid,abs($origDestPid),1);} // origDestPid is retrieve before it may possibly be converted to resolvePid if the table is not sorted anyway. In this way, copying records to after another records which are not sorted still lets you use this function in order to copy fields from the one before.
2838 } else {
2839 $destPropArr = $this->getRecordProperties('pages',$destPid);
2840 $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']);
2841 }
2842 }
2843 } else { // Put after another record
2844 if ($sortRow) { // table is being sorted
2845 $sortInfo = $this->getSortNumber($table,$uid,$destPid);
2846 $destPid = $sortInfo['pid']; // Setting the destPid to the new pid of the record.
2847 if (is_array($sortInfo)) { // If not an array, there was an error (which is already logged)
2848 if ($mayInsertAccess) {
2849 if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid)) {
2850 $this->clear_cache($table,$uid); // clear cache before moving
2851
2852 // We now update the pid and sortnumber
2853 $updateFields['pid'] = $destPid;
2854 $updateFields[$sortRow] = $sortInfo['sortNumber'];
2855 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
2856
2857 // Call post processing hooks:
2858 foreach($hookObjectsArr as $hookObj) {
2859 if (method_exists($hookObj, 'moveRecord_afterAnotherElementPostProcess')) {
2860 $hookObj->moveRecord_afterAnotherElementPostProcess($table, $uid, $destPid, $origDestPid, $moveRec, $updateFields, $this);
2861 }
2862 }
2863
2864 // Logging...
2865 $newPropArr = $this->getRecordProperties($table,$uid);