* Changed version numbers in t3lib/config_default.php
[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 sent 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 sent 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 // If an exclusive key is found, discard all others:
1287 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['exclusiveKeys']) {
1288 $exclusiveKeys = t3lib_div::trimExplode(',', $tcaFieldConf['exclusiveKeys']);
1289 foreach($valueArray as $kk => $vv) {
1290 if (in_array($vv, $exclusiveKeys)) { // $vv is the item key!
1291 $valueArray = Array($kk => $vv);
1292 break;
1293 }
1294 }
1295 }
1296
1297 // This could be a good spot for parsing the array through a validation-function which checks if the values are alright (except that database references are not in their final form - but that is the point, isn't it?)
1298 // NOTE!!! Must check max-items of files before the later check because that check would just leave out filenames if there are too many!!
1299
1300 // Checking for select / authMode, removing elements from $valueArray if any of them is not allowed!
1301 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['authMode']) {
1302 $preCount = count($valueArray);
1303 foreach($valueArray as $kk => $vv) {
1304 if (!$this->BE_USER->checkAuthMode($table,$field,$vv,$tcaFieldConf['authMode'])) {
1305 unset($valueArray[$kk]);
1306 }
1307 }
1308
1309 // 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.
1310 if ($preCount && !count($valueArray)) {
1311 return array();
1312 }
1313 }
1314
1315 // For group types:
1316 if ($tcaFieldConf['type']=='group') {
1317 switch($tcaFieldConf['internal_type']) {
1318 case 'file':
1319 $valueArray = $this->checkValue_group_select_file(
1320 $valueArray,
1321 $tcaFieldConf,
1322 $curValue,
1323 $uploadedFiles,
1324 $status,
1325 $table,
1326 $id,
1327 $recFID
1328 );
1329 break;
1330 case 'db':
1331 $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'group');
1332 break;
1333 }
1334 }
1335 // For select types which has a foreign table attached:
1336 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['foreign_table']) {
1337 $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'select');
1338 }
1339
1340 // 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...
1341
1342 // Checking the number of items, that it is correct.
1343 // If files, there MUST NOT be too many files in the list at this point, so check that prior to this code.
1344 $valueArrayC = count($valueArray);
1345 $minI = isset($tcaFieldConf['minitems']) ? intval($tcaFieldConf['minitems']):0;
1346
1347 // 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.
1348 $maxI = isset($tcaFieldConf['maxitems']) ? intval($tcaFieldConf['maxitems']):1;
1349 if ($valueArrayC > $maxI) {$valueArrayC=$maxI;} // Checking for not too many elements
1350
1351 // Dumping array to list
1352 $newVal=array();
1353 foreach($valueArray as $nextVal) {
1354 if ($valueArrayC==0) {break;}
1355 $valueArrayC--;
1356 $newVal[]=$nextVal;
1357 }
1358 $res['value'] = implode(',',$newVal);
1359
1360 return $res;
1361 }
1362
1363 /**
1364 * Handling files for group/select function
1365 *
1366 * @param [type] $valueArray: ...
1367 * @param [type] $tcaFieldConf: ...
1368 * @param [type] $curValue: ...
1369 * @param [type] $uploadedFileArray: ...
1370 * @param [type] $status: ...
1371 * @param [type] $table: ...
1372 * @param [type] $id: ...
1373 * @param [type] $recFID: ...
1374 * @return array Modified value array
1375 * @see checkValue_group_select()
1376 */
1377 function checkValue_group_select_file($valueArray,$tcaFieldConf,$curValue,$uploadedFileArray,$status,$table,$id,$recFID) {
1378
1379 // If any files are uploaded:
1380 if (is_array($uploadedFileArray) &&
1381 $uploadedFileArray['name'] &&
1382 strcmp($uploadedFileArray['tmp_name'],'none')) {
1383 $valueArray[]=$uploadedFileArray['tmp_name'];
1384 $this->alternativeFileName[$uploadedFileArray['tmp_name']] = $uploadedFileArray['name'];
1385 }
1386
1387 // Creating fileFunc object.
1388 if (!$this->fileFunc) {
1389 $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions');
1390 $this->include_filefunctions=1;
1391 }
1392 // Setting permitted extensions.
1393 $all_files = Array();
1394 $all_files['webspace']['allow'] = $tcaFieldConf['allowed'];
1395 $all_files['webspace']['deny'] = $tcaFieldConf['disallowed'] ? $tcaFieldConf['disallowed'] : '*';
1396 $all_files['ftpspace'] = $all_files['webspace'];
1397 $this->fileFunc->init('', $all_files);
1398
1399 // If there is an upload folder defined:
1400 if ($tcaFieldConf['uploadfolder']) {
1401 // For logging..
1402 $propArr = $this->getRecordProperties($table,$id);
1403
1404 // Get destrination path:
1405 $dest = $this->destPathFromUploadFolder($tcaFieldConf['uploadfolder']);
1406
1407 // If we are updating:
1408 if ($status=='update') {
1409
1410 // Traverse the input values and convert to absolute filenames in case the update happens to an autoVersionized record.
1411 // 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!
1412 // 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_.
1413 // 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.
1414 // Illustration of the problem comes here:
1415 // 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.
1416 // 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.
1417 // 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.
1418 // 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.
1419 if ($this->autoVersioningUpdate===TRUE) {
1420 foreach($valueArray as $key => $theFile) {
1421 if ($theFile===basename($theFile)) {
1422 $valueArray[$key] = PATH_site.$tcaFieldConf['uploadfolder'].'/'.$theFile;
1423 }
1424 }
1425 }
1426
1427 // Finding the CURRENT files listed, either from MM or from the current record.
1428 $theFileValues=array();
1429 if ($tcaFieldConf['MM']) { // If MM relations for the files also!
1430 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
1431 $dbAnalysis->start('','files',$tcaFieldConf['MM'],$id);
1432 reset($dbAnalysis->itemArray);
1433 while (list($somekey,$someval)=each($dbAnalysis->itemArray)) {
1434 if ($someval['id']) {
1435 $theFileValues[]=$someval['id'];
1436 }
1437 }
1438 } else {
1439 $theFileValues=t3lib_div::trimExplode(',',$curValue,1);
1440 }
1441
1442 // DELETE files: If existing files were found, traverse those and register files for deletion which has been removed:
1443 if (count($theFileValues)) {
1444 // 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!)
1445 foreach($valueArray as $key => $theFile) {
1446 if ($theFile && !strstr(t3lib_div::fixWindowsFilePath($theFile),'/')) {
1447 $theFileValues = t3lib_div::removeArrayEntryByValue($theFileValues,$theFile);
1448 }
1449 }
1450
1451 // This array contains the filenames in the uploadfolder that should be deleted:
1452 foreach($theFileValues as $key => $theFile) {
1453 $theFile = trim($theFile);
1454 if (@is_file($dest.'/'.$theFile)) {
1455 $this->removeFilesStore[]=$dest.'/'.$theFile;
1456 } elseif ($theFile) {
1457 $this->log($table,$id,5,0,1,"Could not delete file '%s' (does not exist). (%s)",10,array($dest.'/'.$theFile, $recFID),$propArr['event_pid']);
1458 }
1459 }
1460 }
1461 }
1462
1463 // Traverse the submitted values:
1464 foreach($valueArray as $key => $theFile) {
1465 // 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)
1466 if (strstr(t3lib_div::fixWindowsFilePath($theFile),'/')) {
1467 // Init:
1468 $maxSize = intval($tcaFieldConf['max_size']);
1469 $cmd='';
1470 $theDestFile=''; // Must be cleared. Else a faulty fileref may be inserted if the below code returns an error!! (Change: 22/12/2000)
1471
1472 // Check various things before copying file:
1473 if (@is_dir($dest) && (@is_file($theFile) || @is_uploaded_file($theFile))) { // File and destination must exist
1474
1475 // Finding size. For safe_mode we have to rely on the size in the upload array if the file is uploaded.
1476 if (is_uploaded_file($theFile) && $theFile==$uploadedFileArray['tmp_name']) {
1477 $fileSize = $uploadedFileArray['size'];
1478 } else {
1479 $fileSize = filesize($theFile);
1480 }
1481
1482 if (!$maxSize || $fileSize<=($maxSize*1024)) { // Check file size:
1483 // Prepare filename:
1484 $theEndFileName = isset($this->alternativeFileName[$theFile]) ? $this->alternativeFileName[$theFile] : $theFile;
1485 $fI = t3lib_div::split_fileref($theEndFileName);
1486
1487 // Check for allowed extension:
1488 if ($this->fileFunc->checkIfAllowed($fI['fileext'], $dest, $theEndFileName)) {
1489 $theDestFile = $this->fileFunc->getUniqueName($this->fileFunc->cleanFileName($fI['file']), $dest);
1490
1491 // If we have a unique destination filename, then write the file:
1492 if ($theDestFile) {
1493 t3lib_div::upload_copy_move($theFile,$theDestFile);
1494 $this->copiedFileMap[$theFile] = $theDestFile;
1495 clearstatcache();
1496 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']);
1497 } 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']);
1498 } else $this->log($table,$id,5,0,1,"Fileextension '%s' not allowed. (%s)",12,array($fI['fileext'], $recFID),$propArr['event_pid']);
1499 } 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']);
1500 } 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']);
1501
1502 // If the destination file was created, we will set the new filename in the value array, otherwise unset the entry in the value array!
1503 if (@is_file($theDestFile)) {
1504 $info = t3lib_div::split_fileref($theDestFile);
1505 $valueArray[$key]=$info['file']; // The value is set to the new filename
1506 } else {
1507 unset($valueArray[$key]); // The value is set to the new filename
1508 }
1509 }
1510 }
1511
1512 // 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!
1513 if ($tcaFieldConf['MM']) {
1514 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
1515 $dbAnalysis->tableArray['files']=array(); // dummy
1516
1517 reset($valueArray);
1518 while (list($key,$theFile)=each($valueArray)) {
1519 // explode files
1520 $dbAnalysis->itemArray[]['id']=$theFile;
1521 }
1522 if ($status=='update') {
1523 $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,0);
1524 } else {
1525 $this->dbAnalysisStore[] = array($dbAnalysis, $tcaFieldConf['MM'], $id, 0); // This will be traversed later to execute the actions
1526 }
1527 $cc=count($dbAnalysis->itemArray);
1528 $valueArray = array($cc);
1529 }
1530 }
1531
1532 return $valueArray;
1533 }
1534
1535 /**
1536 * Evaluates 'flex' type values.
1537 *
1538 * @param array The result array. The processed value (if any!) is set in the 'value' key.
1539 * @param string The value to set.
1540 * @param array Field configuration from TCA
1541 * @param array Additional parameters in a numeric array: $table,$id,$curValue,$status,$realPid,$recFID
1542 * @param array Uploaded files for the field
1543 * @param array Current record array.
1544 * @param string Field name
1545 * @return array Modified $res array
1546 */
1547 function checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field) {
1548 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP;
1549
1550 if (is_array($value)) {
1551
1552 // Get current value array:
1553 $dataStructArray = t3lib_BEfunc::getFlexFormDS($tcaFieldConf,$this->checkValue_currentRecord,$table);
1554 #debug($this->checkValue_currentRecord);
1555 $currentValueArray = t3lib_div::xml2array($curValue);
1556 if (!is_array($currentValueArray)) $currentValueArray = array();
1557 if (is_array($currentValueArray['meta']['currentLangId'])) unset($currentValueArray['meta']['currentLangId']); // Remove all old meta for languages...
1558
1559 // Evaluation of input values:
1560 $value['data'] = $this->checkValue_flex_procInData($value['data'],$currentValueArray['data'],$uploadedFiles['data'],$dataStructArray,$PP);
1561
1562 // Create XML and convert charsets from input value:
1563 $xmlValue = $this->checkValue_flexArray2Xml($value,TRUE);
1564
1565 // If we wanted to set UTF fixed:
1566 // $storeInCharset='utf-8';
1567 // $currentCharset=$GLOBALS['LANG']->charSet;
1568 // $xmlValue = $GLOBALS['LANG']->csConvObj->conv($xmlValue,$currentCharset,$storeInCharset,1);
1569 $storeInCharset=$GLOBALS['LANG']->charSet;
1570
1571 // Merge them together IF they are both arrays:
1572 // 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).
1573 if (is_array($currentValueArray)) {
1574 $arrValue = t3lib_div::xml2array($xmlValue);
1575 $arrValue = t3lib_div::array_merge_recursive_overrule($currentValueArray,$arrValue);
1576 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1577 }
1578
1579 // Temporary fix to delete flex form elements:
1580 $deleteCMDs = t3lib_div::_GP('_DELETE_FLEX_FORMdata');
1581 if (is_array($deleteCMDs[$table][$id][$field]['data'])) {
1582 $arrValue = t3lib_div::xml2array($xmlValue);
1583 $this->_DELETE_FLEX_FORMdata($arrValue['data'],$deleteCMDs[$table][$id][$field]['data']);
1584 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1585 }
1586
1587 // Temporary fix to move flex form elements up:
1588 $moveCMDs = t3lib_div::_GP('_MOVEUP_FLEX_FORMdata');
1589 if (is_array($moveCMDs[$table][$id][$field]['data'])) {
1590 $arrValue = t3lib_div::xml2array($xmlValue);
1591 $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'up');
1592 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1593 }
1594
1595 // Temporary fix to move flex form elements down:
1596 $moveCMDs = t3lib_div::_GP('_MOVEDOWN_FLEX_FORMdata');
1597 if (is_array($moveCMDs[$table][$id][$field]['data'])) {
1598 $arrValue = t3lib_div::xml2array($xmlValue);
1599 $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'down');
1600 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE);
1601 }
1602
1603 // Create the value XML:
1604 $res['value']='';
1605 $res['value'].=$xmlValue;
1606 } else { // Passthrough...:
1607 $res['value']=$value;
1608 }
1609
1610 return $res;
1611 }
1612
1613 /**
1614 * Converts an array to FlexForm XML
1615 *
1616 * @param array Array with FlexForm data
1617 * @param boolean If set, the XML prologue is returned as well.
1618 * @return string Input array converted to XML
1619 */
1620 function checkValue_flexArray2Xml($array, $addPrologue=FALSE) {
1621 $flexObj = t3lib_div::makeInstance('t3lib_flexformtools');
1622 return $flexObj->flexArray2Xml($array, $addPrologue);
1623 }
1624
1625 /**
1626 * Deletes a flex form element
1627 *
1628 * @param array &$valueArrayToRemoveFrom: by reference
1629 * @param [type] $deleteCMDS: ... *
1630 * @return void
1631 */
1632 function _DELETE_FLEX_FORMdata(&$valueArrayToRemoveFrom,$deleteCMDS) {
1633 if (is_array($valueArrayToRemoveFrom) && is_array($deleteCMDS)) {
1634 foreach($deleteCMDS as $key => $value) {
1635 if (is_array($deleteCMDS[$key])) {
1636 $this->_DELETE_FLEX_FORMdata($valueArrayToRemoveFrom[$key],$deleteCMDS[$key]);
1637 } else {
1638 unset($valueArrayToRemoveFrom[$key]);
1639 }
1640 }
1641 }
1642 }
1643
1644 /**
1645 * Deletes a flex form element
1646 *
1647 * TODO: Like _DELETE_FLEX_FORMdata, this is only a temporary solution!
1648 *
1649 * @param array &$valueArrayToMoveIn: by reference
1650 * @param [type] $moveCMDS: ... *
1651 * @param string $direction: 'up' or 'down'
1652 * @return void
1653 */
1654 function _MOVE_FLEX_FORMdata(&$valueArrayToMoveIn, $moveCMDS, $direction) {
1655 if (is_array($valueArrayToMoveIn) && is_array($moveCMDS)) {
1656
1657 // Only execute the first move command:
1658 list ($key, $value) = each ($moveCMDS);
1659
1660 if (is_array($moveCMDS[$key])) {
1661 $this->_MOVE_FLEX_FORMdata($valueArrayToMoveIn[$key],$moveCMDS[$key], $direction);
1662 } else {
1663 switch ($direction) {
1664 case 'up':
1665 if ($key > 1) {
1666 $tmpArr = $valueArrayToMoveIn[$key];
1667 $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key-1];
1668 $valueArrayToMoveIn[$key-1] = $tmpArr;
1669 }
1670 break;
1671 case 'down':
1672 if ($key < count($valueArrayToMoveIn)) {
1673 $tmpArr = $valueArrayToMoveIn[$key];
1674 $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key+1];
1675 $valueArrayToMoveIn[$key+1] = $tmpArr;
1676 }
1677 break;
1678 }
1679 }
1680 }
1681 }
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700 /*********************************************
1701 *
1702 * Helper functions for evaluation functions.
1703 *
1704 ********************************************/
1705
1706 /**
1707 * Gets a unique value for $table/$id/$field based on $value
1708 *
1709 * @param string Table name
1710 * @param string Field name for which $value must be unique
1711 * @param string Value string.
1712 * @param integer UID to filter out in the lookup (the record itself...)
1713 * @param integer If set, the value will be unique for this PID
1714 * @return string Modified value (if not-unique). Will be the value appended with a number (until 100, then the function just breaks).
1715 */
1716 function getUnique($table,$field,$value,$id,$newPid=0) {
1717 global $TCA;
1718
1719 // Initialize:
1720 t3lib_div::loadTCA($table);
1721 $whereAdd='';
1722 $newValue='';
1723 if (intval($newPid)) { $whereAdd.=' AND pid='.intval($newPid); } else { $whereAdd.=' AND pid>=0'; } // "AND pid>=0" for versioning
1724 $whereAdd.=$this->deleteClause($table);
1725
1726 // If the field is configured in TCA, proceed:
1727 if (is_array($TCA[$table]) && is_array($TCA[$table]['columns'][$field])) {
1728
1729 // Look for a record which might already have the value:
1730 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $table).' AND uid!='.intval($id).$whereAdd);
1731 $counter = 0;
1732
1733 // For as long as records with the test-value existing, try again (with incremented numbers appended).
1734 while ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) {
1735 $newValue = $value.$counter;
1736 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($newValue, $table).' AND uid!='.intval($id).$whereAdd);
1737 $counter++;
1738 if ($counter>100) { break; } // At "100" it will give up and accept a duplicate - should probably be fixed to a small hash string instead...!
1739 }
1740 // If the new value is there:
1741 $value = strlen($newValue) ? $newValue : $value;
1742 }
1743 return $value;
1744 }
1745
1746 /**
1747 * Evaluation of 'input'-type values based on 'eval' list
1748 *
1749 * @param string Value to evaluate
1750 * @param array Array of evaluations to traverse.
1751 * @param string Is-in string
1752 * @return string Modified $value
1753 */
1754 function checkValue_input_Eval($value,$evalArray,$is_in) {
1755 $res = Array();
1756 $newValue = $value;
1757 $set = true;
1758
1759 foreach($evalArray as $func) {
1760 switch($func) {
1761 case 'int':
1762 case 'year':
1763 case 'date':
1764 case 'datetime':
1765 case 'time':
1766 case 'timesec':
1767 $value = intval($value);
1768 break;
1769 case 'double2':
1770 $theDec = 0;
1771 for ($a=strlen($value); $a>0; $a--) {
1772 if (substr($value,$a-1,1)=='.' || substr($value,$a-1,1)==',') {
1773 $theDec = substr($value,$a);
1774 $value = substr($value,0,$a-1);
1775 break;
1776 }
1777 }
1778 $theDec = ereg_replace('[^0-9]','',$theDec).'00';
1779 $value = intval(str_replace(' ','',$value)).'.'.substr($theDec,0,2);
1780 break;
1781 case 'md5':
1782 if (strlen($value)!=32){$set=false;}
1783 break;
1784 case 'trim':
1785 $value = trim($value);
1786 break;
1787 case 'upper':
1788 $value = strtoupper($value);
1789 # $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.
1790 break;
1791 case 'lower':
1792 $value = strtolower($value);
1793 # $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.
1794 break;
1795 case 'required':
1796 if (!$value) {$set=0;}
1797 break;
1798 case 'is_in':
1799 $c=strlen($value);
1800 if ($c) {
1801 $newVal = '';
1802 for ($a=0;$a<$c;$a++) {
1803 $char = substr($value,$a,1);
1804 if (strstr($is_in,$char)) {
1805 $newVal.=$char;
1806 }
1807 }
1808 $value = $newVal;
1809 }
1810 break;
1811 case 'nospace':
1812 $value = str_replace(' ','',$value);
1813 break;
1814 case 'alpha':
1815 $value = ereg_replace('[^a-zA-Z]','',$value);
1816 break;
1817 case 'num':
1818 $value = ereg_replace('[^0-9]','',$value);
1819 break;
1820 case 'alphanum':
1821 $value = ereg_replace('[^a-zA-Z0-9]','',$value);
1822 break;
1823 case 'alphanum_x':
1824 $value = ereg_replace('[^a-zA-Z0-9_-]','',$value);
1825 break;
1826 default:
1827 if (substr($func, 0, 3) == 'tx_') {
1828 $evalObj = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func].':&'.$func);
1829 if(is_object($evalObj) && method_exists($evalObj, 'evaluateFieldValue')) {
1830 $value = $evalObj->evaluateFieldValue($value, $is_in, $set);
1831 }
1832 }
1833 break;
1834 }
1835 }
1836 if ($set) {$res['value'] = $value;}
1837 return $res;
1838 }
1839
1840 /**
1841 * Returns data for group/db and select fields
1842 *
1843 * @param array Current value array
1844 * @param array TCA field config
1845 * @param integer Record id, used for look-up of MM relations (local_uid)
1846 * @param string Status string ('update' or 'new')
1847 * @param string The type, either 'select' or 'group'
1848 * @return array Modified value array
1849 */
1850 function checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,$type) {
1851 $tables = $type=='group'?$tcaFieldConf['allowed']:$tcaFieldConf['foreign_table'].','.$tcaFieldConf['neg_foreign_table'];
1852 $prep = $type=='group'?$tcaFieldConf['prepend_tname']:$tcaFieldConf['neg_foreign_table'];
1853
1854 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
1855 $dbAnalysis->registerNonTableValues=$tcaFieldConf['allowNonIdValues'] ? 1 : 0;
1856 $dbAnalysis->start(implode(',',$valueArray),$tables);
1857
1858 if ($tcaFieldConf['MM']) {
1859 if ($status=='update') {
1860 $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,$prep);
1861 } else {
1862 $this->dbAnalysisStore[] = array($dbAnalysis,$tcaFieldConf['MM'],$id,$prep); // This will be traversed later to execute the actions
1863 }
1864 $cc=count($dbAnalysis->itemArray);
1865 $valueArray = array($cc);
1866 } else {
1867 $valueArray = $dbAnalysis->getValueArray($prep);
1868 if ($type=='select' && $prep) {
1869 $valueArray = $dbAnalysis->convertPosNeg($valueArray,$tcaFieldConf['foreign_table'],$tcaFieldConf['neg_foreign_table']);
1870 }
1871 }
1872
1873 // 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.
1874 return $valueArray;
1875 }
1876
1877 /**
1878 * Explodes the $value, which is a list of files/uids (group select)
1879 *
1880 * @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.
1881 * @return array The value array.
1882 */
1883 function checkValue_group_select_explodeSelectGroupValue($value) {
1884 $valueArray = t3lib_div::trimExplode(',',$value,1);
1885 reset($valueArray);
1886 while(list($key,$newVal)=each($valueArray)) {
1887 $temp=explode('|',$newVal,2);
1888 $valueArray[$key] = str_replace(',','',str_replace('|','',rawurldecode($temp[0])));
1889 }
1890 return $valueArray;
1891 }
1892
1893 /**
1894 * Starts the processing the input data for flexforms. This will traverse all sheets / languages and for each it will traverse the sub-structure.
1895 * See checkValue_flex_procInData_travDS() for more details.
1896 * 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
1897 *
1898 * @param array The 'data' part of the INPUT flexform data
1899 * @param array The 'data' part of the CURRENT flexform data
1900 * @param array The uploaded files for the 'data' part of the INPUT flexform data
1901 * @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.
1902 * @param array A set of parameters to pass through for the calling of the evaluation functions
1903 * @param string Optional call back function, see checkValue_flex_procInData_travDS() DEPRICATED, use class.t3lib_flexformtools.php instead for traversal!
1904 * @return array The modified 'data' part.
1905 * @see checkValue_flex_procInData_travDS()
1906 */
1907 function checkValue_flex_procInData($dataPart,$dataPart_current,$uploadedFiles,$dataStructArray,$pParams,$callBackFunc='') {
1908 #debug(array($dataPart,$dataPart_current,$dataStructArray));
1909 if (is_array($dataPart)) {
1910 foreach($dataPart as $sKey => $sheetDef) {
1911 list ($dataStruct,$actualSheet) = t3lib_div::resolveSheetDefInDS($dataStructArray,$sKey);
1912 #debug(array($dataStruct,$actualSheet,$sheetDef,$actualSheet,$sKey));
1913 if (is_array($dataStruct) && $actualSheet==$sKey && is_array($sheetDef)) {
1914 foreach($sheetDef as $lKey => $lData) {
1915 $this->checkValue_flex_procInData_travDS(
1916 $dataPart[$sKey][$lKey],
1917 $dataPart_current[$sKey][$lKey],
1918 $uploadedFiles[$sKey][$lKey],
1919 $dataStruct['ROOT']['el'],
1920 $pParams,
1921 $callBackFunc,
1922 $sKey.'/'.$lKey.'/'
1923 );
1924 }
1925 }
1926 }
1927 }
1928
1929 return $dataPart;
1930 }
1931
1932 /**
1933 * Processing of the sheet/language data array
1934 * 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.
1935 *
1936 * @param array New values (those being processed): Multidimensional Data array for sheet/language, passed by reference!
1937 * @param array Current values: Multidimensional Data array. May be empty array() if not needed (for callBackFunctions)
1938 * @param array Uploaded files array for sheet/language. May be empty array() if not needed (for callBackFunctions)
1939 * @param array Data structure which fits the data array
1940 * @param array A set of parameters to pass through for the calling of the evaluation functions / call back function
1941 * @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.
1942 * @param [type] $structurePath: ...
1943 * @return void
1944 * @see checkValue_flex_procInData()
1945 */
1946 function checkValue_flex_procInData_travDS(&$dataValues,$dataValues_current,$uploadedFiles,$DSelements,$pParams,$callBackFunc,$structurePath) {
1947 if (is_array($DSelements)) {
1948
1949 // For each DS element:
1950 foreach($DSelements as $key => $dsConf) {
1951
1952 // Array/Section:
1953 if ($DSelements[$key]['type']=='array') {
1954 if (is_array($dataValues[$key]['el'])) {
1955 if ($DSelements[$key]['section']) {
1956 foreach($dataValues[$key]['el'] as $ik => $el) {
1957 $theKey = key($el);
1958 if (is_array($dataValues[$key]['el'][$ik][$theKey]['el'])) {
1959 $this->checkValue_flex_procInData_travDS(
1960 $dataValues[$key]['el'][$ik][$theKey]['el'],
1961 $dataValues_current[$key]['el'][$ik][$theKey]['el'],
1962 $uploadedFiles[$key]['el'][$ik][$theKey]['el'],
1963 $DSelements[$key]['el'][$theKey]['el'],
1964 $pParams,
1965 $callBackFunc,
1966 $structurePath.$key.'/el/'.$ik.'/'.$theKey.'/el/'
1967 );
1968 }
1969 }
1970 } else {
1971 if (!isset($dataValues[$key]['el'])) $dataValues[$key]['el']=array();
1972 $this->checkValue_flex_procInData_travDS(
1973 $dataValues[$key]['el'],
1974 $dataValues_current[$key]['el'],
1975 $uploadedFiles[$key]['el'],
1976 $DSelements[$key]['el'],
1977 $pParams,
1978 $callBackFunc,
1979 $structurePath.$key.'/el/'
1980 );
1981 }
1982 }
1983 } else {
1984 if (is_array($dsConf['TCEforms']['config']) && is_array($dataValues[$key])) {
1985 foreach($dataValues[$key] as $vKey => $data) {
1986
1987 if ($callBackFunc) {
1988 if (is_object($this->callBackObj)) {
1989 $res = $this->callBackObj->$callBackFunc(
1990 $pParams,
1991 $dsConf['TCEforms']['config'],
1992 $dataValues[$key][$vKey],
1993 $dataValues_current[$key][$vKey],
1994 $uploadedFiles[$key][$vKey],
1995 $structurePath.$key.'/'.$vKey.'/'
1996 );
1997 } else {
1998 $res = $this->$callBackFunc(
1999 $pParams,
2000 $dsConf['TCEforms']['config'],
2001 $dataValues[$key][$vKey],
2002 $dataValues_current[$key][$vKey],
2003 $uploadedFiles[$key][$vKey]
2004 );
2005 }
2006 } else { // Default
2007 list($CVtable,$CVid,$CVcurValue,$CVstatus,$CVrealPid,$CVrecFID,$CVtscPID) = $pParams;
2008
2009 $res = $this->checkValue_SW(
2010 array(),
2011 $dataValues[$key][$vKey],
2012 $dsConf['TCEforms']['config'],
2013 $CVtable,
2014 $CVid,
2015 $dataValues_current[$key][$vKey],
2016 $CVstatus,
2017 $CVrealPid,
2018 $CVrecFID,
2019 '',
2020 $uploadedFiles[$key][$vKey],
2021 array(),
2022 $CVtscPID
2023 );
2024
2025 // Look for RTE transformation of field:
2026 if ($dataValues[$key]['_TRANSFORM_'.$vKey] == 'RTE' && !$this->dontProcessTransformations) {
2027
2028 // Unsetting trigger field - we absolutely don't want that into the data storage!
2029 unset($dataValues[$key]['_TRANSFORM_'.$vKey]);
2030
2031 if (isset($res['value'])) {
2032
2033 // Calculating/Retrieving some values here:
2034 list(,,$recFieldName) = explode(':', $CVrecFID);
2035 $theTypeString = t3lib_BEfunc::getTCAtypeValue($CVtable,$this->checkValue_currentRecord);
2036 $specConf = t3lib_BEfunc::getSpecConfParts('',$dsConf['TCEforms']['defaultExtras']);
2037
2038 // Find, thisConfig:
2039 $RTEsetup = $this->BE_USER->getTSConfig('RTE',t3lib_BEfunc::getPagesTSconfig($CVtscPID));
2040 $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'],$CVtable,$recFieldName,$theTypeString);
2041
2042 // Get RTE object, draw form and set flag:
2043 $RTEobj = &t3lib_BEfunc::RTEgetObj();
2044 if (is_object($RTEobj)) {
2045 $res['value'] = $RTEobj->transformContent('db',$res['value'],$CVtable,$recFieldName,$this->checkValue_currentRecord,$specConf,$thisConfig,'',$CVrealPid);
2046 } else {
2047 debug('NO RTE OBJECT FOUND!');
2048 }
2049 }
2050 }
2051 }
2052
2053 // Adding the value:
2054 if (isset($res['value'])) {
2055 $dataValues[$key][$vKey] = $res['value'];
2056 }
2057 }
2058 }
2059 }
2060 }
2061 }
2062 }
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080 /*********************************************
2081 *
2082 * PROCESSING COMMANDS
2083 *
2084 ********************************************/
2085
2086 /**
2087 * Processing the cmd-array
2088 * See "TYPO3 Core API" for a description of the options.
2089 *
2090 * @return void
2091 */
2092 function process_cmdmap() {
2093 global $TCA, $TYPO3_CONF_VARS;
2094
2095 // Editing frozen:
2096 if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze']) {
2097 $this->newlog('All editing in this workspace has been frozen!',1);
2098 return FALSE;
2099 }
2100
2101 // Hook initialization:
2102 $hookObjectsArr = array();
2103 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'])) {
2104 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'] as $classRef) {
2105 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef);
2106 }
2107 }
2108
2109 // Traverse command map:
2110 reset($this->cmdmap);
2111 while(list($table,) = each($this->cmdmap)) {
2112
2113 // Check if the table may be modified!
2114 $modifyAccessList = $this->checkModifyAccessList($table);
2115 if (!$modifyAccessList) {
2116 $id = 0;
2117 $this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table));
2118 } // FIXME: $id not set here (Comment added by Sebastian Kurfuerst)
2119
2120 // Check basic permissions and circumstances:
2121 if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->cmdmap[$table]) && $modifyAccessList) {
2122
2123 // Traverse the command map:
2124 foreach($this->cmdmap[$table] as $id => $incomingCmdArray) {
2125 if (is_array($incomingCmdArray)) { // have found a command.
2126
2127 // Get command and value (notice, only one command is observed at a time!):
2128 reset($incomingCmdArray);
2129 $command = key($incomingCmdArray);
2130 $value = current($incomingCmdArray);
2131
2132 foreach($hookObjectsArr as $hookObj) {
2133 if (method_exists($hookObj, 'processCmdmap_preProcess')) {
2134 $hookObj->processCmdmap_preProcess($command, $table, $id, $value, $this);
2135 }
2136 }
2137
2138 // Init copyMapping array:
2139 $this->copyMappingArray = Array(); // Must clear this array before call from here to those functions: Contains mapping information between new and old id numbers.
2140
2141 // Branch, based on command
2142 switch ($command) {
2143 case 'move':
2144 $this->moveRecord($table,$id,$value);
2145 break;
2146 case 'copy':
2147 if ($table === 'pages') {
2148 $this->copyPages($id,$value);
2149 } else {
2150 $this->copyRecord($table,$id,$value,1);
2151 }
2152 break;
2153 case 'localize':
2154 $this->localize($table,$id,$value);
2155 break;
2156 case 'version':
2157 switch ((string)$value['action']) {
2158 case 'new':
2159 $versionizeTree = t3lib_div::intInRange(!isset($value['treeLevels'])?-1:$value['treeLevels'],-1,100);
2160 if ($table == 'pages' && $versionizeTree>=0) {
2161 $this->versionizePages($id,$value['label'],$versionizeTree);
2162 } else {
2163 $this->versionizeRecord($table,$id,$value['label']);
2164 }
2165 break;
2166 case 'swap':
2167 $this->version_swap($table,$id,$value['swapWith'],$value['swapIntoWS']);
2168 break;
2169 case 'clearWSID':
2170 $this->version_clearWSID($table,$id);
2171 break;
2172 case 'setStage':
2173 $idList = t3lib_div::trimExplode(',',$id,1);
2174 foreach($idList as $id) {
2175 $this->version_setStage($table,$id,$value['stageId'],$value['comment']?$value['comment']:$this->generalComment);
2176 }
2177 break;
2178 }
2179 break;
2180 case 'delete':
2181 $this->deleteAction($table, $id);
2182 break;
2183 case 'undelete':
2184 $this->undeleteRecord($table, $id);
2185 break;
2186 }
2187
2188 foreach($hookObjectsArr as $hookObj) {
2189 if (method_exists($hookObj, 'processCmdmap_postProcess')) {
2190 $hookObj->processCmdmap_postProcess($command, $table, $id, $value, $this);
2191 }
2192 }
2193
2194 // Merging the copy-array info together for remapping purposes.
2195 $this->copyMappingArray_merged= t3lib_div::array_merge_recursive_overrule($this->copyMappingArray_merged,$this->copyMappingArray);
2196 }
2197 }
2198 }
2199 }
2200
2201 // Finally, before exit, check if there are ID references to remap. This might be the case if versioning or copying has taken place!
2202 $this->remapListedDBRecords();
2203 }
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215 /*********************************************
2216 *
2217 * Cmd: Copying
2218 *
2219 ********************************************/
2220
2221 /**
2222 * Copying a single record
2223 *
2224 * @param string Element table
2225 * @param integer Element UID
2226 * @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
2227 * @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
2228 * @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!
2229 * @param string Commalist of fields to exclude from the copy process (might get default values)
2230 * @return integer ID of new record, if any
2231 */
2232 function copyRecord($table,$uid,$destPid,$first=0,$overrideValues=array(),$excludeFields='') {
2233 global $TCA;
2234
2235 $uid = $origUid = intval($uid);
2236 if ($TCA[$table] && $uid) {
2237 t3lib_div::loadTCA($table);
2238 /*
2239 // 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)
2240 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid')) {
2241 $uid = $lookForLiveVersion['uid'];
2242 }
2243 // Get workspace version of the source record, if any: Then we will copy workspace version instead:
2244 if ($WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid')) {
2245 $uid = $WSversion['uid'];
2246 }
2247 // 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.
2248 */
2249 if ($this->doesRecordExist($table,$uid,'show')) { // This checks if the record can be selected which is all that a copy action requires.
2250 $data = Array();
2251
2252 $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));
2253
2254 #$row = $this->recordInfo($table,$uid,'*');
2255 $row = t3lib_BEfunc::getRecordWSOL($table,$uid); // So it copies (and localized) content from workspace...
2256 if (is_array($row)) {
2257
2258 // Initializing:
2259 $theNewID = uniqid('NEW');
2260 $enableField = isset($TCA[$table]['ctrl']['enablecolumns']) ? $TCA[$table]['ctrl']['enablecolumns']['disabled'] : '';
2261 $headerField = $TCA[$table]['ctrl']['label'];
2262
2263 // Getting default data:
2264 $defaultData = $this->newFieldArray($table);
2265
2266 // Getting "copy-after" fields if applicable:
2267 // 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.
2268 $copyAfterFields = $destPid<0 ? $this->fixCopyAfterDuplFields($table,$uid,abs($destPid),0) : array();
2269
2270 // Page TSconfig related:
2271 $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...
2272 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID);
2273 $tE = $this->getTableEntries($table,$TSConfig);
2274
2275 // Traverse ALL fields of the selected record:
2276 foreach($row as $field => $value) {
2277 if (!in_array($field,$nonFields)) {
2278
2279 // Get TCA configuration for the field:
2280 $conf = $TCA[$table]['columns'][$field]['config'];
2281
2282 // Preparation/Processing of the value:
2283 if ($field=='pid') { // "pid" is hardcoded of course:
2284 $value = $destPid;
2285 } elseif (isset($overrideValues[$field])) { // Override value...
2286 $value = $overrideValues[$field];
2287 } elseif (isset($copyAfterFields[$field])) { // Copy-after value if available:
2288 $value = $copyAfterFields[$field];
2289 } elseif ($TCA[$table]['ctrl']['setToDefaultOnCopy'] && t3lib_div::inList($TCA[$table]['ctrl']['setToDefaultOnCopy'],$field)) { // Revert to default for some fields:
2290 $value = $defaultData[$field];
2291 } else {
2292 // Hide at copy may override:
2293 if ($first && $field==$enableField && $TCA[$table]['ctrl']['hideAtCopy'] && !$this->neverHideAtCopy && !$tE['disableHideAtCopy']) {
2294 $value=1;
2295 }
2296 // Prepend label on copy:
2297 if ($first && $field==$headerField && $TCA[$table]['ctrl']['prependAtCopy'] && !$tE['disablePrependAtCopy']) {
2298 $value = $this->getCopyHeader($table,$this->resolvePid($table,$destPid),$field,$this->clearPrefixFromValue($table,$value),0);
2299 }
2300 // Processing based on the TCA config field type (files, references, flexforms...)
2301 $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf);
2302 }
2303
2304 // Add value to array.
2305 $data[$table][$theNewID][$field] = $value;
2306 }
2307 }
2308
2309 // Overriding values:
2310 if ($TCA[$table]['ctrl']['editlock']) {
2311 $data[$table][$theNewID][$TCA[$table]['ctrl']['editlock']] = 0;
2312 }
2313
2314 // Setting original UID:
2315 if ($TCA[$table]['ctrl']['origUid']) {
2316 $data[$table][$theNewID][$TCA[$table]['ctrl']['origUid']] = $uid;
2317 }
2318
2319 // Do the copy by simply submitting the array through TCEmain:
2320 $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain');
2321 $copyTCE->stripslashes_values = 0;
2322 $copyTCE->copyTree = $this->copyTree;
2323 $copyTCE->cachedTSconfig = $this->cachedTSconfig; // Copy forth the cached TSconfig
2324 $copyTCE->dontProcessTransformations=1; // Transformations should NOT be carried out during copy
2325
2326 $copyTCE->start($data,'',$this->BE_USER);
2327 $copyTCE->process_datamap();
2328
2329 // Getting the new UID:
2330 $theNewSQLID = $copyTCE->substNEWwithIDs[$theNewID];
2331 if ($theNewSQLID) {
2332 $this->copyMappingArray[$table][$origUid] = $theNewSQLID;
2333 }
2334
2335 // Copy back the cached TSconfig
2336 $this->cachedTSconfig = $copyTCE->cachedTSconfig;
2337 $this->errorLog = array_merge($this->errorLog,$copyTCE->errorLog);
2338 unset($copyTCE);
2339
2340 return $theNewSQLID;
2341 } else $this->log($table,$uid,3,0,1,'Attempt to copy record that did not exist!');
2342 } else $this->log($table,$uid,3,0,1,'Attempt to copy record without permission');
2343 }
2344 }
2345
2346 /**
2347 * Copying pages
2348 * Main function for copying pages.
2349 *
2350 * @param integer Page UID to copy
2351 * @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
2352 * @return void
2353 */
2354 function copyPages($uid,$destPid) {
2355
2356 // Initialize:
2357 $uid = intval($uid);
2358 $destPid = intval($destPid);
2359
2360 // Finding list of tables to copy.
2361 $copyTablesArray = $this->admin ? $this->compileAdminTables() : explode(',',$this->BE_USER->groupData['tables_modify']); // These are the tables, the user may modify
2362 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
2363 foreach($copyTablesArray as $k => $table) {
2364 if (!$table || !t3lib_div::inList($this->copyWhichTables.',pages',$table)) { // pages are always going...
2365 unset($copyTablesArray[$k]);
2366 }
2367 }
2368 }
2369 $copyTablesArray = array_unique($copyTablesArray);
2370
2371 // Begin to copy pages if we're allowed to:
2372 if ($this->admin || in_array('pages',$copyTablesArray)) {
2373
2374 // Copy this page we're on. And set first-flag (this will trigger that the record is hidden if that is configured)!
2375 $theNewRootID = $this->copySpecificPage($uid,$destPid,$copyTablesArray,1);
2376
2377 // If we're going to copy recursively...:
2378 if ($theNewRootID && $this->copyTree) {
2379
2380 // Get ALL subpages to copy (read-permissions are respected!):
2381 $CPtable = $this->int_pageTreeInfo(Array(), $uid, intval($this->copyTree), $theNewRootID);
2382
2383 // Now copying the subpages:
2384 foreach($CPtable as $thePageUid => $thePagePid) {
2385 $newPid = $this->copyMappingArray['pages'][$thePagePid];
2386 if (isset($newPid)) {
2387 $this->copySpecificPage($thePageUid,$newPid,$copyTablesArray);
2388 } else {
2389 $this->log('pages',$uid,5,0,1,'Something went wrong during copying branch');
2390 break;
2391 }
2392 }
2393 } // else the page was not copied. Too bad...
2394 } else {
2395 $this->log('pages',$uid,5,0,1,'Attempt to copy page without permission to this table');
2396 }
2397 }
2398
2399 /**
2400 * Copying a single page ($uid) to $destPid and all tables in the array copyTablesArray.
2401 *
2402 * @param integer Page uid
2403 * @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
2404 * @param array Table on pages to copy along with the page.
2405 * @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
2406 * @return integer The id of the new page, if applicable.
2407 */
2408 function copySpecificPage($uid,$destPid,$copyTablesArray,$first=0) {
2409 global $TCA;
2410
2411 // Copy the page itself:
2412 $theNewRootID = $this->copyRecord('pages',$uid,$destPid,$first);
2413
2414 // If a new page was created upon the copy operation we will proceed with all the tables ON that page:
2415 if ($theNewRootID) {
2416 foreach($copyTablesArray as $table) {
2417 if ($table && is_array($TCA[$table]) && $table!='pages') { // all records under the page is copied.
2418 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table), '', ($TCA[$table]['ctrl']['sortby'] ? $TCA[$table]['ctrl']['sortby'].' DESC' : ''));
2419 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
2420 $this->copyRecord($table,$row['uid'], $theNewRootID); // Copying each of the underlying records...
2421 }
2422 }
2423 }
2424 return $theNewRootID;
2425 }
2426 }
2427
2428 /**
2429 * Copying records, but makes a "raw" copy of a record.
2430 * Basically the only thing observed is field processing like the copying of files and correction of ids. All other fields are 1-1 copied.
2431 * 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.
2432 * 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!?
2433 * This function is used to create new versions of a record.
2434 * 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.
2435 *
2436 * @param string Element table
2437 * @param integer Element UID
2438 * @param integer Element PID (real PID, not checked)
2439 * @param array Override array - must NOT contain any fields not in the table!
2440 * @return integer Returns the new ID of the record (if applicable)
2441 */
2442 function copyRecord_raw($table,$uid,$pid,$overrideArray=array()) {
2443 global $TCA;
2444
2445 $uid = intval($uid);
2446 if ($TCA[$table] && $uid) {
2447 t3lib_div::loadTCA($table);
2448 if ($this->doesRecordExist($table,$uid,'show')) {
2449
2450 // Set up fields which should not be processed. They are still written - just passed through no-questions-asked!
2451 $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');
2452
2453 // Select main record:
2454 $row = $this->recordInfo($table,$uid,'*');
2455 if (is_array($row)) {
2456
2457 // Merge in override array.
2458 $row = array_merge($row,$overrideArray);
2459
2460 // Traverse ALL fields of the selected record:
2461 foreach($row as $field => $value) {
2462 if (!in_array($field,$nonFields)) {
2463
2464 // Get TCA configuration for the field:
2465 $conf = $TCA[$table]['columns'][$field]['config'];
2466 if (is_array($conf)) {
2467 // Processing based on the TCA config field type (files, references, flexforms...)
2468 $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf);
2469 }
2470
2471 // Add value to array.
2472 $row[$field] = $value;
2473 }
2474 }
2475
2476 // Force versioning related fields:
2477 $row['pid'] = $pid;
2478
2479 // Setting original UID:
2480 if ($TCA[$table]['ctrl']['origUid']) {
2481 $row[$TCA[$table]['ctrl']['origUid']] = $uid;
2482 }
2483
2484 // Do the copy by internal function
2485 $theNewSQLID = $this->insertNewCopyVersion($table,$row,$pid);
2486 if ($theNewSQLID) {
2487 $this->dbAnalysisStoreExec();
2488 $this->dbAnalysisStore = array();
2489 return $this->copyMappingArray[$table][$uid] = $theNewSQLID;
2490 }
2491 } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record that did not exist!');
2492 } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record without copy permission');
2493 }
2494 }
2495
2496 /**
2497 * Copies all records from tables in $copyTablesArray from page with $old_pid to page with $new_pid
2498 * Uses raw-copy for the operation (meant for versioning!)
2499 *
2500 * @param integer Current page id.
2501 * @param integer New page id
2502 * @param array Array of tables from which to copy
2503 * @return void
2504 * @see versionizePages()
2505 */
2506 function rawCopyPageContent($old_pid,$new_pid,$copyTablesArray) {
2507 global $TCA;
2508
2509 if ($new_pid) {
2510 foreach($copyTablesArray as $table) {
2511 if ($table && is_array($TCA[$table]) && $table!='pages') { // all records under the page is copied.
2512 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($old_pid).$this->deleteClause($table));
2513 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) {
2514 $this->copyRecord_raw($table,$row['uid'],$new_pid); // Copying each of the underlying records (method RAW)
2515 }
2516 }
2517 }
2518 }
2519 }
2520
2521 /**
2522 * Inserts a record in the database, passing TCA configuration values through checkValue() but otherwise does NOTHING and checks nothing regarding permissions.
2523 * 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...
2524 *
2525 * @param string Table name
2526 * @param array Field array to insert as a record
2527 * @param integer The value of PID field. -1 is indication that we are creating a new version!
2528 * @return integer Returns the new ID of the record (if applicable)
2529 */
2530 function insertNewCopyVersion($table,$fieldArray,$realPid) {
2531 global $TCA;
2532
2533 $id = uniqid('NEW');
2534
2535 // $fieldArray is set as current record.
2536 // 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...
2537 $this->checkValue_currentRecord = $fieldArray;
2538
2539 // Traverse record and input-process each value:
2540 foreach($fieldArray as $field => $fieldValue) {
2541 if (isset($TCA[$table]['columns'][$field])) {
2542 // Evaluating the value.
2543 $res = $this->checkValue($table,$field,$fieldValue,$id,'new',$realPid,0);
2544 if (isset($res['value'])) {
2545 $fieldArray[$field] = $res['value'];
2546 }
2547 }
2548 }
2549
2550 // System fields being set:
2551 if ($TCA[$table]['ctrl']['crdate']) {
2552 $fieldArray[$TCA[$table]['ctrl']['crdate']]=time();
2553 }
2554 if ($TCA[$table]['ctrl']['cruser_id']) {
2555 $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid;
2556 }
2557 if ($TCA[$table]['ctrl']['tstamp']) {
2558 $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time();
2559 }
2560
2561 // Finally, insert record:
2562 $this->insertDB($table,$id,$fieldArray, TRUE);
2563
2564 // Return new id:
2565 return $this->substNEWwithIDs[$id];
2566 }
2567
2568 /**
2569 * Processing/Preparing content for copyRecord() function
2570 *
2571 * @param string Table name
2572 * @param integer Record uid
2573 * @param string Field name being processed
2574 * @param string Input value to be processed.
2575 * @param array Record array
2576 * @param array TCA field configuration
2577 * @return mixed Processed value. Normally a string/integer, but can be an array for flexforms!
2578 * @access private
2579 * @see copyRecord()
2580 */
2581 function copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf) {
2582 global $TCA;
2583
2584 // 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)
2585 $value = $this->copyRecord_procFilesRefs($conf, $uid, $value);
2586
2587
2588 // Register if there are references to take care of (no change to value):
2589 if ($this->isReferenceField($conf)) {
2590 $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table'];
2591 $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
2592 if ($conf['MM']) {
2593 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
2594 $dbAnalysis->start('',$allowedTables,$conf['MM'],$uid);
2595 $value = implode(',',$dbAnalysis->getValueArray($prependName));
2596 }
2597 if ($value) { // Setting the value in this array will notify the remapListedDBRecords() function that this field MAY need references to be corrected
2598 $this->registerDBList[$table][$uid][$field] = $value;
2599 }
2600 }
2601
2602 // 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())
2603 if ($conf['type']=='flex') {
2604
2605 // Get current value array:
2606 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $row, $table);
2607 $currentValueArray = t3lib_div::xml2array($value);
2608
2609 // Traversing the XML structure, processing files:
2610 if (is_array($currentValueArray)) {
2611 $currentValueArray['data'] = $this->checkValue_flex_procInData(
2612 $currentValueArray['data'],
2613 array(), // Not used.
2614 array(), // Not used.
2615 $dataStructArray,
2616 array($table,$uid,$field), // Parameters.
2617 'copyRecord_flexFormCallBack'
2618 );
2619 $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.
2620 }
2621 }
2622
2623 return $value;
2624 }
2625
2626 /**
2627 * Callback function for traversing the FlexForm structure in relation to creating copied files of file relations inside of flex form structures.
2628 *
2629 * @param array Array of parameters in num-indexes: table, uid, field
2630 * @param array TCA field configuration (from Data Structure XML)
2631 * @param string The value of the flexForm field
2632 * @param string Not used.
2633 * @param string Not used.
2634 * @return array Result array with key "value" containing the value of the processing.
2635 * @see copyRecord(), checkValue_flex_procInData_travDS()
2636 */
2637 function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2) {
2638
2639 // Extract parameters:
2640 list($table, $uid, $field) = $pParams;
2641
2642 // Process references and files, currently that means only the files, prepending absolute paths:
2643 $dataValue = $this->copyRecord_procFilesRefs($dsConf, $uid, $dataValue);
2644
2645 // If references are set for this field, set flag so they can be corrected later (in ->remapListedDBRecords())
2646 if ($this->isReferenceField($dsConf) && strlen($dataValue)) {
2647 $this->registerDBList[$table][$uid][$field] = 'FlexForm_reference';
2648 }
2649
2650 // Return
2651 return array('value' => $dataValue);
2652 }
2653
2654 /**
2655 * Modifying a field value for any situation regarding files/references:
2656 * For attached files: take current filenames and prepend absolute paths so they get copied.
2657 * For DB references: Nothing done.
2658 *
2659 * @param array TCE field config
2660 * @param integer Record UID
2661 * @param string Field value (eg. list of files)
2662 * @return string The (possibly modified) value
2663 * @see copyRecord(), copyRecord_flexFormCallBack()
2664 */
2665 function copyRecord_procFilesRefs($conf, $uid, $value) {
2666
2667 // Prepend absolute paths to files:
2668 if ($conf['type']=='group' && $conf['internal_type']=='file') {
2669
2670 // Get an array with files as values:
2671 if ($conf['MM']) {
2672 $theFileValues = array();
2673
2674 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
2675 $dbAnalysis->start('', 'files', $conf['MM'], $uid);
2676
2677 foreach($dbAnalysis->itemArray as $somekey => $someval) {
2678 if ($someval['id']) {
2679 $theFileValues[] = $someval['id'];
2680 }
2681 }
2682 } else {
2683 $theFileValues = t3lib_div::trimExplode(',',$value,1);
2684 }
2685
2686 // Traverse this array of files:
2687 $uploadFolder = $conf['uploadfolder'];
2688 $dest = $this->destPathFromUploadFolder($uploadFolder);
2689 $newValue = array();
2690
2691 foreach($theFileValues as $file) {
2692 if (trim($file)) {
2693 $realFile = $dest.'/'.trim($file);
2694 if (@is_file($realFile)) {
2695 $newValue[] = $realFile;
2696 }
2697 }
2698 }
2699
2700 // 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...)
2701 $value = implode(',',$newValue);
2702 }
2703
2704 // Return the new value:
2705 return $value;
2706 }
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720 /*********************************************
2721 *
2722 * Cmd: Moving, Localizing
2723 *
2724 ********************************************/
2725
2726 /**
2727 * Moving single records
2728 *
2729 * @param string Table name to move
2730 * @param integer Record uid to move
2731 * @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
2732 * @return void
2733 */
2734 function moveRecord($table,$uid,$destPid) {
2735 global $TCA, $TYPO3_CONF_VARS;
2736
2737 if ($TCA[$table]) {
2738
2739 // Prepare user defined objects (if any) for hooks which extend this function:
2740 $hookObjectsArr = array();
2741 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'])) {
2742 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'] as $classRef) {
2743 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef);
2744 }
2745 }
2746
2747 // 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)
2748 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid')) {
2749 $uid = $lookForLiveVersion['uid'];
2750 }
2751
2752 // Get workspace version of the source record, if any:
2753 $WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid');
2754
2755 // Initialize:
2756 $sortRow = $TCA[$table]['ctrl']['sortby'];
2757 $destPid = intval($destPid);
2758 $origDestPid = $destPid;
2759
2760 $propArr = $this->getRecordProperties($table,$uid); // Get this before we change the pid (for logging)
2761 $moveRec = $this->getRecordProperties($table,$uid,TRUE);
2762 $resolvedPid = $this->resolvePid($table,$destPid); // This is the actual pid of the moving to destination
2763
2764 // 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.
2765 // 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.
2766 if ($table!='pages' || $resolvedPid==$moveRec['pid']) {
2767 $mayMoveAccess = $this->checkRecordUpdateAccess($table,$uid); // Edit rights for the record...
2768 } else {
2769 $mayMoveAccess = $this->doesRecordExist($table,$uid,'delete');
2770 }
2771
2772 // 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
2773 if ($table!='pages' || $resolvedPid!=$moveRec['pid']) {
2774 $mayInsertAccess = $this->checkRecordInsertAccess($table,$resolvedPid,4); // Insert rights for the record...
2775 } else {
2776 $mayInsertAccess = $this->checkRecordUpdateAccess($table,$uid);
2777 }
2778
2779 // Check workspace permissions:
2780 $workspaceAccessBlocked = array();
2781 $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...
2782 $destRes = $this->BE_USER->workspaceAllowLiveRecordsInPID($resolvedPid,$table);
2783 // Workspace source check:
2784 if ($errorCode = $this->BE_USER->workspaceCannotEditRecord($table, $WSversion['uid'] ? $WSversion['uid'] : $uid)) {
2785 $workspaceAccessBlocked['src1']='Record could not be edited in workspace: '.$errorCode.' ';
2786 } else {
2787 if (!$recIsNewVersion && $this->BE_USER->workspaceAllowLiveRecordsInPID($moveRec['pid'],$table)<=0) {
2788 $workspaceAccessBlocked['src2']='Could not remove record from table "'.$table.'" from its page "'.$moveRec['pid'].'" ';
2789 }
2790 }
2791 // Workspace destination check:
2792 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.
2793 $workspaceAccessBlocked['dest1']='Could not insert record from table "'.$table.'" in destination PID "'.$resolvedPid.'" ';
2794 } elseif ($destRes==1 && $WSversion['uid']) {
2795 $workspaceAccessBlocked['dest2']='Could not insert other versions in destination PID ';
2796 }
2797
2798 // 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...)
2799 if (($destPid<0 && !$sortRow) || $destPid>=0) { // $destPid>=0 because we must correct pid in case of versioning "page" types.
2800 $destPid = $resolvedPid;
2801 }
2802
2803 // Timestamp field:
2804 $updateFields = array();
2805 if ($TCA[$table]['ctrl']['tstamp']) {
2806 $updateFields[$TCA[$table]['ctrl']['tstamp']] = time();
2807 }
2808
2809 // If moving is allowed, begin the processing:
2810 if (!count($workspaceAccessBlocked)) {
2811 if ($mayMoveAccess) {
2812 if ($destPid>=0) { // insert as first element on page (where uid = $destPid)
2813 if ($mayInsertAccess) {
2814 if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid)) {
2815 $this->clear_cache($table,$uid); // clear cache before moving
2816
2817 $updateFields['pid'] = $destPid; // Setting PID
2818
2819 // table is sorted by 'sortby'
2820 if ($sortRow) {
2821 $sortNumber = $this->getSortNumber($table,$uid,$destPid);
2822 $updateFields[$sortRow] = $sortNumber;
2823 }
2824 // Create query for update:
2825 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);
2826
2827 // Call post processing hooks:
2828 foreach($hookObjectsArr as $hookObj) {
2829 if (method_exists($hookObj, 'moveRecord_firstElementPostProcess')) {
2830 $hookObj->moveRecord_firstElementPostProcess($table, $uid, $destPid, $moveRec, $updateFields, $this);
2831 }
2832 }
2833
2834 // Logging...
2835 $newPropArr = $this->getRecordProperties($table,$uid);
2836 $oldpagePropArr = $this->getRecordProperties('pages',$propArr['pid']);
2837 $newpagePropArr = $this->getRecordProperties('pages',$destPid);
2838
2839 if ($destPid!=$propArr['pid']) {
2840 $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
2841 $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
2842 } else {
2843 $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
2844 }
2845 $this->clear_cache($table,$uid); // clear cache after moving
2846 $this->fixUniqueInPid($table,$uid);
2847 // fixCopyAfterDuplFields
2848 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.
2849 } else {
2850 $destPropArr = $this->getRecordProperties('pages',$destPid);
2851 $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']);
2852 }
2853 }
2854 } else { // Put after another record
2855 if ($sortRow) { // table is being sorted
2856 $sortInfo = $this->getSortNumber($table,$uid,$destPid);
2857 $destPid = $sortInfo['pid']; // Setting the destPid to the new pid of the record.
2858 if (is_array($sortInfo)) { // If not an array, there was an error (which is already logged)
2859 if ($mayInsertAccess) {
2860 if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid)) {
2861 $this->clear_cache($table,$uid); // clear cache before moving
2862
2863 // We now update the pid and sortnumber
2864 $updateFields['pid'] = $destPid;
2865 $updateFields[$sortRow] = $sortInfo['sortNumber'];
2866 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields);