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