Documentation TYPO3 par Ameos |
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 1999-2006 Kasper Skaarhoj (kasperYYYY@typo3.com) 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * A copy is found in the textfile GPL.txt and important notices to the license 00017 * from the author is found in LICENSE.txt distributed with these scripts. 00018 * 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 00203 // ******************************* 00204 // Including necessary libraries 00205 // ******************************* 00206 require_once (PATH_t3lib.'class.t3lib_loaddbgroup.php'); 00207 require_once (PATH_t3lib.'class.t3lib_parsehtml_proc.php'); 00208 require_once (PATH_t3lib.'class.t3lib_stdgraphic.php'); 00209 require_once (PATH_t3lib.'class.t3lib_basicfilefunc.php'); 00210 require_once (PATH_t3lib.'class.t3lib_refindex.php'); 00211 require_once (PATH_t3lib.'class.t3lib_flexformtools.php'); 00212 00213 00214 00215 00216 00217 00218 00219 00220 00221 00222 00237 class t3lib_TCEmain { 00238 00239 00240 // ********************* 00241 // Public variables you can configure before using the class: 00242 // ********************* 00243 00244 var $storeLogMessages = TRUE; // Boolean: If true, the default log-messages will be stored. This should not be necessary if the locallang-file for the log-display is properly configured. So disabling this will just save some database-space as the default messages are not saved. 00245 var $enableLogging = TRUE; // Boolean: If true, actions are logged to sys_log. 00246 var $reverseOrder = FALSE; // Boolean: If true, the datamap array is reversed in the order, which is a nice thing if you're creating a whole new bunch of records. 00247 var $checkSimilar = TRUE; // Boolean: If true, only fields which are different from the database values are saved! In fact, if a whole input array is similar, it's not saved then. 00248 var $stripslashes_values = TRUE; // Boolean: If true, incoming values in the data-array have their slashes stripped. ALWAYS SET THIS TO ZERO and supply an unescaped data array instead. This switch may totally disappear in future versions of this class! 00249 var $checkStoredRecords = TRUE; // Boolean: This will read the record after having updated or inserted it. If anything is not properly submitted an error is written to the log. This feature consumes extra time by selecting records 00250 var $checkStoredRecords_loose = TRUE; // Boolean: If set, values '' and 0 will equal each other when the stored records are checked. 00251 var $deleteTree = FALSE; // Boolean. If this is set, then a page is deleted by deleting the whole branch under it (user must have deletepermissions to it all). If not set, then the page is deleted ONLY if it has no branch 00252 var $neverHideAtCopy = FALSE; // Boolean. If set, then the 'hideAtCopy' flag for tables will be ignored. 00253 var $dontProcessTransformations = FALSE; // Boolean: If set, then transformations are NOT performed on the input. 00254 var $bypassWorkspaceRestrictions = FALSE; // Boolean: If true, workspace restrictions are bypassed on edit an create actions (process_datamap()). YOU MUST KNOW what you do if you use this feature! 00255 var $bypassFileHandling = FALSE; // Boolean: If true, file handling of attached files (addition, deletion etc) is bypassed - the value is saved straight away. YOU MUST KNOW what you are doing with this feature! 00256 var $bypassAccessCheckForRecords = FALSE; // Boolean: If true, access check, check for deleted etc. for records is bypassed. YOU MUST KNOW what you are doing if you use this feature! 00257 00258 var $copyWhichTables = '*'; // String. Comma-list. This list of tables decides which tables will be copied. If empty then none will. If '*' then all will (that the user has permission to of course) 00259 var $generalComment = ''; // General comment, eg. for staging in workspaces. 00260 00261 var $copyTree = 0; // Integer. If 0 then branch is NOT copied. If 1 then pages on the 1st level is copied. If 2 then pages on the second level is copied ... and so on 00262 00263 var $defaultValues = array(); // Array [table][fields]=value: New records are created with default values and you can set this array on the form $defaultValues[$table][$field] = $value to override the default values fetched from TCA. If ->setDefaultsFromUserTS is called UserTSconfig default values will overrule existing values in this array (thus UserTSconfig overrules externally set defaults which overrules TCA defaults) 00264 var $overrideValues = array(); // Array [table][fields]=value: You can set this array on the form $overrideValues[$table][$field] = $value to override the incoming data. You must set this externally. You must make sure the fields in this array are also found in the table, because it's not checked. All columns can be set by this array! 00265 var $alternativeFileName = array(); // Array [filename]=alternative_filename: Use this array to force another name onto a file. Eg. if you set ['/tmp/blablabal'] = 'my_file.txt' and '/tmp/blablabal' is set for a certain file-field, then 'my_file.txt' will be used as the name instead. 00266 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. 00267 var $suggestedInsertUids=array(); // Use this array to validate suggested uids for tables by setting [table]:[uid]. This is a dangerous option since it will force the inserted record to have a certain UID. The value just have to be true, but if you set it to "DELETE" it will make sure any record with that UID will be deleted first (raw delete). The option is used for import of T3D files when synchronizing between two mirrored servers. As a security measure this feature is available only for Admin Users (for now) 00268 00269 var $callBackObj; // Object. Call back object for flex form traversation. Useful when external classes wants to use the iteration functions inside tcemain for traversing a FlexForm structure. 00270 00271 00272 00273 00274 // ********************* 00275 // Internal variables (mapping arrays) which can be used (read-only) from outside 00276 // ********************* 00277 var $autoVersionIdMap = Array(); // Contains mapping of auto-versionized records. 00278 var $substNEWwithIDs = Array(); // When new elements are created, this array contains a map between their "NEW..." string IDs and the eventual UID they got when stored in database 00279 var $substNEWwithIDs_table = Array(); // Like $substNEWwithIDs, but where each old "NEW..." id is mapped to the table it was from. 00280 var $newRelatedIDs = Array(); // Holds the tables and there the ids of newly created child records from IRRE 00281 var $copyMappingArray_merged = Array(); // This array is the sum of all copying operations in this class. May be READ from outside, thus partly public. 00282 var $copiedFileMap = Array(); // A map between input file name and final destination for files being attached to records. 00283 var $errorLog = Array(); // Errors are collected in this variable. 00284 00285 00286 00287 // ********************* 00288 // Internal Variables, do not touch. 00289 // ********************* 00290 00291 // Variables set in init() function: 00292 var $BE_USER; // The user-object the script uses. If not set from outside, this is set to the current global $BE_USER. 00293 var $userid; // will be set to uid of be_user executing this script 00294 var $username; // will be set to username of be_user executing this script 00295 var $admin; // will be set if user is admin 00296 00297 var $defaultPermissions = array( // Can be overridden from $TYPO3_CONF_VARS 00298 'user' => 'show,edit,delete,new,editcontent', 00299 'group' => 'show,edit,new,editcontent', 00300 'everybody' => '' 00301 ); 00302 00303 var $exclude_array; // The list of <table>-<fields> that cannot be edited by user. This is compiled from TCA/exclude-flag combined with non_exclude_fields for the user. 00304 var $datamap = Array(); // Set with incoming data array 00305 var $cmdmap = Array(); // Set with incoming cmd array 00306 00307 // Internal static: 00308 var $pMap = Array( // Permission mapping 00309 'show' => 1, // 1st bit 00310 'edit' => 2, // 2nd bit 00311 'delete' => 4, // 3rd bit 00312 'new' => 8, // 4th bit 00313 'editcontent' => 16 // 5th bit 00314 ); 00315 var $sortIntervals = 256; // Integer: The interval between sorting numbers used with tables with a 'sorting' field defined. Min 1 00316 00317 // Internal caching arrays 00318 var $recUpdateAccessCache = Array(); // Used by function checkRecordUpdateAccess() to store whether a record is updateable or not. 00319 var $recInsertAccessCache = Array(); // User by function checkRecordInsertAccess() to store whether a record can be inserted on a page id 00320 var $isRecordInWebMount_Cache=array(); // Caching array for check of whether records are in a webmount 00321 var $isInWebMount_Cache=array(); // Caching array for page ids in webmounts 00322 var $cachedTSconfig = array(); // Caching for collecting TSconfig for page ids 00323 var $pageCache = Array(); // Used for caching page records in pageInfo() 00324 var $checkWorkspaceCache = Array(); // Array caching workspace access for BE_USER 00325 00326 // Other arrays: 00327 var $dbAnalysisStore=array(); // For accumulation of MM relations that must be written after new records are created. 00328 var $removeFilesStore=array(); // For accumulation of files which must be deleted after processing of all input content 00329 var $uploadedFileArray = array(); // Uploaded files, set by process_uploads() 00330 var $registerDBList=array(); // Used for tracking references that might need correction after operations 00331 var $copyMappingArray = Array(); // Used by the copy action to track the ids of new pages so subpages are correctly inserted! THIS is internally cleared for each executed copy operation! DO NOT USE THIS FROM OUTSIDE! Read from copyMappingArray_merged instead which is accumulating this information. 00332 var $remapStack = array(); // array used for remapping uids and values at the end of process_datamap 00333 var $updateRefIndexStack = array(); // array used for additional calls to $this->updateRefIndex 00334 var $callFromImpExp = false; // tells, that this TCEmain was called from tx_impext - this variable is set by tx_impexp 00335 00336 // Various 00337 var $fileFunc; // For "singleTon" file-manipulation object 00338 var $checkValue_currentRecord=array(); // Set to "currentRecord" during checking of values. 00339 var $autoVersioningUpdate = FALSE; // A signal flag used to tell file processing that autoversioning has happend and hence certain action should be applied. 00340 00341 00342 00343 00344 00345 00346 00347 00348 00349 00350 00351 00362 function start($data,$cmd,$altUserObject='') { 00363 00364 // Initializing BE_USER 00365 $this->BE_USER = is_object($altUserObject) ? $altUserObject : $GLOBALS['BE_USER']; 00366 $this->userid = $this->BE_USER->user['uid']; 00367 $this->username = $this->BE_USER->user['username']; 00368 $this->admin = $this->BE_USER->user['admin']; 00369 00370 if ($GLOBALS['BE_USER']->uc['recursiveDelete']) { 00371 $this->deleteTree = 1; 00372 } 00373 00374 // Initializing default permissions for pages 00375 $defaultPermissions = $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPermissions']; 00376 if (isset($defaultPermissions['user'])) {$this->defaultPermissions['user'] = $defaultPermissions['user'];} 00377 if (isset($defaultPermissions['group'])) {$this->defaultPermissions['group'] = $defaultPermissions['group'];} 00378 if (isset($defaultPermissions['everybody'])) {$this->defaultPermissions['everybody'] = $defaultPermissions['everybody'];} 00379 00380 // generates the excludelist, based on TCA/exclude-flag and non_exclude_fields for the user: 00381 $this->exclude_array = $this->admin ? array() : $this->getExcludeListArray(); 00382 00383 // Setting the data and cmd arrays 00384 if (is_array($data)) { 00385 reset($data); 00386 $this->datamap = $data; 00387 } 00388 if (is_array($cmd)) { 00389 reset($cmd); 00390 $this->cmdmap = $cmd; 00391 } 00392 } 00393 00401 function setMirror($mirror) { 00402 if (is_array($mirror)) { 00403 reset($mirror); 00404 while(list($table,$uid_array)=each($mirror)) { 00405 if (isset($this->datamap[$table])) { 00406 reset($uid_array); 00407 while (list($id,$uidList) = each($uid_array)) { 00408 if (isset($this->datamap[$table][$id])) { 00409 $theIdsInArray = t3lib_div::trimExplode(',',$uidList,1); 00410 while(list(,$copyToUid)=each($theIdsInArray)) { 00411 $this->datamap[$table][$copyToUid] = $this->datamap[$table][$id]; 00412 } 00413 } 00414 } 00415 } 00416 } 00417 } 00418 } 00419 00426 function setDefaultsFromUserTS($userTS) { 00427 global $TCA; 00428 if (is_array($userTS)) { 00429 foreach($userTS as $k => $v) { 00430 $k = substr($k,0,-1); 00431 if ($k && is_array($v) && isset($TCA[$k])) { 00432 if (is_array($this->defaultValues[$k])) { 00433 $this->defaultValues[$k] = array_merge($this->defaultValues[$k],$v); 00434 } else { 00435 $this->defaultValues[$k] = $v; 00436 } 00437 } 00438 } 00439 } 00440 } 00441 00449 function process_uploads($postFiles) { 00450 00451 if (is_array($postFiles)) { 00452 00453 // Editing frozen: 00454 if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze']) { 00455 $this->newlog('All editing in this workspace has been frozen!',1); 00456 return FALSE; 00457 } 00458 00459 reset($postFiles); 00460 $subA = current($postFiles); 00461 if (is_array($subA)) { 00462 if (is_array($subA['name']) && is_array($subA['type']) && is_array($subA['tmp_name']) && is_array($subA['size'])) { 00463 // Initialize the uploadedFilesArray: 00464 $this->uploadedFileArray=array(); 00465 00466 // For each entry: 00467 foreach($subA as $key => $values) { 00468 $this->process_uploads_traverseArray($this->uploadedFileArray,$values,$key); 00469 } 00470 } else { 00471 $this->uploadedFileArray=$subA; 00472 } 00473 } 00474 } 00475 } 00476 00487 function process_uploads_traverseArray(&$outputArr,$inputArr,$keyToSet) { 00488 if (is_array($inputArr)) { 00489 foreach($inputArr as $key => $value) { 00490 $this->process_uploads_traverseArray($outputArr[$key],$inputArr[$key],$keyToSet); 00491 } 00492 } else { 00493 $outputArr[$keyToSet]=$inputArr; 00494 } 00495 } 00496 00497 00498 00499 00500 00501 00502 00503 00504 00505 00506 00507 00508 00509 00510 00511 /********************************************* 00512 * 00513 * PROCESSING DATA 00514 * 00515 *********************************************/ 00516 00523 function process_datamap() { 00524 global $TCA, $TYPO3_CONF_VARS; 00525 00526 // Editing frozen: 00527 if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze']) { 00528 $this->newlog('All editing in this workspace has been frozen!',1); 00529 return FALSE; 00530 } 00531 00532 // First prepare user defined objects (if any) for hooks which extend this function: 00533 $hookObjectsArr = array(); 00534 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'])) { 00535 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'] as $classRef) { 00536 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef); 00537 } 00538 } 00539 00540 // Organize tables so that the pages-table is always processed first. This is required if you want to make sure that content pointing to a new page will be created. 00541 $orderOfTables = Array(); 00542 if (isset($this->datamap['pages'])) { // Set pages first. 00543 $orderOfTables[]='pages'; 00544 } 00545 reset($this->datamap); 00546 while (list($table,) = each($this->datamap)) { 00547 if ($table!='pages') { 00548 $orderOfTables[]=$table; 00549 } 00550 } 00551 00552 // Process the tables... 00553 foreach($orderOfTables as $table) { 00554 /* Check if 00555 - table is set in $TCA, 00556 - table is NOT readOnly 00557 - the table is set with content in the data-array (if not, there's nothing to process...) 00558 - permissions for tableaccess OK 00559 */ 00560 $modifyAccessList = $this->checkModifyAccessList($table); 00561 if (!$modifyAccessList) { 00562 $id = 0; 00563 $this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table)); 00564 } 00565 if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->datamap[$table]) && $modifyAccessList) { 00566 if ($this->reverseOrder) { 00567 $this->datamap[$table] = array_reverse($this->datamap[$table], 1); 00568 } 00569 00570 // For each record from the table, do: 00571 // $id is the record uid, may be a string if new records... 00572 // $incomingFieldArray is the array of fields 00573 foreach($this->datamap[$table] as $id => $incomingFieldArray) { 00574 if (is_array($incomingFieldArray)) { 00575 00576 // Hook: processDatamap_preProcessIncomingFieldArray 00577 foreach($hookObjectsArr as $hookObj) { 00578 if (method_exists($hookObj, 'processDatamap_preProcessFieldArray')) { 00579 $hookObj->processDatamap_preProcessFieldArray($incomingFieldArray, $table, $id, $this); 00580 } 00581 } 00582 00583 // ****************************** 00584 // Checking access to the record 00585 // ****************************** 00586 $createNewVersion = FALSE; 00587 $recordAccess = FALSE; 00588 $old_pid_value = ''; 00589 $resetRejected = FALSE; 00590 $this->autoVersioningUpdate = FALSE; 00591 00592 if (!t3lib_div::testInt($id)) { // Is it a new record? (Then Id is a string) 00593 $fieldArray = $this->newFieldArray($table); // Get a fieldArray with default values 00594 if (isset($incomingFieldArray['pid'])) { // A pid must be set for new records. 00595 // $value = the pid 00596 $pid_value = $incomingFieldArray['pid']; 00597 00598 // Checking and finding numerical pid, it may be a string-reference to another value 00599 $OK = 1; 00600 if (strstr($pid_value,'NEW')) { // If a NEW... id 00601 if (substr($pid_value,0,1)=='-') {$negFlag=-1;$pid_value=substr($pid_value,1);} else {$negFlag=1;} 00602 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. 00603 $old_pid_value = $pid_value; 00604 $pid_value=intval($negFlag*$this->substNEWwithIDs[$pid_value]); 00605 } else {$OK = 0;} // If not found in the substArray we must stop the process... 00606 } elseif ($pid_value>=0 && $this->BE_USER->workspace!==0 && $TCA[$table]['ctrl']['versioning_followPages']) { // PID points to page, the workspace is an offline space and the table follows page during versioning: This means we must check if the PID page has a version in the workspace with swapmode set to 0 (zero = page+content) and if so, change the pid to the uid of that version. 00607 if ($WSdestPage = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, 'pages', $pid_value, 'uid,t3ver_swapmode')) { // Looks for workspace version of page. 00608 if ($WSdestPage['t3ver_swapmode']==0) { // if swapmode is zero, then change pid value. 00609 $pid_value = $WSdestPage['uid']; 00610 } 00611 } 00612 } 00613 $pid_value = intval($pid_value); 00614 00615 // The $pid_value is now the numerical pid at this point 00616 if ($OK) { 00617 $sortRow = $TCA[$table]['ctrl']['sortby']; 00618 if ($pid_value>=0) { // Points to a page on which to insert the element, possibly in the top of the page 00619 if ($sortRow) { // If this table is sorted we better find the top sorting number 00620 $fieldArray[$sortRow] = $this->getSortNumber($table,0,$pid_value); 00621 } 00622 $fieldArray['pid'] = $pid_value; // The numerical pid is inserted in the data array 00623 } else { // points to another record before ifself 00624 if ($sortRow) { // If this table is sorted we better find the top sorting number 00625 $tempArray=$this->getSortNumber($table,0,$pid_value); // Because $pid_value is < 0, getSortNumber returns an array 00626 $fieldArray['pid'] = $tempArray['pid']; 00627 $fieldArray[$sortRow] = $tempArray['sortNumber']; 00628 } else { // Here we fetch the PID of the record that we point to... 00629 $tempdata = $this->recordInfo($table,abs($pid_value),'pid'); 00630 $fieldArray['pid']=$tempdata['pid']; 00631 } 00632 } 00633 } 00634 } 00635 $theRealPid = $fieldArray['pid']; 00636 00637 // Now, check if we may insert records on this pid. 00638 if ($theRealPid>=0) { 00639 $recordAccess = $this->checkRecordInsertAccess($table,$theRealPid); // Checks if records can be inserted on this $pid. 00640 if ($recordAccess) { 00641 $this->addDefaultPermittedLanguageIfNotSet($table,$incomingFieldArray); 00642 $recordAccess = $this->BE_USER->recordEditAccessInternals($table,$incomingFieldArray,TRUE); 00643 if (!$recordAccess) { 00644 $this->newlog("recordEditAccessInternals() check failed. [".$this->BE_USER->errorMsg."]",1); 00645 } elseif(!$this->bypassWorkspaceRestrictions) { 00646 // Workspace related processing: 00647 if ($res = $this->BE_USER->workspaceAllowLiveRecordsInPID($theRealPid,$table)) { // If LIVE records cannot be created in the current PID due to workspace restrictions, prepare creation of placeholder-record 00648 if ($res<0) { 00649 $recordAccess = FALSE; 00650 $this->newlog('Stage for versioning root point and users access level did not allow for editing',1); 00651 } 00652 } else { // So, if no live records were allowed, we have to create a new version of this record: 00653 if ($TCA[$table]['ctrl']['versioningWS']) { 00654 $createNewVersion = TRUE; 00655 } else { 00656 $recordAccess = FALSE; 00657 $this->newlog('Record could not be created in this workspace in this branch',1); 00658 } 00659 } 00660 } 00661 } 00662 } else { 00663 debug('Internal ERROR: pid should not be less than zero!'); 00664 } 00665 $status = 'new'; // Yes new record, change $record_status to 'insert' 00666 } else { // Nope... $id is a number 00667 $fieldArray = array(); 00668 $recordAccess = $this->checkRecordUpdateAccess($table,$id); 00669 if (!$recordAccess) { 00670 $propArr = $this->getRecordProperties($table,$id); 00671 $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']); 00672 } else { // Next check of the record permissions (internals) 00673 $recordAccess = $this->BE_USER->recordEditAccessInternals($table,$id); 00674 if (!$recordAccess) { 00675 $propArr = $this->getRecordProperties($table,$id); 00676 $this->newlog("recordEditAccessInternals() check failed. [".$this->BE_USER->errorMsg."]",1); 00677 } else { // Here we fetch the PID of the record that we point to... 00678 $tempdata = $this->recordInfo($table,$id,'pid'.($TCA[$table]['ctrl']['versioningWS']?',t3ver_wsid,t3ver_stage':'')); 00679 $theRealPid = $tempdata['pid']; 00680 00681 // Prepare the reset of the rejected flag if set: 00682 if ($TCA[$table]['ctrl']['versioningWS'] && $tempdata['t3ver_stage']<0) { 00683 $resetRejected = TRUE; 00684 } 00685 00686 // Checking access in case of offline workspace: 00687 if (!$this->bypassWorkspaceRestrictions && $errorCode = $this->BE_USER->workspaceCannotEditRecord($table,$tempdata)) { 00688 $recordAccess = FALSE; // Versioning is required and it must be offline version! 00689 00690 // Auto-creation of version: In offline workspace, test if versioning is enabled and look for workspace version of input record. If there is no versionized record found we will create one and save to that. 00691 if ($this->BE_USER->workspaceAllowAutoCreation($table,$id,$theRealPid)) { 00692 $tce = t3lib_div::makeInstance('t3lib_TCEmain'); 00693 $tce->stripslashes_values = 0; 00694 00695 // Setting up command for creating a new version of the record: 00696 $cmd = array(); 00697 $cmd[$table][$id]['version'] = array( 00698 'action' => 'new', 00699 'treeLevels' => -1, // Default is to create a version of the individual records... 00700 'label' => 'Auto-created for WS #'.$this->BE_USER->workspace 00701 ); 00702 $tce->start(array(),$cmd); 00703 $tce->process_cmdmap(); 00704 $this->errorLog = array_merge($this->errorLog,$tce->errorLog); 00705 00706 if ($tce->copyMappingArray[$table][$id]) { 00707 $this->uploadedFileArray[$table][$tce->copyMappingArray[$table][$id]] = $this->uploadedFileArray[$table][$id]; 00708 $id = $this->autoVersionIdMap[$table][$id] = $tce->copyMappingArray[$table][$id]; 00709 $recordAccess = TRUE; 00710 $this->autoVersioningUpdate = TRUE; 00711 } else $this->newlog("Could not be edited in offline workspace in the branch where found (failure state: '".$errorCode."'). Auto-creation of version failed!",1); 00712 } else $this->newlog("Could not be edited in offline workspace in the branch where found (failure state: '".$errorCode."'). Auto-creation of version not allowed in workspace!",1); 00713 } 00714 } 00715 } 00716 $status = 'update'; // the default is 'update' 00717 } 00718 00719 // If access was granted above, proceed to create or update record: 00720 if ($recordAccess) { 00721 00722 list($tscPID) = t3lib_BEfunc::getTSCpid($table,$id,$old_pid_value ? $old_pid_value : $fieldArray['pid']); // Here the "pid" is set IF NOT the old pid was a string pointing to a place in the subst-id array. 00723 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 00724 if ($status=='new' && $table=='pages' && is_array($TSConfig['permissions.'])) { 00725 $fieldArray = $this->setTSconfigPermissions($fieldArray,$TSConfig['permissions.']); 00726 } 00727 if ($createNewVersion) { 00728 $newVersion_placeholderFieldArray = $fieldArray; 00729 } 00730 00731 // Processing of all fields in incomingFieldArray and setting them in $fieldArray 00732 $fieldArray = $this->fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$theRealPid,$status,$tscPID); 00733 00734 // NOTICE! All manipulation beyond this point bypasses both "excludeFields" AND possible "MM" relations / file uploads to field! 00735 00736 // Forcing some values unto field array: 00737 $fieldArray = $this->overrideFieldArray($table,$fieldArray); // NOTICE: This overriding is potentially dangerous; permissions per field is not checked!!! 00738 if ($createNewVersion) { 00739 $newVersion_placeholderFieldArray = $this->overrideFieldArray($table,$newVersion_placeholderFieldArray); 00740 } 00741 00742 // Setting system fields 00743 if ($status=='new') { 00744 if ($TCA[$table]['ctrl']['crdate']) { 00745 $fieldArray[$TCA[$table]['ctrl']['crdate']]=time(); 00746 if ($createNewVersion) $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['crdate']]=time(); 00747 } 00748 if ($TCA[$table]['ctrl']['cruser_id']) { 00749 $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid; 00750 if ($createNewVersion) $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid; 00751 } 00752 } elseif ($this->checkSimilar) { // Removing fields which are equal to the current value: 00753 $fieldArray = $this->compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray); 00754 } 00755 if ($TCA[$table]['ctrl']['tstamp'] && count($fieldArray)) { 00756 $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time(); 00757 if ($createNewVersion) $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['tstamp']]=time(); 00758 } 00759 if ($resetRejected) { 00760 $fieldArray['t3ver_stage'] = 0; 00761 } 00762 00763 // Hook: processDatamap_postProcessFieldArray 00764 foreach($hookObjectsArr as $hookObj) { 00765 if (method_exists($hookObj, 'processDatamap_postProcessFieldArray')) { 00766 $hookObj->processDatamap_postProcessFieldArray($status, $table, $id, $fieldArray, $this); 00767 } 00768 } 00769 00770 // Performing insert/update. If fieldArray has been unset by some userfunction (see hook above), don't do anything 00771 // Kasper: Unsetting the fieldArray is dangerous; MM relations might be saved already and files could have been uploaded that are now "lost" 00772 if (is_array($fieldArray)) { 00773 if ($status=='new') { 00774 if ($createNewVersion) { // This creates a new version of the record with online placeholder and offline version 00775 $versioningType = $table==='pages' ? $this->BE_USER->workspaceVersioningTypeGetClosest(t3lib_div::intInRange($TYPO3_CONF_VARS['BE']['newPagesVersioningType'],-1,1)) : -1; 00776 if ($this->BE_USER->workspaceVersioningTypeAccess($versioningType)) { 00777 $newVersion_placeholderFieldArray['t3ver_label'] = 'INITIAL PLACEHOLDER'; 00778 $newVersion_placeholderFieldArray['t3ver_state'] = 1; // Setting placeholder state value for temporary record 00779 $newVersion_placeholderFieldArray['t3ver_wsid'] = $this->BE_USER->workspace; // Setting workspace - only so display of place holders can filter out those from other workspaces. 00780 $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['label']] = '[PLACEHOLDER, WS#'.$this->BE_USER->workspace.']'; 00781 $this->insertDB($table,$id,$newVersion_placeholderFieldArray,FALSE); // Saving placeholder as 'original' 00782 00783 // For the actual new offline version, set versioning values to point to placeholder: 00784 $fieldArray['pid'] = -1; 00785 $fieldArray['t3ver_oid'] = $this->substNEWwithIDs[$id]; 00786 $fieldArray['t3ver_id'] = 1; 00787 $fieldArray['t3ver_state'] = -1; // Setting placeholder state value for version (so it can know it is currently a new version...) 00788 $fieldArray['t3ver_label'] = 'First draft version'; 00789 $fieldArray['t3ver_wsid'] = $this->BE_USER->workspace; 00790 if ($table==='pages') { // Swap mode set to "branch" so we can build branches for pages. 00791 $fieldArray['t3ver_swapmode'] = $versioningType; 00792 } 00793 $phShadowId = $this->insertDB($table,$id,$fieldArray,TRUE,0,TRUE); // When inserted, $this->substNEWwithIDs[$id] will be changed to the uid of THIS version and so the interface will pick it up just nice! 00794 if ($phShadowId) { 00795 $this->placeholderShadowing($table,$phShadowId); 00796 } 00797 } else $this->newlog('Versioning type "'.$versioningType.'" was not allowed, so could not create new record.',1); 00798 } else { 00799 $this->insertDB($table,$id,$fieldArray,FALSE,$incomingFieldArray['uid']); 00800 } 00801 } else { 00802 $this->updateDB($table,$id,$fieldArray); 00803 $this->placeholderShadowing($table,$id); 00804 } 00805 } 00806 00807 /* 00808 * Hook: processDatamap_afterDatabaseOperations 00809 * 00810 * Note: When using the hook after INSERT operations, you will only get the temporary NEW... id passed to your hook as $id, 00811 * but you can easily translate it to the real uid of the inserted record using the $this->substNEWwithIDs array. 00812 */ 00813 foreach($hookObjectsArr as $hookObj) { 00814 if (method_exists($hookObj, 'processDatamap_afterDatabaseOperations')) { 00815 $hookObj->processDatamap_afterDatabaseOperations($status, $table, $id, $fieldArray, $this); 00816 } 00817 } 00818 } // if ($recordAccess) { 00819 } // if (is_array($incomingFieldArray)) { 00820 } 00821 } 00822 } 00823 00824 // call_user_func_array 00825 00826 // Process the stack of relations to remap/correct 00827 if(is_array($this->remapStack)) { 00828 foreach($this->remapStack as $remapAction) { 00829 // if no position index for the arguments was set, skip this remap action 00830 if (!is_array($remapAction['pos'])) continue; 00831 00832 // load values from the argument array in remapAction 00833 $field = $remapAction['field']; 00834 $id = $remapAction['args'][$remapAction['pos']['id']]; 00835 $table = $remapAction['args'][$remapAction['pos']['table']]; 00836 $valueArray = $remapAction['args'][$remapAction['pos']['valueArray']]; 00837 $tcaFieldConf = $remapAction['args'][$remapAction['pos']['tcaFieldConf']]; 00838 00839 // Replace NEW... IDs with real uids. 00840 if(strpos($id, 'NEW') !== false) { 00841 $id = $this->substNEWwithIDs[$id]; 00842 $remapAction['args'][$remapAction['pos']['id']] = $id; 00843 } 00844 00845 // Replace relations to NEW...-IDs in values 00846 if(is_array($valueArray)) { 00847 foreach($valueArray as $key => $value) { 00848 if(strpos($value, 'NEW') !== false) { 00849 // fetch the proper uid as integer for the NEW...-ID 00850 $valueArray[$key] = $this->substNEWwithIDs[$value]; 00851 // set a hint that this was a new child record 00852 $this->newRelatedIDs[$table][] = $valueArray[$key]; 00853 } 00854 } 00855 $remapAction['args'][$remapAction['pos']['valueArray']] = $valueArray; 00856 } 00857 00858 // process the arguments with the defined function 00859 $remapAction['args'][$remapAction['pos']['valueArray']] = call_user_func_array( 00860 array($this, $remapAction['func']), 00861 $remapAction['args'] 00862 ); 00863 00864 // @TODO: Add option to disable count-field 00865 $newVal = $this->checkValue_checkMax($tcaFieldConf, $remapAction['args'][$remapAction['pos']['valueArray']]); 00866 $this->updateDB($table,$id,array($field => implode(',', $newVal))); 00867 } 00868 } 00869 00870 $this->dbAnalysisStoreExec(); 00871 $this->removeRegisteredFiles(); 00872 } 00873 00881 function placeholderShadowing($table,$id) { 00882 global $TCA; 00883 00884 t3lib_div::loadTCA($table); 00885 if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'*')) { 00886 if ((int)$liveRec['t3ver_state']===1) { 00887 $justStoredRecord = t3lib_BEfunc::getRecord($table,$id); 00888 $newRecord = array(); 00889 00890 $shadowCols = $TCA[$table]['ctrl']['shadowColumnsForNewPlaceholders']; 00891 $shadowCols.= ','.$TCA[$table]['ctrl']['languageField']; 00892 $shadowCols.= ','.$TCA[$table]['ctrl']['transOrigPointerField']; 00893 $shadowCols.= ','.$TCA[$table]['ctrl']['type']; 00894 $shadowCols.= ','.$TCA[$table]['ctrl']['label']; 00895 00896 $shadowColumns = array_unique(t3lib_div::trimExplode(',', $shadowCols,1)); 00897 foreach($shadowColumns as $fieldName) { 00898 if (strcmp($justStoredRecord[$fieldName],$liveRec[$fieldName]) && isset($TCA[$table]['columns'][$fieldName]) && $fieldName!=='uid' && $fieldName!=='pid') { 00899 $newRecord[$fieldName] = $justStoredRecord[$fieldName]; 00900 } 00901 } 00902 00903 if (count($newRecord)) { 00904 $this->newlog('Shadowing done on fields '.implode(',',array_keys($newRecord)).' in Placeholder record '.$table.':'.$liveRec['uid'].' (offline version UID='.$id.')'); 00905 $this->updateDB($table,$liveRec['uid'],$newRecord); 00906 } 00907 } 00908 } 00909 } 00910 00924 function fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$realPid,$status,$tscPID) { 00925 global $TCA; 00926 00927 // Initialize: 00928 t3lib_div::loadTCA($table); 00929 $originalLanguageRecord = NULL; 00930 $originalLanguage_diffStorage = NULL; 00931 $diffStorageFlag = FALSE; 00932 00933 // Setting 'currentRecord' and 'checkValueRecord': 00934 if (strstr($id,'NEW')) { 00935 $currentRecord = $checkValueRecord = $fieldArray; // must have the 'current' array - not the values after processing below... 00936 00937 // IF $incomingFieldArray is an array, overlay it. 00938 // 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... 00939 if (is_array($incomingFieldArray) && is_array($checkValueRecord)) { 00940 $checkValueRecord = t3lib_div::array_merge_recursive_overrule($checkValueRecord, $incomingFieldArray); 00941 } 00942 } else { 00943 $currentRecord = $checkValueRecord = $this->recordInfo($table,$id,'*'); // We must use the current values as basis for this! 00944 00945 t3lib_BEfunc::fixVersioningPid($table,$currentRecord); // This is done to make the pid positive for offline versions; Necessary to have diff-view for pages_language_overlay in workspaces. 00946 00947 // Get original language record if available: 00948 if (is_array($currentRecord) 00949 && $TCA[$table]['ctrl']['transOrigDiffSourceField'] 00950 && $TCA[$table]['ctrl']['languageField'] 00951 && $currentRecord[$TCA[$table]['ctrl']['languageField']] > 0 00952 && $TCA[$table]['ctrl']['transOrigPointerField'] 00953 && intval($currentRecord[$TCA[$table]['ctrl']['transOrigPointerField']]) > 0) { 00954 00955 $lookUpTable = $TCA[$table]['ctrl']['transOrigPointerTable'] ? $TCA[$table]['ctrl']['transOrigPointerTable'] : $table; 00956 $originalLanguageRecord = $this->recordInfo($lookUpTable,$currentRecord[$TCA[$table]['ctrl']['transOrigPointerField']],'*'); 00957 t3lib_BEfunc::workspaceOL($lookUpTable,$originalLanguageRecord); 00958 $originalLanguage_diffStorage = unserialize($currentRecord[$TCA[$table]['ctrl']['transOrigDiffSourceField']]); 00959 } 00960 } 00961 $this->checkValue_currentRecord = $checkValueRecord; 00962 00963 /* 00964 In the following all incoming value-fields are tested: 00965 - Are the user allowed to change the field? 00966 - Is the field uid/pid (which are already set) 00967 - perms-fields for pages-table, then do special things... 00968 - If the field is nothing of the above and the field is configured in TCA, the fieldvalues are evaluated by ->checkValue 00969 00970 If everything is OK, the field is entered into $fieldArray[] 00971 */ 00972 foreach($incomingFieldArray as $field => $fieldValue) { 00973 if (!in_array($table.'-'.$field, $this->exclude_array) && !$this->data_disableFields[$table][$id][$field]) { // The field must be editable. 00974 00975 // Checking if a value for language can be changed: 00976 $languageDeny = $TCA[$table]['ctrl']['languageField'] && !strcmp($TCA[$table]['ctrl']['languageField'], $field) && !$this->BE_USER->checkLanguageAccess($fieldValue); 00977 00978 if (!$languageDeny) { 00979 // Stripping slashes - will probably be removed the day $this->stripslashes_values is removed as an option... 00980 if ($this->stripslashes_values) { 00981 if (is_array($fieldValue)) { 00982 t3lib_div::stripSlashesOnArray($fieldValue); 00983 } else $fieldValue = stripslashes($fieldValue); 00984 } 00985 00986 switch ($field) { 00987 case 'uid': 00988 case 'pid': 00989 // Nothing happens, already set 00990 break; 00991 case 'perms_userid': 00992 case 'perms_groupid': 00993 case 'perms_user': 00994 case 'perms_group': 00995 case 'perms_everybody': 00996 // Permissions can be edited by the owner or the administrator 00997 if ($table=='pages' && ($this->admin || $status=='new' || $this->pageInfo($id,'perms_userid')==$this->userid) ) { 00998 $value=intval($fieldValue); 00999 switch($field) { 01000 case 'perms_userid': 01001 $fieldArray[$field]=$value; 01002 break; 01003 case 'perms_groupid': 01004 $fieldArray[$field]=$value; 01005 break; 01006 default: 01007 if ($value>=0 && $value<pow(2,5)) { 01008 $fieldArray[$field]=$value; 01009 } 01010 break; 01011 } 01012 } 01013 break; 01014 case 't3ver_oid': 01015 case 't3ver_id': 01016 case 't3ver_wsid': 01017 case 't3ver_state': 01018 case 't3ver_swapmode': 01019 case 't3ver_count': 01020 case 't3ver_stage': 01021 case 't3ver_tstamp': 01022 // t3ver_label is not here because it CAN be edited as a regular field! 01023 break; 01024 default: 01025 if (isset($TCA[$table]['columns'][$field])) { 01026 // Evaluating the value. 01027 $res = $this->checkValue($table,$field,$fieldValue,$id,$status,$realPid,$tscPID); 01028 if (isset($res['value'])) { 01029 $fieldArray[$field]=$res['value']; 01030 01031 // Add the value of the original record to the diff-storage content: 01032 if ($TCA[$table]['ctrl']['transOrigDiffSourceField']) { 01033 $originalLanguage_diffStorage[$field] = $originalLanguageRecord[$field]; 01034 $diffStorageFlag = TRUE; 01035 } 01036 } 01037 } elseif ($TCA[$table]['ctrl']['origUid']===$field) { // Allow value for original UID to pass by... 01038 $fieldArray[$field] = $fieldValue; 01039 } 01040 break; 01041 } 01042 } // Checking language. 01043 } // Check exclude fields / disabled fields... 01044 } 01045 01046 // Add diff-storage information: 01047 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... 01048 $fieldArray[$TCA[$table]['ctrl']['transOrigDiffSourceField']] = serialize($originalLanguage_diffStorage); 01049 } 01050 01051 // Checking for RTE-transformations of fields: 01052 $types_fieldConfig = t3lib_BEfunc::getTCAtypes($table,$currentRecord); 01053 $theTypeString = t3lib_BEfunc::getTCAtypeValue($table,$currentRecord); 01054 if (is_array($types_fieldConfig)) { 01055 reset($types_fieldConfig); 01056 while(list(,$vconf) = each($types_fieldConfig)) { 01057 // Write file configuration: 01058 $eFile = t3lib_parsehtml_proc::evalWriteFile($vconf['spec']['static_write'],array_merge($currentRecord,$fieldArray)); // inserted array_merge($currentRecord,$fieldArray) 170502 01059 01060 // RTE transformations: 01061 if (!$this->dontProcessTransformations) { 01062 if (isset($fieldArray[$vconf['field']])) { 01063 // Look for transformation flag: 01064 switch((string)$incomingFieldArray['_TRANSFORM_'.$vconf['field']]) { 01065 case 'RTE': 01066 $RTEsetup = $this->BE_USER->getTSConfig('RTE',t3lib_BEfunc::getPagesTSconfig($tscPID)); 01067 $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'],$table,$vconf['field'],$theTypeString); 01068 01069 // Set alternative relative path for RTE images/links: 01070 $RTErelPath = is_array($eFile) ? dirname($eFile['relEditFile']) : ''; 01071 01072 // Get RTE object, draw form and set flag: 01073 $RTEobj = &t3lib_BEfunc::RTEgetObj(); 01074 if (is_object($RTEobj)) { 01075 $fieldArray[$vconf['field']] = $RTEobj->transformContent('db',$fieldArray[$vconf['field']],$table,$vconf['field'],$currentRecord,$vconf['spec'],$thisConfig,$RTErelPath,$currentRecord['pid']); 01076 } else { 01077 debug('NO RTE OBJECT FOUND!'); 01078 } 01079 break; 01080 } 01081 } 01082 } 01083 01084 // Write file configuration: 01085 if (is_array($eFile)) { 01086 $mixedRec = array_merge($currentRecord,$fieldArray); 01087 $SW_fileContent = t3lib_div::getUrl($eFile['editFile']); 01088 $parseHTML = t3lib_div::makeInstance('t3lib_parsehtml_proc'); 01089 $parseHTML->init('',''); 01090 01091 $eFileMarker = $eFile['markerField']&&trim($mixedRec[$eFile['markerField']]) ? trim($mixedRec[$eFile['markerField']]) : '###TYPO3_STATICFILE_EDIT###'; 01092 $insertContent = str_replace($eFileMarker,'',$mixedRec[$eFile['contentField']]); // must replace the marker if present in content! 01093 01094 $SW_fileNewContent = $parseHTML->substituteSubpart($SW_fileContent, $eFileMarker, chr(10).$insertContent.chr(10), 1, 1); 01095 t3lib_div::writeFile($eFile['editFile'],$SW_fileNewContent); 01096 01097 // Write status: 01098 if (!strstr($id,'NEW') && $eFile['statusField']) { 01099 $GLOBALS['TYPO3_DB']->exec_UPDATEquery( 01100 $table, 01101 'uid='.intval($id), 01102 array( 01103 $eFile['statusField'] => $eFile['relEditFile'].' updated '.date('d-m-Y H:i:s').', bytes '.strlen($mixedRec[$eFile['contentField']]) 01104 ) 01105 ); 01106 } 01107 } elseif ($eFile && is_string($eFile)) { 01108 $this->log($table,$id,2,0,1,"Write-file error: '%s'",13,array($eFile),$realPid); 01109 } 01110 } 01111 } 01112 // Return fieldArray 01113 return $fieldArray; 01114 } 01115 01116 01117 01118 01119 01120 01121 01122 01123 01124 01125 01126 01127 /********************************************* 01128 * 01129 * Evaluation of input values 01130 * 01131 ********************************************/ 01132 01147 function checkValue($table,$field,$value,$id,$status,$realPid,$tscPID) { 01148 global $TCA, $PAGES_TYPES; 01149 t3lib_div::loadTCA($table); 01150 01151 $res = Array(); // result array 01152 $recFID = $table.':'.$id.':'.$field; 01153 01154 // Processing special case of field pages.doktype 01155 if ($table=='pages' && $field=='doktype') { 01156 // If the user may not use this specific doktype, we issue a warning 01157 if (! ($this->admin || t3lib_div::inList($this->BE_USER->groupData['pagetypes_select'],$value))) { 01158 $propArr = $this->getRecordProperties($table,$id); 01159 $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']); 01160 return $res; 01161 }; 01162 if ($status=='update') { 01163 // This checks 1) if we should check for disallowed tables and 2) if there are records from disallowed tables on the current page 01164 $onlyAllowedTables = isset($PAGES_TYPES[$value]['onlyAllowedTables']) ? $PAGES_TYPES[$value]['onlyAllowedTables'] : $PAGES_TYPES['default']['onlyAllowedTables']; 01165 if ($onlyAllowedTables) { 01166 $theWrongTables = $this->doesPageHaveUnallowedTables($id,$value); 01167 if ($theWrongTables) { 01168 $propArr = $this->getRecordProperties($table,$id); 01169 $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']); 01170 return $res; 01171 } 01172 } 01173 } 01174 } 01175 01176 // Get current value: 01177 $curValueRec = $this->recordInfo($table,$id,$field); 01178 $curValue = $curValueRec[$field]; 01179 01180 // Getting config for the field 01181 $tcaFieldConf = $TCA[$table]['columns'][$field]['config']; 01182 01183 // Preform processing: 01184 $res = $this->checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$this->uploadedFileArray[$table][$id][$field],$tscPID); 01185 01186 return $res; 01187 } 01188 01207 function checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$uploadedFiles,$tscPID) { 01208 01209 $PP = array($table,$id,$curValue,$status,$realPid,$recFID,$tscPID); 01210 01211 switch ($tcaFieldConf['type']) { 01212 case 'text': 01213 case 'passthrough': 01214 case 'user': 01215 $res['value'] = $value; 01216 break; 01217 case 'input': 01218 $res = $this->checkValue_input($res,$value,$tcaFieldConf,$PP,$field); 01219 break; 01220 case 'check': 01221 $res = $this->checkValue_check($res,$value,$tcaFieldConf,$PP); 01222 break; 01223 case 'radio': 01224 $res = $this->checkValue_radio($res,$value,$tcaFieldConf,$PP); 01225 break; 01226 case 'group': 01227 case 'select': 01228 $res = $this->checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field); 01229 break; 01230 case 'inline': 01231 $res = $this->checkValue_inline($res,$value,$tcaFieldConf,$PP,$field); 01232 break; 01233 case 'flex': 01234 if ($field) { // FlexForms are only allowed for real fields. 01235 $res = $this->checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field); 01236 } 01237 break; 01238 default: 01239 #debug(array($tcaFieldConf,$res,$value),'NON existing field type:'); 01240 break; 01241 } 01242 01243 return $res; 01244 } 01245 01256 function checkValue_input($res,$value,$tcaFieldConf,$PP,$field='') { 01257 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01258 01259 // Secures the string-length to be less than max. Will probably make problems with multi-byte strings! 01260 if (intval($tcaFieldConf['max'])>0) {$value = substr($value,0,intval($tcaFieldConf['max']));} 01261 01262 // Checking range of value: 01263 if ($tcaFieldConf['range'] && $value!=$tcaFieldConf['checkbox']) { // If value is not set to the allowed checkbox-value then it is checked against the ranges 01264 if (isset($tcaFieldConf['range']['upper'])&&$value>$tcaFieldConf['range']['upper']) {$value=$tcaFieldConf['range']['upper'];} 01265 if (isset($tcaFieldConf['range']['lower'])&&$value<$tcaFieldConf['range']['lower']) {$value=$tcaFieldConf['range']['lower'];} 01266 } 01267 01268 // Process evaluation settings: 01269 $evalCodesArray = t3lib_div::trimExplode(',',$tcaFieldConf['eval'],1); 01270 $res = $this->checkValue_input_Eval($value,$evalCodesArray,$tcaFieldConf['is_in']); 01271 01272 // Process UNIQUE settings: 01273 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... 01274 if ($res['value'] && in_array('uniqueInPid',$evalCodesArray)) { 01275 $res['value'] = $this->getUnique($table,$field,$res['value'],$id,$realPid); 01276 } 01277 if ($res['value'] && in_array('unique',$evalCodesArray)) { 01278 $res['value'] = $this->getUnique($table,$field,$res['value'],$id); 01279 } 01280 } 01281 01282 return $res; 01283 } 01284 01294 function checkValue_check($res,$value,$tcaFieldConf,$PP) { 01295 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01296 01297 $itemC = count($tcaFieldConf['items']); 01298 if (!$itemC) {$itemC=1;} 01299 $maxV = pow(2,$itemC); 01300 01301 if ($value<0) {$value=0;} 01302 if ($value>$maxV) {$value=$maxV;} 01303 $res['value'] = $value; 01304 01305 return $res; 01306 } 01307 01317 function checkValue_radio($res,$value,$tcaFieldConf,$PP) { 01318 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01319 01320 if (is_array($tcaFieldConf['items'])) { 01321 foreach($tcaFieldConf['items'] as $set) { 01322 if (!strcmp($set[1],$value)) { 01323 $res['value'] = $value; 01324 break; 01325 } 01326 } 01327 } 01328 01329 return $res; 01330 } 01331 01343 function checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field) { 01344 01345 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01346 01347 // Detecting if value sent is an array and if so, implode it around a comma: 01348 if (is_array($value)) { 01349 $value = implode(',',$value); 01350 } 01351 01352 // This converts all occurencies of '{' to the byte 123 in the string - this is needed in very rare cases where filenames with special characters (like ???, umlaud etc) gets sent to the server as HTML entities instead of bytes. The error is done only by MSIE, not Mozilla and Opera. 01353 // Anyways, this should NOT disturb anything else: 01354 $value = $this->convNumEntityToByteValue($value); 01355 01356 // When values are sent as group or select they come as comma-separated values which are exploded by this function: 01357 $valueArray = $this->checkValue_group_select_explodeSelectGroupValue($value); 01358 01359 // If not multiple is set, then remove duplicates: 01360 if (!$tcaFieldConf['multiple']) { 01361 $valueArray = array_unique($valueArray); 01362 } 01363 01364 // If an exclusive key is found, discard all others: 01365 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['exclusiveKeys']) { 01366 $exclusiveKeys = t3lib_div::trimExplode(',', $tcaFieldConf['exclusiveKeys']); 01367 foreach($valueArray as $kk => $vv) { 01368 if (in_array($vv, $exclusiveKeys)) { // $vv is the item key! 01369 $valueArray = Array($kk => $vv); 01370 break; 01371 } 01372 } 01373 } 01374 01375 // This could be a good spot for parsing the array through a validation-function which checks if the values are alright (except that database references are not in their final form - but that is the point, isn't it?) 01376 // NOTE!!! Must check max-items of files before the later check because that check would just leave out filenames if there are too many!! 01377 01378 // Checking for select / authMode, removing elements from $valueArray if any of them is not allowed! 01379 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['authMode']) { 01380 $preCount = count($valueArray); 01381 foreach($valueArray as $kk => $vv) { 01382 if (!$this->BE_USER->checkAuthMode($table,$field,$vv,$tcaFieldConf['authMode'])) { 01383 unset($valueArray[$kk]); 01384 } 01385 } 01386 01387 // 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. 01388 if ($preCount && !count($valueArray)) { 01389 return array(); 01390 } 01391 } 01392 01393 // For group types: 01394 if ($tcaFieldConf['type']=='group') { 01395 switch($tcaFieldConf['internal_type']) { 01396 case 'file': 01397 $valueArray = $this->checkValue_group_select_file( 01398 $valueArray, 01399 $tcaFieldConf, 01400 $curValue, 01401 $uploadedFiles, 01402 $status, 01403 $table, 01404 $id, 01405 $recFID 01406 ); 01407 break; 01408 case 'db': 01409 $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'group', $table); 01410 break; 01411 } 01412 } 01413 // For select types which has a foreign table attached: 01414 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['foreign_table']) { 01415 // check, if there is a NEW... id in the value, that should be substituded later 01416 if (strpos($value, 'NEW') !== false) { 01417 $this->remapStack[] = array( 01418 'func' => 'checkValue_group_select_processDBdata', 01419 'args' => array($valueArray,$tcaFieldConf,$id,$status,'select',$table), 01420 'pos' => array('valueArray' => 0, 'tcaFieldConf' => 1, 'id' => 2, 'table' => 5), 01421 'field' => $field 01422 ); 01423 $unsetResult = true; 01424 } else { 01425 $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'select', $table); 01426 } 01427 } 01428 01429 if (!$unsetResult) { 01430 $newVal=$this->checkValue_checkMax($tcaFieldConf, $valueArray); 01431 $res['value'] = implode(',',$newVal); 01432 } else { 01433 unset($res['value']); 01434 } 01435 01436 return $res; 01437 } 01438 01453 function checkValue_group_select_file($valueArray,$tcaFieldConf,$curValue,$uploadedFileArray,$status,$table,$id,$recFID) { 01454 01455 if (!$this->bypassFileHandling) { // If filehandling should NOT be bypassed, do processing: 01456 01457 // If any files are uploaded, add them to value array 01458 if (is_array($uploadedFileArray) && 01459 $uploadedFileArray['name'] && 01460 strcmp($uploadedFileArray['tmp_name'],'none')) { 01461 $valueArray[]=$uploadedFileArray['tmp_name']; 01462 $this->alternativeFileName[$uploadedFileArray['tmp_name']] = $uploadedFileArray['name']; 01463 } 01464 01465 // Creating fileFunc object. 01466 if (!$this->fileFunc) { 01467 $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions'); 01468 $this->include_filefunctions=1; 01469 } 01470 // Setting permitted extensions. 01471 $all_files = Array(); 01472 $all_files['webspace']['allow'] = $tcaFieldConf['allowed']; 01473 $all_files['webspace']['deny'] = $tcaFieldConf['disallowed'] ? $tcaFieldConf['disallowed'] : '*'; 01474 $all_files['ftpspace'] = $all_files['webspace']; 01475 $this->fileFunc->init('', $all_files); 01476 } 01477 01478 // If there is an upload folder defined: 01479 if ($tcaFieldConf['uploadfolder']) { 01480 if (!$this->bypassFileHandling) { // If filehandling should NOT be bypassed, do processing: 01481 // For logging.. 01482 $propArr = $this->getRecordProperties($table,$id); 01483 01484 // Get destrination path: 01485 $dest = $this->destPathFromUploadFolder($tcaFieldConf['uploadfolder']); 01486 01487 // If we are updating: 01488 if ($status=='update') { 01489 01490 // Traverse the input values and convert to absolute filenames in case the update happens to an autoVersionized record. 01491 // Background: This is a horrible workaround! The problem is that when a record is auto-versionized the files of the record get copied and therefore get new names which is overridden with the names from the original record in the incoming data meaning both lost files and double-references! 01492 // The only solution I could come up with (except removing support for managing files when autoversioning) was to convert all relative files to absolute names so they are copied again (and existing files deleted). This should keep references intact but means that some files are copied, then deleted after being copied _again_. 01493 // Actually, the same problem applies to database references in case auto-versioning would include sub-records since in such a case references are remapped - and they would be overridden due to the same principle then. 01494 // Illustration of the problem comes here: 01495 // We have a record 123 with a file logo.gif. We open and edit the files header in a workspace. So a new version is automatically made. 01496 // The versions uid is 456 and the file is copied to "logo_01.gif". But the form data that we sents was based on uid 123 and hence contains the filename "logo.gif" from the original. 01497 // The file management code below will do two things: First it will blindly accept "logo.gif" as a file attached to the record (thus creating a double reference) and secondly it will find that "logo_01.gif" was not in the incoming filelist and therefore should be deleted. 01498 // If we prefix the incoming file "logo.gif" with its absolute path it will be seen as a new file added. Thus it will be copied to "logo_02.gif". "logo_01.gif" will still be deleted but since the files are the same the difference is zero - only more processing and file copying for no reason. But it will work. 01499 if ($this->autoVersioningUpdate===TRUE) { 01500 foreach($valueArray as $key => $theFile) { 01501 if ($theFile===basename($theFile)) { // If it is an already attached file... 01502 $valueArray[$key] = PATH_site.$tcaFieldConf['uploadfolder'].'/'.$theFile; 01503 } 01504 } 01505 } 01506 01507 // Finding the CURRENT files listed, either from MM or from the current record. 01508 $theFileValues=array(); 01509 if ($tcaFieldConf['MM']) { // If MM relations for the files also! 01510 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 01511 $dbAnalysis->start('','files',$tcaFieldConf['MM'],$id); 01512 reset($dbAnalysis->itemArray); 01513 while (list($somekey,$someval)=each($dbAnalysis->itemArray)) { 01514 if ($someval['id']) { 01515 $theFileValues[]=$someval['id']; 01516 } 01517 } 01518 } else { 01519 $theFileValues=t3lib_div::trimExplode(',',$curValue,1); 01520 } 01521 01522 // DELETE files: If existing files were found, traverse those and register files for deletion which has been removed: 01523 if (count($theFileValues)) { 01524 // 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!) 01525 foreach($valueArray as $key => $theFile) { 01526 if ($theFile && !strstr(t3lib_div::fixWindowsFilePath($theFile),'/')) { 01527 $theFileValues = t3lib_div::removeArrayEntryByValue($theFileValues,$theFile); 01528 } 01529 } 01530 01531 // This array contains the filenames in the uploadfolder that should be deleted: 01532 foreach($theFileValues as $key => $theFile) { 01533 $theFile = trim($theFile); 01534 if (@is_file($dest.'/'.$theFile)) { 01535 $this->removeFilesStore[]=$dest.'/'.$theFile; 01536 } elseif ($theFile) { 01537 $this->log($table,$id,5,0,1,"Could not delete file '%s' (does not exist). (%s)",10,array($dest.'/'.$theFile, $recFID),$propArr['event_pid']); 01538 } 01539 } 01540 } 01541 } 01542 01543 // Traverse the submitted values: 01544 foreach($valueArray as $key => $theFile) { 01545 // 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) 01546 if (strstr(t3lib_div::fixWindowsFilePath($theFile),'/')) { 01547 // Init: 01548 $maxSize = intval($tcaFieldConf['max_size']); 01549 $cmd=''; 01550 $theDestFile=''; // Must be cleared. Else a faulty fileref may be inserted if the below code returns an error! 01551 01552 // Check various things before copying file: 01553 if (@is_dir($dest) && (@is_file($theFile) || @is_uploaded_file($theFile))) { // File and destination must exist 01554 01555 // Finding size. For safe_mode we have to rely on the size in the upload array if the file is uploaded. 01556 if (is_uploaded_file($theFile) && $theFile==$uploadedFileArray['tmp_name']) { 01557 $fileSize = $uploadedFileArray['size']; 01558 } else { 01559 $fileSize = filesize($theFile); 01560 } 01561 01562 if (!$maxSize || $fileSize<=($maxSize*1024)) { // Check file size: 01563 // Prepare filename: 01564 $theEndFileName = isset($this->alternativeFileName[$theFile]) ? $this->alternativeFileName[$theFile] : $theFile; 01565 $fI = t3lib_div::split_fileref($theEndFileName); 01566 01567 // Check for allowed extension: 01568 if ($this->fileFunc->checkIfAllowed($fI['fileext'], $dest, $theEndFileName)) { 01569 $theDestFile = $this->fileFunc->getUniqueName($this->fileFunc->cleanFileName($fI['file']), $dest); 01570 01571 // If we have a unique destination filename, then write the file: 01572 if ($theDestFile) { 01573 t3lib_div::upload_copy_move($theFile,$theDestFile); 01574 $this->copiedFileMap[$theFile] = $theDestFile; 01575 clearstatcache(); 01576 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']); 01577 } 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']); 01578 } else $this->log($table,$id,5,0,1,"Fileextension '%s' not allowed. (%s)",12,array($fI['fileext'], $recFID),$propArr['event_pid']); 01579 } 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']); 01580 } 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']); 01581 01582 // If the destination file was created, we will set the new filename in the value array, otherwise unset the entry in the value array! 01583 if (@is_file($theDestFile)) { 01584 $info = t3lib_div::split_fileref($theDestFile); 01585 $valueArray[$key]=$info['file']; // The value is set to the new filename 01586 } else { 01587 unset($valueArray[$key]); // The value is set to the new filename 01588 } 01589 } 01590 } 01591 } 01592 01593 // 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! 01594 if ($tcaFieldConf['MM']) { 01595 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 01596 $dbAnalysis->tableArray['files']=array(); // dummy 01597 01598 reset($valueArray); 01599 while (list($key,$theFile)=each($valueArray)) { 01600 // explode files 01601 $dbAnalysis->itemArray[]['id']=$theFile; 01602 } 01603 if ($status=='update') { 01604 $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,0); 01605 } else { 01606 $this->dbAnalysisStore[] = array($dbAnalysis, $tcaFieldConf['MM'], $id, 0); // This will be traversed later to execute the actions 01607 } 01608 $valueArray = $dbAnalysis->countItems(); 01609 } 01610 } 01611 01612 return $valueArray; 01613 } 01614 01627 function checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field) { 01628 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01629 01630 if (is_array($value)) { 01631 01632 // This value is necessary for flex form processing to happen on flexform fields in page records when they are copied. 01633 // The problem is, that when copying a page, flexfrom XML comes along in the array for the new record - but since $this->checkValue_currentRecord does not have a uid or pid for that sake, the t3lib_BEfunc::getFlexFormDS() function returns no good DS. For new records we do know the expected PID so therefore we send that with this special parameter. Only active when larger than zero. 01634 $newRecordPidValue = $status=='new' ? $realPid : 0; 01635 01636 // Get current value array: 01637 $dataStructArray = t3lib_BEfunc::getFlexFormDS($tcaFieldConf,$this->checkValue_currentRecord,$table,'',TRUE,$newRecordPidValue); 01638 01639 $currentValueArray = t3lib_div::xml2array($curValue); 01640 if (!is_array($currentValueArray)) $currentValueArray = array(); 01641 if (is_array($currentValueArray['meta']['currentLangId'])) unset($currentValueArray['meta']['currentLangId']); // Remove all old meta for languages... 01642 01643 // Evaluation of input values: 01644 $value['data'] = $this->checkValue_flex_procInData($value['data'],$currentValueArray['data'],$uploadedFiles['data'],$dataStructArray,$PP); 01645 01646 // Create XML and convert charsets from input value: 01647 $xmlValue = $this->checkValue_flexArray2Xml($value,TRUE); 01648 01649 // If we wanted to set UTF fixed: 01650 // $storeInCharset='utf-8'; 01651 // $currentCharset=$GLOBALS['LANG']->charSet; 01652 // $xmlValue = $GLOBALS['LANG']->csConvObj->conv($xmlValue,$currentCharset,$storeInCharset,1); 01653 $storeInCharset=$GLOBALS['LANG']->charSet; 01654 01655 // Merge them together IF they are both arrays: 01656 // 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). 01657 if (is_array($currentValueArray)) { 01658 $arrValue = t3lib_div::xml2array($xmlValue); 01659 $arrValue = t3lib_div::array_merge_recursive_overrule($currentValueArray,$arrValue); 01660 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01661 } 01662 01663 // Temporary fix to delete flex form elements: 01664 $deleteCMDs = t3lib_div::_GP('_DELETE_FLEX_FORMdata'); 01665 if (is_array($deleteCMDs[$table][$id][$field]['data'])) { 01666 $arrValue = t3lib_div::xml2array($xmlValue); 01667 $this->_DELETE_FLEX_FORMdata($arrValue['data'],$deleteCMDs[$table][$id][$field]['data']); 01668 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01669 } 01670 01671 // Temporary fix to move flex form elements up: 01672 $moveCMDs = t3lib_div::_GP('_MOVEUP_FLEX_FORMdata'); 01673 if (is_array($moveCMDs[$table][$id][$field]['data'])) { 01674 $arrValue = t3lib_div::xml2array($xmlValue); 01675 $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'up'); 01676 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01677 } 01678 01679 // Temporary fix to move flex form elements down: 01680 $moveCMDs = t3lib_div::_GP('_MOVEDOWN_FLEX_FORMdata'); 01681 if (is_array($moveCMDs[$table][$id][$field]['data'])) { 01682 $arrValue = t3lib_div::xml2array($xmlValue); 01683 $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'down'); 01684 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01685 } 01686 01687 // Create the value XML: 01688 $res['value']=''; 01689 $res['value'].=$xmlValue; 01690 } else { // Passthrough...: 01691 $res['value']=$value; 01692 } 01693 01694 return $res; 01695 } 01696 01704 function checkValue_flexArray2Xml($array, $addPrologue=FALSE) { 01705 $flexObj = t3lib_div::makeInstance('t3lib_flexformtools'); 01706 return $flexObj->flexArray2Xml($array, $addPrologue); 01707 } 01708 01716 function _DELETE_FLEX_FORMdata(&$valueArrayToRemoveFrom,$deleteCMDS) { 01717 if (is_array($valueArrayToRemoveFrom) && is_array($deleteCMDS)) { 01718 foreach($deleteCMDS as $key => $value) { 01719 if (is_array($deleteCMDS[$key])) { 01720 $this->_DELETE_FLEX_FORMdata($valueArrayToRemoveFrom[$key],$deleteCMDS[$key]); 01721 } else { 01722 unset($valueArrayToRemoveFrom[$key]); 01723 } 01724 } 01725 } 01726 } 01727 01738 function _MOVE_FLEX_FORMdata(&$valueArrayToMoveIn, $moveCMDS, $direction) { 01739 if (is_array($valueArrayToMoveIn) && is_array($moveCMDS)) { 01740 01741 // Only execute the first move command: 01742 list ($key, $value) = each ($moveCMDS); 01743 01744 if (is_array($moveCMDS[$key])) { 01745 $this->_MOVE_FLEX_FORMdata($valueArrayToMoveIn[$key],$moveCMDS[$key], $direction); 01746 } else { 01747 switch ($direction) { 01748 case 'up': 01749 if ($key > 1) { 01750 $tmpArr = $valueArrayToMoveIn[$key]; 01751 $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key-1]; 01752 $valueArrayToMoveIn[$key-1] = $tmpArr; 01753 } 01754 break; 01755 case 'down': 01756 if ($key < count($valueArrayToMoveIn)) { 01757 $tmpArr = $valueArrayToMoveIn[$key]; 01758 $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key+1]; 01759 $valueArrayToMoveIn[$key+1] = $tmpArr; 01760 } 01761 break; 01762 } 01763 } 01764 } 01765 } 01766 01778 function checkValue_inline($res,$value,$tcaFieldConf,$PP,$field) { 01779 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01780 01781 if (!$tcaFieldConf['foreign_table']) { 01782 return false; // Fatal error, inline fields should always have a foreign_table defined 01783 } 01784 01785 // When values are sent they come as comma-separated values which are exploded by this function: 01786 $valueArray = t3lib_div::trimExplode(',', $value); 01787 01788 // Remove duplicates: (should not be needed) 01789 $valueArray = array_unique($valueArray); 01790 01791 // Example for received data: 01792 // $value = 45,NEW4555fdf59d154,12,123 01793 // We need to decide whether we use the stack or can save the relation directly. 01794 if(strpos($value, 'NEW') !== false || !t3lib_div::testInt($id)) { 01795 $this->remapStack[] = array( 01796 'func' => 'checkValue_group_select_processDBdata', 01797 'args' => array($valueArray,$tcaFieldConf,$id,$status,'inline',$table), 01798 'pos' => array('valueArray' => 0, 'tcaFieldConf' => 1, 'id' => 2, 'table' => 5), 01799 'field' => $field 01800 ); 01801 unset($res['value']); 01802 } elseif($value || t3lib_div::testInt($id)) { 01803 $newValueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'inline', $table); 01804 01805 // Checking that the number of items is correct 01806 $newVal = $this->checkValue_checkMax($tcaFieldConf, $newValueArray); 01807 $res['value'] = implode(',',$newVal); 01808 } 01809 01810 return $res; 01811 } 01812 01821 function checkValue_checkMax($tcaFieldConf, $valueArray) { 01822 // 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... 01823 01824 $valueArrayC = count($valueArray); 01825 01826 // 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. 01827 $maxI = isset($tcaFieldConf['maxitems']) ? intval($tcaFieldConf['maxitems']):1; 01828 if ($valueArrayC > $maxI) {$valueArrayC=$maxI;} // Checking for not too many elements 01829 01830 // Dumping array to list 01831 $newVal=array(); 01832 foreach($valueArray as $nextVal) { 01833 if ($valueArrayC==0) {break;} 01834 $valueArrayC--; 01835 $newVal[]=$nextVal; 01836 } 01837 01838 return $newVal; 01839 } 01840 01841 01842 01843 01844 01845 01846 01847 01848 01849 01850 01851 01852 01853 01854 01855 01856 01857 /********************************************* 01858 * 01859 * Helper functions for evaluation functions. 01860 * 01861 ********************************************/ 01862 01873 function getUnique($table,$field,$value,$id,$newPid=0) { 01874 global $TCA; 01875 01876 // Initialize: 01877 t3lib_div::loadTCA($table); 01878 $whereAdd=''; 01879 $newValue=''; 01880 if (intval($newPid)) { $whereAdd.=' AND pid='.intval($newPid); } else { $whereAdd.=' AND pid>=0'; } // "AND pid>=0" for versioning 01881 $whereAdd.=$this->deleteClause($table); 01882 01883 // If the field is configured in TCA, proceed: 01884 if (is_array($TCA[$table]) && is_array($TCA[$table]['columns'][$field])) { 01885 01886 // Look for a record which might already have the value: 01887 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $table).' AND uid!='.intval($id).$whereAdd); 01888 $counter = 0; 01889 01890 // For as long as records with the test-value existing, try again (with incremented numbers appended). 01891 while ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 01892 $newValue = $value.$counter; 01893 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($newValue, $table).' AND uid!='.intval($id).$whereAdd); 01894 $counter++; 01895 if ($counter>100) { break; } // At "100" it will give up and accept a duplicate - should probably be fixed to a small hash string instead...! 01896 } 01897 // If the new value is there: 01898 $value = strlen($newValue) ? $newValue : $value; 01899 } 01900 return $value; 01901 } 01902 01911 function checkValue_input_Eval($value,$evalArray,$is_in) { 01912 $res = Array(); 01913 $newValue = $value; 01914 $set = true; 01915 01916 foreach($evalArray as $func) { 01917 switch($func) { 01918 case 'int': 01919 case 'year': 01920 case 'date': 01921 case 'datetime': 01922 case 'time': 01923 case 'timesec': 01924 $value = intval($value); 01925 break; 01926 case 'double2': 01927 $theDec = 0; 01928 for ($a=strlen($value); $a>0; $a--) { 01929 if (substr($value,$a-1,1)=='.' || substr($value,$a-1,1)==',') { 01930 $theDec = substr($value,$a); 01931 $value = substr($value,0,$a-1); 01932 break; 01933 } 01934 } 01935 $theDec = ereg_replace('[^0-9]','',$theDec).'00'; 01936 $value = intval(str_replace(' ','',$value)).'.'.substr($theDec,0,2); 01937 break; 01938 case 'md5': 01939 if (strlen($value)!=32){$set=false;} 01940 break; 01941 case 'trim': 01942 $value = trim($value); 01943 break; 01944 case 'upper': 01945 $value = strtoupper($value); 01946 # $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. 01947 break; 01948 case 'lower': 01949 $value = strtolower($value); 01950 # $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. 01951 break; 01952 case 'required': 01953 if (!$value) {$set=0;} 01954 break; 01955 case 'is_in': 01956 $c=strlen($value); 01957 if ($c) { 01958 $newVal = ''; 01959 for ($a=0;$a<$c;$a++) { 01960 $char = substr($value,$a,1); 01961 if (strstr($is_in,$char)) { 01962 $newVal.=$char; 01963 } 01964 } 01965 $value = $newVal; 01966 } 01967 break; 01968 case 'nospace': 01969 $value = str_replace(' ','',$value); 01970 break; 01971 case 'alpha': 01972 $value = ereg_replace('[^a-zA-Z]','',$value); 01973 break; 01974 case 'num': 01975 $value = ereg_replace('[^0-9]','',$value); 01976 break; 01977 case 'alphanum': 01978 $value = ereg_replace('[^a-zA-Z0-9]','',$value); 01979 break; 01980 case 'alphanum_x': 01981 $value = ereg_replace('[^a-zA-Z0-9_-]','',$value); 01982 break; 01983 default: 01984 if (substr($func, 0, 3) == 'tx_') { 01985 $evalObj = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func].':&'.$func); 01986 if (is_object($evalObj) && method_exists($evalObj, 'evaluateFieldValue')) { 01987 $value = $evalObj->evaluateFieldValue($value, $is_in, $set); 01988 } 01989 } 01990 break; 01991 } 01992 } 01993 if ($set) {$res['value'] = $value;} 01994 return $res; 01995 } 01996 02008 function checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,$type,$currentTable) { 02009 $tables = $type=='group'?$tcaFieldConf['allowed']:$tcaFieldConf['foreign_table'].','.$tcaFieldConf['neg_foreign_table']; 02010 $prep = $type=='group'?$tcaFieldConf['prepend_tname']:$tcaFieldConf['neg_foreign_table']; 02011 02012 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 02013 $dbAnalysis->registerNonTableValues=$tcaFieldConf['allowNonIdValues'] ? 1 : 0; 02014 $dbAnalysis->start(implode(',',$valueArray),$tables, '', 0, $currentTable, $tcaFieldConf); 02015 02016 if ($tcaFieldConf['MM']) { 02017 if ($status=='update') { 02018 $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,$prep); 02019 } else { 02020 $this->dbAnalysisStore[] = array($dbAnalysis,$tcaFieldConf['MM'],$id,$prep); // This will be traversed later to execute the actions 02021 } 02022 $valueArray = $dbAnalysis->countItems(); 02023 } elseif ($type == 'inline') { 02024 if ($tcaFieldConf['foreign_field']) { 02025 // if the record was imported, sorting was also imported, so skip this 02026 $skipSorting = $this->callFromImpExp ? true : false; 02027 // update record in intermediate table (sorting & pointer uid to parent record) 02028 $dbAnalysis->writeForeignField($tcaFieldConf, $id, 0, $skipSorting); 02029 $valueArray = $dbAnalysis->countItems(); 02030 } else { 02031 $valueArray = $dbAnalysis->getValueArray($prep); 02032 if ($prep) { 02033 // @TODO: Do we want to support relations to multiple tables in Comma Separated Lists? 02034 $valueArray = $dbAnalysis->convertPosNeg($valueArray,$tcaFieldConf['foreign_table'],$tcaFieldConf['neg_foreign_table']); 02035 } 02036 } 02037 } else { 02038 $valueArray = $dbAnalysis->getValueArray($prep); 02039 if ($type=='select' && $prep) { 02040 $valueArray = $dbAnalysis->convertPosNeg($valueArray,$tcaFieldConf['foreign_table'],$tcaFieldConf['neg_foreign_table']); 02041 } 02042 } 02043 02044 // Here we should see if 1) the records exist anymore, 2) which are new and check if the BE_USER has read-access to the new ones. 02045 return $valueArray; 02046 } 02047 02054 function checkValue_group_select_explodeSelectGroupValue($value) { 02055 $valueArray = t3lib_div::trimExplode(',',$value,1); 02056 reset($valueArray); 02057 while(list($key,$newVal)=each($valueArray)) { 02058 $temp=explode('|',$newVal,2); 02059 $valueArray[$key] = str_replace(',','',str_replace('|','',rawurldecode($temp[0]))); 02060 } 02061 return $valueArray; 02062 } 02063 02078 function checkValue_flex_procInData($dataPart,$dataPart_current,$uploadedFiles,$dataStructArray,$pParams,$callBackFunc='') { 02079 #debug(array($dataPart,$dataPart_current,$dataStructArray)); 02080 if (is_array($dataPart)) { 02081 foreach($dataPart as $sKey => $sheetDef) { 02082 list ($dataStruct,$actualSheet) = t3lib_div::resolveSheetDefInDS($dataStructArray,$sKey); 02083 #debug(array($dataStruct,$actualSheet,$sheetDef,$actualSheet,$sKey)); 02084 if (is_array($dataStruct) && $actualSheet==$sKey && is_array($sheetDef)) { 02085 foreach($sheetDef as $lKey => $lData) { 02086 $this->checkValue_flex_procInData_travDS( 02087 $dataPart[$sKey][$lKey], 02088 $dataPart_current[$sKey][$lKey], 02089 $uploadedFiles[$sKey][$lKey], 02090 $dataStruct['ROOT']['el'], 02091 $pParams, 02092 $callBackFunc, 02093 $sKey.'/'.$lKey.'/' 02094 ); 02095 } 02096 } 02097 } 02098 } 02099 02100 return $dataPart; 02101 } 02102 02117 function checkValue_flex_procInData_travDS(&$dataValues,$dataValues_current,$uploadedFiles,$DSelements,$pParams,$callBackFunc,$structurePath) { 02118 if (is_array($DSelements)) { 02119 02120 // For each DS element: 02121 foreach($DSelements as $key => $dsConf) { 02122 02123 // Array/Section: 02124 if ($DSelements[$key]['type']=='array') { 02125 if (is_array($dataValues[$key]['el'])) { 02126 if ($DSelements[$key]['section']) { 02127 foreach($dataValues[$key]['el'] as $ik => $el) { 02128 $theKey = key($el); 02129 if (is_array($dataValues[$key]['el'][$ik][$theKey]['el'])) { 02130 $this->checkValue_flex_procInData_travDS( 02131 $dataValues[$key]['el'][$ik][$theKey]['el'], 02132 $dataValues_current[$key]['el'][$ik][$theKey]['el'], 02133 $uploadedFiles[$key]['el'][$ik][$theKey]['el'], 02134 $DSelements[$key]['el'][$theKey]['el'], 02135 $pParams, 02136 $callBackFunc, 02137 $structurePath.$key.'/el/'.$ik.'/'.$theKey.'/el/' 02138 ); 02139 } 02140 } 02141 } else { 02142 if (!isset($dataValues[$key]['el'])) $dataValues[$key]['el']=array(); 02143 $this->checkValue_flex_procInData_travDS( 02144 $dataValues[$key]['el'], 02145 $dataValues_current[$key]['el'], 02146 $uploadedFiles[$key]['el'], 02147 $DSelements[$key]['el'], 02148 $pParams, 02149 $callBackFunc, 02150 $structurePath.$key.'/el/' 02151 ); 02152 } 02153 } 02154 } else { 02155 if (is_array($dsConf['TCEforms']['config']) && is_array($dataValues[$key])) { 02156 foreach($dataValues[$key] as $vKey => $data) { 02157 02158 if ($callBackFunc) { 02159 if (is_object($this->callBackObj)) { 02160 $res = $this->callBackObj->$callBackFunc( 02161 $pParams, 02162 $dsConf['TCEforms']['config'], 02163 $dataValues[$key][$vKey], 02164 $dataValues_current[$key][$vKey], 02165 $uploadedFiles[$key][$vKey], 02166 $structurePath.$key.'/'.$vKey.'/' 02167 ); 02168 } else { 02169 $res = $this->$callBackFunc( 02170 $pParams, 02171 $dsConf['TCEforms']['config'], 02172 $dataValues[$key][$vKey], 02173 $dataValues_current[$key][$vKey], 02174 $uploadedFiles[$key][$vKey] 02175 ); 02176 } 02177 } else { // Default 02178 list($CVtable,$CVid,$CVcurValue,$CVstatus,$CVrealPid,$CVrecFID,$CVtscPID) = $pParams; 02179 02180 $res = $this->checkValue_SW( 02181 array(), 02182 $dataValues[$key][$vKey], 02183 $dsConf['TCEforms']['config'], 02184 $CVtable, 02185 $CVid, 02186 $dataValues_current[$key][$vKey], 02187 $CVstatus, 02188 $CVrealPid, 02189 $CVrecFID, 02190 '', 02191 $uploadedFiles[$key][$vKey], 02192 array(), 02193 $CVtscPID 02194 ); 02195 02196 // Look for RTE transformation of field: 02197 if ($dataValues[$key]['_TRANSFORM_'.$vKey] == 'RTE' && !$this->dontProcessTransformations) { 02198 02199 // Unsetting trigger field - we absolutely don't want that into the data storage! 02200 unset($dataValues[$key]['_TRANSFORM_'.$vKey]); 02201 02202 if (isset($res['value'])) { 02203 02204 // Calculating/Retrieving some values here: 02205 list(,,$recFieldName) = explode(':', $CVrecFID); 02206 $theTypeString = t3lib_BEfunc::getTCAtypeValue($CVtable,$this->checkValue_currentRecord); 02207 $specConf = t3lib_BEfunc::getSpecConfParts('',$dsConf['TCEforms']['defaultExtras']); 02208 02209 // Find, thisConfig: 02210 $RTEsetup = $this->BE_USER->getTSConfig('RTE',t3lib_BEfunc::getPagesTSconfig($CVtscPID)); 02211 $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'],$CVtable,$recFieldName,$theTypeString); 02212 02213 // Get RTE object, draw form and set flag: 02214 $RTEobj = &t3lib_BEfunc::RTEgetObj(); 02215 if (is_object($RTEobj)) { 02216 $res['value'] = $RTEobj->transformContent('db',$res['value'],$CVtable,$recFieldName,$this->checkValue_currentRecord,$specConf,$thisConfig,'',$CVrealPid); 02217 } else { 02218 debug('NO RTE OBJECT FOUND!'); 02219 } 02220 } 02221 } 02222 } 02223 02224 // Adding the value: 02225 if (isset($res['value'])) { 02226 $dataValues[$key][$vKey] = $res['value']; 02227 } 02228 } 02229 } 02230 } 02231 } 02232 } 02233 } 02234 02235 02236 02237 02238 02239 02240 02241 02242 02243 02244 02245 02246 02247 02248 02249 02250 02251 /********************************************* 02252 * 02253 * PROCESSING COMMANDS 02254 * 02255 ********************************************/ 02256 02263 function process_cmdmap() { 02264 global $TCA, $TYPO3_CONF_VARS; 02265 02266 // Editing frozen: 02267 if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze']) { 02268 $this->newlog('All editing in this workspace has been frozen!',1); 02269 return FALSE; 02270 } 02271 02272 // Hook initialization: 02273 $hookObjectsArr = array(); 02274 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'])) { 02275 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'] as $classRef) { 02276 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef); 02277 } 02278 } 02279 02280 // Traverse command map: 02281 reset($this->cmdmap); 02282 while(list($table,) = each($this->cmdmap)) { 02283 02284 // Check if the table may be modified! 02285 $modifyAccessList = $this->checkModifyAccessList($table); 02286 if (!$modifyAccessList) { 02287 $id = 0; 02288 $this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table)); 02289 } // FIXME: $id not set here (Comment added by Sebastian Kurfuerst) 02290 02291 // Check basic permissions and circumstances: 02292 if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->cmdmap[$table]) && $modifyAccessList) { 02293 02294 // Traverse the command map: 02295 foreach($this->cmdmap[$table] as $id => $incomingCmdArray) { 02296 if (is_array($incomingCmdArray)) { // have found a command. 02297 02298 // Get command and value (notice, only one command is observed at a time!): 02299 reset($incomingCmdArray); 02300 $command = key($incomingCmdArray); 02301 $value = current($incomingCmdArray); 02302 02303 foreach($hookObjectsArr as $hookObj) { 02304 if (method_exists($hookObj, 'processCmdmap_preProcess')) { 02305 $hookObj->processCmdmap_preProcess($command, $table, $id, $value, $this); 02306 } 02307 } 02308 02309 // Init copyMapping array: 02310 $this->copyMappingArray = Array(); // Must clear this array before call from here to those functions: Contains mapping information between new and old id numbers. 02311 02312 // Branch, based on command 02313 switch ($command) { 02314 case 'move': 02315 $this->moveRecord($table,$id,$value); 02316 break; 02317 case 'copy': 02318 if ($table === 'pages') { 02319 $this->copyPages($id,$value); 02320 } else { 02321 $this->copyRecord($table,$id,$value,1); 02322 } 02323 break; 02324 case 'localize': 02325 $this->localize($table,$id,$value); 02326 break; 02327 case 'version': 02328 switch ((string)$value['action']) { 02329 case 'new': 02330 $versionizeTree = t3lib_div::intInRange(!isset($value['treeLevels'])?-1:$value['treeLevels'],-1,100); 02331 if ($table == 'pages' && $versionizeTree>=0) { 02332 $this->versionizePages($id,$value['label'],$versionizeTree); 02333 } else { 02334 $this->versionizeRecord($table,$id,$value['label']); 02335 } 02336 break; 02337 case 'swap': 02338 $this->version_swap($table,$id,$value['swapWith'],$value['swapIntoWS']); 02339 break; 02340 case 'clearWSID': 02341 $this->version_clearWSID($table,$id); 02342 break; 02343 case 'setStage': 02344 $idList = t3lib_div::trimExplode(',',$id,1); 02345 foreach($idList as $id) { 02346 $this->version_setStage($table,$id,$value['stageId'],$value['comment']?$value['comment']:$this->generalComment); 02347 } 02348 break; 02349 } 02350 break; 02351 case 'delete': 02352 $this->deleteAction($table, $id); 02353 break; 02354 case 'undelete': 02355 $this->undeleteRecord($table, $id); 02356 break; 02357 } 02358 02359 foreach($hookObjectsArr as $hookObj) { 02360 if (method_exists($hookObj, 'processCmdmap_postProcess')) { 02361 $hookObj->processCmdmap_postProcess($command, $table, $id, $value, $this); 02362 } 02363 } 02364 02365 // Merging the copy-array info together for remapping purposes. 02366 $this->copyMappingArray_merged= t3lib_div::array_merge_recursive_overrule($this->copyMappingArray_merged,$this->copyMappingArray); 02367 } 02368 } 02369 } 02370 } 02371 02372 // Finally, before exit, check if there are ID references to remap. This might be the case if versioning or copying has taken place! 02373 $this->remapListedDBRecords(); 02374 } 02375 02376 02377 02378 02379 02380 02381 02382 02383 02384 02385 02386 /********************************************* 02387 * 02388 * Cmd: Copying 02389 * 02390 ********************************************/ 02391 02403 function copyRecord($table,$uid,$destPid,$first=0,$overrideValues=array(),$excludeFields='') { 02404 global $TCA; 02405 02406 $uid = $origUid = intval($uid); 02407 if ($TCA[$table] && $uid) { 02408 t3lib_div::loadTCA($table); 02409 /* 02410 // In case the record to be moved turns out to be an offline version, we have to find the live version and work on that one (this case happens for pages with "branch" versioning type) 02411 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid')) { 02412 $uid = $lookForLiveVersion['uid']; 02413 } 02414 // Get workspace version of the source record, if any: Then we will copy workspace version instead: 02415 if ($WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid')) { 02416 $uid = $WSversion['uid']; 02417 } 02418 // Now, the $uid is the actual record we will copy while $origUid is the record we asked to get copied - but that could be a live version. 02419 */ 02420 if ($this->doesRecordExist($table,$uid,'show')) { // This checks if the record can be selected which is all that a copy action requires. 02421 $data = Array(); 02422 02423 $nonFields = array_unique(t3lib_div::trimExplode(',','uid,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,t3ver_oid,t3ver_wsid,t3ver_id,t3ver_label,t3ver_state,t3ver_swapmode,t3ver_count,t3ver_stage,t3ver_tstamp,'.$excludeFields,1)); 02424 02425 // $row = $this->recordInfo($table,$uid,'*'); 02426 $row = t3lib_BEfunc::getRecordWSOL($table,$uid); // So it copies (and localized) content from workspace... 02427 if (is_array($row)) { 02428 02429 // Initializing: 02430 $theNewID = uniqid('NEW'); 02431 $enableField = isset($TCA[$table]['ctrl']['enablecolumns']) ? $TCA[$table]['ctrl']['enablecolumns']['disabled'] : ''; 02432 $headerField = $TCA[$table]['ctrl']['label']; 02433 02434 // Getting default data: 02435 $defaultData = $this->newFieldArray($table); 02436 02437 // Getting "copy-after" fields if applicable: 02438 // 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. 02439 $copyAfterFields = $destPid<0 ? $this->fixCopyAfterDuplFields($table,$uid,abs($destPid),0) : array(); 02440 02441 // Page TSconfig related: 02442 $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... 02443 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 02444 $tE = $this->getTableEntries($table,$TSConfig); 02445 02446 // Traverse ALL fields of the selected record: 02447 foreach($row as $field => $value) { 02448 if (!in_array($field,$nonFields)) { 02449 02450 // Get TCA configuration for the field: 02451 $conf = $TCA[$table]['columns'][$field]['config']; 02452 02453 // Preparation/Processing of the value: 02454 if ($field=='pid') { // "pid" is hardcoded of course: 02455 $value = $destPid; 02456 } elseif (isset($overrideValues[$field])) { // Override value... 02457 $value = $overrideValues[$field]; 02458 } elseif (isset($copyAfterFields[$field])) { // Copy-after value if available: 02459 $value = $copyAfterFields[$field]; 02460 } elseif ($TCA[$table]['ctrl']['setToDefaultOnCopy'] && t3lib_div::inList($TCA[$table]['ctrl']['setToDefaultOnCopy'],$field)) { // Revert to default for some fields: 02461 $value = $defaultData[$field]; 02462 } else { 02463 // Hide at copy may override: 02464 if ($first && $field==$enableField && $TCA[$table]['ctrl']['hideAtCopy'] && !$this->neverHideAtCopy && !$tE['disableHideAtCopy']) { 02465 $value=1; 02466 } 02467 // Prepend label on copy: 02468 if ($first && $field==$headerField && $TCA[$table]['ctrl']['prependAtCopy'] && !$tE['disablePrependAtCopy']) { 02469 $value = $this->getCopyHeader($table,$this->resolvePid($table,$destPid),$field,$this->clearPrefixFromValue($table,$value),0); 02470 } 02471 // Processing based on the TCA config field type (files, references, flexforms...) 02472 $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf,$tscPID); 02473 } 02474 02475 // Add value to array. 02476 $data[$table][$theNewID][$field] = $value; 02477 } 02478 } 02479 02480 // Overriding values: 02481 if ($TCA[$table]['ctrl']['editlock']) { 02482 $data[$table][$theNewID][$TCA[$table]['ctrl']['editlock']] = 0; 02483 } 02484 02485 // Setting original UID: 02486 if ($TCA[$table]['ctrl']['origUid']) { 02487 $data[$table][$theNewID][$TCA[$table]['ctrl']['origUid']] = $uid; 02488 } 02489 02490 // Do the copy by simply submitting the array through TCEmain: 02491 $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain'); 02492 $copyTCE->stripslashes_values = 0; 02493 $copyTCE->copyTree = $this->copyTree; 02494 $copyTCE->cachedTSconfig = $this->cachedTSconfig; // Copy forth the cached TSconfig 02495 $copyTCE->dontProcessTransformations=1; // Transformations should NOT be carried out during copy 02496 02497 $copyTCE->start($data,'',$this->BE_USER); 02498 $copyTCE->process_datamap(); 02499 02500 // Getting the new UID: 02501 $theNewSQLID = $copyTCE->substNEWwithIDs[$theNewID]; 02502 if ($theNewSQLID) { 02503 $this->copyMappingArray[$table][$origUid] = $theNewSQLID; 02504 } 02505 02506 // Copy back the cached TSconfig 02507 $this->cachedTSconfig = $copyTCE->cachedTSconfig; 02508 $this->errorLog = array_merge($this->errorLog,$copyTCE->errorLog); 02509 unset($copyTCE); 02510 02511 return $theNewSQLID; 02512 } else $this->log($table,$uid,3,0,1,'Attempt to copy record that did not exist!'); 02513 } else $this->log($table,$uid,3,0,1,'Attempt to copy record without permission'); 02514 } 02515 } 02516 02525 function copyPages($uid,$destPid) { 02526 02527 // Initialize: 02528 $uid = intval($uid); 02529 $destPid = intval($destPid); 02530 02531 // Finding list of tables to copy. 02532 $copyTablesArray = $this->admin ? $this->compileAdminTables() : explode(',',$this->BE_USER->groupData['tables_modify']); // These are the tables, the user may modify 02533 if (!strstr($this->copyWhichTables,'*')) { // If not all tables are allowed then make a list of allowed tables: That is the tables that figure in both allowed tables AND the copyTable-list 02534 foreach($copyTablesArray as $k => $table) { 02535 if (!$table || !t3lib_div::inList($this->copyWhichTables.',pages',$table)) { // pages are always going... 02536 unset($copyTablesArray[$k]); 02537 } 02538 } 02539 } 02540 $copyTablesArray = array_unique($copyTablesArray); 02541 02542 // Begin to copy pages if we're allowed to: 02543 if ($this->admin || in_array('pages',$copyTablesArray)) { 02544 02545 // Copy this page we're on. And set first-flag (this will trigger that the record is hidden if that is configured)! 02546 $theNewRootID = $this->copySpecificPage($uid,$destPid,$copyTablesArray,1); 02547 02548 // If we're going to copy recursively...: 02549 if ($theNewRootID && $this->copyTree) { 02550 02551 // Get ALL subpages to copy (read-permissions are respected!): 02552 $CPtable = $this->int_pageTreeInfo(Array(), $uid, intval($this->copyTree), $theNewRootID); 02553 02554 // Now copying the subpages: 02555 foreach($CPtable as $thePageUid => $thePagePid) { 02556 $newPid = $this->copyMappingArray['pages'][$thePagePid]; 02557 if (isset($newPid)) { 02558 $this->copySpecificPage($thePageUid,$newPid,$copyTablesArray); 02559 } else { 02560 $this->log('pages',$uid,5,0,1,'Something went wrong during copying branch'); 02561 break; 02562 } 02563 } 02564 } // else the page was not copied. Too bad... 02565 } else { 02566 $this->log('pages',$uid,5,0,1,'Attempt to copy page without permission to this table'); 02567 } 02568 } 02569 02579 function copySpecificPage($uid,$destPid,$copyTablesArray,$first=0) { 02580 global $TCA; 02581 02582 // Copy the page itself: 02583 $theNewRootID = $this->copyRecord('pages',$uid,$destPid,$first); 02584 02585 // If a new page was created upon the copy operation we will proceed with all the tables ON that page: 02586 if ($theNewRootID) { 02587 foreach($copyTablesArray as $table) { 02588 if ($table && is_array($TCA[$table]) && $table!='pages') { // all records under the page is copied. 02589 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table), '', ($TCA[$table]['ctrl']['sortby'] ? $TCA[$table]['ctrl']['sortby'].' DESC' : '')); 02590 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 02591 $this->copyRecord($table,$row['uid'], $theNewRootID); // Copying each of the underlying records... 02592 } 02593 } 02594 } 02595 return $theNewRootID; 02596 } 02597 } 02598 02613 function copyRecord_raw($table,$uid,$pid,$overrideArray=array()) { 02614 global $TCA; 02615 02616 $uid = intval($uid); 02617 if ($TCA[$table] && $uid) { 02618 t3lib_div::loadTCA($table); 02619 if ($this->doesRecordExist($table,$uid,'show')) { 02620 02621 // Set up fields which should not be processed. They are still written - just passed through no-questions-asked! 02622 $nonFields = array('uid','pid','t3ver_id','t3ver_oid','t3ver_wsid','t3ver_label','t3ver_state','t3ver_swapmode','t3ver_count','t3ver_stage','t3ver_tstamp','perms_userid','perms_groupid','perms_user','perms_group','perms_everybody'); 02623 02624 // Select main record: 02625 $row = $this->recordInfo($table,$uid,'*'); 02626 if (is_array($row)) { 02627 02628 // Merge in override array. 02629 $row = array_merge($row,$overrideArray); 02630 02631 // Traverse ALL fields of the selected record: 02632 foreach($row as $field => $value) { 02633 if (!in_array($field,$nonFields)) { 02634 02635 // Get TCA configuration for the field: 02636 $conf = $TCA[$table]['columns'][$field]['config']; 02637 if (is_array($conf)) { 02638 // Processing based on the TCA config field type (files, references, flexforms...) 02639 $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf,$pid); 02640 } 02641 02642 // Add value to array. 02643 $row[$field] = $value; 02644 } 02645 } 02646 02647 // Force versioning related fields: 02648 $row['pid'] = $pid; 02649 02650 // Setting original UID: 02651 if ($TCA[$table]['ctrl']['origUid']) { 02652 $row[$TCA[$table]['ctrl']['origUid']] = $uid; 02653 } 02654 02655 // Do the copy by internal function 02656 $theNewSQLID = $this->insertNewCopyVersion($table,$row,$pid); 02657 if ($theNewSQLID) { 02658 $this->dbAnalysisStoreExec(); 02659 $this->dbAnalysisStore = array(); 02660 return $this->copyMappingArray[$table][$uid] = $theNewSQLID; 02661 } 02662 } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record that did not exist!'); 02663 } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record without copy permission'); 02664 } 02665 } 02666 02677 function rawCopyPageContent($old_pid,$new_pid,$copyTablesArray) { 02678 global $TCA; 02679 02680 if ($new_pid) { 02681 foreach($copyTablesArray as $table) { 02682 if ($table && is_array($TCA[$table]) && $table!='pages') { // all records under the page is copied. 02683 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($old_pid).$this->deleteClause($table)); 02684 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 02685 $this->copyRecord_raw($table,$row['uid'],$new_pid); // Copying each of the underlying records (method RAW) 02686 } 02687 } 02688 } 02689 } 02690 } 02691 02701 function insertNewCopyVersion($table,$fieldArray,$realPid) { 02702 global $TCA; 02703 02704 $id = uniqid('NEW'); 02705 02706 // $fieldArray is set as current record. 02707 // 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... 02708 $this->checkValue_currentRecord = $fieldArray; 02709 02710 // Traverse record and input-process each value: 02711 foreach($fieldArray as $field => $fieldValue) { 02712 if (isset($TCA[$table]['columns'][$field])) { 02713 // Evaluating the value. 02714 $res = $this->checkValue($table,$field,$fieldValue,$id,'new',$realPid,0); 02715 if (isset($res['value'])) { 02716 $fieldArray[$field] = $res['value']; 02717 } 02718 } 02719 } 02720 02721 // System fields being set: 02722 if ($TCA[$table]['ctrl']['crdate']) { 02723 $fieldArray[$TCA[$table]['ctrl']['crdate']]=time(); 02724 } 02725 if ($TCA[$table]['ctrl']['cruser_id']) { 02726 $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid; 02727 } 02728 if ($TCA[$table]['ctrl']['tstamp']) { 02729 $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time(); 02730 } 02731 02732 // Finally, insert record: 02733 $this->insertDB($table,$id,$fieldArray, TRUE); 02734 02735 // Return new id: 02736 return $this->substNEWwithIDs[$id]; 02737 } 02738 02753 function copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf,$realDestPid) { 02754 global $TCA; 02755 02756 // Process references and files, currently that means only the files, prepending absolute paths (so the TCEmain engine will detect the file as new and one that should be made into a copy) 02757 $value = $this->copyRecord_procFilesRefs($conf, $uid, $value); 02758 $inlineSubType = $this->getInlineFieldType($conf); 02759 02760 // Register if there are references to take care of or MM is used on an inline field (no change to value): 02761 if ($this->isReferenceField($conf) || $inlineSubType == 'mm') { 02762 $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table']; 02763 $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : $conf['neg_foreign_table']; 02764 if ($conf['MM']) { 02765 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 02766 $dbAnalysis->start('', $allowedTables, $conf['MM'], $uid, $table, $conf); 02767 $value = implode(',',$dbAnalysis->getValueArray($prependName)); 02768 } 02769 if ($value) { // Setting the value in this array will notify the remapListedDBRecords() function that this field MAY need references to be corrected 02770 $this->registerDBList[$table][$uid][$field] = $value; 02771 } 02772 02773 // if another inline subtype is used (foreign_field, mm with attributes or simply item list) 02774 } elseif ($inlineSubType !== false) { 02775 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 02776 $dbAnalysis->start($value, $conf['foreign_table'], $conf['MM'], $uid, $table, $conf); 02777 02778 // walk through the items, copy them and remember the new id 02779 foreach ($dbAnalysis->itemArray as $k => $v) { 02780 // Take the real positive integer if available: 02781 if (t3lib_div::testInt($realDestPid) && $realDestPid >= 0) { 02782 $childDestPid = $realDestPid; 02783 // If the $realDestPid is not a valid integer or negative (e.g. workspaces): 02784 // @TODO: Check again concerning workspaces 02785 } else { 02786 $childDestPid = -$v['id']; 02787 } 02788 $newId = $this->copyRecord($v['table'], $v['id'], $childDestPid); 02789 $dbAnalysis->itemArray[$k]['id'] = $newId; 02790 } 02791 02792 // store the new values, we will set up the uids for the subtype later on 02793 $value = implode(',',$dbAnalysis->getValueArray()); 02794 $this->registerDBList[$table][$uid][$field] = $value; 02795 } 02796 02797 // For "flex" fieldtypes we need to traverse the structure for two reasons: If there are file references they have to be prepended with absolute paths and if there are database reference they MIGHT need to be remapped (still done in remapListedDBRecords()) 02798 if ($conf['type']=='flex') { 02799 02800 // Get current value array: 02801 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $row, $table); 02802 $currentValueArray = t3lib_div::xml2array($value); 02803 02804 // Traversing the XML structure, processing files: 02805 if (is_array($currentValueArray)) { 02806 $currentValueArray['data'] = $this->checkValue_flex_procInData( 02807 $currentValueArray['data'], 02808 array(), // Not used. 02809 array(), // Not used. 02810 $dataStructArray, 02811 array($table,$uid,$field), // Parameters. 02812 'copyRecord_flexFormCallBack' 02813 ); 02814 $value = $currentValueArray; // Setting value as an array! -> which means the input will be processed according to the 'flex' type when the new copy is created. 02815 } 02816 } 02817 02818 return $value; 02819 } 02820 02832 function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2) { 02833 02834 // Extract parameters: 02835 list($table, $uid, $field) = $pParams; 02836 02837 // Process references and files, currently that means only the files, prepending absolute paths: 02838 $dataValue = $this->copyRecord_procFilesRefs($dsConf, $uid, $dataValue); 02839 02840 // If references are set for this field, set flag so they can be corrected later (in ->remapListedDBRecords()) 02841 if ($this->isReferenceField($dsConf) && strlen($dataValue)) { 02842 $this->registerDBList[$table][$uid][$field] = 'FlexForm_reference'; 02843 } 02844 02845 // Return 02846 return array('value' => $dataValue); 02847 } 02848 02860 function copyRecord_procFilesRefs($conf, $uid, $value) { 02861 02862 // Prepend absolute paths to files: 02863 if ($conf['type']=='group' && $conf['internal_type']=='file') { 02864 02865 // Get an array with files as values: 02866 if ($conf['MM']) { 02867 $theFileValues = array(); 02868 02869 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 02870 $dbAnalysis->start('', 'files', $conf['MM'], $uid); 02871 02872 foreach($dbAnalysis->itemArray as $somekey => $someval) { 02873 if ($someval['id']) { 02874 $theFileValues[] = $someval['id']; 02875 } 02876 } 02877 } else { 02878 $theFileValues = t3lib_div::trimExplode(',',$value,1); 02879 } 02880 02881 // Traverse this array of files: 02882 $uploadFolder = $conf['uploadfolder']; 02883 $dest = $this->destPathFromUploadFolder($uploadFolder); 02884 $newValue = array(); 02885 02886 foreach($theFileValues as $file) { 02887 if (trim($file)) { 02888 $realFile = $dest.'/'.trim($file); 02889 if (@is_file($realFile)) { 02890 $newValue[] = $realFile; 02891 } 02892 } 02893 } 02894 02895 // Implode the new filelist into the new value (all files have absolute paths now which means they will get copied when entering TCEmain as new values...) 02896 $value = implode(',',$newValue); 02897 } 02898 02899 // Return the new value: 02900 return $value; 02901 } 02902 02903 02904 02905 02906 02907 02908 02909 02910 02911 02912 02913 02914 02915 /********************************************* 02916 * 02917 * Cmd: Moving, Localizing 02918 * 02919 ********************************************/ 02920 02929 function moveRecord($table,$uid,$destPid) { 02930 global $TCA, $TYPO3_CONF_VARS; 02931 02932 if ($TCA[$table]) { 02933 02934 // Prepare user defined objects (if any) for hooks which extend this function: 02935 $hookObjectsArr = array(); 02936 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'])) { 02937 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'] as $classRef) { 02938 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef); 02939 } 02940 } 02941 02942 // In case the record to be moved turns out to be an offline version, we have to find the live version and work on that one (this case happens for pages with "branch" versioning type) 02943 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid')) { 02944 $uid = $lookForLiveVersion['uid']; 02945 } 02946 02947 // Get workspace version of the source record, if any: 02948 $WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid'); 02949 02950 // Initialize: 02951 $sortRow = $TCA[$table]['ctrl']['sortby']; 02952 $destPid = intval($destPid); 02953 $origDestPid = $destPid; 02954 02955 $propArr = $this->getRecordProperties($table,$uid); // Get this before we change the pid (for logging) 02956 $moveRec = $this->getRecordProperties($table,$uid,TRUE); 02957 $resolvedPid = $this->resolvePid($table,$destPid); // This is the actual pid of the moving to destination 02958 02959 // 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. 02960 // 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. 02961 if ($table!='pages' || $resolvedPid==$moveRec['pid']) { 02962 $mayMoveAccess = $this->checkRecordUpdateAccess($table,$uid); // Edit rights for the record... 02963 } else { 02964 $mayMoveAccess = $this->doesRecordExist($table,$uid,'delete'); 02965 } 02966 02967 // Finding out, if the record may be moved TO another place. Here we check insert-rights (non-pages = edit, pages = new), unless the pages are moved on the same pid, then edit-rights are checked 02968 if ($table!='pages' || $resolvedPid!=$moveRec['pid']) { 02969 $mayInsertAccess = $this->checkRecordInsertAccess($table,$resolvedPid,4); // Insert rights for the record... 02970 } else { 02971 $mayInsertAccess = $this->checkRecordUpdateAccess($table,$uid); 02972 } 02973 02974 // Check workspace permissions: 02975 $workspaceAccessBlocked = array(); 02976 $recIsNewVersion = !strcmp($moveRec['_ORIG_pid'],'') && (int)$moveRec['t3ver_state']==1; // Element was an online version AND it was in "New state" so it can be moved... 02977 $destRes = $this->BE_USER->workspaceAllowLiveRecordsInPID($resolvedPid,$table); 02978 // Workspace source check: 02979 if ($errorCode = $this->BE_USER->workspaceCannotEditRecord($table, $WSversion['uid'] ? $WSversion['uid'] : $uid)) { 02980 $workspaceAccessBlocked['src1']='Record could not be edited in workspace: '.$errorCode.' '; 02981 } else { 02982 if (!$recIsNewVersion && $this->BE_USER->workspaceAllowLiveRecordsInPID($moveRec['pid'],$table)<=0) { 02983 $workspaceAccessBlocked['src2']='Could not remove record from table "'.$table.'" from its page "'.$moveRec['pid'].'" '; 02984 } 02985 } 02986 // Workspace destination check: 02987 if (!($destRes>0 || ($recIsNewVersion && !$destRes))) { // All records can be inserted if $destRes is greater than zero. Only new versions can be inserted if $destRes is false. NO RECORDS can be inserted if $destRes is negative which indicates a stage not allowed for use. 02988 $workspaceAccessBlocked['dest1']='Could not insert record from table "'.$table.'" in destination PID "'.$resolvedPid.'" '; 02989 } elseif ($destRes==1 && $WSversion['uid']) { 02990 $workspaceAccessBlocked['dest2']='Could not insert other versions in destination PID '; 02991 } 02992 02993 // Checking if the pid is negative, but no sorting row is defined. In that case, find the correct pid. Basically this check make the error message 4-13 meaning less... But you can always remove this check if you prefer the error instead of a no-good action (which is to move the record to its own page...) 02994 if (($destPid<0 && !$sortRow) || $destPid>=0) { // $destPid>=0 because we must correct pid in case of versioning "page" types. 02995 $destPid = $resolvedPid; 02996 } 02997 02998 // Timestamp field: 02999 $updateFields = array(); 03000 if ($TCA[$table]['ctrl']['tstamp']) { 03001 $updateFields[$TCA[$table]['ctrl']['tstamp']] = time(); 03002 } 03003 03004 // If moving is allowed, begin the processing: 03005 if (!count($workspaceAccessBlocked)) { 03006 if ($mayMoveAccess) { 03007 if ($destPid>=0) { // insert as first element on page (where uid = $destPid) 03008 if ($mayInsertAccess) { 03009 if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid)) { 03010 $this->clear_cache($table,$uid); // clear cache before moving 03011 03012 $updateFields['pid'] = $destPid; // Setting PID 03013 03014 // table is sorted by 'sortby' 03015 if ($sortRow) { 03016 $sortNumber = $this->getSortNumber($table,$uid,$destPid); 03017 $updateFields[$sortRow] = $sortNumber; 03018 } 03019 03020 // check for child records that have also to be moved 03021 $this->moveRecord_procFields($table,$uid,$destPid); 03022 // Create query for update: 03023 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields); 03024 03025 // Call post processing hooks: 03026 foreach($hookObjectsArr as $hookObj) { 03027 if (method_exists($hookObj, 'moveRecord_firstElementPostProcess')) { 03028 $hookObj->moveRecord_firstElementPostProcess($table, $uid, $destPid, $moveRec, $updateFields, $this); 03029 } 03030 } 03031 03032 // Logging... 03033 $newPropArr = $this->getRecordProperties($table,$uid); 03034 $oldpagePropArr = $this->getRecordProperties('pages',$propArr['pid']); 03035 $newpagePropArr = $this->getRecordProperties('pages',$destPid); 03036 03037 if ($destPid!=$propArr['pid']) { 03038 $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 03039 $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 03040 } else { 03041 $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 03042 } 03043 $this->clear_cache($table,$uid); // clear cache after moving 03044 $this->fixUniqueInPid($table,$uid); 03045 // fixCopyAfterDuplFields 03046 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. 03047 } else { 03048 $destPropArr = $this->getRecordProperties('pages',$destPid); 03049 $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']); 03050 } 03051 } 03052 } else { // Put after another record 03053 if ($sortRow) { // table is being sorted 03054 $sortInfo = $this->getSortNumber($table,$uid,$destPid); 03055 $destPid = $sortInfo['pid']; // Setting the destPid to the new pid of the record. 03056 if (is_array($sortInfo)) { // If not an array, there was an error (which is already logged) 03057 if ($mayInsertAccess) { 03058 if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid)) { 03059 $this->clear_cache($table,$uid); // clear cache before moving 03060 03061 // We now update the pid and sortnumber 03062 $updateFields['pid'] = $destPid; 03063 $updateFields[$sortRow] = $sortInfo['sortNumber']; 03064 03065 // check for child records that have also to be moved 03066 $this->moveRecord_procFields($table,$uid,$destPid); 03067 // Create query for update: 03068 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields); 03069 03070 // Call post processing hooks: 03071 foreach($hookObjectsArr as $hookObj) { 03072 if (method_exists($hookObj, 'moveRecord_afterAnotherElementPostProcess')) { 03073 $hookObj->moveRecord_afterAnotherElementPostProcess($table, $uid, $destPid, $origDestPid, $moveRec, $updateFields, $this); 03074 } 03075 } 03076 03077 // Logging... 03078 $newPropArr = $this->getRecordProperties($table,$uid); 03079 $oldpagePropArr = $this->getRecordProperties('pages',$propArr['pid']); 03080 if ($destPid!=$propArr['pid']) { 03081 $newpagePropArr = $this->getRecordProperties('pages',$destPid); 03082 $this->log($table,$uid,4,0,0,"Moved record '%s' (%s) to page '%s' (%s)",2,array($propArr['header'],$table.':'.$uid, $newpagePropArr['header'], $newPropArr['pid']),$propArr['pid']); // Logged to old page 03083 $this->log($table,$uid,4,0,0,"Moved record '%s' (%s) from page '%s' (%s)",3,array($propArr['header'],$table.':'.$uid, $oldpagePropArr['header'], $propArr['pid']),$destPid); // Logged to new page 03084 } else { 03085 $this->log($table,$uid,4,0,0,"Moved record '%s' (%s) on page '%s' (%s)",4,array($propArr['header'],$table.':'.$uid, $oldpagePropArr['header'], $propArr['pid']),$destPid); // Logged to new page 03086 } 03087 03088 // clear cache after moving 03089 $this->clear_cache($table,$uid); 03090 03091 // fixUniqueInPid 03092 $this->fixUniqueInPid($table,$uid); 03093 03094 // fixCopyAfterDuplFields 03095 if ($origDestPid<0) {$this->fixCopyAfterDuplFields($table,$uid,abs($origDestPid),1);} 03096 } else { 03097 $destPropArr = $this->getRecordProperties('pages',$destPid); 03098 $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']); 03099 } 03100 } 03101 } 03102 } else { 03103 $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']); 03104 } 03105 } 03106 } else { 03107 $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']); 03108 } 03109 } else { 03110 $this->newlog("Move attempt failed due to workspace restrictions: ".implode(' ',$workspaceAccessBlocked),1); 03111 } 03112 } 03113 } 03114 03124 function moveRecord_procFields($table,$uid,$destPid) { 03125 t3lib_div::loadTCA($table); 03126 $conf = $GLOBALS['TCA'][$table]['columns']; 03127 $row = t3lib_BEfunc::getRecordWSOL($table,$uid); 03128 foreach ($row as $field => $value) { 03129 $this->moveRecord_procBasedOnFieldType($table,$uid,$destPid,$field,$value,$conf[$field]['config']); 03130 } 03131 } 03132 03144 function moveRecord_procBasedOnFieldType($table,$uid,$destPid,$field,$value,$conf) { 03145 $moveTable = ''; 03146 $moveIds = array(); 03147 03148 if ($conf['type'] == 'inline') { 03149 $foreign_table = $conf['foreign_table']; 03150 03151 if ($foreign_table) { 03152 $inlineType = $this->getInlineFieldType($conf); 03153 if ($inlineType == 'list' || $inlineType == 'field') { 03154 $moveTable = $foreign_table; 03155 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 03156 $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf); 03157 } 03158 } 03159 } 03160 03161 // move the records 03162 if (isset($dbAnalysis)) { 03163 foreach ($dbAnalysis->itemArray as $v) { 03164 $this->moveRecord($v['table'],$v['id'],$destPid); 03165 } 03166 } 03167 } 03168 03178 function localize($table,$uid,$language) { 03179 global $TCA; 03180 03181 $uid = intval($uid); 03182 03183 if ($TCA[$table] && $uid) { 03184 t3lib_div::loadTCA($table); 03185 03186 if (($TCA[$table]['ctrl']['languageField'] && $TCA[$table]['ctrl']['transOrigPointerField'] && !$TCA[$table]['ctrl']['transOrigPointerTable']) || $table==='pages') { 03187 if ($langRec = t3lib_BEfunc::getRecord('sys_language',intval($language),'uid,title')) { 03188 if ($this->doesRecordExist($table,$uid,'show')) { 03189 03190 $row = t3lib_BEfunc::getRecordWSOL($table,$uid); // Getting workspace overlay if possible - this will localize versions in workspace if any 03191 if (is_array($row)) { 03192 if ($row[$TCA[$table]['ctrl']['languageField']] <= 0 || $table==='pages') { 03193 if ($row[$TCA[$table]['ctrl']['transOrigPointerField']] == 0 || $table==='pages') { 03194 if ($table==='pages') { 03195 $pass = $TCA[$table]['ctrl']['transForeignTable']==='pages_language_overlay' && !t3lib_BEfunc::getRecordsByField('pages_language_overlay','pid',$uid,' AND '.$TCA['pages_language_overlay']['ctrl']['languageField'].'='.intval($langRec['uid'])); 03196 $Ttable = 'pages_language_overlay'; 03197 t3lib_div::loadTCA($Ttable); 03198 } else { 03199 $pass = !t3lib_BEfunc::getRecordsByField($table,$TCA[$table]['ctrl']['transOrigPointerField'],$uid,'AND pid='.intval($row['pid']).' AND '.$TCA[$table]['ctrl']['languageField'].'='.intval($langRec['uid'])); 03200 $Ttable = $table; 03201 } 03202 03203 if ($pass) { 03204 03205 // Initialize: 03206 $overrideValues = array(); 03207 $excludeFields = array(); 03208 03209 // Set override values: 03210 $overrideValues[$TCA[$Ttable]['ctrl']['languageField']] = $langRec['uid']; 03211 $overrideValues[$TCA[$Ttable]['ctrl']['transOrigPointerField']] = $uid; 03212 03213 // Set exclude Fields: 03214 foreach($TCA[$Ttable]['columns'] as $fN => $fCfg) { 03215 if ($fCfg['l10n_mode']=='prefixLangTitle') { // Check if we are just prefixing: 03216 if (($fCfg['config']['type']=='text' || $fCfg['config']['type']=='input') && strlen($row[$fN])) { 03217 list($tscPID) = t3lib_BEfunc::getTSCpid($table,$uid,''); 03218 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 03219 03220 if (isset($TSConfig['translateToMessage']) && strlen($TSConfig['translateToMessage'])) { 03221 $translateToMsg = @sprintf($TSConfig['translateToMessage'], $langRec['title']); 03222 } 03223 if (!strlen($translateToMsg)) { 03224 $translateToMsg = 'Translate to '.$langRec['title'].':'; 03225 } 03226 03227 $overrideValues[$fN] = '['.$translateToMsg.'] '.$row[$fN]; 03228 } 03229 } elseif (t3lib_div::inList('exclude,noCopy,mergeIfNotBlank',$fCfg['l10n_mode']) && $fN!=$TCA[$Ttable]['ctrl']['languageField'] && $fN!=$TCA[$Ttable]['ctrl']['transOrigPointerField']) { // Otherwise, do not copy field (unless it is the language field or pointer to the original language) 03230 $excludeFields[] = $fN; 03231 } 03232 } 03233 03234 if ($Ttable === $table) { 03235 03236 // Execute the copy: 03237 $this->copyRecord($table,$uid,-$uid,1,$overrideValues,implode(',',$excludeFields)); 03238 } else { 03239 03240 // Create new record: 03241 $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain'); 03242 $copyTCE->stripslashes_values = 0; 03243 $copyTCE->cachedTSconfig = $this->cachedTSconfig; // Copy forth the cached TSconfig 03244 $copyTCE->dontProcessTransformations=1; // Transformations should NOT be carried out during copy 03245 03246 $copyTCE->start(array($Ttable=>array('NEW'=>$overrideValues)),'',$this->BE_USER); 03247 $copyTCE->process_datamap(); 03248 03249 // Getting the new UID as if it had been copied: 03250 $theNewSQLID = $copyTCE->substNEWwithIDs['NEW']; 03251 if ($theNewSQLID) { 03252 // If is by design that $Ttable is used and not $table! See "l10nmgr" extension. Could be debated, but this is what I chose for this "pseudo case" 03253 $this->copyMappingArray[$Ttable][$uid] = $theNewSQLID; 03254 } 03255 } 03256 } else $this->newlog('Localization failed; There already was a localization for this language of the record!',1); 03257 } else $this->newlog('Localization failed; Source record contained a reference to an original default record (which is strange)!',1); 03258 } else $this->newlog('Localization failed; Source record had another language than "Default" or "All" defined!',1); 03259 } else $this->newlog('Attempt to localize record that did not exist!',1); 03260 } else $this->newlog('Attempt to localize record without permission',1); 03261 } else $this->newlog('Sys language UID "'.$language.'" not found valid!',1); 03262 } else $this->newlog('Localization failed; "languageField" and "transOrigPointerField" must be defined for the table!',1); 03263 } 03264 } 03265 03266 03267 03268 03269 03270 03271 03272 03273 03274 03275 03276 03277 03278 03279 /********************************************* 03280 * 03281 * Cmd: Deleting 03282 * 03283 ********************************************/ 03284 03292 function deleteAction($table, $id) { 03293 global $TCA; 03294 03295 $delRec = t3lib_BEfunc::getRecord($table, $id); 03296 03297 if (is_array($delRec)) { // Record asked to be deleted was found: 03298 03299 // For Live version, try if there is a workspace version because if so, rather "delete" that instead 03300 if ($delRec['pid']!=-1) { // Look, if record is an offline version, then delete directly: 03301 if ($wsVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $id)) { 03302 $delRec = $wsVersion; 03303 $id = $delRec['uid']; 03304 } 03305 } 03306 03307 if ($delRec['pid']==-1) { // Look, if record is an offline version, then delete directly: 03308 if ($TCA[$table]['ctrl']['versioningWS']) { 03309 if ($this->BE_USER->workspace==0 || (int)$delRec['t3ver_wsid']==$this->BE_USER->workspace) { // In Live workspace, delete any. In other workspaces there must be match. 03310 $liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'uid,t3ver_state'); 03311 03312 if ($delRec['t3ver_wsid']==0 || (int)$liveRec['t3ver_state']!==1) { // Delete those in WS 0 + if their live records state was not "Placeholder". 03313 $this->deleteEl($table, $id); 03314 } else { // If live record was placeholder, rather clear it from workspace (because it clears both version and placeholder). 03315 $this->version_clearWSID($table,$id); 03316 } 03317 } else $this->newlog('Tried to delete record from another workspace',1); 03318 } else $this->newlog('Versioning not enabled for record with PID = -1!',2); 03319 } elseif ($res = $this->BE_USER->workspaceAllowLiveRecordsInPID($delRec['pid'], $table)) { // Look, if record is "online" or in a versionized branch, then delete directly. 03320 if ($res>0) { 03321 $this->deleteEl($table, $id); 03322 } else $this->newlog('Stage of root point did not allow for deletion',1); 03323 } else { 03324 // Otherwise, try to delete by versionization: 03325 $this->versionizeRecord($table,$id,'DELETED!',TRUE); 03326 } 03327 } 03328 } 03329 03339 function deleteEl($table, $uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE) { 03340 if ($table == 'pages') { 03341 $this->deletePages($uid, $noRecordCheck, $forceHardDelete); 03342 } else { 03343 $this->deleteVersionsForRecord($table,$uid,$forceHardDelete); 03344 $this->deleteRecord($table, $uid, $noRecordCheck, $forceHardDelete); 03345 } 03346 } 03347 03356 function deleteVersionsForRecord($table, $uid, $forceHardDelete) { 03357 $versions = t3lib_BEfunc::selectVersionsOfRecord($table, $uid, 'uid,pid'); 03358 if (is_array($versions)) { 03359 foreach($versions as $verRec) { 03360 if (!$verRec['_CURRENT_VERSION']) { 03361 if ($table == 'pages') { 03362 $this->deletePages($verRec['uid'], TRUE, $forceHardDelete); 03363 } else { 03364 $this->deleteRecord($table, $verRec['uid'], TRUE, $forceHardDelete); 03365 } 03366 } 03367 } 03368 } 03369 } 03370 03378 function undeleteRecord($table,$uid) { 03379 $this->deleteRecord($table,$uid,TRUE,FALSE,TRUE); 03380 } 03381 03395 function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE,$undeleteRecord=FALSE) { 03396 global $TCA; 03397 03398 $uid = intval($uid); 03399 if ($TCA[$table] && $uid) { 03400 if ($noRecordCheck || $this->doesRecordExist($table,$uid,'delete')) { 03401 $this->clear_cache($table,$uid); // clear cache before deleting the record, else the correct page cannot be identified by clear_cache 03402 03403 $propArr = $this->getRecordProperties($table, $uid); 03404 $pagePropArr = $this->getRecordProperties('pages', $propArr['pid']); 03405 03406 $deleteRow = $TCA[$table]['ctrl']['delete']; 03407 if ($deleteRow && !$forceHardDelete) { 03408 $value = $undeleteRecord ? 0 : 1; 03409 $updateFields = array( 03410 $deleteRow => $value 03411 ); 03412 03413 if ($TCA[$table]['ctrl']['tstamp']) { 03414 $updateFields[$TCA[$table]['ctrl']['tstamp']] = time(); 03415 } 03416 03417 // If the table is sorted, then the sorting number is set very high 03418 if ($TCA[$table]['ctrl']['sortby'] && !$undeleteRecord) { 03419 $updateFields[$TCA[$table]['ctrl']['sortby']] = 1000000000; 03420 } 03421 03422 // before (un-)deleting this record, check for child records or references 03423 $this->deleteRecord_procFields($table, $uid, $undeleteRecord); 03424 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields); 03425 } else { 03426 03427 // Fetches all fields with flexforms and look for files to delete: 03428 t3lib_div::loadTCA($table); 03429 foreach($TCA[$table]['columns'] as $fieldName => $cfg) { 03430 $conf = $cfg['config']; 03431 03432 switch($conf['type']) { 03433 case 'flex': 03434 $flexObj = t3lib_div::makeInstance('t3lib_flexformtools'); 03435 $flexObj->traverseFlexFormXMLData($table,$fieldName,t3lib_BEfunc::getRecordRaw($table,'uid='.intval($uid)),$this,'deleteRecord_flexFormCallBack'); 03436 break; 03437 } 03438 } 03439 03440 // Fetches all fields that holds references to files 03441 $fileFieldArr = $this->extFileFields($table); 03442 if (count($fileFieldArr)) { 03443 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',',$fileFieldArr), $table, 'uid='.intval($uid)); 03444 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 03445 $fArray = $fileFieldArr; 03446 foreach($fArray as $theField) { // MISSING: Support for MM file relations! 03447 $this->extFileFunctions($table,$theField,$row[$theField],'deleteAll'); // This deletes files that belonged to this record. 03448 } 03449 } else { 03450 $this->log($table,$uid,3,0,100,'Delete: Zero rows in result when trying to read filenames from record which should be deleted'); 03451 } 03452 } 03453 03454 // Delete the hard way...: 03455 $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($uid)); 03456 } 03457 03458 $state = $undeleteRecord ? 1 : 3; // 1 means insert, 3 means delete 03459 if (!$GLOBALS['TYPO3_DB']->sql_error()) { 03460 if ($forceHardDelete) { 03461 $message = "Record '%s' (%s) was deleted unrecoverable from page '%s' (%s)"; 03462 } 03463 else { 03464 $message = $state == 1 ? 03465 "Record '%s' (%s) was restored on page '%s' (%s)" : 03466 "Record '%s' (%s) was deleted from page '%s' (%s)"; 03467 } 03468 $this->log($table, $uid, $state, 0, 0, 03469 $message, 0, 03470 array( 03471 $propArr['header'], 03472 $table.':'.$uid, 03473 $pagePropArr['header'], 03474 $propArr['pid'] 03475 ), 03476 $propArr['pid']); 03477 03478 } else { 03479 $this->log($table,$uid,$state,0,100,$GLOBALS['TYPO3_DB']->sql_error()); 03480 } 03481 03482 // Update reference index: 03483 $this->updateRefIndex($table,$uid); 03484 03485 // if there are entries in the updateRefIndexStack 03486 if (is_array($this->updateRefIndexStack[$table]) && is_array($this->updateRefIndexStack[$table][$uid])) { 03487 while ($args = array_pop($this->updateRefIndexStack[$table][$uid])) { 03488 // $args[0]: table, $args[1]: uid 03489 $this->updateRefIndex($args[0], $args[1]); 03490 } 03491 unset($this->updateRefIndexStack[$table][$uid]); 03492 } 03493 03494 } else $this->log($table,$uid,3,0,1,'Attempt to delete record without delete-permissions'); 03495 } 03496 } 03497 03501 function deleteRecord_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj) { 03502 03503 // Use reference index object to find files in fields: 03504 $refIndexObj = t3lib_div::makeInstance('t3lib_refindex'); 03505 $files = $refIndexObj->getRelations_procFiles($dataValue, $dsArr['TCEforms']['config'], $PA['uid']); 03506 03507 // Traverse files and delete them: 03508 if (is_array($files)) { 03509 foreach($files as $dat) { 03510 if (@is_file($dat['ID_absFile'])) { 03511 unlink ($dat['ID_absFile']); 03512 #echo 'DELETE FlexFormFile:'.$dat['ID_absFile'].chr(10); 03513 } else { 03514 $this->log($table,0,3,0,100,"Delete: Referenced file '".$dat['ID_absFile']."' that was supposed to be deleted together with it's record didn't exist"); 03515 } 03516 } 03517 } 03518 } 03519 03528 function deletePages($uid,$force=FALSE,$forceHardDelete=FALSE) { 03529 // Getting list of pages to delete: 03530 if ($force) { 03531 $brExist = $this->doesBranchExist('',$uid,0,1); // returns the branch WITHOUT permission checks (0 secures that) 03532 $res = t3lib_div::trimExplode(',',$brExist.$uid,1); 03533 } else { 03534 $res = $this->canDeletePage($uid); 03535 } 03536 03537 // Perform deletion if not error: 03538 if (is_array($res)) { 03539 foreach($res as $deleteId) { 03540 $this->deleteSpecificPage($deleteId,$forceHardDelete); 03541 } 03542 } else { 03543 $this->newlog($res,1); 03544 } 03545 } 03546 03556 function deleteSpecificPage($uid,$forceHardDelete=FALSE) { 03557 global $TCA; 03558 reset ($TCA); 03559 $uid = intval($uid); 03560 if ($uid) { 03561 while (list($table)=each($TCA)) { 03562 if ($table!='pages') { 03563 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table)); 03564 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 03565 $this->deleteVersionsForRecord($table,$row['uid'],$forceHardDelete); 03566 $this->deleteRecord($table,$row['uid'], TRUE, $forceHardDelete); 03567 } 03568 } 03569 } 03570 $this->deleteVersionsForRecord('pages',$uid,$forceHardDelete); 03571 $this->deleteRecord('pages',$uid, TRUE, $forceHardDelete); 03572 } 03573 } 03574 03581 function canDeletePage($uid) { 03582 if ($this->doesRecordExist('pages',$uid,'delete')) { // If we may at all delete this page 03583 if ($this->deleteTree) { 03584 $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1); // returns the branch 03585 if ($brExist != -1) { // Checks if we had permissions 03586 if ($this->noRecordsFromUnallowedTables($brExist.$uid)) { 03587 return t3lib_div::trimExplode(',',$brExist.$uid,1); 03588 } else return 'Attempt to delete records from disallowed tables'; 03589 } else return 'Attempt to delete pages in branch without permissions'; 03590 } else { 03591 $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1); // returns the branch 03592 if ($brExist == '') { // Checks if branch exists 03593 if ($this->noRecordsFromUnallowedTables($uid)) { 03594 return array($uid); 03595 } else return 'Attempt to delete records from disallowed tables'; 03596 } else return 'Attempt to delete page which has subpages'; 03597 } 03598 } else return 'Attempt to delete page without permissions'; 03599 } 03600 03608 function cannotDeleteRecord($table,$id) { 03609 if ($table==='pages') { 03610 $res = $this->canDeletePage($id); 03611 return is_array($res) ? FALSE : $res; 03612 } else { 03613 return $this->doesRecordExist($table,$id,'delete') ? FALSE : 'No permission to delete record'; 03614 } 03615 } 03616 03627 function deleteRecord_procFields($table, $uid, $undeleteRecord = false) { 03628 t3lib_div::loadTCA($table); 03629 $conf = $GLOBALS['TCA'][$table]['columns']; 03630 $row = t3lib_BEfunc::getRecord($table, $uid, '*', '', false); 03631 03632 foreach ($row as $field => $value) { 03633 $this->deleteRecord_procBasedOnFieldType($table, $uid, $field, $value, $conf[$field]['config'], $undeleteRecord); 03634 } 03635 } 03636 03650 function deleteRecord_procBasedOnFieldType($table, $uid, $field, $value, $conf, $undeleteRecord = false) { 03651 if ($conf['type'] == 'inline') { 03652 $foreign_table = $conf['foreign_table']; 03653 03654 if ($foreign_table) { 03655 $inlineType = $this->getInlineFieldType($conf); 03656 if ($inlineType == 'list' || $inlineType == 'field') { 03657 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 03658 $dbAnalysis->start($value, $conf['foreign_table'], '', $uid, $table, $conf); 03659 $dbAnalysis->undeleteRecord = true; 03660 03661 // walk through the items and remove them 03662 foreach ($dbAnalysis->itemArray as $v) { 03663 if (!$undeleteRecord) { 03664 $this->deleteAction($v['table'], $v['id']); 03665 } else { 03666 $this->undeleteRecord($v['table'], $v['id']); 03667 } 03668 } 03669 } 03670 } 03671 03672 // no delete action but calls to updateRefIndex *AFTER* this record was deleted 03673 } elseif ($this->isReferenceField($conf)) { 03674 $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table']; 03675 $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : $conf['neg_foreign_table']; 03676 03677 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 03678 $dbAnalysis->start($value, $allowedTables, $conf['MM'], $uid, $table, $conf); 03679 03680 foreach ($dbAnalysis->itemArray as $v) { 03681 $this->updateRefIndexStack[$table][$uid][] = array($v['table'], $v['id']); 03682 } 03683 } 03684 } 03685 03686 03687 03688 03689 03690 03691 03692 03693 /********************************************* 03694 * 03695 * Cmd: Versioning 03696 * 03697 ********************************************/ 03698 03711 function versionizeRecord($table,$id,$label,$delete=FALSE,$versionizeTree=-1) { 03712 global $TCA; 03713 03714 $id = intval($id); 03715 03716 if ($TCA[$table] && $TCA[$table]['ctrl']['versioningWS'] && $id>0) { 03717 if ($this->doesRecordExist($table,$id,'show')) { 03718 if ($this->BE_USER->workspaceVersioningTypeAccess($versionizeTree)) { 03719 03720 // Select main record: 03721 $row = $this->recordInfo($table,$id,'pid,t3ver_id'); 03722 if (is_array($row)) { 03723 if ($row['pid']>=0) { // record must be online record 03724 if (!$delete || !$this->cannotDeleteRecord($table,$id)) { 03725 03726 // Look for next version number: 03727 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 03728 't3ver_id', 03729 $table, 03730 '(t3ver_oid='.$id.' OR uid='.$id.')'.$this->deleteClause($table), 03731 '', 03732 't3ver_id DESC', 03733 '1' 03734 ); 03735 list($highestVerNumber) = $GLOBALS['TYPO3_DB']->sql_fetch_row($res); 03736 03737 // Look for version number of the current: 03738 $subVer = $row['t3ver_id'].'.'.($highestVerNumber+1); 03739 03740 // Set up the values to override when making a raw-copy: 03741 $overrideArray = array( 03742 't3ver_id' => $highestVerNumber+1, 03743 't3ver_oid' => $id, 03744 't3ver_label' => ($label ? $label : $subVer.' / '.date('d-m-Y H:m:s')), 03745 't3ver_wsid' => $this->BE_USER->workspace, 03746 't3ver_state' => $delete ? 2 : 0, 03747 't3ver_count' => 0, 03748 't3ver_stage' => 0, 03749 't3ver_tstamp' => 0 03750 ); 03751 if ($TCA[$table]['ctrl']['editlock']) { 03752 $overrideArray[$TCA[$table]['ctrl']['editlock']] = 0; 03753 } 03754 if ($table==='pages') { 03755 $overrideArray['t3ver_swapmode'] = $versionizeTree; 03756 } 03757 03758 // Checking if the record already has a version in the current workspace of the backend user 03759 $workspaceCheck = TRUE; 03760 if ($this->BE_USER->workspace!==0) { 03761 // Look for version already in workspace: 03762 $workspaceCheck = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace,$table,$id,'uid') ? FALSE : TRUE; 03763 } 03764 03765 if ($workspaceCheck) { 03766 03767 // Create raw-copy and return result: 03768 return $this->copyRecord_raw($table,$id,-1,$overrideArray); 03769 } else $this->newlog('Record you wanted to versionize was already a version in the workspace (wsid='.$this->BE_USER->workspace.')!',1); 03770 } else $this->newlog('Record cannot be deleted: '.$this->cannotDeleteRecord($table,$id),1); 03771 } else $this->newlog('Record you wanted to versionize was already a version in archive (pid=-1)!',1); 03772 } else $this->newlog('Record you wanted to versionize did not exist!',1); 03773 } else $this->newlog('The versioning type '.$versionizeTree.' mode you requested was not allowed',1); 03774 } else $this->newlog('You didnt have correct permissions to make a new version (copy) of this record "'.$table.'" / '.$id,1); 03775 } else $this->newlog('Versioning is not supported for this table "'.$table.'" / '.$id,1); 03776 } 03777 03787 function versionizePages($uid,$label,$versionizeTree) { 03788 global $TCA; 03789 03790 $uid = intval($uid); 03791 $brExist = $this->doesBranchExist('',$uid,$this->pMap['show'],1); // returns the branch 03792 03793 if ($brExist != -1) { // Checks if we had permissions 03794 03795 // Finding list of tables ALLOWED to be copied 03796 $allowedTablesArray = $this->admin ? $this->compileAdminTables() : explode(',',$this->BE_USER->groupData['tables_modify']); // These are the tables, the user may modify 03797 $allowedTablesArray = $this->compileAdminTables(); // These are ALL tables because a new version should be ALL of them regardless of permission of the user executing the request. 03798 03799 // Make list of tables that should come along with a new version of the page: 03800 $verTablesArray = array(); 03801 $allTables = array_keys($TCA); 03802 foreach($allTables as $tN) { 03803 if ($tN!='pages' && ($versionizeTree>0 || $TCA[$tN]['ctrl']['versioning_followPages']) && ($this->admin || in_array($tN, $allowedTablesArray))) { 03804 $verTablesArray[] = $tN; 03805 } 03806 } 03807 03808 // Begin to copy pages if we're allowed to: 03809 if ($this->admin || in_array('pages',$allowedTablesArray)) { 03810 if ($this->BE_USER->workspaceVersioningTypeAccess($versionizeTree)) { 03811 // Versionize this page: 03812 $theNewRootID = $this->versionizeRecord('pages',$uid,$label,FALSE,$versionizeTree); 03813 if ($theNewRootID) { 03814 $this->rawCopyPageContent($uid,$theNewRootID,$verTablesArray); 03815 03816 // If we're going to copy recursively...: 03817 if ($versionizeTree>0) { 03818 03819 // Get ALL subpages to copy (read permissions respected - they should NOT be...): 03820 $CPtable = $this->int_pageTreeInfo(Array(), $uid, intval($versionizeTree), $theNewRootID); 03821 03822 // Now copying the subpages: 03823 foreach($CPtable as $thePageUid => $thePagePid) { 03824 $newPid = $this->copyMappingArray['pages'][$thePagePid]; 03825 if (isset($newPid)) { 03826 $theNewRootID = $this->copyRecord_raw('pages',$thePageUid,$newPid); 03827 $this->rawCopyPageContent($thePageUid,$theNewRootID,$verTablesArray); 03828 } else { 03829 $this->newlog('Something went wrong during copying branch (for versioning)',1); 03830 break; 03831 } 03832 } 03833 } // else the page was not copied. Too bad... 03834 } else $this->newlog('The root version could not be created!',1); 03835 } else $this->newlog('Versioning type "'.$versionizeTree.'" was not allowed in workspace',1); 03836 } else $this->newlog('Attempt to versionize page without permission to this table',1); 03837 } else $this->newlog('Could not read all subpages to versionize.',1); 03838 } 03839 03850 function version_swap($table,$id,$swapWith,$swapIntoWS=0) { 03851 global $TCA; 03852 03853 /* 03854 Version ID swapping principles: 03855 - Version from archive (future/past, called "swap version") will get the uid of the "t3ver_oid", the official element with uid = "t3ver_oid" will get the new versions old uid. PIDs are swapped also 03856 03857 uid pid uid t3ver_oid pid 03858 1: 13 123 --> -13 247 123 (Original has negated UID, and sets t3ver_oid to the final UID (which is nice to know for recovery). PID is unchanged at this point) 03859 2: 247 -1 --> 13 13 123 (Swap version gets original UID, correct t3ver_oid (not required for online version) and is moved to the final PID (123)) 03860 3: -13 123 --> 247 13 -1 (Original gets the swap versions old UID, has t3ver_oid set correctly (important) and the ver. repository PID set right.) 03861 03862 13 is online UID, 03863 247 is specific versions UID 03864 123 is the PID of the original record 03865 -1 is the versioning repository PID 03866 03867 Recovery Process: 03868 Search for negative UID (here "-13"): 03869 YES: Step 1 completed, but at least step 3 didn't. 03870 Search for the negativ UIDs positive (here: "13") 03871 YES: Step 2 completed: Rollback: "t3ver_oid" of the -uid record shows the original UID of the swap record. Use that to change back UID and pid to -1. After that, proceed with recovery for step 1 (see below) 03872 NO: Only Step 1 completed! Rollback: Just change uid "-13" to "13" and "t3ver_oid" to "13" (not important) 03873 NO: No problems. 03874 */ 03875 03876 // First, check if we may actually edit the online record 03877 if ($this->checkRecordUpdateAccess($table,$id)) { 03878 03879 // Select the two versions: 03880 $curVersion = t3lib_BEfunc::getRecord($table,$id,'*'); 03881 $swapVersion = t3lib_BEfunc::getRecord($table,$swapWith,'*'); 03882 03883 if (is_array($curVersion) && is_array($swapVersion)) { 03884 if ($this->BE_USER->workspacePublishAccess($swapVersion['t3ver_wsid'])) { 03885 $wsAccess = $this->BE_USER->checkWorkspace($swapVersion['t3ver_wsid']); 03886 if ($swapVersion['t3ver_wsid']<=0 || !($wsAccess['publish_access']&1) || (int)$swapVersion['t3ver_stage']===10) { 03887 if ($this->doesRecordExist($table,$swapWith,'show') && $this->checkRecordUpdateAccess($table,$swapWith)) { 03888 if (!$swapIntoWS || $this->BE_USER->workspaceSwapAccess()) { 03889 03890 // Check if the swapWith record really IS a version of the original! 03891 if ((int)$swapVersion['pid']==-1 && (int)$curVersion['pid']>=0 && !strcmp($swapVersion['t3ver_oid'],$id)) { 03892 03893 // Lock file name: 03894 $lockFileName = PATH_site.'typo3temp/swap_locking/'.$table.':'.$id.'.ser'; 03895 03896 if (!@is_file($lockFileName)) { 03897 03898 // Write lock-file: 03899 t3lib_div::writeFileToTypo3tempDir($lockFileName,serialize(array( 03900 'tstamp'=>time(), 03901 'user'=>$GLOBALS['BE_USER']->user['username'], 03902 'curVersion'=>$curVersion, 03903 'swapVersion'=>$swapVersion 03904 ))); 03905 03906 // Find fields to keep 03907 $keepFields = $this->getUniqueFields($table); 03908 if ($TCA[$table]['ctrl']['sortby']) { 03909 $keepFields[] = $TCA[$table]['ctrl']['sortby']; 03910 } 03911 03912 // Swap "keepfields" 03913 foreach($keepFields as $fN) { 03914 $tmp = $swapVersion[$fN]; 03915 $swapVersion[$fN] = $curVersion[$fN]; 03916 $curVersion[$fN] = $tmp; 03917 } 03918 03919 // Preserve states: 03920 $t3ver_state = array(); 03921 $t3ver_state['swapVersion'] = $swapVersion['t3ver_state']; 03922 $t3ver_state['curVersion'] = $curVersion['t3ver_state']; 03923 03924 // Modify offline version to become online: 03925 $tmp_wsid = $swapVersion['t3ver_wsid']; 03926 unset($swapVersion['uid']); 03927 $swapVersion['pid'] = intval($curVersion['pid']); // Set pid for ONLINE 03928 $swapVersion['t3ver_oid'] = intval($id); 03929 $swapVersion['t3ver_wsid'] = $swapIntoWS ? intval($curVersion['t3ver_wsid']) : 0; 03930 $swapVersion['t3ver_tstamp'] = time(); 03931 $swapVersion['t3ver_stage'] = $swapVersion['t3ver_state'] = 0; 03932 03933 // Modify online version to become offline: 03934 unset($curVersion['uid']); 03935 $curVersion['pid'] = -1; // Set pid for OFFLINE 03936 $curVersion['t3ver_oid'] = intval($id); 03937 $curVersion['t3ver_wsid'] = $swapIntoWS ? intval($tmp_wsid) : 0; 03938 $curVersion['t3ver_tstamp'] = time(); 03939 $curVersion['t3ver_count'] = $curVersion['t3ver_count']+1; // Increment lifecycle counter 03940 $curVersion['t3ver_stage'] = $curVersion['t3ver_state'] = 0; 03941 03942 if ($table==='pages') { // Keeping the swapmode state 03943 $curVersion['t3ver_swapmode'] = $swapVersion['t3ver_swapmode']; 03944 } 03945 03946 // Execute swapping: 03947 $sqlErrors = array(); 03948 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($id),$swapVersion); 03949 if ($GLOBALS['TYPO3_DB']->sql_error()) { 03950 $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error(); 03951 } else { 03952 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($swapWith),$curVersion); 03953 if ($GLOBALS['TYPO3_DB']->sql_error()) { 03954 $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03955 } else { 03956 unlink($lockFileName); 03957 } 03958 } 03959 03960 if (!count($sqlErrors)) { 03961 03962 // Checking for delete: 03963 if ($t3ver_state['swapVersion']==2) { 03964 $this->deleteEl($table,$id,TRUE); // Force delete 03965 } 03966 03967 $this->newlog('Swapping successful for table "'.$table.'" uid '.$id.'=>'.$swapWith); 03968 03969 // Update reference index: 03970 $this->updateRefIndex($table,$id); 03971 $this->updateRefIndex($table,$swapWith); 03972 03973 // SWAPPING pids for subrecords: 03974 if ($table=='pages' && $swapVersion['t3ver_swapmode']>=0) { 03975 03976 // Collect table names that should be copied along with the tables: 03977 foreach($TCA as $tN => $tCfg) { 03978 if ($swapVersion['t3ver_swapmode']>0 || $TCA[$tN]['ctrl']['versioning_followPages']) { // For "Branch" publishing swap ALL, otherwise for "page" publishing, swap only "versioning_followPages" tables 03979 $temporaryPid = -($id+1000000); 03980 03981 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($id),array('pid'=>$temporaryPid)); 03982 if ($GLOBALS['TYPO3_DB']->sql_error()) $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03983 03984 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($swapWith),array('pid'=>$id)); 03985 if ($GLOBALS['TYPO3_DB']->sql_error()) $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03986 03987 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($temporaryPid),array('pid'=>$swapWith)); 03988 if ($GLOBALS['TYPO3_DB']->sql_error()) $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03989 03990 if (count($sqlErrors)) { 03991 $this->newlog('During Swapping: SQL errors happend: '.implode('; ',$sqlErrors),2); 03992 } 03993 } 03994 } 03995 } 03996 // Clear cache: 03997 $this->clear_cache($table,$id); 03998 03999 // Checking for "new-placeholder" and if found, delete it (BUT FIRST after swapping!): 04000 if ($t3ver_state['curVersion']==1) { 04001 $this->deleteEl($table, $swapWith, TRUE, TRUE); // For delete + completely delete! 04002 } 04003 } else $this->newlog('During Swapping: SQL errors happend: '.implode('; ',$sqlErrors),2); 04004 } else $this->newlog('A swapping lock file was present. Either another swap process is already running or a previous swap process failed. Ask your administrator to handle the situation.',2); 04005 } else $this->newlog('In swap version, either pid was not -1 or the t3ver_oid didn\'t match the id of the online version as it must!',2); 04006 } else $this->newlog('Workspace #'.$swapVersion['t3ver_wsid'].' does not support swapping.',1); 04007 } else $this->newlog('You cannot publish a record you do not have edit and show permissions for',1); 04008 } else $this->newlog('Records in workspace #'.$swapVersion['t3ver_wsid'].' can only be published when in "Publish" stage.',1); 04009 } else $this->newlog('User could not publish records from workspace #'.$swapVersion['t3ver_wsid'],1); 04010 } else $this->newlog('Error: Either online or swap version could not be selected!',2); 04011 } else $this->newlog('Error: You cannot swap versions for a record you do not have access to edit!',1); 04012 } 04013 04021 function version_clearWSID($table,$id) { 04022 if ($errorCode = $this->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) { 04023 $this->newlog('Attempt to reset workspace for record failed: '.$errorCode,1); 04024 } elseif ($this->checkRecordUpdateAccess($table,$id)) { 04025 if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'uid,t3ver_state')) { 04026 // Clear workspace ID: 04027 $sArray = array(); 04028 $sArray['t3ver_wsid'] = 0; 04029 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($id),$sArray); 04030 04031 // Clear workspace ID for live version AND DELETE IT as well because it is a new record! 04032 if ((int)$liveRec['t3ver_state']===1) { 04033 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($liveRec['uid']),$sArray); 04034 $this->deleteEl($table, $liveRec['uid'], TRUE); // THIS assumes that the record was placeholder ONLY for ONE record (namely $id) 04035 } 04036 04037 // If "deleted" flag is set for the version that got released it doesn't make sense to keep that "placeholder" anymore and we delete it completly. 04038 $wsRec = t3lib_BEfunc::getRecord($table,$id); 04039 if ((int)$wsRec['t3ver_state']===2) { 04040 $this->deleteEl($table, $id, TRUE, TRUE); 04041 } 04042 } 04043 } else $this->newlog('Attempt to reset workspace for record failed because you do not have edit access',1); 04044 } 04045 04055 function version_setStage($table,$id,$stageId,$comment='') { 04056 if ($errorCode = $this->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) { 04057 $this->newlog('Attempt to set stage for record failed: '.$errorCode,1); 04058 } elseif ($this->checkRecordUpdateAccess($table,$id)) { 04059 $stat = $this->BE_USER->checkWorkspaceCurrent(); 04060 if (t3lib_div::inList('admin,online,offline,reviewer,owner', $stat['_ACCESS']) || ($stageId<=1 && $stat['_ACCESS']==='member')) { 04061 04062 // Set stage of record: 04063 $sArray = array(); 04064 $sArray['t3ver_stage'] = $stageId; 04065 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($id), $sArray); 04066 $this->newlog('Stage for record was changed to '.$stageId.'. Comment was: "'.substr($comment,0,100).'"'); 04067 // TEMPORARY, except 6-30 as action/detail number which is observed elsewhere! 04068 $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stage'=>$stageId)); 04069 04070 if ((int)$stat['stagechg_notification']>0) { 04071 $this->notifyStageChange($stat,$stageId,$table,$id,$comment); 04072 } 04073 } else $this->newlog('The member user tried to set a stage value "'.$stageId.'" that was not allowed',1); 04074 } else $this->newlog('Attempt to set stage for record failed because you do not have edit access',1); 04075 } 04076 04077 04078 04079 04080 04081 04082 04083 04084 04085 04086 04087 04088 04089 /********************************************* 04090 * 04091 * Cmd: Helper functions 04092 * 04093 ********************************************/ 04094 04100 function remapListedDBRecords() { 04101 global $TCA; 04102 04103 if (count($this->registerDBList)) { 04104 reset($this->registerDBList); 04105 while(list($table,$records)=each($this->registerDBList)) { 04106 t3lib_div::loadTCA($table); 04107 reset($records); 04108 while(list($uid,$fields)=each($records)) { 04109 $newData = array(); 04110 $theUidToUpdate = $this->copyMappingArray_merged[$table][$uid]; 04111 $theUidToUpdate_saveTo = t3lib_BEfunc::wsMapId($table,$theUidToUpdate); 04112 04113 foreach($fields as $fieldName => $value) { 04114 $conf = $TCA[$table]['columns'][$fieldName]['config']; 04115 04116 switch($conf['type']) { 04117 case 'group': 04118 case 'select': 04119 $vArray = $this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate, $table); 04120 if (is_array($vArray)) { 04121 $newData[$fieldName] = implode(',',$vArray); 04122 } 04123 break; 04124 case 'flex': 04125 if ($value=='FlexForm_reference') { 04126 $origRecordRow = $this->recordInfo($table,$theUidToUpdate,'*'); // This will fetch the new row for the element 04127 04128 if (is_array($origRecordRow)) { 04129 t3lib_BEfunc::workspaceOL($table,$origRecordRow); 04130 04131 // Get current data structure and value array: 04132 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $origRecordRow, $table); 04133 $currentValueArray = t3lib_div::xml2array($origRecordRow[$fieldName]); 04134 04135 // Do recursive processing of the XML data: 04136 $currentValueArray['data'] = $this->checkValue_flex_procInData( 04137 $currentValueArray['data'], 04138 array(), // Not used. 04139 array(), // Not used. 04140 $dataStructArray, 04141 array($table,$theUidToUpdate,$fieldName), // Parameters. 04142 'remapListedDBRecords_flexFormCallBack' 04143 ); 04144 04145 // The return value should be compiled back into XML, ready to insert directly in the field (as we call updateDB() directly later): 04146 if (is_array($currentValueArray['data'])) { 04147 $newData[$fieldName] = 04148 $this->checkValue_flexArray2Xml($currentValueArray,TRUE); 04149 } 04150 } 04151 } 04152 break; 04153 case 'inline': 04154 $this->remapListedDBRecords_procInline($conf, $value, $uid, $table); 04155 break; 04156 default: 04157 debug('Field type should not appear here: '. $conf['type']); 04158 break; 04159 } 04160 } 04161 04162 if (count($newData)) { // If any fields were changed, those fields are updated! 04163 $this->updateDB($table,$theUidToUpdate_saveTo,$newData); 04164 } 04165 } 04166 } 04167 } 04168 } 04169 04181 function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2) { 04182 04183 // Extract parameters: 04184 list($table,$uid,$field) = $pParams; 04185 04186 // If references are set for this field, set flag so they can be corrected later: 04187 if ($this->isReferenceField($dsConf) && strlen($dataValue)) { 04188 $vArray = $this->remapListedDBRecords_procDBRefs($dsConf, $dataValue, $uid, $table); 04189 if (is_array($vArray)) { 04190 $dataValue = implode(',',$vArray); 04191 } 04192 } 04193 04194 // Return 04195 return array('value' => $dataValue); 04196 } 04197 04208 function remapListedDBRecords_procDBRefs($conf, $value, $MM_localUid, $table) { 04209 04210 // Initialize variables 04211 $set = FALSE; // Will be set true if an upgrade should be done... 04212 $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table']; // Allowed tables for references. 04213 $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : ''; // Table name to prepend the UID 04214 $dontRemapTables = t3lib_div::trimExplode(',',$conf['dontRemapTablesOnCopy'],1); // Which tables that should possibly not be remapped 04215 04216 // Convert value to list of references: 04217 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 04218 $dbAnalysis->registerNonTableValues = ($conf['type']=='select' && $conf['allowNonIdValues']) ? 1 : 0; 04219 $dbAnalysis->start($value, $allowedTables, $conf['MM'], $MM_localUid, $table, $conf); 04220 04221 // Traverse those references and map IDs: 04222 foreach($dbAnalysis->itemArray as $k => $v) { 04223 $mapID = $this->copyMappingArray_merged[$v['table']][$v['id']]; 04224 if ($mapID && !in_array($v['table'],$dontRemapTables)) { 04225 $dbAnalysis->itemArray[$k]['id'] = $mapID; 04226 $set = TRUE; 04227 } 04228 } 04229 04230 // If a change has been done, set the new value(s) 04231 if ($set) { 04232 if ($conf['MM']) { 04233 // FIXME $theUidToUpdate is undefined 04234 $dbAnalysis->writeMM($conf['MM'], $theUidToUpdate, $prependName); 04235 } else { 04236 $vArray = $dbAnalysis->getValueArray($prependName); 04237 if ($conf['type']=='select') { 04238 $vArray = $dbAnalysis->convertPosNeg($vArray, $conf['foreign_table'], $conf['neg_foreign_table']); 04239 } 04240 return $vArray; 04241 } 04242 } 04243 } 04244 04254 function remapListedDBRecords_procInline($conf, $value, $uid, $table) { 04255 $theUidToUpdate = $this->copyMappingArray_merged[$table][$uid]; 04256 04257 if ($conf['foreign_table']) { 04258 $inlineType = $this->getInlineFieldType($conf); 04259 04260 if ($inlineType == 'field') { 04261 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 04262 $dbAnalysis->start($value, $conf['foreign_table'], $conf['MM'], 0, $table, $conf); 04263 04264 $dbAnalysis->writeForeignField($conf, $uid, $theUidToUpdate); 04265 } elseif ($inlineType == 'mm') { 04266 $vArray = $this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate, $table); 04267 } 04268 } 04269 } 04270 04271 04272 04273 04274 04275 04276 04277 04278 04279 04280 04281 04282 04283 04284 04285 /***************************** 04286 * 04287 * Access control / Checking functions 04288 * 04289 *****************************/ 04290 04297 function checkModifyAccessList($table) { 04298 $res = ($this->admin || (!$this->tableAdminOnly($table) && t3lib_div::inList($this->BE_USER->groupData['tables_modify'],$table))); 04299 return $res; 04300 } 04301 04309 function isRecordInWebMount($table,$id) { 04310 if (!isset($this->isRecordInWebMount_Cache[$table.':'.$id])) { 04311 $recP=$this->getRecordProperties($table,$id); 04312 $this->isRecordInWebMount_Cache[$table.':'.$id]=$this->isInWebMount($recP['event_pid']); 04313 } 04314 return $this->isRecordInWebMount_Cache[$table.':'.$id]; 04315 } 04316 04323 function isInWebMount($pid) { 04324 if (!isset($this->isInWebMount_Cache[$pid])) { 04325 $this->isInWebMount_Cache[$pid]=$this->BE_USER->isInWebMount($pid); 04326 } 04327 return $this->isInWebMount_Cache[$pid]; 04328 } 04329 04337 function checkRecordUpdateAccess($table,$id) { 04338 global $TCA; 04339 $res = 0; 04340 if ($TCA[$table] && intval($id)>0) { 04341 if (isset($this->recUpdateAccessCache[$table][$id])) { // If information is cached, return it 04342 return $this->recUpdateAccessCache[$table][$id]; 04343 // Check if record exists and 1) if 'pages' the page may be edited, 2) if page-content the page allows for editing 04344 } elseif ($this->doesRecordExist($table,$id,'edit')) { 04345 $res = 1; 04346 } 04347 $this->recUpdateAccessCache[$table][$id]=$res; // Cache the result 04348 } 04349 return $res; 04350 } 04351 04361 function checkRecordInsertAccess($insertTable,$pid,$action=1) { 04362 global $TCA; 04363 04364 $res = 0; 04365 $pid = intval($pid); 04366 if ($pid>=0) { 04367 if (isset($this->recInsertAccessCache[$insertTable][$pid])) { // If information is cached, return it 04368 return $this->recInsertAccessCache[$insertTable][$pid]; 04369 } else { 04370 // 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 04371 if ( (!$pid && $this->admin) || $this->doesRecordExist('pages',$pid,($insertTable=='pages'?$this->pMap['new']:$this->pMap['editcontent'])) ) { // Check permissions 04372 if ($this->isTableAllowedForThisPage($pid, $insertTable)) { 04373 $res = 1; 04374 $this->recInsertAccessCache[$insertTable][$pid]=$res; // Cache the result 04375 } else { 04376 $propArr = $this->getRecordProperties('pages',$pid); 04377 $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']); 04378 } 04379 } else { 04380 $propArr = $this->getRecordProperties('pages',$pid); 04381 $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']); 04382 } 04383 } 04384 } 04385 return $res; 04386 } 04387 04395 function isTableAllowedForThisPage($page_uid, $checkTable) { 04396 global $TCA, $PAGES_TYPES; 04397 $page_uid = intval($page_uid); 04398 04399 // 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. 04400 if (($TCA[$checkTable]['ctrl']['rootLevel'] xor !$page_uid) && $TCA[$checkTable]['ctrl']['rootLevel']!=-1 && $checkTable!='pages') { 04401 return false; 04402 } 04403 04404 // Check root-level 04405 if (!$page_uid) { 04406 if ($this->admin) { 04407 return true; 04408 } 04409 } else { 04410 // Check non-root-level 04411 $doktype = $this->pageInfo($page_uid,'doktype'); 04412 $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables']; 04413 $allowedArray = t3lib_div::trimExplode(',',$allowedTableList,1); 04414 if (strstr($allowedTableList,'*') || in_array($checkTable,$allowedArray)) { // If all tables or the table is listed as a allowed type, return true 04415 return true; 04416 } 04417 } 04418 } 04419 04428 function doesRecordExist($table,$id,$perms) { 04429 global $TCA; 04430 04431 if ($this->bypassAccessCheckForRecords) { 04432 return is_array(t3lib_BEfunc::getRecordRaw($table,'uid='.intval($id),'uid')); 04433 } 04434 04435 $res = 0; 04436 $id = intval($id); 04437 04438 // Processing the incoming $perms (from possible string to integer that can be AND'ed) 04439 if (!t3lib_div::testInt($perms)) { 04440 if ($table!='pages') { 04441 switch($perms) { 04442 case 'edit': 04443 case 'delete': 04444 case 'new': 04445 $perms = 'editcontent'; // This holds it all in case the record is not page!! 04446 break; 04447 } 04448 } 04449 $perms = intval($this->pMap[$perms]); 04450 } else { 04451 $perms = intval($perms); 04452 } 04453 04454 if (!$perms) {die('Internal ERROR: no permissions to check for non-admin user.');} 04455 04456 // For all tables: Check if record exists: 04457 if (is_array($TCA[$table]) && $id>0 && ($this->isRecordInWebMount($table,$id) || $this->admin)) { 04458 if ($table != 'pages') { 04459 04460 // Find record without checking page: 04461 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid', $table, 'uid='.intval($id).$this->deleteClause($table)); // THIS SHOULD CHECK FOR editlock I think! 04462 $output = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres); 04463 t3lib_BEfunc::fixVersioningPid($table,$output,TRUE); 04464 04465 // If record found, check page as well: 04466 if (is_array($output)) { 04467 04468 // Looking up the page for record: 04469 $mres = $this->doesRecordExist_pageLookUp($output['pid'], $perms); 04470 $pageRec = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres); 04471 // 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): 04472 if (is_array($pageRec) || (!$output['pid'] && $this->admin)) { 04473 return TRUE; 04474 } 04475 } 04476 return FALSE; 04477 } else { 04478 $mres = $this->doesRecordExist_pageLookUp($id, $perms); 04479 return $GLOBALS['TYPO3_DB']->sql_num_rows($mres); 04480 } 04481 } 04482 } 04483 04493 function doesRecordExist_pageLookUp($id, $perms) { 04494 global $TCA; 04495 04496 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 04497 'uid', 04498 'pages', 04499 'uid='.intval($id). 04500 $this->deleteClause('pages'). 04501 ($perms && !$this->admin ? ' AND '.$this->BE_USER->getPagePermsClause($perms) : ''). 04502 (!$this->admin && $TCA['pages']['ctrl']['editlock'] && ($perms & (2+4+16)) ? ' AND '.$TCA['pages']['ctrl']['editlock'].'=0':'') // admin users don't need check 04503 ); 04504 } 04505 04519 function doesBranchExist($inList,$pid,$perms,$recurse) { 04520 global $TCA; 04521 $pid = intval($pid); 04522 $perms = intval($perms); 04523 04524 if ($pid>=0) { 04525 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 04526 'uid, perms_userid, perms_groupid, perms_user, perms_group, perms_everybody', 04527 'pages', 04528 'pid='.intval($pid).$this->deleteClause('pages'), 04529 '', 04530 'sorting' 04531 ); 04532 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 04533 if ($this->admin || $this->BE_USER->doesUserHaveAccess($row,$perms)) { // IF admin, then it's OK 04534 $inList.=$row['uid'].','; 04535 if ($recurse) { // Follow the subpages recursively... 04536 $inList = $this->doesBranchExist($inList, $row['uid'], $perms, $recurse); 04537 if ($inList == -1) {return -1;} // No permissions somewhere in the branch 04538 } 04539 } else { 04540 return -1; // No permissions 04541 } 04542 } 04543 } 04544 return $inList; 04545 } 04546 04553 function tableReadOnly($table) { 04554 // returns true if table is readonly 04555 global $TCA; 04556 return ($TCA[$table]['ctrl']['readOnly'] ? 1 : 0); 04557 } 04558 04565 function tableAdminOnly($table) { 04566 // returns true if table is admin-only 04567 global $TCA; 04568 return ($TCA[$table]['ctrl']['adminOnly'] ? 1 : 0); 04569 } 04570 04579 function destNotInsideSelf($dest,$id) { 04580 $loopCheck = 100; 04581 $dest = intval($dest); 04582 $id = intval($id); 04583 04584 if ($dest==$id) { 04585 return FALSE; 04586 } 04587 04588 while ($dest!=0 && $loopCheck>0) { 04589 $loopCheck--; 04590 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid, uid, t3ver_oid,t3ver_wsid', 'pages', 'uid='.intval($dest).$this->deleteClause('pages')); 04591 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 04592 t3lib_BEfunc::fixVersioningPid('pages',$row); 04593 if ($row['pid']==$id) { 04594 return FALSE; 04595 } else { 04596 $dest = $row['pid']; 04597 } 04598 } else { 04599 return FALSE; 04600 } 04601 } 04602 return TRUE; 04603 } 04604 04611 function getExcludeListArray() { 04612 global $TCA; 04613 04614 $list = array(); 04615 reset($TCA); 04616 while (list($table)=each($TCA)) { 04617 t3lib_div::loadTCA($table); 04618 while (list($field,$config)=each($TCA[$table]['columns'])) { 04619 if ($config['exclude'] && !t3lib_div::inList($this->BE_USER->groupData['non_exclude_fields'],$table.':'.$field)) { 04620 $list[]=$table.'-'.$field; 04621 } 04622 } 04623 } 04624 return $list; 04625 } 04626 04634 function doesPageHaveUnallowedTables($page_uid,$doktype) { 04635 global $TCA, $PAGES_TYPES; 04636 04637 $page_uid = intval($page_uid); 04638 if (!$page_uid) { 04639 return FALSE; // Not a number. Probably a new page 04640 } 04641 04642 $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables']; 04643 $allowedArray = t3lib_div::trimExplode(',',$allowedTableList,1); 04644 if (strstr($allowedTableList,'*')) { // If all tables is OK the return true 04645 return FALSE; // OK... 04646 } 04647 04648 reset ($TCA); 04649 $tableList = array(); 04650 while (list($table)=each($TCA)) { 04651 if (!in_array($table,$allowedArray)) { // If the table is not in the allowed list, check if there are records... 04652 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid='.intval($page_uid)); 04653 $count = $GLOBALS['TYPO3_DB']->sql_fetch_row($mres); 04654 if ($count[0]) { 04655 $tableList[]=$table; 04656 } 04657 } 04658 } 04659 return implode(',',$tableList); 04660 } 04661 04662 04663 04664 04665 04666 04667 04668 04669 /***************************** 04670 * 04671 * Information lookup 04672 * 04673 *****************************/ 04674 04683 function pageInfo($id,$field) { 04684 if (!isset($this->pageCache[$id])) { 04685 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'pages', 'uid='.intval($id)); 04686 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 04687 $this->pageCache[$id] = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 04688 } 04689 $GLOBALS['TYPO3_DB']->sql_free_result($res); 04690 } 04691 return $this->pageCache[$id][$field]; 04692 } 04693 04703 function recordInfo($table,$id,$fieldList) { 04704 global $TCA; 04705 if (is_array($TCA[$table])) { 04706 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fieldList, $table, 'uid='.intval($id)); 04707 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 04708 return $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 04709 } 04710 } 04711 } 04712 04724 function getRecordProperties($table,$id,$noWSOL=FALSE) { 04725 $row = ($table=='pages' && !$id) ? array('title'=>'[root-level]', 'uid' => 0, 'pid' => 0) :$this->recordInfo($table,$id,'*'); 04726 if (!$noWSOL) { 04727 t3lib_BEfunc::workspaceOL($table,$row); 04728 } 04729 t3lib_BEfunc::fixVersioningPid($table,$row); 04730 return $this->getRecordPropertiesFromRow($table,$row); 04731 } 04732 04740 function getRecordPropertiesFromRow($table,$row) { 04741 global $TCA; 04742 if ($TCA[$table]) { 04743 $out = array( 04744 'header' => $row[$TCA[$table]['ctrl']['label']], 04745 'pid' => $row['pid'], 04746 'event_pid' => ($table=='pages'?$row['uid']:$row['pid']), 04747 't3ver_state' => $TCA[$table]['ctrl']['versioningWS'] ? $row['t3ver_state'] : '', 04748 '_ORIG_pid' => $row['_ORIG_pid'] 04749 ); 04750 return $out; 04751 } 04752 } 04753 04754 04755 04756 04757 04758 04759 04760 04761 04762 04763 04764 04765 04766 04767 04768 /********************************************* 04769 * 04770 * Storing data to Database Layer 04771 * 04772 ********************************************/ 04773 04783 function updateDB($table,$id,$fieldArray) { 04784 global $TCA; 04785 04786 if (is_array($fieldArray) && is_array($TCA[$table]) && intval($id)) { 04787 unset($fieldArray['uid']); // Do NOT update the UID field, ever! 04788 04789 if (count($fieldArray)) { 04790 04791 // Execute the UPDATE query: 04792 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($id), $fieldArray); 04793 04794 // If succees, do...: 04795 if (!$GLOBALS['TYPO3_DB']->sql_error()) { 04796 04797 if ($this->checkStoredRecords) { 04798 $newRow = $this->checkStoredRecord($table,$id,$fieldArray,2); 04799 } 04800 04801 // Update reference index: 04802 $this->updateRefIndex($table,$id); 04803 04804 // Set log entry: 04805 $propArr = $this->getRecordPropertiesFromRow($table,$newRow); 04806 $theLogId = $this->log($table,$id,2,$propArr['pid'],0,"Record '%s' (%s) was updated.",10,array($propArr['header'],$table.':'.$id),$propArr['event_pid']); 04807 04808 // Set History data: 04809 $this->setHistory($table,$id,$theLogId); 04810 04811 // Clear cache for relevant pages: 04812 $this->clear_cache($table,$id); 04813 04814 // Unset the pageCache for the id if table was page. 04815 if ($table=='pages') unset($this->pageCache[$id]); 04816 } else { 04817 $this->log($table,$id,2,0,2,"SQL error: '%s' (%s)",12,array($GLOBALS['TYPO3_DB']->sql_error(),$table.':'.$id)); 04818 } 04819 } 04820 } 04821 } 04822 04835 function insertDB($table,$id,$fieldArray,$newVersion=FALSE,$suggestedUid=0,$dontSetNewIdIndex=FALSE) { 04836 global $TCA; 04837 04838 if (is_array($fieldArray) && is_array($TCA[$table]) && isset($fieldArray['pid'])) { 04839 unset($fieldArray['uid']); // Do NOT insert the UID field, ever! 04840 04841 if (count($fieldArray)) { 04842 04843 // Check for "suggestedUid". 04844 // This feature is used by the import functionality to force a new record to have a certain UID value. 04845 // This is only recommended for use when the destination server is a passive mirrow of another server. 04846 // As a security measure this feature is available only for Admin Users (for now) 04847 $suggestedUid = intval($suggestedUid); 04848 if ($this->BE_USER->isAdmin() && $suggestedUid && $this->suggestedInsertUids[$table.':'.$suggestedUid]) { 04849 // When the value of ->suggestedInsertUids[...] is "DELETE" it will try to remove the previous record 04850 if ($this->suggestedInsertUids[$table.':'.$suggestedUid]==='DELETE') { 04851 // DELETE: 04852 $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($suggestedUid)); 04853 } 04854 $fieldArray['uid'] = $suggestedUid; 04855 } 04856 04857 // Execute the INSERT query: 04858 $GLOBALS['TYPO3_DB']->exec_INSERTquery($table, $fieldArray); 04859 04860 // If succees, do...: 04861 if (!$GLOBALS['TYPO3_DB']->sql_error()) { 04862 04863 // Set mapping for NEW... -> real uid: 04864 $NEW_id = $id; // the NEW_id now holds the 'NEW....' -id 04865 $id = $GLOBALS['TYPO3_DB']->sql_insert_id(); 04866 if (!$dontSetNewIdIndex) { 04867 $this->substNEWwithIDs[$NEW_id] = $id; 04868 $this->substNEWwithIDs_table[$NEW_id] = $table; 04869 } 04870 04871 // Checking the record is properly saved and writing to log 04872 if ($this->checkStoredRecords) { 04873 $newRow = $this->checkStoredRecord($table,$id,$fieldArray,1); 04874 } 04875 04876 // Update reference index: 04877 $this->updateRefIndex($table,$id); 04878 04879 if ($newVersion) { 04880 $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); 04881 } else { 04882 $propArr = $this->getRecordPropertiesFromRow($table,$newRow); 04883 $page_propArr = $this->getRecordProperties('pages',$propArr['pid']); 04884 $this->log($table,$id,1,0,0,"Record '%s' (%s) was inserted on page '%s' (%s)",10,array($propArr['header'],$table.':'.$id,$page_propArr['header'],$newRow['pid']),$newRow['pid'],$NEW_id); 04885 04886 // Clear cache for relavant pages: 04887 $this->clear_cache($table,$id); 04888 } 04889 04890 return $id; 04891 } else { 04892 $this->log($table,$id,1,0,2,"SQL error: '%s' (%s)",12,array($GLOBALS['TYPO3_DB']->sql_error(),$table.':'.$id)); 04893 } 04894 } 04895 } 04896 } 04897 04908 function checkStoredRecord($table,$id,$fieldArray,$action) { 04909 global $TCA; 04910 04911 $id = intval($id); 04912 if (is_array($TCA[$table]) && $id) { 04913 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid='.intval($id)); 04914 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 04915 // Traverse array of values that was inserted into the database and compare with the actually stored value: 04916 $errorString = array(); 04917 foreach($fieldArray as $key => $value) { 04918 if ($this->checkStoredRecords_loose && !$value && !$row[$key]) { 04919 // Nothing... 04920 } elseif (strcmp($value,$row[$key])) { 04921 $errorString[] = $key; 04922 } 04923 } 04924 04925 // Set log message if there were fields with unmatching values: 04926 if (count($errorString)) { 04927 $this->log($table,$id,$action,0,102,'These fields are not properly updated in database: ('.implode(',',$errorString).') Probably value mismatch with fieldtype.'); 04928 } 04929 04930 // Return selected rows: 04931 return $row; 04932 } 04933 $GLOBALS['TYPO3_DB']->sql_free_result($res); 04934 } 04935 } 04936 04945 function setHistory($table,$id,$logId) { 04946 if (isset($this->historyRecords[$table.':'.$id])) { 04947 04948 // Initialize settings: 04949 list($tscPID) = t3lib_BEfunc::getTSCpid($table,$id,''); 04950 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 04951 04952 $tE = $this->getTableEntries($table,$TSConfig); 04953 $maxAgeSeconds = 60*60*24*(strcmp($tE['history.']['maxAgeDays'],'') ? t3lib_div::intInRange($tE['history.']['maxAgeDays'],0,365) : 30); // one month 04954 04955 // Garbage collect old entries: 04956 $this->clearHistory($maxAgeSeconds, $table); 04957 04958 // Set history data: 04959 $fields_values = array(); 04960 $fields_values['history_data'] = serialize($this->historyRecords[$table.':'.$id]); 04961 $fields_values['fieldlist'] = implode(',',array_keys($this->historyRecords[$table.':'.$id]['newRecord'])); 04962 $fields_values['tstamp'] = time(); 04963 $fields_values['tablename'] = $table; 04964 $fields_values['recuid'] = $id; 04965 $fields_values['sys_log_uid'] = $logId; 04966 04967 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_history', $fields_values); 04968 } 04969 } 04970 04978 function clearHistory($maxAgeSeconds=604800,$table) { 04979 $tstampLimit = $maxAgeSeconds ? time()-$maxAgeSeconds : 0; 04980 04981 $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_history', 'tstamp<'.intval($tstampLimit).' AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history')); 04982 } 04983 04992 function updateRefIndex($table,$id) { 04993 $refIndexObj = t3lib_div::makeInstance('t3lib_refindex'); 04994 $result = $refIndexObj->updateRefIndexTable($table,$id); 04995 } 04996 04997 04998 04999 05000 05001 05002 05003 05004 05005 05006 05007 05008 05009 /********************************************* 05010 * 05011 * Misc functions 05012 * 05013 ********************************************/ 05014 05024 function getSortNumber($table,$uid,$pid) { 05025 global $TCA; 05026 if ($TCA[$table] && $TCA[$table]['ctrl']['sortby']) { 05027 $sortRow = $TCA[$table]['ctrl']['sortby']; 05028 if ($pid>=0) { // Sorting number is in the top 05029 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($sortRow.',pid,uid', $table, 'pid='.intval($pid).$this->deleteClause($table), '', $sortRow.' ASC', '1'); // Fetches the first record under this pid 05030 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { // There was an element 05031 if ($row['uid']==$uid) { // The top record was the record it self, so we return its current sortnumber 05032 return $row[$sortRow]; 05033 } 05034 if ($row[$sortRow] < 1) { // If the pages sortingnumber < 1 we must resort the records under this pid 05035 $this->resorting($table,$pid,$sortRow,0); 05036 return $this->sortIntervals; // First sorting number after resorting 05037 } else { 05038 return floor($row[$sortRow]/2); // Sorting number between current top element and zero 05039 } 05040 } else { // No pages, so we choose the default value as sorting-number 05041 return $this->sortIntervals; // First sorting number if no elements. 05042 } 05043 } else { // Sorting number is inside the list 05044 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($sortRow.',pid,uid', $table, 'uid='.abs($pid).$this->deleteClause($table)); // Fetches the record which is supposed to be the prev record 05045 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { // There was a record 05046 05047 // Look, if the record UID happens to be an offline record. If so, find its live version. Offline uids will be used when a page is versionized as "branch" so this is when we must correct - otherwise a pid of "-1" and a wrong sort-row number is returned which we don't want. 05048 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$row['uid'],$sortRow.',pid,uid')) { 05049 $row = $lookForLiveVersion; 05050 } 05051 05052 // If the record happends to be it self 05053 if ($row['uid']==$uid) { 05054 $sortNumber = $row[$sortRow]; 05055 } else { 05056 $subres = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05057 $sortRow.',pid,uid', 05058 $table, 05059 'pid='.intval($row['pid']).' AND '.$sortRow.'>='.intval($row[$sortRow]).$this->deleteClause($table), 05060 '', 05061 $sortRow.' ASC', 05062 '2' 05063 ); // Fetches the next record in order to calculate the in between sortNumber 05064 if ($GLOBALS['TYPO3_DB']->sql_num_rows($subres)==2) { // There was a record afterwards 05065 $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres); // Forward to the second result... 05066 $subrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres); // There was a record afterwards 05067 $sortNumber = $row[$sortRow]+ floor(($subrow[$sortRow]-$row[$sortRow])/2); // The sortNumber is found in between these values 05068 if ($sortNumber<=$row[$sortRow] || $sortNumber>=$subrow[$sortRow]) { // The sortNumber happend NOT to be between the two surrounding numbers, so we'll have to resort the list 05069 $sortNumber = $this->resorting($table,$row['pid'],$sortRow, $row['uid']); // By this special param, resorting reserves and returns the sortnumber after the uid 05070 } 05071 } else { // If after the last record in the list, we just add the sortInterval to the last sortvalue 05072 $sortNumber = $row[$sortRow]+$this->sortIntervals; 05073 } 05074 } 05075 return Array('pid' => $row['pid'], 'sortNumber' => $sortNumber); 05076 } else { 05077 $propArr = $this->getRecordProperties($table,$uid); 05078 $this->log($table,$uid,4,0,1,"Attempt to move record '%s' (%s) to after a non-existing record (uid=%s)",1,array($propArr['header'],$table.':'.$uid,abs($pid)),$propArr['pid']); // OK, dont insert $propArr['event_pid'] here... 05079 return false; // There MUST be a page or else this cannot work 05080 } 05081 } 05082 } 05083 } 05084 05097 function resorting($table,$pid,$sortRow, $return_SortNumber_After_This_Uid) { 05098 global $TCA; 05099 if ($TCA[$table] && $sortRow && $TCA[$table]['ctrl']['sortby']==$sortRow) { 05100 $returnVal = 0; 05101 $intervals = $this->sortIntervals; 05102 $i = $intervals*2; 05103 05104 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($pid).$this->deleteClause($table), '', $sortRow.' ASC'); 05105 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 05106 $uid=intval($row['uid']); 05107 if ($uid) { 05108 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), array($sortRow=>$i)); 05109 if ($uid==$return_SortNumber_After_This_Uid) { // This is used to return a sortingValue if the list is resorted because of inserting records inside the list and not in the top 05110 $i = $i+$intervals; 05111 $returnVal=$i; 05112 } 05113 } else {die ('Fatal ERROR!! No Uid at resorting.');} 05114 $i = $i+$intervals; 05115 } 05116 return $returnVal; 05117 } 05118 } 05119 05128 function setTSconfigPermissions($fieldArray,$TSConfig_p) { 05129 if (strcmp($TSConfig_p['userid'],'')) $fieldArray['perms_userid']=intval($TSConfig_p['userid']); 05130 if (strcmp($TSConfig_p['groupid'],'')) $fieldArray['perms_groupid']=intval($TSConfig_p['groupid']); 05131 if (strcmp($TSConfig_p['user'],'')) $fieldArray['perms_user']=t3lib_div::testInt($TSConfig_p['user']) ? $TSConfig_p['user'] : $this->assemblePermissions($TSConfig_p['user']); 05132 if (strcmp($TSConfig_p['group'],'')) $fieldArray['perms_group']=t3lib_div::testInt($TSConfig_p['group']) ? $TSConfig_p['group'] : $this->assemblePermissions($TSConfig_p['group']); 05133 if (strcmp($TSConfig_p['everybody'],'')) $fieldArray['perms_everybody']=t3lib_div::testInt($TSConfig_p['everybody']) ? $TSConfig_p['everybody'] : $this->assemblePermissions($TSConfig_p['everybody']); 05134 05135 return $fieldArray; 05136 } 05137 05145 function newFieldArray($table) { 05146 global $TCA; 05147 05148 t3lib_div::loadTCA($table); 05149 $fieldArray=Array(); 05150 if (is_array($TCA[$table]['columns'])) { 05151 reset ($TCA[$table]['columns']); 05152 while (list($field,$content)=each($TCA[$table]['columns'])) { 05153 if (isset($this->defaultValues[$table][$field])) { 05154 $fieldArray[$field] = $this->defaultValues[$table][$field]; 05155 } elseif (isset($content['config']['default'])) { 05156 $fieldArray[$field] = $content['config']['default']; 05157 } 05158 } 05159 } 05160 if ($table==='pages') { // Set default permissions for a page. 05161 $fieldArray['perms_userid'] = $this->userid; 05162 $fieldArray['perms_groupid'] = intval($this->BE_USER->firstMainGroup); 05163 $fieldArray['perms_user'] = $this->assemblePermissions($this->defaultPermissions['user']); 05164 $fieldArray['perms_group'] = $this->assemblePermissions($this->defaultPermissions['group']); 05165 $fieldArray['perms_everybody'] = $this->assemblePermissions($this->defaultPermissions['everybody']); 05166 } 05167 return $fieldArray; 05168 } 05169 05177 function addDefaultPermittedLanguageIfNotSet($table,&$incomingFieldArray) { 05178 global $TCA; 05179 05180 // Checking languages: 05181 if ($TCA[$table]['ctrl']['languageField']) { 05182 if (!isset($incomingFieldArray[$TCA[$table]['ctrl']['languageField']])) { // Language field must be found in input row - otherwise it does not make sense. 05183 $rows = array_merge(array(array('uid'=>0)),$GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid','sys_language','pid=0'.t3lib_BEfunc::deleteClause('sys_language')),array(array('uid'=>-1))); 05184 foreach($rows as $r) { 05185 if ($this->BE_USER->checkLanguageAccess($r['uid'])) { 05186 $incomingFieldArray[$TCA[$table]['ctrl']['languageField']] = $r['uid']; 05187 break; 05188 } 05189 } 05190 } 05191 } 05192 } 05193 05201 function overrideFieldArray($table,$data) { 05202 if (is_array($this->overrideValues[$table])) { 05203 $data = array_merge($data,$this->overrideValues[$table]); 05204 } 05205 return $data; 05206 } 05207 05217 function compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray) { 05218 05219 // Fetch the original record: 05220 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid='.intval($id)); 05221 $currentRecord = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 05222 05223 // If the current record exists (which it should...), begin comparison: 05224 if (is_array($currentRecord)) { 05225 05226 // Read all field types: 05227 $c = 0; 05228 $cRecTypes = array(); 05229 foreach($currentRecord as $col => $val) { 05230 $cRecTypes[$col] = $GLOBALS['TYPO3_DB']->sql_field_type($res,$c); 05231 $c++; 05232 } 05233 05234 // Free result: 05235 $GLOBALS['TYPO3_DB']->sql_free_result($res); 05236 05237 // Unset the fields which are similar: 05238 foreach($fieldArray as $col => $val) { 05239 if ( 05240 !strcmp($val,$currentRecord[$col]) || // Unset fields which matched exactly. 05241 ($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. 05242 ) { 05243 unset($fieldArray[$col]); 05244 } else { 05245 $this->historyRecords[$table.':'.$id]['oldRecord'][$col] = $currentRecord[$col]; 05246 $this->historyRecords[$table.':'.$id]['newRecord'][$col] = $fieldArray[$col]; 05247 } 05248 } 05249 } else { // If the current record does not exist this is an error anyways and we just return an empty array here. 05250 $fieldArray = array(); 05251 } 05252 05253 return $fieldArray; 05254 } 05255 05263 function assemblePermissions($string) { 05264 $keyArr = t3lib_div::trimExplode(',',$string,1); 05265 $value=0; 05266 while(list(,$key)=each($keyArr)) { 05267 if ($key && isset($this->pMap[$key])) { 05268 $value |= $this->pMap[$key]; 05269 } 05270 } 05271 return $value; 05272 } 05273 05280 function rmComma($input) { 05281 return ereg_replace(',$','',$input); 05282 } 05283 05290 function convNumEntityToByteValue($input) { 05291 $token = md5(microtime()); 05292 $parts = explode($token,ereg_replace('(&#([0-9]+);)',$token.'\2'.$token,$input)); 05293 05294 foreach($parts as $k => $v) { 05295 if ($k%2) { 05296 $v = intval($v); 05297 if ($v > 32) { // Just to make sure that control bytes are not converted. 05298 $parts[$k] =chr(intval($v)); 05299 } 05300 } 05301 } 05302 05303 return implode('',$parts); 05304 } 05305 05312 function destPathFromUploadFolder($folder) { 05313 return PATH_site.$folder; 05314 } 05315 05322 function deleteClause($table) { 05323 // Returns the proper delete-clause if any for a table from TCA 05324 global $TCA; 05325 if ($TCA[$table]['ctrl']['delete']) { 05326 return ' AND '.$table.'.'.$TCA[$table]['ctrl']['delete'].'=0'; 05327 } else { 05328 return ''; 05329 } 05330 } 05331 05338 function getTCEMAIN_TSconfig($tscPID) { 05339 if (!isset($this->cachedTSconfig[$tscPID])) { 05340 $this->cachedTSconfig[$tscPID] = $this->BE_USER->getTSConfig('TCEMAIN',t3lib_BEfunc::getPagesTSconfig($tscPID)); 05341 } 05342 return $this->cachedTSconfig[$tscPID]['properties']; 05343 } 05344 05353 function getTableEntries($table,$TSconfig) { 05354 $tA = is_array($TSconfig['table.'][$table.'.']) ? $TSconfig['table.'][$table.'.'] : array();; 05355 $dA = is_array($TSconfig['default.']) ? $TSconfig['default.'] : array(); 05356 return t3lib_div::array_merge_recursive_overrule($dA,$tA); 05357 } 05358 05366 function getPID($table,$uid) { 05367 $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', $table, 'uid='.intval($uid)); 05368 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) { 05369 return $row['pid']; 05370 } 05371 } 05372 05379 function dbAnalysisStoreExec() { 05380 reset($this->dbAnalysisStore); 05381 while(list($k,$v)=each($this->dbAnalysisStore)) { 05382 $id = $this->substNEWwithIDs[$v[2]]; 05383 if ($id) { 05384 $v[2] = $id; 05385 $v[0]->writeMM($v[1],$v[2],$v[3]); 05386 } 05387 } 05388 } 05389 05395 function removeRegisteredFiles() { 05396 reset($this->removeFilesStore); 05397 while(list($k,$v)=each($this->removeFilesStore)) { 05398 unlink($v); 05399 } 05400 } 05401 05407 function removeCacheFiles() { 05408 return t3lib_extMgm::removeCacheFiles(); 05409 } 05410 05421 function int_pageTreeInfo($CPtable,$pid,$counter, $rootID) { 05422 if ($counter) { 05423 $addW = !$this->admin ? ' AND '.$this->BE_USER->getPagePermsClause($this->pMap['show']) : ''; 05424 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', 'pid='.intval($pid).$this->deleteClause('pages').$addW, '', 'sorting DESC'); 05425 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 05426 if ($row['uid']!=$rootID) { 05427 $CPtable[$row['uid']] = $pid; 05428 if ($counter-1) { // If the uid is NOT the rootID of the copyaction and if we are supposed to walk further down 05429 $CPtable = $this->int_pageTreeInfo($CPtable,$row['uid'],$counter-1, $rootID); 05430 } 05431 } 05432 } 05433 } 05434 return $CPtable; 05435 } 05436 05442 function compileAdminTables() { 05443 global $TCA; 05444 reset ($TCA); 05445 $listArr = array(); 05446 while (list($table)=each($TCA)) { 05447 $listArr[]=$table; 05448 } 05449 return $listArr; 05450 } 05451 05459 function fixUniqueInPid($table,$uid) { 05460 global $TCA; 05461 if ($TCA[$table]) { 05462 t3lib_div::loadTCA($table); 05463 reset ($TCA[$table]['columns']); 05464 $curData=$this->recordInfo($table,$uid,'*'); 05465 $newData=array(); 05466 while (list($field,$conf)=each($TCA[$table]['columns'])) { 05467 if ($conf['config']['type']=='input') { 05468 $evalCodesArray = t3lib_div::trimExplode(',',$conf['config']['eval'],1); 05469 if (in_array('uniqueInPid',$evalCodesArray)) { 05470 $newV = $this->getUnique($table,$field,$curData[$field],$uid,$curData['pid']); 05471 if (strcmp($newV,$curData[$field])) { 05472 $newData[$field]=$newV; 05473 } 05474 } 05475 } 05476 } 05477 // IF there are changed fields, then update the database 05478 if (count($newData)) { 05479 $this->updateDB($table,$uid,$newData); 05480 } 05481 } 05482 } 05483 05495 function fixCopyAfterDuplFields($table,$uid,$prevUid,$update, $newData=array()) { 05496 global $TCA; 05497 if ($TCA[$table] && $TCA[$table]['ctrl']['copyAfterDuplFields']) { 05498 t3lib_div::loadTCA($table); 05499 $prevData=$this->recordInfo($table,$prevUid,'*'); 05500 $theFields = t3lib_div::trimExplode(',',$TCA[$table]['ctrl']['copyAfterDuplFields'],1); 05501 reset($theFields); 05502 while(list(,$field)=each($theFields)) { 05503 if ($TCA[$table]['columns'][$field] && ($update || !isset($newData[$field]))) { 05504 $newData[$field]=$prevData[$field]; 05505 } 05506 } 05507 if ($update && count($newData)) { 05508 $this->updateDB($table,$uid,$newData); 05509 } 05510 } 05511 return $newData; 05512 } 05513 05520 function extFileFields($table) { 05521 global $TCA; 05522 $listArr=array(); 05523 t3lib_div::loadTCA($table); 05524 if ($TCA[$table]['columns']) { 05525 reset($TCA[$table]['columns']); 05526 while (list($field,$configArr)=each($TCA[$table]['columns'])) { 05527 if ($configArr['config']['type']=='group' && $configArr['config']['internal_type']=='file') { 05528 $listArr[]=$field; 05529 } 05530 } 05531 } 05532 return $listArr; 05533 } 05534 05541 function getUniqueFields($table) { 05542 global $TCA; 05543 05544 $listArr=array(); 05545 t3lib_div::loadTCA($table); 05546 if ($TCA[$table]['columns']) { 05547 reset($TCA[$table]['columns']); 05548 while (list($field,$configArr)=each($TCA[$table]['columns'])) { 05549 if ($configArr['config']['type']==='input') { 05550 $evalCodesArray = t3lib_div::trimExplode(',',$configArr['config']['eval'],1); 05551 if (in_array('uniqueInPid',$evalCodesArray) || in_array('unique',$evalCodesArray)) { 05552 $listArr[]=$field; 05553 } 05554 } 05555 } 05556 } 05557 return $listArr; 05558 } 05559 05566 function isReferenceField($conf) { 05567 return ($conf['type']=='group' && $conf['internal_type']=='db') || ($conf['type']=='select' && $conf['foreign_table']); 05568 } 05569 05577 function getInlineFieldType($conf) { 05578 if ($conf['type'] == 'inline' && $conf['foreign_table']) { 05579 if ($conf['foreign_field']) 05580 return 'field'; // the reference to the parent is stored in a pointer field in the child record 05581 elseif ($conf['MM']) 05582 return 'mm'; // regular MM intermediate table is used to store data 05583 else 05584 return 'list'; // an item list (separated by comma) is stored (like select type is doing) 05585 } 05586 return false; 05587 } 05588 05600 function getCopyHeader($table,$pid,$field,$value,$count,$prevTitle='') { 05601 global $TCA; 05602 05603 // Set title value to check for: 05604 if ($count) { 05605 $checkTitle = $value.rtrim(' '.sprintf($this->prependLabel($table),$count)); 05606 } else { 05607 $checkTitle = $value; 05608 } 05609 05610 // Do check: 05611 if ($prevTitle != $checkTitle || $count<100) { 05612 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($pid).' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($checkTitle, $table).$this->deleteClause($table), '', '', '1'); 05613 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 05614 return $this->getCopyHeader($table,$pid,$field,$value,$count+1,$checkTitle); 05615 } 05616 } 05617 05618 // Default is to just return the current input title if no other was returned before: 05619 return $checkTitle; 05620 } 05621 05629 function prependLabel($table) { 05630 global $TCA; 05631 if (is_object($GLOBALS['LANG'])) { 05632 $label = $GLOBALS['LANG']->sL($TCA[$table]['ctrl']['prependAtCopy']); 05633 } else { 05634 list($label) = explode('|',$TCA[$table]['ctrl']['prependAtCopy']); 05635 } 05636 return $label; 05637 } 05638 05646 function resolvePid($table,$pid) { 05647 global $TCA; 05648 $pid = intval($pid); 05649 if ($pid < 0) { 05650 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', $table, 'uid='.abs($pid)); 05651 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 05652 05653 // Look, if the record UID happens to be an offline record. If so, find its live version. Offline uids will be used when a page is versionized as "branch" so this is when we must correct - otherwise a pid of "-1" and a wrong sort-row number is returned which we don't want. 05654 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,abs($pid),'pid')) { 05655 $row = $lookForLiveVersion; 05656 } 05657 05658 $pid = intval($row['pid']); 05659 } elseif ($this->BE_USER->workspace!==0 && $TCA[$table]['ctrl']['versioning_followPages']) { // PID points to page, the workspace is an offline space and the table follows page during versioning: This means we must check if the PID page has a version in the workspace with swapmode set to 0 (zero = page+content) and if so, change the pid to the uid of that version. 05660 if ($WSdestPage = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, 'pages', $pid, 'uid,t3ver_swapmode')) { // Looks for workspace version of page. 05661 if ($WSdestPage['t3ver_swapmode']==0) { // if swapmode is zero, then change pid value. 05662 $pid = $WSdestPage['uid']; 05663 } 05664 } 05665 } 05666 return $pid; 05667 } 05668 05676 function clearPrefixFromValue($table,$value) { 05677 global $TCA; 05678 $regex = sprintf(quotemeta($this->prependLabel($table)),'[0-9]*').'$'; 05679 return @ereg_replace($regex,'',$value); 05680 } 05681 05691 function extFileFunctions($table,$field,$filelist,$func) { 05692 global $TCA; 05693 t3lib_div::loadTCA($table); 05694 $uploadFolder = $TCA[$table]['columns'][$field]['config']['uploadfolder']; 05695 if ($uploadFolder && trim($filelist)) { 05696 $uploadPath = $this->destPathFromUploadFolder($uploadFolder); 05697 $fileArray = explode(',',$filelist); 05698 while (list(,$theFile)=each($fileArray)) { 05699 $theFile=trim($theFile); 05700 if ($theFile) { 05701 switch($func) { 05702 case 'deleteAll': 05703 if (@is_file($uploadPath.'/'.$theFile)) { 05704 unlink ($uploadPath.'/'.$theFile); 05705 } else { 05706 $this->log($table,0,3,0,100,"Delete: Referenced file that was supposed to be deleted together with it's record didn't exist"); 05707 } 05708 break; 05709 } 05710 } 05711 } 05712 } 05713 } 05714 05721 function noRecordsFromUnallowedTables($inList) { 05722 global $TCA; 05723 reset ($TCA); 05724 $inList = trim($this->rmComma(trim($inList))); 05725 if ($inList && !$this->admin) { 05726 while (list($table) = each($TCA)) { 05727 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid IN ('.$inList.')'.t3lib_BEfunc::deleteClause($table)); 05728 $count = $GLOBALS['TYPO3_DB']->sql_fetch_row($mres); 05729 if ($count[0] && ($this->tableReadOnly($table) || !$this->checkModifyAccessList($table))) { 05730 return FALSE; 05731 } 05732 } 05733 } 05734 return TRUE; 05735 } 05736 05747 function notifyStageChange($stat,$stageId,$table,$id,$comment) { 05748 $workspaceRec = t3lib_BEfunc::getRecord('sys_workspace', $stat['uid']); 05749 05750 if (is_array($workspaceRec)) { 05751 05752 // Compile label: 05753 switch((int)$stageId) { 05754 case 1: 05755 $newStage = 'Ready for review'; 05756 break; 05757 case 10: 05758 $newStage = 'Ready for publishing'; 05759 break; 05760 case -1: 05761 $newStage = 'Element was rejected!'; 05762 break; 05763 case 0: 05764 $newStage = 'Rejected element was noticed and edited'; 05765 break; 05766 default: 05767 $newStage = 'Unknown state change!?'; 05768 break; 05769 } 05770 05771 // Compile list of recipients: 05772 $emails = array(); 05773 switch((int)$stat['stagechg_notification']) { 05774 case 1: 05775 switch((int)$stageId) { 05776 case 1: 05777 $emails = $this->notifyStageChange_getEmails($workspaceRec['reviewers']); 05778 break; 05779 case 10: 05780 $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE); 05781 break; 05782 case -1: 05783 $emails = $this->notifyStageChange_getEmails($workspaceRec['reviewers']); 05784 $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['members'])); 05785 break; 05786 case 0: 05787 $emails = $this->notifyStageChange_getEmails($workspaceRec['members']); 05788 break; 05789 default: 05790 $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE); 05791 break; 05792 } 05793 break; 05794 case 10: 05795 $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE); 05796 $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['reviewers'])); 05797 $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['members'])); 05798 break; 05799 } 05800 $emails = array_unique($emails); 05801 05802 // Send email: 05803 if (count($emails)) { 05804 $message = sprintf(' 05805 At the TYPO3 site "%s" (%s) 05806 in workspace "%s" (#%s) 05807 the stage has changed for the element "%s": 05808 05809 ==> %s 05810 05811 User Comment: 05812 "%s" 05813 05814 State was change by %s (username: %s) 05815 ', 05816 $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], 05817 t3lib_div::getIndpEnv('TYPO3_SITE_URL').TYPO3_mainDir, 05818 $workspaceRec['title'], 05819 $workspaceRec['uid'], 05820 $table.':'.$id, 05821 $newStage, 05822 $comment, 05823 $this->BE_USER->user['realName'], 05824 $this->BE_USER->user['username']); 05825 05826 t3lib_div::plainMailEncoded( 05827 implode(',',$emails), 05828 'TYPO3 Workspace Note: Stage Change for '.$table.':'.$id, 05829 trim($message) 05830 ); 05831 } 05832 } 05833 } 05834 05842 function notifyStageChange_getEmails($listOfUsers,$noTablePrefix=FALSE) { 05843 $users = t3lib_div::trimExplode(',',$listOfUsers,1); 05844 $emails = array(); 05845 foreach($users as $userIdent) { 05846 if ($noTablePrefix) { 05847 $id = intval($userIdent); 05848 } else { 05849 list($table,$id) = t3lib_div::revExplode('_',$userIdent,2); 05850 } 05851 if ($table==='be_users' || $noTablePrefix) { 05852 if ($userRecord = t3lib_BEfunc::getRecord('be_users', $id, 'email')) { 05853 if (strlen(trim($userRecord['email']))) { 05854 $emails[$id] = $userRecord['email']; 05855 } 05856 } 05857 } 05858 } 05859 return $emails; 05860 } 05861 05862 05863 05864 05865 05866 05867 05868 05869 05870 05871 05872 05873 /****************************** 05874 * 05875 * Clearing cache 05876 * 05877 ******************************/ 05878 05888 function clear_cache($table,$uid) { 05889 global $TCA, $TYPO3_CONF_VARS; 05890 05891 $uid = intval($uid); 05892 if (is_array($TCA[$table]) && $uid > 0) { 05893 05894 // Get Page TSconfig relavant: 05895 list($tscPID) = t3lib_BEfunc::getTSCpid($table,$uid,''); 05896 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 05897 05898 if (!$TSConfig['clearCache_disable']) { 05899 05900 // If table is "pages": 05901 if (t3lib_extMgm::isLoaded('cms')) { 05902 $list_cache = array(); 05903 if ($table=='pages') { 05904 05905 // Builds list of pages on the SAME level as this page (siblings) 05906 $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05907 'A.pid AS pid, B.uid AS uid', 05908 'pages A, pages B', 05909 'A.uid='.intval($uid).' AND B.pid=A.pid AND B.deleted=0' 05910 ); 05911 05912 $pid_tmp = 0; 05913 while ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) { 05914 $list_cache[] = $row_tmp['uid']; 05915 $pid_tmp = $row_tmp['pid']; 05916 05917 // Add children as well: 05918 if ($TSConfig['clearCache_pageSiblingChildren']) { 05919 $res_tmp2 = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05920 'uid', 05921 'pages', 05922 'pid='.intval($row_tmp['uid']).' AND deleted=0' 05923 ); 05924 while ($row_tmp2 = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp2)) { 05925 $list_cache[] = $row_tmp2['uid']; 05926 } 05927 } 05928 } 05929 05930 // Finally, add the parent page as well: 05931 $list_cache[] = $pid_tmp; 05932 05933 // Add grand-parent as well: 05934 if ($TSConfig['clearCache_pageGrandParent']) { 05935 $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05936 'pid', 05937 'pages', 05938 'uid='.intval($pid_tmp) 05939 ); 05940 if ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) { 05941 $list_cache[] = $row_tmp['pid']; 05942 } 05943 } 05944 } else { // For other tables than "pages", delete cache for the records "parent page". 05945 $list_cache[] = intval($this->getPID($table,$uid)); 05946 } 05947 05948 // Call pre-processing function for clearing of cache for page ids: 05949 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) { 05950 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) { 05951 $_params = array('pageIdArray' => &$list_cache, 'table' => $table, 'uid' => $uid, 'functionID' => 'clear_cache()'); 05952 // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array! 05953 t3lib_div::callUserFunction($funcName,$_params,$this); 05954 } 05955 } 05956 05957 // Delete cache for selected pages: 05958 if (is_array($list_cache)) { 05959 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')'); 05960 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection', 'page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')'); 05961 } 05962 } 05963 } 05964 05965 // Clear cache for pages entered in TSconfig: 05966 if ($TSConfig['clearCacheCmd']) { 05967 $Commands = t3lib_div::trimExplode(',',strtolower($TSConfig['clearCacheCmd']),1); 05968 $Commands = array_unique($Commands); 05969 foreach($Commands as $cmdPart) { 05970 $this->clear_cacheCmd($cmdPart); 05971 } 05972 } 05973 05974 // Call post processing function for clear-cache: 05975 global $TYPO3_CONF_VARS; 05976 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) { 05977 // FIXME $uid_page is undefined 05978 $_params = array('table' => $table,'uid' => $uid,'uid_page' => $uid_page,'TSConfig' => $TSConfig); 05979 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) { 05980 t3lib_div::callUserFunction($_funcRef,$_params,$this); 05981 } 05982 } 05983 } 05984 } 05985 05998 function clear_cacheCmd($cacheCmd) { 05999 global $TYPO3_CONF_VARS; 06000 06001 // Clear cache for either ALL pages or ALL tables! 06002 switch($cacheCmd) { 06003 case 'pages': 06004 if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.pages')) { 06005 if (t3lib_extMgm::isLoaded('cms')) { 06006 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages',''); 06007 } 06008 } 06009 break; 06010 case 'all': 06011 if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.all')) { 06012 if (t3lib_extMgm::isLoaded('cms')) { 06013 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages',''); 06014 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection',''); 06015 } 06016 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_hash',''); 06017 06018 // Clearing additional cache tables: 06019 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'])) { 06020 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'] as $tableName) { 06021 if (!ereg('[^[:alnum:]_]',$tableName) && substr($tableName,-5)=='cache') { 06022 $GLOBALS['TYPO3_DB']->exec_DELETEquery($tableName,''); 06023 } else { 06024 die('Fatal Error: Trying to flush table "'.$tableName.'" with "Clear All Cache"'); 06025 } 06026 } 06027 } 06028 } 06029 break; 06030 case 'temp_CACHED': 06031 if ($this->admin && $TYPO3_CONF_VARS['EXT']['extCache']) { 06032 $this->removeCacheFiles(); 06033 } 06034 break; 06035 } 06036 06037 // Clear cache for a page ID! 06038 if (t3lib_div::testInt($cacheCmd)) { 06039 if (t3lib_extMgm::isLoaded('cms')) { 06040 06041 $list_cache = array($cacheCmd); 06042 06043 // Call pre-processing function for clearing of cache for page ids: 06044 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) { 06045 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) { 06046 $_params = array('pageIdArray' => &$list_cache, 'cacheCmd' => $cacheCmd, 'functionID' => 'clear_cacheCmd()'); 06047 // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array! 06048 t3lib_div::callUserFunction($funcName,$_params,$this); 06049 } 06050 } 06051 06052 // Delete cache for selected pages: 06053 if (is_array($list_cache)) { 06054 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')'); 06055 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection', 'page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')'); // Originally, cache_pagesection was not cleared with cache_pages! 06056 } 06057 } 06058 } 06059 06060 // Call post processing function for clear-cache: 06061 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) { 06062 $_params = array('cacheCmd'=>$cacheCmd); 06063 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) { 06064 t3lib_div::callUserFunction($_funcRef,$_params,$this); 06065 } 06066 } 06067 } 06068 06069 06070 06071 06072 06073 06074 06075 06076 06077 06078 06079 06080 06081 06082 /***************************** 06083 * 06084 * Logging 06085 * 06086 *****************************/ 06087 06104 function log($table,$recuid,$action,$recpid,$error,$details,$details_nr=-1,$data=array(),$event_pid=-1,$NEWid='') { 06105 if ($this->enableLogging) { 06106 $type=1; // Type value for tce_db.php 06107 if (!$this->storeLogMessages) {$details='';} 06108 if ($error>0) $this->errorLog[] = '['.$type.'.'.$action.'.'.$details_nr.']: '.$details; 06109 return $this->BE_USER->writelog($type,$action,$error,$details_nr,$details,$data,$table,$recuid,$recpid,$event_pid,$NEWid); 06110 } 06111 } 06112 06121 function newlog($message, $error=0) { 06122 return $this->log('',0,0,0,$error,$message,-1); 06123 } 06124 06131 function printLogErrorMessages($redirect) { 06132 06133 $res_log = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 06134 '*', 06135 'sys_log', 06136 'type=1 AND userid='.intval($this->BE_USER->user['uid']).' AND tstamp='.intval($GLOBALS['EXEC_TIME']).' AND error!=0' 06137 ); 06138 $errorJS = array(); 06139 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_log)) { 06140 $log_data = unserialize($row['log_data']); 06141 $errorJS[] = $row['error'].': '.sprintf($row['details'], $log_data[0],$log_data[1],$log_data[2],$log_data[3],$log_data[4]); 06142 } 06143 06144 if (count($errorJS)) { 06145 $error_doc = t3lib_div::makeInstance('template'); 06146 $error_doc->backPath = $GLOBALS['BACK_PATH']; 06147 06148 $content.= $error_doc->startPage('tce_db.php Error output'); 06149 06150 $lines[] = ' 06151 <tr class="bgColor5"> 06152 <td colspan="2" align="center"><strong>Errors:</strong></td> 06153 </tr>'; 06154 06155 foreach($errorJS as $line) { 06156 $lines[] = ' 06157 <tr class="bgColor4"> 06158 <td valign="top"><img'.t3lib_iconWorks::skinImg($error_doc->backPath,'gfx/icon_fatalerror.gif','width="18" height="16"').' alt="" /></td> 06159 <td>'.htmlspecialchars($line).'</td> 06160 </tr>'; 06161 } 06162 06163 $lines[] = ' 06164 <tr> 06165 <td colspan="2" align="center"><br />'. 06166 '<form action=""><input type="submit" value="Continue" onclick="'.htmlspecialchars('window.location.href=\''.$redirect.'\';return false;').'"></form>'. 06167 '</td> 06168 </tr>'; 06169 06170 $content.= ' 06171 <br/><br/> 06172 <table border="0" cellpadding="1" cellspacing="1" width="300" align="center"> 06173 '.implode('',$lines).' 06174 </table>'; 06175 06176 $content.= $error_doc->endPage(); 06177 echo $content; 06178 exit; 06179 } 06180 } 06181 } 06182 06183 06184 06185 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tcemain.php']) { 06186 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tcemain.php']); 06187 } 06188 ?>