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