"TYPO3 4.0.1: typo3_src-4.0.1/t3lib/class.t3lib_tcemain.php Source File", "datetime" => "Sat Dec 2 19:22:19 2006", "date" => "2 Dec 2006", "doxygenversion" => "1.4.6", "projectname" => "TYPO3 4.0.1", "projectnumber" => "4.0.1" ); get_header($doxygen_vars); ?>
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 ***************************************************************/ 00195 // ******************************* 00196 // Including necessary libraries 00197 // ******************************* 00198 require_once (PATH_t3lib.'class.t3lib_loaddbgroup.php'); 00199 require_once (PATH_t3lib.'class.t3lib_parsehtml_proc.php'); 00200 require_once (PATH_t3lib.'class.t3lib_stdgraphic.php'); 00201 require_once (PATH_t3lib.'class.t3lib_basicfilefunc.php'); 00202 require_once (PATH_t3lib.'class.t3lib_refindex.php'); 00203 require_once (PATH_t3lib.'class.t3lib_flexformtools.php'); 00204 00205 00206 00207 00208 00209 00210 00211 00212 00213 00214 00229 class t3lib_TCEmain { 00230 00231 00232 // ********************* 00233 // Public variables you can configure before using the class: 00234 // ********************* 00235 00236 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. 00237 var $enableLogging = TRUE; // Boolean: If true, actions are logged to sys_log. 00238 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. 00239 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. 00240 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! 00241 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 00242 var $checkStoredRecords_loose = TRUE; // Boolean: If set, values '' and 0 will equal each other when the stored records are checked. 00243 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 00244 var $neverHideAtCopy = FALSE; // Boolean. If set, then the 'hideAtCopy' flag for tables will be ignored. 00245 var $dontProcessTransformations = FALSE; // Boolean: If set, then transformations are NOT performed on the input. 00246 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! 00247 00248 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) 00249 var $generalComment = ''; // General comment, eg. for staging in workspaces. 00250 00251 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 00252 00253 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) 00254 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! 00255 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. 00256 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. 00257 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) 00258 00259 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. 00260 00261 00262 00263 00264 // ********************* 00265 // Internal variables (mapping arrays) which can be used (read-only) from outside 00266 // ********************* 00267 var $autoVersionIdMap = Array(); // Contains mapping of auto-versionized records. 00268 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 00269 var $substNEWwithIDs_table = Array(); // Like $substNEWwithIDs, but where each old "NEW..." id is mapped to the table it was from. 00270 var $copyMappingArray_merged = Array(); // This array is the sum of all copying operations in this class. May be READ from outside, thus partly public. 00271 var $copiedFileMap = Array(); // A map between input file name and final destination for files being attached to records. 00272 var $errorLog = Array(); // Errors are collected in this variable. 00273 00274 00275 00276 // ********************* 00277 // Internal Variables, do not touch. 00278 // ********************* 00279 00280 // Variables set in init() function: 00281 var $BE_USER; // The user-object the script uses. If not set from outside, this is set to the current global $BE_USER. 00282 var $userid; // will be set to uid of be_user executing this script 00283 var $username; // will be set to username of be_user executing this script 00284 var $admin; // will be set if user is admin 00285 00286 var $defaultPermissions = array( // Can be overridden from $TYPO3_CONF_VARS 00287 'user' => 'show,edit,delete,new,editcontent', 00288 'group' => 'show,edit,new,editcontent', 00289 'everybody' => '' 00290 ); 00291 00292 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. 00293 var $datamap = Array(); // Set with incoming data array 00294 var $cmdmap = Array(); // Set with incoming cmd array 00295 00296 // Internal static: 00297 var $pMap = Array( // Permission mapping 00298 'show' => 1, // 1st bit 00299 'edit' => 2, // 2nd bit 00300 'delete' => 4, // 3rd bit 00301 'new' => 8, // 4th bit 00302 'editcontent' => 16 // 5th bit 00303 ); 00304 var $sortIntervals = 256; // Integer: The interval between sorting numbers used with tables with a 'sorting' field defined. Min 1 00305 00306 // Internal caching arrays 00307 var $recUpdateAccessCache = Array(); // Used by function checkRecordUpdateAccess() to store whether a record is updateable or not. 00308 var $recInsertAccessCache = Array(); // User by function checkRecordInsertAccess() to store whether a record can be inserted on a page id 00309 var $isRecordInWebMount_Cache=array(); // Caching array for check of whether records are in a webmount 00310 var $isInWebMount_Cache=array(); // Caching array for page ids in webmounts 00311 var $cachedTSconfig = array(); // Caching for collecting TSconfig for page ids 00312 var $pageCache = Array(); // Used for caching page records in pageInfo() 00313 var $checkWorkspaceCache = Array(); // Array caching workspace access for BE_USER 00314 00315 // Other arrays: 00316 var $dbAnalysisStore=array(); // For accumulation of MM relations that must be written after new records are created. 00317 var $removeFilesStore=array(); // For accumulation of files which must be deleted after processing of all input content 00318 var $uploadedFileArray = array(); // Uploaded files, set by process_uploads() 00319 var $registerDBList=array(); // Used for tracking references that might need correction after operations 00320 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. 00321 00322 // Various 00323 var $fileFunc; // For "singleTon" file-manipulation object 00324 var $checkValue_currentRecord=array(); // Set to "currentRecord" during checking of values. 00325 var $autoVersioningUpdate = FALSE; // A signal flag used to tell file processing that autoversioning has happend and hence certain action should be applied. 00326 00327 00328 00329 00330 00331 00332 00333 00334 00335 00336 00337 00348 function start($data,$cmd,$altUserObject='') { 00349 00350 // Initializing BE_USER 00351 $this->BE_USER = is_object($altUserObject) ? $altUserObject : $GLOBALS['BE_USER']; 00352 $this->userid = $this->BE_USER->user['uid']; 00353 $this->username = $this->BE_USER->user['username']; 00354 $this->admin = $this->BE_USER->user['admin']; 00355 00356 if ($GLOBALS['BE_USER']->uc['recursiveDelete']) { 00357 $this->deleteTree = 1; 00358 } 00359 00360 // Initializing default permissions for pages 00361 $defaultPermissions = $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPermissions']; 00362 if (isset($defaultPermissions['user'])) {$this->defaultPermissions['user'] = $defaultPermissions['user'];} 00363 if (isset($defaultPermissions['group'])) {$this->defaultPermissions['group'] = $defaultPermissions['group'];} 00364 if (isset($defaultPermissions['everybody'])) {$this->defaultPermissions['everybody'] = $defaultPermissions['everybody'];} 00365 00366 // generates the excludelist, based on TCA/exclude-flag and non_exclude_fields for the user: 00367 $this->exclude_array = $this->admin ? array() : $this->getExcludeListArray(); 00368 00369 // Setting the data and cmd arrays 00370 if (is_array($data)) { 00371 reset($data); 00372 $this->datamap = $data; 00373 } 00374 if (is_array($cmd)) { 00375 reset($cmd); 00376 $this->cmdmap = $cmd; 00377 } 00378 } 00379 00387 function setMirror($mirror) { 00388 if (is_array($mirror)) { 00389 reset($mirror); 00390 while(list($table,$uid_array)=each($mirror)) { 00391 if (isset($this->datamap[$table])) { 00392 reset($uid_array); 00393 while (list($id,$uidList) = each($uid_array)) { 00394 if (isset($this->datamap[$table][$id])) { 00395 $theIdsInArray = t3lib_div::trimExplode(',',$uidList,1); 00396 while(list(,$copyToUid)=each($theIdsInArray)) { 00397 $this->datamap[$table][$copyToUid] = $this->datamap[$table][$id]; 00398 } 00399 } 00400 } 00401 } 00402 } 00403 } 00404 } 00405 00412 function setDefaultsFromUserTS($userTS) { 00413 global $TCA; 00414 if (is_array($userTS)) { 00415 foreach($userTS as $k => $v) { 00416 $k = substr($k,0,-1); 00417 if ($k && is_array($v) && isset($TCA[$k])) { 00418 if (is_array($this->defaultValues[$k])) { 00419 $this->defaultValues[$k] = array_merge($this->defaultValues[$k],$v); 00420 } else { 00421 $this->defaultValues[$k] = $v; 00422 } 00423 } 00424 } 00425 } 00426 } 00427 00435 function process_uploads($postFiles) { 00436 00437 if (is_array($postFiles)) { 00438 00439 // Editing frozen: 00440 if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze']) { 00441 $this->newlog('All editing in this workspace has been frozen!',1); 00442 return FALSE; 00443 } 00444 00445 reset($postFiles); 00446 $subA = current($postFiles); 00447 if (is_array($subA)) { 00448 if (is_array($subA['name']) && is_array($subA['type']) && is_array($subA['tmp_name']) && is_array($subA['size'])) { 00449 // Initialize the uploadedFilesArray: 00450 $this->uploadedFileArray=array(); 00451 00452 // For each entry: 00453 foreach($subA as $key => $values) { 00454 $this->process_uploads_traverseArray($this->uploadedFileArray,$values,$key); 00455 } 00456 } else { 00457 $this->uploadedFileArray=$subA; 00458 } 00459 } 00460 } 00461 } 00462 00473 function process_uploads_traverseArray(&$outputArr,$inputArr,$keyToSet) { 00474 if (is_array($inputArr)) { 00475 foreach($inputArr as $key => $value) { 00476 $this->process_uploads_traverseArray($outputArr[$key],$inputArr[$key],$keyToSet); 00477 } 00478 } else { 00479 $outputArr[$keyToSet]=$inputArr; 00480 } 00481 } 00482 00483 00484 00485 00486 00487 00488 00489 00490 00491 00492 00493 00494 00495 00496 00497 /********************************************* 00498 * 00499 * PROCESSING DATA 00500 * 00501 *********************************************/ 00502 00509 function process_datamap() { 00510 global $TCA, $TYPO3_CONF_VARS; 00511 00512 // Editing frozen: 00513 if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze']) { 00514 $this->newlog('All editing in this workspace has been frozen!',1); 00515 return FALSE; 00516 } 00517 00518 // First prepare user defined objects (if any) for hooks which extend this function: 00519 $hookObjectsArr = array(); 00520 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'])) { 00521 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass'] as $classRef) { 00522 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef); 00523 } 00524 } 00525 00526 // 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. 00527 $orderOfTables = Array(); 00528 if (isset($this->datamap['pages'])) { // Set pages first. 00529 $orderOfTables[]='pages'; 00530 } 00531 reset($this->datamap); 00532 while (list($table,) = each($this->datamap)) { 00533 if ($table!='pages') { 00534 $orderOfTables[]=$table; 00535 } 00536 } 00537 00538 // Process the tables... 00539 foreach($orderOfTables as $table) { 00540 /* Check if 00541 - table is set in $TCA, 00542 - table is NOT readOnly 00543 - the table is set with content in the data-array (if not, there's nothing to process...) 00544 - permissions for tableaccess OK 00545 */ 00546 $modifyAccessList = $this->checkModifyAccessList($table); 00547 if (!$modifyAccessList) { 00548 $id = 0; 00549 $this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table)); 00550 } 00551 if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->datamap[$table]) && $modifyAccessList) { 00552 if ($this->reverseOrder) { 00553 $this->datamap[$table] = array_reverse($this->datamap[$table], 1); 00554 } 00555 00556 // For each record from the table, do: 00557 // $id is the record uid, may be a string if new records... 00558 // $incomingFieldArray is the array of fields 00559 foreach($this->datamap[$table] as $id => $incomingFieldArray) { 00560 if (is_array($incomingFieldArray)) { 00561 00562 // Hook: processDatamap_preProcessIncomingFieldArray 00563 foreach($hookObjectsArr as $hookObj) { 00564 if (method_exists($hookObj, 'processDatamap_preProcessFieldArray')) { 00565 $hookObj->processDatamap_preProcessFieldArray($incomingFieldArray, $table, $id, $this); 00566 } 00567 } 00568 00569 // ****************************** 00570 // Checking access to the record 00571 // ****************************** 00572 $createNewVersion = FALSE; 00573 $recordAccess = FALSE; 00574 $old_pid_value = ''; 00575 $resetRejected = FALSE; 00576 $this->autoVersioningUpdate = FALSE; 00577 00578 if (!t3lib_div::testInt($id)) { // Is it a new record? (Then Id is a string) 00579 $fieldArray = $this->newFieldArray($table); // Get a fieldArray with default values 00580 if (isset($incomingFieldArray['pid'])) { // A pid must be set for new records. 00581 // $value = the pid 00582 $pid_value = $incomingFieldArray['pid']; 00583 00584 // Checking and finding numerical pid, it may be a string-reference to another value 00585 $OK = 1; 00586 if (strstr($pid_value,'NEW')) { // If a NEW... id 00587 if (substr($pid_value,0,1)=='-') {$negFlag=-1;$pid_value=substr($pid_value,1);} else {$negFlag=1;} 00588 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. 00589 $old_pid_value = $pid_value; 00590 $pid_value=intval($negFlag*$this->substNEWwithIDs[$pid_value]); 00591 } else {$OK = 0;} // If not found in the substArray we must stop the process... 00592 } 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. 00593 if ($WSdestPage = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, 'pages', $pid_value, 'uid,t3ver_swapmode')) { // Looks for workspace version of page. 00594 if ($WSdestPage['t3ver_swapmode']==0) { // if swapmode is zero, then change pid value. 00595 $pid_value = $WSdestPage['uid']; 00596 } 00597 } 00598 } 00599 $pid_value = intval($pid_value); 00600 00601 // The $pid_value is now the numerical pid at this point 00602 if ($OK) { 00603 $sortRow = $TCA[$table]['ctrl']['sortby']; 00604 if ($pid_value>=0) { // Points to a page on which to insert the element, possibly in the top of the page 00605 if ($sortRow) { // If this table is sorted we better find the top sorting number 00606 $fieldArray[$sortRow] = $this->getSortNumber($table,0,$pid_value); 00607 } 00608 $fieldArray['pid'] = $pid_value; // The numerical pid is inserted in the data array 00609 } else { // points to another record before ifself 00610 if ($sortRow) { // If this table is sorted we better find the top sorting number 00611 $tempArray=$this->getSortNumber($table,0,$pid_value); // Because $pid_value is < 0, getSortNumber returns an array 00612 $fieldArray['pid'] = $tempArray['pid']; 00613 $fieldArray[$sortRow] = $tempArray['sortNumber']; 00614 } else { // Here we fetch the PID of the record that we point to... 00615 $tempdata = $this->recordInfo($table,abs($pid_value),'pid'); 00616 $fieldArray['pid']=$tempdata['pid']; 00617 } 00618 } 00619 } 00620 } 00621 $theRealPid = $fieldArray['pid']; 00622 00623 // Now, check if we may insert records on this pid. 00624 if ($theRealPid>=0) { 00625 $recordAccess = $this->checkRecordInsertAccess($table,$theRealPid); // Checks if records can be inserted on this $pid. 00626 if ($recordAccess) { 00627 $this->addDefaultPermittedLanguageIfNotSet($table,$incomingFieldArray); 00628 $recordAccess = $this->BE_USER->recordEditAccessInternals($table,$incomingFieldArray,TRUE); 00629 if (!$recordAccess) { 00630 $this->newlog("recordEditAccessInternals() check failed. [".$this->BE_USER->errorMsg."]",1); 00631 } elseif(!$this->bypassWorkspaceRestrictions) { 00632 // Workspace related processing: 00633 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 00634 if ($res<0) { 00635 $recordAccess = FALSE; 00636 $this->newlog('Stage for versioning root point and users access level did not allow for editing',1); 00637 } 00638 } else { // So, if no live records were allowed, we have to create a new version of this record: 00639 if ($TCA[$table]['ctrl']['versioningWS']) { 00640 $createNewVersion = TRUE; 00641 } else { 00642 $recordAccess = FALSE; 00643 $this->newlog('Record could not be created in this workspace in this branch',1); 00644 } 00645 } 00646 } 00647 } 00648 } else { 00649 debug('Internal ERROR: pid should not be less than zero!'); 00650 } 00651 $status = 'new'; // Yes new record, change $record_status to 'insert' 00652 } else { // Nope... $id is a number 00653 $fieldArray = array(); 00654 $recordAccess = $this->checkRecordUpdateAccess($table,$id); 00655 if (!$recordAccess) { 00656 $propArr = $this->getRecordProperties($table,$id); 00657 $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']); 00658 } else { // Next check of the record permissions (internals) 00659 $recordAccess = $this->BE_USER->recordEditAccessInternals($table,$id); 00660 if (!$recordAccess) { 00661 $propArr = $this->getRecordProperties($table,$id); 00662 $this->newlog("recordEditAccessInternals() check failed. [".$this->BE_USER->errorMsg."]",1); 00663 } else { // Here we fetch the PID of the record that we point to... 00664 $tempdata = $this->recordInfo($table,$id,'pid'.($TCA[$table]['ctrl']['versioningWS']?',t3ver_wsid,t3ver_stage':'')); 00665 $theRealPid = $tempdata['pid']; 00666 00667 // Prepare the reset of the rejected flag if set: 00668 if ($TCA[$table]['ctrl']['versioningWS'] && $tempdata['t3ver_stage']<0) { 00669 $resetRejected = TRUE; 00670 } 00671 00672 // Checking access in case of offline workspace: 00673 if (!$this->bypassWorkspaceRestrictions && $errorCode = $this->BE_USER->workspaceCannotEditRecord($table,$tempdata)) { 00674 $recordAccess = FALSE; // Versioning is required and it must be offline version! 00675 00676 // 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. 00677 if ($this->BE_USER->workspaceAllowAutoCreation($table,$id,$theRealPid)) { 00678 $tce = t3lib_div::makeInstance('t3lib_TCEmain'); 00679 $tce->stripslashes_values = 0; 00680 00681 // Setting up command for creating a new version of the record: 00682 $cmd = array(); 00683 $cmd[$table][$id]['version'] = array( 00684 'action' => 'new', 00685 'treeLevels' => -1, // Default is to create a version of the individual records... 00686 'label' => 'Auto-created for WS #'.$this->BE_USER->workspace 00687 ); 00688 $tce->start(array(),$cmd); 00689 $tce->process_cmdmap(); 00690 $this->errorLog = array_merge($this->errorLog,$tce->errorLog); 00691 00692 if ($tce->copyMappingArray[$table][$id]) { 00693 $this->uploadedFileArray[$table][$tce->copyMappingArray[$table][$id]] = $this->uploadedFileArray[$table][$id]; 00694 $id = $this->autoVersionIdMap[$table][$id] = $tce->copyMappingArray[$table][$id]; 00695 $recordAccess = TRUE; 00696 $this->autoVersioningUpdate = TRUE; 00697 } else $this->newlog("Could not be edited in offline workspace in the branch where found (failure state: '".$errorCode."'). Auto-creation of version failed!",1); 00698 } 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); 00699 } 00700 } 00701 } 00702 $status = 'update'; // the default is 'update' 00703 } 00704 00705 // If access was granted above, proceed to create or update record: 00706 if ($recordAccess) { 00707 00708 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. 00709 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 00710 if ($status=='new' && $table=='pages' && is_array($TSConfig['permissions.'])) { 00711 $fieldArray = $this->setTSconfigPermissions($fieldArray,$TSConfig['permissions.']); 00712 } 00713 if ($createNewVersion) { 00714 $newVersion_placeholderFieldArray = $fieldArray; 00715 } 00716 00717 // Processing of all fields in incomingFieldArray and setting them in $fieldArray 00718 $fieldArray = $this->fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$theRealPid,$status,$tscPID); 00719 00720 // NOTICE! All manipulation beyond this point bypasses both "excludeFields" AND possible "MM" relations / file uploads to field! 00721 00722 // Forcing some values unto field array: 00723 $fieldArray = $this->overrideFieldArray($table,$fieldArray); // NOTICE: This overriding is potentially dangerous; permissions per field is not checked!!! 00724 if ($createNewVersion) { 00725 $newVersion_placeholderFieldArray = $this->overrideFieldArray($table,$newVersion_placeholderFieldArray); 00726 } 00727 00728 // Setting system fields 00729 if ($status=='new') { 00730 if ($TCA[$table]['ctrl']['crdate']) { 00731 $fieldArray[$TCA[$table]['ctrl']['crdate']]=time(); 00732 if ($createNewVersion) $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['crdate']]=time(); 00733 } 00734 if ($TCA[$table]['ctrl']['cruser_id']) { 00735 $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid; 00736 if ($createNewVersion) $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid; 00737 } 00738 } elseif ($this->checkSimilar) { // Removing fields which are equal to the current value: 00739 $fieldArray = $this->compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray); 00740 } 00741 if ($TCA[$table]['ctrl']['tstamp'] && count($fieldArray)) { 00742 $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time(); 00743 if ($createNewVersion) $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['tstamp']]=time(); 00744 } 00745 if ($resetRejected) { 00746 $fieldArray['t3ver_stage'] = 0; 00747 } 00748 00749 // Hook: processDatamap_postProcessFieldArray 00750 foreach($hookObjectsArr as $hookObj) { 00751 if (method_exists($hookObj, 'processDatamap_postProcessFieldArray')) { 00752 $hookObj->processDatamap_postProcessFieldArray($status, $table, $id, $fieldArray, $this); 00753 } 00754 } 00755 00756 // Performing insert/update. If fieldArray has been unset by some userfunction (see hook above), don't do anything 00757 // Kasper: Unsetting the fieldArray is dangerous; MM relations might be saved already and files could have been uploaded that are now "lost" 00758 if (is_array($fieldArray)) { 00759 if ($status=='new') { 00760 if ($createNewVersion) { // This creates a new version of the record with online placeholder and offline version 00761 $versioningType = $table==='pages' ? $this->BE_USER->workspaceVersioningTypeGetClosest(t3lib_div::intInRange($TYPO3_CONF_VARS['BE']['newPagesVersioningType'],-1,1)) : -1; 00762 if ($this->BE_USER->workspaceVersioningTypeAccess($versioningType)) { 00763 $newVersion_placeholderFieldArray['t3ver_label'] = 'INITIAL PLACEHOLDER'; 00764 $newVersion_placeholderFieldArray['t3ver_state'] = 1; // Setting placeholder state value for temporary record 00765 $newVersion_placeholderFieldArray['t3ver_wsid'] = $this->BE_USER->workspace; // Setting workspace - only so display of place holders can filter out those from other workspaces. 00766 $newVersion_placeholderFieldArray[$TCA[$table]['ctrl']['label']] = '[PLACEHOLDER, WS#'.$this->BE_USER->workspace.']'; 00767 $this->insertDB($table,$id,$newVersion_placeholderFieldArray,FALSE); // Saving placeholder as 'original' 00768 00769 // For the actual new offline version, set versioning values to point to placeholder: 00770 $fieldArray['pid'] = -1; 00771 $fieldArray['t3ver_oid'] = $this->substNEWwithIDs[$id]; 00772 $fieldArray['t3ver_id'] = 1; 00773 $fieldArray['t3ver_state'] = -1; // Setting placeholder state value for version (so it can know it is currently a new version...) 00774 $fieldArray['t3ver_label'] = 'First draft version'; 00775 $fieldArray['t3ver_wsid'] = $this->BE_USER->workspace; 00776 if ($table==='pages') { // Swap mode set to "branch" so we can build branches for pages. 00777 $fieldArray['t3ver_swapmode'] = $versioningType; 00778 } 00779 $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! 00780 if ($phShadowId) { 00781 $this->placeholderShadowing($table,$phShadowId); 00782 } 00783 } else $this->newlog('Versioning type "'.$versioningType.'" was not allowed, so could not create new record.',1); 00784 } else { 00785 $this->insertDB($table,$id,$fieldArray,FALSE,$incomingFieldArray['uid']); 00786 } 00787 } else { 00788 $this->updateDB($table,$id,$fieldArray); 00789 $this->placeholderShadowing($table,$id); 00790 } 00791 } 00792 00793 // Hook: processDatamap_afterDatabaseOperations 00794 foreach($hookObjectsArr as $hookObj) { 00795 if (method_exists($hookObj, 'processDatamap_afterDatabaseOperations')) { 00796 $hookObj->processDatamap_afterDatabaseOperations($status, $table, $id, $fieldArray, $this); 00797 } 00798 } 00799 } // if ($recordAccess) { 00800 } // if (is_array($incomingFieldArray)) { 00801 } 00802 } 00803 } 00804 $this->dbAnalysisStoreExec(); 00805 $this->removeRegisteredFiles(); 00806 } 00807 00815 function placeholderShadowing($table,$id) { 00816 global $TCA; 00817 00818 if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'*')) { 00819 if ((int)$liveRec['t3ver_state']===1) { 00820 $justStoredRecord = t3lib_BEfunc::getRecord($table,$id); 00821 $newRecord = array(); 00822 00823 $shadowColumns = t3lib_div::trimExplode(',', $TCA[$table]['ctrl']['shadowColumnsForNewPlaceholders'],1); 00824 foreach($shadowColumns as $fieldName) { 00825 if (strcmp($justStoredRecord[$fieldName],$liveRec[$fieldName])) { 00826 $newRecord[$fieldName] = $justStoredRecord[$fieldName]; 00827 } 00828 } 00829 00830 if (count($newRecord)) { 00831 $this->newlog('Shadowing done on fields '.implode(',',array_keys($newRecord)).' in Placeholder record '.$table.':'.$liveRec['uid'].' (offline version UID='.$id.')'); 00832 $this->updateDB($table,$liveRec['uid'],$newRecord); 00833 } 00834 } 00835 } 00836 } 00837 00851 function fillInFieldArray($table,$id,$fieldArray,$incomingFieldArray,$realPid,$status,$tscPID) { 00852 global $TCA; 00853 00854 // Initialize: 00855 t3lib_div::loadTCA($table); 00856 $originalLanguageRecord = NULL; 00857 $originalLanguage_diffStorage = NULL; 00858 $diffStorageFlag = FALSE; 00859 00860 // Setting 'currentRecord' and 'checkValueRecord': 00861 if (strstr($id,'NEW')) { 00862 $currentRecord = $checkValueRecord = $fieldArray; // must have the 'current' array - not the values after processing below... 00863 00864 // IF $incomingFieldArray is an array, overlay it. 00865 // 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... 00866 if (is_array($incomingFieldArray) && is_array($checkValueRecord)) { 00867 $checkValueRecord = t3lib_div::array_merge_recursive_overrule($checkValueRecord, $incomingFieldArray); 00868 } 00869 } else { 00870 $currentRecord = $checkValueRecord = $this->recordInfo($table,$id,'*'); // We must use the current values as basis for this! 00871 00872 // Get original language record if available: 00873 if (is_array($currentRecord) 00874 && $TCA[$table]['ctrl']['transOrigDiffSourceField'] 00875 && $TCA[$table]['ctrl']['languageField'] 00876 && $currentRecord[$TCA[$table]['ctrl']['languageField']] > 0 00877 && $TCA[$table]['ctrl']['transOrigPointerField'] 00878 && intval($currentRecord[$TCA[$table]['ctrl']['transOrigPointerField']]) > 0) { 00879 00880 $lookUpTable = $TCA[$table]['ctrl']['transOrigPointerTable'] ? $TCA[$table]['ctrl']['transOrigPointerTable'] : $table; 00881 $originalLanguageRecord = $this->recordInfo($lookUpTable,$currentRecord[$TCA[$table]['ctrl']['transOrigPointerField']],'*'); 00882 t3lib_BEfunc::workspaceOL($lookUpTable,$originalLanguageRecord); 00883 $originalLanguage_diffStorage = unserialize($currentRecord[$TCA[$table]['ctrl']['transOrigDiffSourceField']]); 00884 } 00885 } 00886 $this->checkValue_currentRecord = $checkValueRecord; 00887 00888 /* 00889 In the following all incoming value-fields are tested: 00890 - Are the user allowed to change the field? 00891 - Is the field uid/pid (which are already set) 00892 - perms-fields for pages-table, then do special things... 00893 - If the field is nothing of the above and the field is configured in TCA, the fieldvalues are evaluated by ->checkValue 00894 00895 If everything is OK, the field is entered into $fieldArray[] 00896 */ 00897 foreach($incomingFieldArray as $field => $fieldValue) { 00898 if (!in_array($table.'-'.$field, $this->exclude_array) && !$this->data_disableFields[$table][$id][$field]) { // The field must be editable. 00899 00900 // Checking if a value for language can be changed: 00901 $languageDeny = $TCA[$table]['ctrl']['languageField'] && !strcmp($TCA[$table]['ctrl']['languageField'], $field) && !$this->BE_USER->checkLanguageAccess($fieldValue); 00902 00903 if (!$languageDeny) { 00904 // Stripping slashes - will probably be removed the day $this->stripslashes_values is removed as an option... 00905 if ($this->stripslashes_values) { 00906 if (is_array($fieldValue)) { 00907 t3lib_div::stripSlashesOnArray($fieldValue); 00908 } else $fieldValue = stripslashes($fieldValue); 00909 } 00910 00911 switch ($field) { 00912 case 'uid': 00913 case 'pid': 00914 // Nothing happens, already set 00915 break; 00916 case 'perms_userid': 00917 case 'perms_groupid': 00918 case 'perms_user': 00919 case 'perms_group': 00920 case 'perms_everybody': 00921 // Permissions can be edited by the owner or the administrator 00922 if ($table=='pages' && ($this->admin || $status=='new' || $this->pageInfo($id,'perms_userid')==$this->userid) ) { 00923 $value=intval($fieldValue); 00924 switch($field) { 00925 case 'perms_userid': 00926 $fieldArray[$field]=$value; 00927 break; 00928 case 'perms_groupid': 00929 $fieldArray[$field]=$value; 00930 break; 00931 default: 00932 if ($value>=0 && $value<pow(2,5)) { 00933 $fieldArray[$field]=$value; 00934 } 00935 break; 00936 } 00937 } 00938 break; 00939 case 't3ver_oid': 00940 case 't3ver_id': 00941 case 't3ver_wsid': 00942 case 't3ver_state': 00943 case 't3ver_swapmode': 00944 case 't3ver_count': 00945 case 't3ver_stage': 00946 case 't3ver_tstamp': 00947 // t3ver_label is not here because it CAN be edited as a regular field! 00948 break; 00949 default: 00950 if (isset($TCA[$table]['columns'][$field])) { 00951 // Evaluating the value. 00952 $res = $this->checkValue($table,$field,$fieldValue,$id,$status,$realPid,$tscPID); 00953 if (isset($res['value'])) { 00954 $fieldArray[$field]=$res['value']; 00955 00956 // Add the value of the original record to the diff-storage content: 00957 if ($TCA[$table]['ctrl']['transOrigDiffSourceField']) { 00958 $originalLanguage_diffStorage[$field] = $originalLanguageRecord[$field]; 00959 $diffStorageFlag = TRUE; 00960 } 00961 } 00962 } elseif ($TCA[$table]['ctrl']['origUid']===$field) { // Allow value for original UID to pass by... 00963 $fieldArray[$field] = $fieldValue; 00964 } 00965 break; 00966 } 00967 } // Checking language. 00968 } // Check exclude fields / disabled fields... 00969 } 00970 00971 // Add diff-storage information: 00972 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... 00973 $fieldArray[$TCA[$table]['ctrl']['transOrigDiffSourceField']] = serialize($originalLanguage_diffStorage); 00974 } 00975 00976 // Checking for RTE-transformations of fields: 00977 $types_fieldConfig = t3lib_BEfunc::getTCAtypes($table,$currentRecord); 00978 $theTypeString = t3lib_BEfunc::getTCAtypeValue($table,$currentRecord); 00979 if (is_array($types_fieldConfig)) { 00980 reset($types_fieldConfig); 00981 while(list(,$vconf) = each($types_fieldConfig)) { 00982 // Write file configuration: 00983 $eFile = t3lib_parsehtml_proc::evalWriteFile($vconf['spec']['static_write'],array_merge($currentRecord,$fieldArray)); // inserted array_merge($currentRecord,$fieldArray) 170502 00984 00985 // RTE transformations: 00986 if (!$this->dontProcessTransformations) { 00987 if (isset($fieldArray[$vconf['field']])) { 00988 // Look for transformation flag: 00989 switch((string)$incomingFieldArray['_TRANSFORM_'.$vconf['field']]) { 00990 case 'RTE': 00991 $RTEsetup = $this->BE_USER->getTSConfig('RTE',t3lib_BEfunc::getPagesTSconfig($tscPID)); 00992 $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'],$table,$vconf['field'],$theTypeString); 00993 00994 // Set alternative relative path for RTE images/links: 00995 $RTErelPath = is_array($eFile) ? dirname($eFile['relEditFile']) : ''; 00996 00997 // Get RTE object, draw form and set flag: 00998 $RTEobj = &t3lib_BEfunc::RTEgetObj(); 00999 if (is_object($RTEobj)) { 01000 $fieldArray[$vconf['field']] = $RTEobj->transformContent('db',$fieldArray[$vconf['field']],$table,$vconf['field'],$currentRecord,$vconf['spec'],$thisConfig,$RTErelPath,$currentRecord['pid']); 01001 } else { 01002 debug('NO RTE OBJECT FOUND!'); 01003 } 01004 break; 01005 } 01006 } 01007 } 01008 01009 // Write file configuration: 01010 if (is_array($eFile)) { 01011 $mixedRec = array_merge($currentRecord,$fieldArray); 01012 $SW_fileContent = t3lib_div::getUrl($eFile['editFile']); 01013 $parseHTML = t3lib_div::makeInstance('t3lib_parsehtml_proc'); 01014 $parseHTML->init('',''); 01015 01016 $eFileMarker = $eFile['markerField']&&trim($mixedRec[$eFile['markerField']]) ? trim($mixedRec[$eFile['markerField']]) : '###TYPO3_STATICFILE_EDIT###'; 01017 $insertContent = str_replace($eFileMarker,'',$mixedRec[$eFile['contentField']]); // must replace the marker if present in content! 01018 01019 $SW_fileNewContent = $parseHTML->substituteSubpart($SW_fileContent, $eFileMarker, chr(10).$insertContent.chr(10), 1, 1); 01020 t3lib_div::writeFile($eFile['editFile'],$SW_fileNewContent); 01021 01022 // Write status: 01023 if (!strstr($id,'NEW') && $eFile['statusField']) { 01024 $GLOBALS['TYPO3_DB']->exec_UPDATEquery( 01025 $table, 01026 'uid='.intval($id), 01027 array( 01028 $eFile['statusField'] => $eFile['relEditFile'].' updated '.date('d-m-Y H:i:s').', bytes '.strlen($mixedRec[$eFile['contentField']]) 01029 ) 01030 ); 01031 } 01032 } elseif ($eFile && is_string($eFile)) { 01033 $this->log($table,$id,2,0,1,"Write-file error: '%s'",13,array($eFile),$realPid); 01034 } 01035 } 01036 } 01037 // Return fieldArray 01038 return $fieldArray; 01039 } 01040 01041 01042 01043 01044 01045 01046 01047 01048 01049 01050 01051 01052 /********************************************* 01053 * 01054 * Evaluation of input values 01055 * 01056 ********************************************/ 01057 01072 function checkValue($table,$field,$value,$id,$status,$realPid,$tscPID) { 01073 global $TCA, $PAGES_TYPES; 01074 t3lib_div::loadTCA($table); 01075 01076 $res = Array(); // result array 01077 $recFID = $table.':'.$id.':'.$field; 01078 01079 // Processing special case of field pages.doktype 01080 if ($table=='pages' && $field=='doktype') { 01081 // If the user may not use this specific doktype, we issue a warning 01082 if (! ($this->admin || t3lib_div::inList($this->BE_USER->groupData['pagetypes_select'],$value))) { 01083 $propArr = $this->getRecordProperties($table,$id); 01084 $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']); 01085 return $res; 01086 }; 01087 if ($status=='update') { 01088 // This checks 1) if we should check for disallowed tables and 2) if there are records from disallowed tables on the current page 01089 $onlyAllowedTables = isset($PAGES_TYPES[$value]['onlyAllowedTables']) ? $PAGES_TYPES[$value]['onlyAllowedTables'] : $PAGES_TYPES['default']['onlyAllowedTables']; 01090 if ($onlyAllowedTables) { 01091 $theWrongTables = $this->doesPageHaveUnallowedTables($id,$value); 01092 if ($theWrongTables) { 01093 $propArr = $this->getRecordProperties($table,$id); 01094 $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']); 01095 return $res; 01096 } 01097 } 01098 } 01099 } 01100 01101 // Get current value: 01102 $curValueRec = $this->recordInfo($table,$id,$field); 01103 $curValue = $curValueRec[$field]; 01104 01105 // Getting config for the field 01106 $tcaFieldConf = $TCA[$table]['columns'][$field]['config']; 01107 01108 // Preform processing: 01109 $res = $this->checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$this->uploadedFileArray[$table][$id][$field],$tscPID); 01110 01111 return $res; 01112 } 01113 01132 function checkValue_SW($res,$value,$tcaFieldConf,$table,$id,$curValue,$status,$realPid,$recFID,$field,$uploadedFiles,$tscPID) { 01133 01134 $PP = array($table,$id,$curValue,$status,$realPid,$recFID,$tscPID); 01135 01136 switch ($tcaFieldConf['type']) { 01137 case 'text': 01138 case 'passthrough': 01139 case 'user': 01140 $res['value'] = $value; 01141 break; 01142 case 'input': 01143 $res = $this->checkValue_input($res,$value,$tcaFieldConf,$PP,$field); 01144 break; 01145 case 'check': 01146 $res = $this->checkValue_check($res,$value,$tcaFieldConf,$PP); 01147 break; 01148 case 'radio': 01149 $res = $this->checkValue_radio($res,$value,$tcaFieldConf,$PP); 01150 break; 01151 case 'group': 01152 case 'select': 01153 $res = $this->checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field); 01154 break; 01155 case 'flex': 01156 if ($field) { // FlexForms are only allowed for real fields. 01157 $res = $this->checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field); 01158 } 01159 break; 01160 default: 01161 #debug(array($tcaFieldConf,$res,$value),'NON existing field type:'); 01162 break; 01163 } 01164 01165 return $res; 01166 } 01167 01178 function checkValue_input($res,$value,$tcaFieldConf,$PP,$field='') { 01179 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01180 01181 // Secures the string-length to be less than max. Will probably make problems with multi-byte strings! 01182 if (intval($tcaFieldConf['max'])>0) {$value = substr($value,0,intval($tcaFieldConf['max']));} 01183 01184 // Checking range of value: 01185 if ($tcaFieldConf['range'] && $value!=$tcaFieldConf['checkbox']) { // If value is not set to the allowed checkbox-value then it is checked against the ranges 01186 if (isset($tcaFieldConf['range']['upper'])&&$value>$tcaFieldConf['range']['upper']) {$value=$tcaFieldConf['range']['upper'];} 01187 if (isset($tcaFieldConf['range']['lower'])&&$value<$tcaFieldConf['range']['lower']) {$value=$tcaFieldConf['range']['lower'];} 01188 } 01189 01190 // Process evaluation settings: 01191 $evalCodesArray = t3lib_div::trimExplode(',',$tcaFieldConf['eval'],1); 01192 $res = $this->checkValue_input_Eval($value,$evalCodesArray,$tcaFieldConf['is_in']); 01193 01194 // Process UNIQUE settings: 01195 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... 01196 if ($res['value'] && in_array('uniqueInPid',$evalCodesArray)) { 01197 $res['value'] = $this->getUnique($table,$field,$res['value'],$id,$realPid); 01198 } 01199 if ($res['value'] && in_array('unique',$evalCodesArray)) { 01200 $res['value'] = $this->getUnique($table,$field,$res['value'],$id); 01201 } 01202 } 01203 01204 return $res; 01205 } 01206 01216 function checkValue_check($res,$value,$tcaFieldConf,$PP) { 01217 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01218 01219 $itemC = count($tcaFieldConf['items']); 01220 if (!$itemC) {$itemC=1;} 01221 $maxV = pow(2,$itemC); 01222 01223 if ($value<0) {$value=0;} 01224 if ($value>$maxV) {$value=$maxV;} 01225 $res['value'] = $value; 01226 01227 return $res; 01228 } 01229 01239 function checkValue_radio($res,$value,$tcaFieldConf,$PP) { 01240 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01241 01242 if (is_array($tcaFieldConf['items'])) { 01243 foreach($tcaFieldConf['items'] as $set) { 01244 if (!strcmp($set[1],$value)) { 01245 $res['value'] = $value; 01246 break; 01247 } 01248 } 01249 } 01250 01251 return $res; 01252 } 01253 01265 function checkValue_group_select($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field) { 01266 01267 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01268 01269 // Detecting if value sent is an array and if so, implode it around a comma: 01270 if (is_array($value)) { 01271 $value = implode(',',$value); 01272 } 01273 01274 // 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. 01275 // Anyways, this should NOT disturb anything else: 01276 $value = $this->convNumEntityToByteValue($value); 01277 01278 // When values are sent as group or select they come as comma-separated values which are exploded by this function: 01279 $valueArray = $this->checkValue_group_select_explodeSelectGroupValue($value); 01280 01281 // If not multiple is set, then remove duplicates: 01282 if (!$tcaFieldConf['multiple']) { 01283 $valueArray = array_unique($valueArray); 01284 } 01285 01286 // If an exclusive key is found, discard all others: 01287 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['exclusiveKeys']) { 01288 $exclusiveKeys = t3lib_div::trimExplode(',', $tcaFieldConf['exclusiveKeys']); 01289 foreach($valueArray as $kk => $vv) { 01290 if (in_array($vv, $exclusiveKeys)) { // $vv is the item key! 01291 $valueArray = Array($kk => $vv); 01292 break; 01293 } 01294 } 01295 } 01296 01297 // 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?) 01298 // NOTE!!! Must check max-items of files before the later check because that check would just leave out filenames if there are too many!! 01299 01300 // Checking for select / authMode, removing elements from $valueArray if any of them is not allowed! 01301 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['authMode']) { 01302 $preCount = count($valueArray); 01303 foreach($valueArray as $kk => $vv) { 01304 if (!$this->BE_USER->checkAuthMode($table,$field,$vv,$tcaFieldConf['authMode'])) { 01305 unset($valueArray[$kk]); 01306 } 01307 } 01308 01309 // 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. 01310 if ($preCount && !count($valueArray)) { 01311 return array(); 01312 } 01313 } 01314 01315 // For group types: 01316 if ($tcaFieldConf['type']=='group') { 01317 switch($tcaFieldConf['internal_type']) { 01318 case 'file': 01319 $valueArray = $this->checkValue_group_select_file( 01320 $valueArray, 01321 $tcaFieldConf, 01322 $curValue, 01323 $uploadedFiles, 01324 $status, 01325 $table, 01326 $id, 01327 $recFID 01328 ); 01329 break; 01330 case 'db': 01331 $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'group'); 01332 break; 01333 } 01334 } 01335 // For select types which has a foreign table attached: 01336 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['foreign_table']) { 01337 $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'select'); 01338 } 01339 01340 // 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... 01341 01342 // Checking the number of items, that it is correct. 01343 // If files, there MUST NOT be too many files in the list at this point, so check that prior to this code. 01344 $valueArrayC = count($valueArray); 01345 $minI = isset($tcaFieldConf['minitems']) ? intval($tcaFieldConf['minitems']):0; 01346 01347 // 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. 01348 $maxI = isset($tcaFieldConf['maxitems']) ? intval($tcaFieldConf['maxitems']):1; 01349 if ($valueArrayC > $maxI) {$valueArrayC=$maxI;} // Checking for not too many elements 01350 01351 // Dumping array to list 01352 $newVal=array(); 01353 foreach($valueArray as $nextVal) { 01354 if ($valueArrayC==0) {break;} 01355 $valueArrayC--; 01356 $newVal[]=$nextVal; 01357 } 01358 $res['value'] = implode(',',$newVal); 01359 01360 return $res; 01361 } 01362 01377 function checkValue_group_select_file($valueArray,$tcaFieldConf,$curValue,$uploadedFileArray,$status,$table,$id,$recFID) { 01378 01379 // If any files are uploaded: 01380 if (is_array($uploadedFileArray) && 01381 $uploadedFileArray['name'] && 01382 strcmp($uploadedFileArray['tmp_name'],'none')) { 01383 $valueArray[]=$uploadedFileArray['tmp_name']; 01384 $this->alternativeFileName[$uploadedFileArray['tmp_name']] = $uploadedFileArray['name']; 01385 } 01386 01387 // Creating fileFunc object. 01388 if (!$this->fileFunc) { 01389 $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions'); 01390 $this->include_filefunctions=1; 01391 } 01392 // Setting permitted extensions. 01393 $all_files = Array(); 01394 $all_files['webspace']['allow'] = $tcaFieldConf['allowed']; 01395 $all_files['webspace']['deny'] = $tcaFieldConf['disallowed'] ? $tcaFieldConf['disallowed'] : '*'; 01396 $all_files['ftpspace'] = $all_files['webspace']; 01397 $this->fileFunc->init('', $all_files); 01398 01399 // If there is an upload folder defined: 01400 if ($tcaFieldConf['uploadfolder']) { 01401 // For logging.. 01402 $propArr = $this->getRecordProperties($table,$id); 01403 01404 // Get destrination path: 01405 $dest = $this->destPathFromUploadFolder($tcaFieldConf['uploadfolder']); 01406 01407 // If we are updating: 01408 if ($status=='update') { 01409 01410 // Traverse the input values and convert to absolute filenames in case the update happens to an autoVersionized record. 01411 // 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! 01412 // 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_. 01413 // 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. 01414 // Illustration of the problem comes here: 01415 // 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. 01416 // 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. 01417 // 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. 01418 // 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. 01419 if ($this->autoVersioningUpdate===TRUE) { 01420 foreach($valueArray as $key => $theFile) { 01421 if ($theFile===basename($theFile)) { 01422 $valueArray[$key] = PATH_site.$tcaFieldConf['uploadfolder'].'/'.$theFile; 01423 } 01424 } 01425 } 01426 01427 // Finding the CURRENT files listed, either from MM or from the current record. 01428 $theFileValues=array(); 01429 if ($tcaFieldConf['MM']) { // If MM relations for the files also! 01430 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 01431 $dbAnalysis->start('','files',$tcaFieldConf['MM'],$id); 01432 reset($dbAnalysis->itemArray); 01433 while (list($somekey,$someval)=each($dbAnalysis->itemArray)) { 01434 if ($someval['id']) { 01435 $theFileValues[]=$someval['id']; 01436 } 01437 } 01438 } else { 01439 $theFileValues=t3lib_div::trimExplode(',',$curValue,1); 01440 } 01441 01442 // DELETE files: If existing files were found, traverse those and register files for deletion which has been removed: 01443 if (count($theFileValues)) { 01444 // 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!) 01445 foreach($valueArray as $key => $theFile) { 01446 if ($theFile && !strstr(t3lib_div::fixWindowsFilePath($theFile),'/')) { 01447 $theFileValues = t3lib_div::removeArrayEntryByValue($theFileValues,$theFile); 01448 } 01449 } 01450 01451 // This array contains the filenames in the uploadfolder that should be deleted: 01452 foreach($theFileValues as $key => $theFile) { 01453 $theFile = trim($theFile); 01454 if (@is_file($dest.'/'.$theFile)) { 01455 $this->removeFilesStore[]=$dest.'/'.$theFile; 01456 } elseif ($theFile) { 01457 $this->log($table,$id,5,0,1,"Could not delete file '%s' (does not exist). (%s)",10,array($dest.'/'.$theFile, $recFID),$propArr['event_pid']); 01458 } 01459 } 01460 } 01461 } 01462 01463 // Traverse the submitted values: 01464 foreach($valueArray as $key => $theFile) { 01465 // 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) 01466 if (strstr(t3lib_div::fixWindowsFilePath($theFile),'/')) { 01467 // Init: 01468 $maxSize = intval($tcaFieldConf['max_size']); 01469 $cmd=''; 01470 $theDestFile=''; // Must be cleared. Else a faulty fileref may be inserted if the below code returns an error!! (Change: 22/12/2000) 01471 01472 // Check various things before copying file: 01473 if (@is_dir($dest) && (@is_file($theFile) || @is_uploaded_file($theFile))) { // File and destination must exist 01474 01475 // Finding size. For safe_mode we have to rely on the size in the upload array if the file is uploaded. 01476 if (is_uploaded_file($theFile) && $theFile==$uploadedFileArray['tmp_name']) { 01477 $fileSize = $uploadedFileArray['size']; 01478 } else { 01479 $fileSize = filesize($theFile); 01480 } 01481 01482 if (!$maxSize || $fileSize<=($maxSize*1024)) { // Check file size: 01483 // Prepare filename: 01484 $theEndFileName = isset($this->alternativeFileName[$theFile]) ? $this->alternativeFileName[$theFile] : $theFile; 01485 $fI = t3lib_div::split_fileref($theEndFileName); 01486 01487 // Check for allowed extension: 01488 if ($this->fileFunc->checkIfAllowed($fI['fileext'], $dest, $theEndFileName)) { 01489 $theDestFile = $this->fileFunc->getUniqueName($this->fileFunc->cleanFileName($fI['file']), $dest); 01490 01491 // If we have a unique destination filename, then write the file: 01492 if ($theDestFile) { 01493 t3lib_div::upload_copy_move($theFile,$theDestFile); 01494 $this->copiedFileMap[$theFile] = $theDestFile; 01495 clearstatcache(); 01496 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']); 01497 } 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']); 01498 } else $this->log($table,$id,5,0,1,"Fileextension '%s' not allowed. (%s)",12,array($fI['fileext'], $recFID),$propArr['event_pid']); 01499 } 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']); 01500 } 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']); 01501 01502 // If the destination file was created, we will set the new filename in the value array, otherwise unset the entry in the value array! 01503 if (@is_file($theDestFile)) { 01504 $info = t3lib_div::split_fileref($theDestFile); 01505 $valueArray[$key]=$info['file']; // The value is set to the new filename 01506 } else { 01507 unset($valueArray[$key]); // The value is set to the new filename 01508 } 01509 } 01510 } 01511 01512 // 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! 01513 if ($tcaFieldConf['MM']) { 01514 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 01515 $dbAnalysis->tableArray['files']=array(); // dummy 01516 01517 reset($valueArray); 01518 while (list($key,$theFile)=each($valueArray)) { 01519 // explode files 01520 $dbAnalysis->itemArray[]['id']=$theFile; 01521 } 01522 if ($status=='update') { 01523 $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,0); 01524 } else { 01525 $this->dbAnalysisStore[] = array($dbAnalysis, $tcaFieldConf['MM'], $id, 0); // This will be traversed later to execute the actions 01526 } 01527 $cc=count($dbAnalysis->itemArray); 01528 $valueArray = array($cc); 01529 } 01530 } 01531 01532 return $valueArray; 01533 } 01534 01547 function checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field) { 01548 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01549 01550 if (is_array($value)) { 01551 01552 // Get current value array: 01553 $dataStructArray = t3lib_BEfunc::getFlexFormDS($tcaFieldConf,$this->checkValue_currentRecord,$table); 01554 #debug($this->checkValue_currentRecord); 01555 $currentValueArray = t3lib_div::xml2array($curValue); 01556 if (!is_array($currentValueArray)) $currentValueArray = array(); 01557 if (is_array($currentValueArray['meta']['currentLangId'])) unset($currentValueArray['meta']['currentLangId']); // Remove all old meta for languages... 01558 01559 // Evaluation of input values: 01560 $value['data'] = $this->checkValue_flex_procInData($value['data'],$currentValueArray['data'],$uploadedFiles['data'],$dataStructArray,$PP); 01561 01562 // Create XML and convert charsets from input value: 01563 $xmlValue = $this->checkValue_flexArray2Xml($value,TRUE); 01564 01565 // If we wanted to set UTF fixed: 01566 // $storeInCharset='utf-8'; 01567 // $currentCharset=$GLOBALS['LANG']->charSet; 01568 // $xmlValue = $GLOBALS['LANG']->csConvObj->conv($xmlValue,$currentCharset,$storeInCharset,1); 01569 $storeInCharset=$GLOBALS['LANG']->charSet; 01570 01571 // Merge them together IF they are both arrays: 01572 // 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). 01573 if (is_array($currentValueArray)) { 01574 $arrValue = t3lib_div::xml2array($xmlValue); 01575 $arrValue = t3lib_div::array_merge_recursive_overrule($currentValueArray,$arrValue); 01576 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01577 } 01578 01579 // Temporary fix to delete flex form elements: 01580 $deleteCMDs = t3lib_div::_GP('_DELETE_FLEX_FORMdata'); 01581 if (is_array($deleteCMDs[$table][$id][$field]['data'])) { 01582 $arrValue = t3lib_div::xml2array($xmlValue); 01583 $this->_DELETE_FLEX_FORMdata($arrValue['data'],$deleteCMDs[$table][$id][$field]['data']); 01584 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01585 } 01586 01587 // Temporary fix to move flex form elements up: 01588 $moveCMDs = t3lib_div::_GP('_MOVEUP_FLEX_FORMdata'); 01589 if (is_array($moveCMDs[$table][$id][$field]['data'])) { 01590 $arrValue = t3lib_div::xml2array($xmlValue); 01591 $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'up'); 01592 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01593 } 01594 01595 // Temporary fix to move flex form elements down: 01596 $moveCMDs = t3lib_div::_GP('_MOVEDOWN_FLEX_FORMdata'); 01597 if (is_array($moveCMDs[$table][$id][$field]['data'])) { 01598 $arrValue = t3lib_div::xml2array($xmlValue); 01599 $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'down'); 01600 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01601 } 01602 01603 // Create the value XML: 01604 $res['value']=''; 01605 $res['value'].=$xmlValue; 01606 } else { // Passthrough...: 01607 $res['value']=$value; 01608 } 01609 01610 return $res; 01611 } 01612 01620 function checkValue_flexArray2Xml($array, $addPrologue=FALSE) { 01621 $flexObj = t3lib_div::makeInstance('t3lib_flexformtools'); 01622 return $flexObj->flexArray2Xml($array, $addPrologue); 01623 } 01624 01632 function _DELETE_FLEX_FORMdata(&$valueArrayToRemoveFrom,$deleteCMDS) { 01633 if (is_array($valueArrayToRemoveFrom) && is_array($deleteCMDS)) { 01634 foreach($deleteCMDS as $key => $value) { 01635 if (is_array($deleteCMDS[$key])) { 01636 $this->_DELETE_FLEX_FORMdata($valueArrayToRemoveFrom[$key],$deleteCMDS[$key]); 01637 } else { 01638 unset($valueArrayToRemoveFrom[$key]); 01639 } 01640 } 01641 } 01642 } 01643 01654 function _MOVE_FLEX_FORMdata(&$valueArrayToMoveIn, $moveCMDS, $direction) { 01655 if (is_array($valueArrayToMoveIn) && is_array($moveCMDS)) { 01656 01657 // Only execute the first move command: 01658 list ($key, $value) = each ($moveCMDS); 01659 01660 if (is_array($moveCMDS[$key])) { 01661 $this->_MOVE_FLEX_FORMdata($valueArrayToMoveIn[$key],$moveCMDS[$key], $direction); 01662 } else { 01663 switch ($direction) { 01664 case 'up': 01665 if ($key > 1) { 01666 $tmpArr = $valueArrayToMoveIn[$key]; 01667 $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key-1]; 01668 $valueArrayToMoveIn[$key-1] = $tmpArr; 01669 } 01670 break; 01671 case 'down': 01672 if ($key < count($valueArrayToMoveIn)) { 01673 $tmpArr = $valueArrayToMoveIn[$key]; 01674 $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key+1]; 01675 $valueArrayToMoveIn[$key+1] = $tmpArr; 01676 } 01677 break; 01678 } 01679 } 01680 } 01681 } 01682 01683 01684 01685 01686 01687 01688 01689 01690 01691 01692 01693 01694 01695 01696 01697 01698 01699 01700 /********************************************* 01701 * 01702 * Helper functions for evaluation functions. 01703 * 01704 ********************************************/ 01705 01716 function getUnique($table,$field,$value,$id,$newPid=0) { 01717 global $TCA; 01718 01719 // Initialize: 01720 t3lib_div::loadTCA($table); 01721 $whereAdd=''; 01722 $newValue=''; 01723 if (intval($newPid)) { $whereAdd.=' AND pid='.intval($newPid); } else { $whereAdd.=' AND pid>=0'; } // "AND pid>=0" for versioning 01724 $whereAdd.=$this->deleteClause($table); 01725 01726 // If the field is configured in TCA, proceed: 01727 if (is_array($TCA[$table]) && is_array($TCA[$table]['columns'][$field])) { 01728 01729 // Look for a record which might already have the value: 01730 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $table).' AND uid!='.intval($id).$whereAdd); 01731 $counter = 0; 01732 01733 // For as long as records with the test-value existing, try again (with incremented numbers appended). 01734 while ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 01735 $newValue = $value.$counter; 01736 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($newValue, $table).' AND uid!='.intval($id).$whereAdd); 01737 $counter++; 01738 if ($counter>100) { break; } // At "100" it will give up and accept a duplicate - should probably be fixed to a small hash string instead...! 01739 } 01740 // If the new value is there: 01741 $value = strlen($newValue) ? $newValue : $value; 01742 } 01743 return $value; 01744 } 01745 01754 function checkValue_input_Eval($value,$evalArray,$is_in) { 01755 $res = Array(); 01756 $newValue = $value; 01757 $set = true; 01758 01759 foreach($evalArray as $func) { 01760 switch($func) { 01761 case 'int': 01762 case 'year': 01763 case 'date': 01764 case 'datetime': 01765 case 'time': 01766 case 'timesec': 01767 $value = intval($value); 01768 break; 01769 case 'double2': 01770 $theDec = 0; 01771 for ($a=strlen($value); $a>0; $a--) { 01772 if (substr($value,$a-1,1)=='.' || substr($value,$a-1,1)==',') { 01773 $theDec = substr($value,$a); 01774 $value = substr($value,0,$a-1); 01775 break; 01776 } 01777 } 01778 $theDec = ereg_replace('[^0-9]','',$theDec).'00'; 01779 $value = intval(str_replace(' ','',$value)).'.'.substr($theDec,0,2); 01780 break; 01781 case 'md5': 01782 if (strlen($value)!=32){$set=false;} 01783 break; 01784 case 'trim': 01785 $value = trim($value); 01786 break; 01787 case 'upper': 01788 $value = strtoupper($value); 01789 # $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. 01790 break; 01791 case 'lower': 01792 $value = strtolower($value); 01793 # $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. 01794 break; 01795 case 'required': 01796 if (!$value) {$set=0;} 01797 break; 01798 case 'is_in': 01799 $c=strlen($value); 01800 if ($c) { 01801 $newVal = ''; 01802 for ($a=0;$a<$c;$a++) { 01803 $char = substr($value,$a,1); 01804 if (strstr($is_in,$char)) { 01805 $newVal.=$char; 01806 } 01807 } 01808 $value = $newVal; 01809 } 01810 break; 01811 case 'nospace': 01812 $value = str_replace(' ','',$value); 01813 break; 01814 case 'alpha': 01815 $value = ereg_replace('[^a-zA-Z]','',$value); 01816 break; 01817 case 'num': 01818 $value = ereg_replace('[^0-9]','',$value); 01819 break; 01820 case 'alphanum': 01821 $value = ereg_replace('[^a-zA-Z0-9]','',$value); 01822 break; 01823 case 'alphanum_x': 01824 $value = ereg_replace('[^a-zA-Z0-9_-]','',$value); 01825 break; 01826 default: 01827 if (substr($func, 0, 3) == 'tx_') { 01828 $evalObj = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func].':&'.$func); 01829 if(is_object($evalObj) && method_exists($evalObj, 'evaluateFieldValue')) { 01830 $value = $evalObj->evaluateFieldValue($value, $is_in, $set); 01831 } 01832 } 01833 break; 01834 } 01835 } 01836 if ($set) {$res['value'] = $value;} 01837 return $res; 01838 } 01839 01850 function checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,$type) { 01851 $tables = $type=='group'?$tcaFieldConf['allowed']:$tcaFieldConf['foreign_table'].','.$tcaFieldConf['neg_foreign_table']; 01852 $prep = $type=='group'?$tcaFieldConf['prepend_tname']:$tcaFieldConf['neg_foreign_table']; 01853 01854 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 01855 $dbAnalysis->registerNonTableValues=$tcaFieldConf['allowNonIdValues'] ? 1 : 0; 01856 $dbAnalysis->start(implode(',',$valueArray),$tables); 01857 01858 if ($tcaFieldConf['MM']) { 01859 if ($status=='update') { 01860 $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,$prep); 01861 } else { 01862 $this->dbAnalysisStore[] = array($dbAnalysis,$tcaFieldConf['MM'],$id,$prep); // This will be traversed later to execute the actions 01863 } 01864 $cc=count($dbAnalysis->itemArray); 01865 $valueArray = array($cc); 01866 } else { 01867 $valueArray = $dbAnalysis->getValueArray($prep); 01868 if ($type=='select' && $prep) { 01869 $valueArray = $dbAnalysis->convertPosNeg($valueArray,$tcaFieldConf['foreign_table'],$tcaFieldConf['neg_foreign_table']); 01870 } 01871 } 01872 01873 // Here we should se if 1) the records exist anymore, 2) which are new and check if the BE_USER has read-access to the new ones. 01874 return $valueArray; 01875 } 01876 01883 function checkValue_group_select_explodeSelectGroupValue($value) { 01884 $valueArray = t3lib_div::trimExplode(',',$value,1); 01885 reset($valueArray); 01886 while(list($key,$newVal)=each($valueArray)) { 01887 $temp=explode('|',$newVal,2); 01888 $valueArray[$key] = str_replace(',','',str_replace('|','',rawurldecode($temp[0]))); 01889 } 01890 return $valueArray; 01891 } 01892 01907 function checkValue_flex_procInData($dataPart,$dataPart_current,$uploadedFiles,$dataStructArray,$pParams,$callBackFunc='') { 01908 #debug(array($dataPart,$dataPart_current,$dataStructArray)); 01909 if (is_array($dataPart)) { 01910 foreach($dataPart as $sKey => $sheetDef) { 01911 list ($dataStruct,$actualSheet) = t3lib_div::resolveSheetDefInDS($dataStructArray,$sKey); 01912 #debug(array($dataStruct,$actualSheet,$sheetDef,$actualSheet,$sKey)); 01913 if (is_array($dataStruct) && $actualSheet==$sKey && is_array($sheetDef)) { 01914 foreach($sheetDef as $lKey => $lData) { 01915 $this->checkValue_flex_procInData_travDS( 01916 $dataPart[$sKey][$lKey], 01917 $dataPart_current[$sKey][$lKey], 01918 $uploadedFiles[$sKey][$lKey], 01919 $dataStruct['ROOT']['el'], 01920 $pParams, 01921 $callBackFunc, 01922 $sKey.'/'.$lKey.'/' 01923 ); 01924 } 01925 } 01926 } 01927 } 01928 01929 return $dataPart; 01930 } 01931 01946 function checkValue_flex_procInData_travDS(&$dataValues,$dataValues_current,$uploadedFiles,$DSelements,$pParams,$callBackFunc,$structurePath) { 01947 if (is_array($DSelements)) { 01948 01949 // For each DS element: 01950 foreach($DSelements as $key => $dsConf) { 01951 01952 // Array/Section: 01953 if ($DSelements[$key]['type']=='array') { 01954 if (is_array($dataValues[$key]['el'])) { 01955 if ($DSelements[$key]['section']) { 01956 foreach($dataValues[$key]['el'] as $ik => $el) { 01957 $theKey = key($el); 01958 if (is_array($dataValues[$key]['el'][$ik][$theKey]['el'])) { 01959 $this->checkValue_flex_procInData_travDS( 01960 $dataValues[$key]['el'][$ik][$theKey]['el'], 01961 $dataValues_current[$key]['el'][$ik][$theKey]['el'], 01962 $uploadedFiles[$key]['el'][$ik][$theKey]['el'], 01963 $DSelements[$key]['el'][$theKey]['el'], 01964 $pParams, 01965 $callBackFunc, 01966 $structurePath.$key.'/el/'.$ik.'/'.$theKey.'/el/' 01967 ); 01968 } 01969 } 01970 } else { 01971 if (!isset($dataValues[$key]['el'])) $dataValues[$key]['el']=array(); 01972 $this->checkValue_flex_procInData_travDS( 01973 $dataValues[$key]['el'], 01974 $dataValues_current[$key]['el'], 01975 $uploadedFiles[$key]['el'], 01976 $DSelements[$key]['el'], 01977 $pParams, 01978 $callBackFunc, 01979 $structurePath.$key.'/el/' 01980 ); 01981 } 01982 } 01983 } else { 01984 if (is_array($dsConf['TCEforms']['config']) && is_array($dataValues[$key])) { 01985 foreach($dataValues[$key] as $vKey => $data) { 01986 01987 if ($callBackFunc) { 01988 if (is_object($this->callBackObj)) { 01989 $res = $this->callBackObj->$callBackFunc( 01990 $pParams, 01991 $dsConf['TCEforms']['config'], 01992 $dataValues[$key][$vKey], 01993 $dataValues_current[$key][$vKey], 01994 $uploadedFiles[$key][$vKey], 01995 $structurePath.$key.'/'.$vKey.'/' 01996 ); 01997 } else { 01998 $res = $this->$callBackFunc( 01999 $pParams, 02000 $dsConf['TCEforms']['config'], 02001 $dataValues[$key][$vKey], 02002 $dataValues_current[$key][$vKey], 02003 $uploadedFiles[$key][$vKey] 02004 ); 02005 } 02006 } else { // Default 02007 list($CVtable,$CVid,$CVcurValue,$CVstatus,$CVrealPid,$CVrecFID,$CVtscPID) = $pParams; 02008 02009 $res = $this->checkValue_SW( 02010 array(), 02011 $dataValues[$key][$vKey], 02012 $dsConf['TCEforms']['config'], 02013 $CVtable, 02014 $CVid, 02015 $dataValues_current[$key][$vKey], 02016 $CVstatus, 02017 $CVrealPid, 02018 $CVrecFID, 02019 '', 02020 $uploadedFiles[$key][$vKey], 02021 array(), 02022 $CVtscPID 02023 ); 02024 02025 // Look for RTE transformation of field: 02026 if ($dataValues[$key]['_TRANSFORM_'.$vKey] == 'RTE' && !$this->dontProcessTransformations) { 02027 02028 // Unsetting trigger field - we absolutely don't want that into the data storage! 02029 unset($dataValues[$key]['_TRANSFORM_'.$vKey]); 02030 02031 if (isset($res['value'])) { 02032 02033 // Calculating/Retrieving some values here: 02034 list(,,$recFieldName) = explode(':', $CVrecFID); 02035 $theTypeString = t3lib_BEfunc::getTCAtypeValue($CVtable,$this->checkValue_currentRecord); 02036 $specConf = t3lib_BEfunc::getSpecConfParts('',$dsConf['TCEforms']['defaultExtras']); 02037 02038 // Find, thisConfig: 02039 $RTEsetup = $this->BE_USER->getTSConfig('RTE',t3lib_BEfunc::getPagesTSconfig($CVtscPID)); 02040 $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'],$CVtable,$recFieldName,$theTypeString); 02041 02042 // Get RTE object, draw form and set flag: 02043 $RTEobj = &t3lib_BEfunc::RTEgetObj(); 02044 if (is_object($RTEobj)) { 02045 $res['value'] = $RTEobj->transformContent('db',$res['value'],$CVtable,$recFieldName,$this->checkValue_currentRecord,$specConf,$thisConfig,'',$CVrealPid); 02046 } else { 02047 debug('NO RTE OBJECT FOUND!'); 02048 } 02049 } 02050 } 02051 } 02052 02053 // Adding the value: 02054 if (isset($res['value'])) { 02055 $dataValues[$key][$vKey] = $res['value']; 02056 } 02057 } 02058 } 02059 } 02060 } 02061 } 02062 } 02063 02064 02065 02066 02067 02068 02069 02070 02071 02072 02073 02074 02075 02076 02077 02078 02079 02080 /********************************************* 02081 * 02082 * PROCESSING COMMANDS 02083 * 02084 ********************************************/ 02085 02092 function process_cmdmap() { 02093 global $TCA, $TYPO3_CONF_VARS; 02094 02095 // Editing frozen: 02096 if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze']) { 02097 $this->newlog('All editing in this workspace has been frozen!',1); 02098 return FALSE; 02099 } 02100 02101 // Hook initialization: 02102 $hookObjectsArr = array(); 02103 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'])) { 02104 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'] as $classRef) { 02105 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef); 02106 } 02107 } 02108 02109 // Traverse command map: 02110 reset($this->cmdmap); 02111 while(list($table,) = each($this->cmdmap)) { 02112 02113 // Check if the table may be modified! 02114 $modifyAccessList = $this->checkModifyAccessList($table); 02115 if (!$modifyAccessList) { 02116 $id = 0; 02117 $this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table)); 02118 } // FIXME: $id not set here (Comment added by Sebastian Kurfuerst) 02119 02120 // Check basic permissions and circumstances: 02121 if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->cmdmap[$table]) && $modifyAccessList) { 02122 02123 // Traverse the command map: 02124 foreach($this->cmdmap[$table] as $id => $incomingCmdArray) { 02125 if (is_array($incomingCmdArray)) { // have found a command. 02126 02127 // Get command and value (notice, only one command is observed at a time!): 02128 reset($incomingCmdArray); 02129 $command = key($incomingCmdArray); 02130 $value = current($incomingCmdArray); 02131 02132 foreach($hookObjectsArr as $hookObj) { 02133 if (method_exists($hookObj, 'processCmdmap_preProcess')) { 02134 $hookObj->processCmdmap_preProcess($command, $table, $id, $value, $this); 02135 } 02136 } 02137 02138 // Init copyMapping array: 02139 $this->copyMappingArray = Array(); // Must clear this array before call from here to those functions: Contains mapping information between new and old id numbers. 02140 02141 // Branch, based on command 02142 switch ($command) { 02143 case 'move': 02144 $this->moveRecord($table,$id,$value); 02145 break; 02146 case 'copy': 02147 if ($table === 'pages') { 02148 $this->copyPages($id,$value); 02149 } else { 02150 $this->copyRecord($table,$id,$value,1); 02151 } 02152 break; 02153 case 'localize': 02154 $this->localize($table,$id,$value); 02155 break; 02156 case 'version': 02157 switch ((string)$value['action']) { 02158 case 'new': 02159 $versionizeTree = t3lib_div::intInRange(!isset($value['treeLevels'])?-1:$value['treeLevels'],-1,100); 02160 if ($table == 'pages' && $versionizeTree>=0) { 02161 $this->versionizePages($id,$value['label'],$versionizeTree); 02162 } else { 02163 $this->versionizeRecord($table,$id,$value['label']); 02164 } 02165 break; 02166 case 'swap': 02167 $this->version_swap($table,$id,$value['swapWith'],$value['swapIntoWS']); 02168 break; 02169 case 'clearWSID': 02170 $this->version_clearWSID($table,$id); 02171 break; 02172 case 'setStage': 02173 $idList = t3lib_div::trimExplode(',',$id,1); 02174 foreach($idList as $id) { 02175 $this->version_setStage($table,$id,$value['stageId'],$value['comment']?$value['comment']:$this->generalComment); 02176 } 02177 break; 02178 } 02179 break; 02180 case 'delete': 02181 $this->deleteAction($table, $id); 02182 break; 02183 case 'undelete': 02184 $this->undeleteRecord($table, $id); 02185 break; 02186 } 02187 02188 foreach($hookObjectsArr as $hookObj) { 02189 if (method_exists($hookObj, 'processCmdmap_postProcess')) { 02190 $hookObj->processCmdmap_postProcess($command, $table, $id, $value, $this); 02191 } 02192 } 02193 02194 // Merging the copy-array info together for remapping purposes. 02195 $this->copyMappingArray_merged= t3lib_div::array_merge_recursive_overrule($this->copyMappingArray_merged,$this->copyMappingArray); 02196 } 02197 } 02198 } 02199 } 02200 02201 // Finally, before exit, check if there are ID references to remap. This might be the case if versioning or copying has taken place! 02202 $this->remapListedDBRecords(); 02203 } 02204 02205 02206 02207 02208 02209 02210 02211 02212 02213 02214 02215 /********************************************* 02216 * 02217 * Cmd: Copying 02218 * 02219 ********************************************/ 02220 02232 function copyRecord($table,$uid,$destPid,$first=0,$overrideValues=array(),$excludeFields='') { 02233 global $TCA; 02234 02235 $uid = $origUid = intval($uid); 02236 if ($TCA[$table] && $uid) { 02237 t3lib_div::loadTCA($table); 02238 /* 02239 // 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) 02240 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid')) { 02241 $uid = $lookForLiveVersion['uid']; 02242 } 02243 // Get workspace version of the source record, if any: Then we will copy workspace version instead: 02244 if ($WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid')) { 02245 $uid = $WSversion['uid']; 02246 } 02247 // 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. 02248 */ 02249 if ($this->doesRecordExist($table,$uid,'show')) { // This checks if the record can be selected which is all that a copy action requires. 02250 $data = Array(); 02251 02252 $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)); 02253 02254 // $row = $this->recordInfo($table,$uid,'*'); 02255 $row = t3lib_BEfunc::getRecordWSOL($table,$uid); // So it copies (and localized) content from workspace... 02256 if (is_array($row)) { 02257 02258 // Initializing: 02259 $theNewID = uniqid('NEW'); 02260 $enableField = isset($TCA[$table]['ctrl']['enablecolumns']) ? $TCA[$table]['ctrl']['enablecolumns']['disabled'] : ''; 02261 $headerField = $TCA[$table]['ctrl']['label']; 02262 02263 // Getting default data: 02264 $defaultData = $this->newFieldArray($table); 02265 02266 // Getting "copy-after" fields if applicable: 02267 // 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. 02268 $copyAfterFields = $destPid<0 ? $this->fixCopyAfterDuplFields($table,$uid,abs($destPid),0) : array(); 02269 02270 // Page TSconfig related: 02271 $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... 02272 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 02273 $tE = $this->getTableEntries($table,$TSConfig); 02274 02275 // Traverse ALL fields of the selected record: 02276 foreach($row as $field => $value) { 02277 if (!in_array($field,$nonFields)) { 02278 02279 // Get TCA configuration for the field: 02280 $conf = $TCA[$table]['columns'][$field]['config']; 02281 02282 // Preparation/Processing of the value: 02283 if ($field=='pid') { // "pid" is hardcoded of course: 02284 $value = $destPid; 02285 } elseif (isset($overrideValues[$field])) { // Override value... 02286 $value = $overrideValues[$field]; 02287 } elseif (isset($copyAfterFields[$field])) { // Copy-after value if available: 02288 $value = $copyAfterFields[$field]; 02289 } elseif ($TCA[$table]['ctrl']['setToDefaultOnCopy'] && t3lib_div::inList($TCA[$table]['ctrl']['setToDefaultOnCopy'],$field)) { // Revert to default for some fields: 02290 $value = $defaultData[$field]; 02291 } else { 02292 // Hide at copy may override: 02293 if ($first && $field==$enableField && $TCA[$table]['ctrl']['hideAtCopy'] && !$this->neverHideAtCopy && !$tE['disableHideAtCopy']) { 02294 $value=1; 02295 } 02296 // Prepend label on copy: 02297 if ($first && $field==$headerField && $TCA[$table]['ctrl']['prependAtCopy'] && !$tE['disablePrependAtCopy']) { 02298 $value = $this->getCopyHeader($table,$this->resolvePid($table,$destPid),$field,$this->clearPrefixFromValue($table,$value),0); 02299 } 02300 // Processing based on the TCA config field type (files, references, flexforms...) 02301 $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf); 02302 } 02303 02304 // Add value to array. 02305 $data[$table][$theNewID][$field] = $value; 02306 } 02307 } 02308 02309 // Overriding values: 02310 if ($TCA[$table]['ctrl']['editlock']) { 02311 $data[$table][$theNewID][$TCA[$table]['ctrl']['editlock']] = 0; 02312 } 02313 02314 // Setting original UID: 02315 if ($TCA[$table]['ctrl']['origUid']) { 02316 $data[$table][$theNewID][$TCA[$table]['ctrl']['origUid']] = $uid; 02317 } 02318 02319 // Do the copy by simply submitting the array through TCEmain: 02320 $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain'); 02321 $copyTCE->stripslashes_values = 0; 02322 $copyTCE->copyTree = $this->copyTree; 02323 $copyTCE->cachedTSconfig = $this->cachedTSconfig; // Copy forth the cached TSconfig 02324 $copyTCE->dontProcessTransformations=1; // Transformations should NOT be carried out during copy 02325 02326 $copyTCE->start($data,'',$this->BE_USER); 02327 $copyTCE->process_datamap(); 02328 02329 // Getting the new UID: 02330 $theNewSQLID = $copyTCE->substNEWwithIDs[$theNewID]; 02331 if ($theNewSQLID) { 02332 $this->copyMappingArray[$table][$origUid] = $theNewSQLID; 02333 } 02334 02335 // Copy back the cached TSconfig 02336 $this->cachedTSconfig = $copyTCE->cachedTSconfig; 02337 $this->errorLog = array_merge($this->errorLog,$copyTCE->errorLog); 02338 unset($copyTCE); 02339 02340 return $theNewSQLID; 02341 } else $this->log($table,$uid,3,0,1,'Attempt to copy record that did not exist!'); 02342 } else $this->log($table,$uid,3,0,1,'Attempt to copy record without permission'); 02343 } 02344 } 02345 02354 function copyPages($uid,$destPid) { 02355 02356 // Initialize: 02357 $uid = intval($uid); 02358 $destPid = intval($destPid); 02359 02360 // Finding list of tables to copy. 02361 $copyTablesArray = $this->admin ? $this->compileAdminTables() : explode(',',$this->BE_USER->groupData['tables_modify']); // These are the tables, the user may modify 02362 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 02363 foreach($copyTablesArray as $k => $table) { 02364 if (!$table || !t3lib_div::inList($this->copyWhichTables.',pages',$table)) { // pages are always going... 02365 unset($copyTablesArray[$k]); 02366 } 02367 } 02368 } 02369 $copyTablesArray = array_unique($copyTablesArray); 02370 02371 // Begin to copy pages if we're allowed to: 02372 if ($this->admin || in_array('pages',$copyTablesArray)) { 02373 02374 // Copy this page we're on. And set first-flag (this will trigger that the record is hidden if that is configured)! 02375 $theNewRootID = $this->copySpecificPage($uid,$destPid,$copyTablesArray,1); 02376 02377 // If we're going to copy recursively...: 02378 if ($theNewRootID && $this->copyTree) { 02379 02380 // Get ALL subpages to copy (read-permissions are respected!): 02381 $CPtable = $this->int_pageTreeInfo(Array(), $uid, intval($this->copyTree), $theNewRootID); 02382 02383 // Now copying the subpages: 02384 foreach($CPtable as $thePageUid => $thePagePid) { 02385 $newPid = $this->copyMappingArray['pages'][$thePagePid]; 02386 if (isset($newPid)) { 02387 $this->copySpecificPage($thePageUid,$newPid,$copyTablesArray); 02388 } else { 02389 $this->log('pages',$uid,5,0,1,'Something went wrong during copying branch'); 02390 break; 02391 } 02392 } 02393 } // else the page was not copied. Too bad... 02394 } else { 02395 $this->log('pages',$uid,5,0,1,'Attempt to copy page without permission to this table'); 02396 } 02397 } 02398 02408 function copySpecificPage($uid,$destPid,$copyTablesArray,$first=0) { 02409 global $TCA; 02410 02411 // Copy the page itself: 02412 $theNewRootID = $this->copyRecord('pages',$uid,$destPid,$first); 02413 02414 // If a new page was created upon the copy operation we will proceed with all the tables ON that page: 02415 if ($theNewRootID) { 02416 foreach($copyTablesArray as $table) { 02417 if ($table && is_array($TCA[$table]) && $table!='pages') { // all records under the page is copied. 02418 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table), '', ($TCA[$table]['ctrl']['sortby'] ? $TCA[$table]['ctrl']['sortby'].' DESC' : '')); 02419 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 02420 $this->copyRecord($table,$row['uid'], $theNewRootID); // Copying each of the underlying records... 02421 } 02422 } 02423 } 02424 return $theNewRootID; 02425 } 02426 } 02427 02442 function copyRecord_raw($table,$uid,$pid,$overrideArray=array()) { 02443 global $TCA; 02444 02445 $uid = intval($uid); 02446 if ($TCA[$table] && $uid) { 02447 t3lib_div::loadTCA($table); 02448 if ($this->doesRecordExist($table,$uid,'show')) { 02449 02450 // Set up fields which should not be processed. They are still written - just passed through no-questions-asked! 02451 $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'); 02452 02453 // Select main record: 02454 $row = $this->recordInfo($table,$uid,'*'); 02455 if (is_array($row)) { 02456 02457 // Merge in override array. 02458 $row = array_merge($row,$overrideArray); 02459 02460 // Traverse ALL fields of the selected record: 02461 foreach($row as $field => $value) { 02462 if (!in_array($field,$nonFields)) { 02463 02464 // Get TCA configuration for the field: 02465 $conf = $TCA[$table]['columns'][$field]['config']; 02466 if (is_array($conf)) { 02467 // Processing based on the TCA config field type (files, references, flexforms...) 02468 $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf); 02469 } 02470 02471 // Add value to array. 02472 $row[$field] = $value; 02473 } 02474 } 02475 02476 // Force versioning related fields: 02477 $row['pid'] = $pid; 02478 02479 // Setting original UID: 02480 if ($TCA[$table]['ctrl']['origUid']) { 02481 $row[$TCA[$table]['ctrl']['origUid']] = $uid; 02482 } 02483 02484 // Do the copy by internal function 02485 $theNewSQLID = $this->insertNewCopyVersion($table,$row,$pid); 02486 if ($theNewSQLID) { 02487 $this->dbAnalysisStoreExec(); 02488 $this->dbAnalysisStore = array(); 02489 return $this->copyMappingArray[$table][$uid] = $theNewSQLID; 02490 } 02491 } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record that did not exist!'); 02492 } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record without copy permission'); 02493 } 02494 } 02495 02506 function rawCopyPageContent($old_pid,$new_pid,$copyTablesArray) { 02507 global $TCA; 02508 02509 if ($new_pid) { 02510 foreach($copyTablesArray as $table) { 02511 if ($table && is_array($TCA[$table]) && $table!='pages') { // all records under the page is copied. 02512 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($old_pid).$this->deleteClause($table)); 02513 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 02514 $this->copyRecord_raw($table,$row['uid'],$new_pid); // Copying each of the underlying records (method RAW) 02515 } 02516 } 02517 } 02518 } 02519 } 02520 02530 function insertNewCopyVersion($table,$fieldArray,$realPid) { 02531 global $TCA; 02532 02533 $id = uniqid('NEW'); 02534 02535 // $fieldArray is set as current record. 02536 // 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... 02537 $this->checkValue_currentRecord = $fieldArray; 02538 02539 // Traverse record and input-process each value: 02540 foreach($fieldArray as $field => $fieldValue) { 02541 if (isset($TCA[$table]['columns'][$field])) { 02542 // Evaluating the value. 02543 $res = $this->checkValue($table,$field,$fieldValue,$id,'new',$realPid,0); 02544 if (isset($res['value'])) { 02545 $fieldArray[$field] = $res['value']; 02546 } 02547 } 02548 } 02549 02550 // System fields being set: 02551 if ($TCA[$table]['ctrl']['crdate']) { 02552 $fieldArray[$TCA[$table]['ctrl']['crdate']]=time(); 02553 } 02554 if ($TCA[$table]['ctrl']['cruser_id']) { 02555 $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid; 02556 } 02557 if ($TCA[$table]['ctrl']['tstamp']) { 02558 $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time(); 02559 } 02560 02561 // Finally, insert record: 02562 $this->insertDB($table,$id,$fieldArray, TRUE); 02563 02564 // Return new id: 02565 return $this->substNEWwithIDs[$id]; 02566 } 02567 02581 function copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf) { 02582 global $TCA; 02583 02584 // 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) 02585 $value = $this->copyRecord_procFilesRefs($conf, $uid, $value); 02586 02587 02588 // Register if there are references to take care of (no change to value): 02589 if ($this->isReferenceField($conf)) { 02590 $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table']; 02591 $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : $conf['neg_foreign_table']; 02592 if ($conf['MM']) { 02593 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 02594 $dbAnalysis->start('',$allowedTables,$conf['MM'],$uid); 02595 $value = implode(',',$dbAnalysis->getValueArray($prependName)); 02596 } 02597 if ($value) { // Setting the value in this array will notify the remapListedDBRecords() function that this field MAY need references to be corrected 02598 $this->registerDBList[$table][$uid][$field] = $value; 02599 } 02600 } 02601 02602 // 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()) 02603 if ($conf['type']=='flex') { 02604 02605 // Get current value array: 02606 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $row, $table); 02607 $currentValueArray = t3lib_div::xml2array($value); 02608 02609 // Traversing the XML structure, processing files: 02610 if (is_array($currentValueArray)) { 02611 $currentValueArray['data'] = $this->checkValue_flex_procInData( 02612 $currentValueArray['data'], 02613 array(), // Not used. 02614 array(), // Not used. 02615 $dataStructArray, 02616 array($table,$uid,$field), // Parameters. 02617 'copyRecord_flexFormCallBack' 02618 ); 02619 $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. 02620 } 02621 } 02622 02623 return $value; 02624 } 02625 02637 function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2) { 02638 02639 // Extract parameters: 02640 list($table, $uid, $field) = $pParams; 02641 02642 // Process references and files, currently that means only the files, prepending absolute paths: 02643 $dataValue = $this->copyRecord_procFilesRefs($dsConf, $uid, $dataValue); 02644 02645 // If references are set for this field, set flag so they can be corrected later (in ->remapListedDBRecords()) 02646 if ($this->isReferenceField($dsConf) && strlen($dataValue)) { 02647 $this->registerDBList[$table][$uid][$field] = 'FlexForm_reference'; 02648 } 02649 02650 // Return 02651 return array('value' => $dataValue); 02652 } 02653 02665 function copyRecord_procFilesRefs($conf, $uid, $value) { 02666 02667 // Prepend absolute paths to files: 02668 if ($conf['type']=='group' && $conf['internal_type']=='file') { 02669 02670 // Get an array with files as values: 02671 if ($conf['MM']) { 02672 $theFileValues = array(); 02673 02674 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 02675 $dbAnalysis->start('', 'files', $conf['MM'], $uid); 02676 02677 foreach($dbAnalysis->itemArray as $somekey => $someval) { 02678 if ($someval['id']) { 02679 $theFileValues[] = $someval['id']; 02680 } 02681 } 02682 } else { 02683 $theFileValues = t3lib_div::trimExplode(',',$value,1); 02684 } 02685 02686 // Traverse this array of files: 02687 $uploadFolder = $conf['uploadfolder']; 02688 $dest = $this->destPathFromUploadFolder($uploadFolder); 02689 $newValue = array(); 02690 02691 foreach($theFileValues as $file) { 02692 if (trim($file)) { 02693 $realFile = $dest.'/'.trim($file); 02694 if (@is_file($realFile)) { 02695 $newValue[] = $realFile; 02696 } 02697 } 02698 } 02699 02700 // 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...) 02701 $value = implode(',',$newValue); 02702 } 02703 02704 // Return the new value: 02705 return $value; 02706 } 02707 02708 02709 02710 02711 02712 02713 02714 02715 02716 02717 02718 02719 02720 /********************************************* 02721 * 02722 * Cmd: Moving, Localizing 02723 * 02724 ********************************************/ 02725 02734 function moveRecord($table,$uid,$destPid) { 02735 global $TCA, $TYPO3_CONF_VARS; 02736 02737 if ($TCA[$table]) { 02738 02739 // Prepare user defined objects (if any) for hooks which extend this function: 02740 $hookObjectsArr = array(); 02741 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'])) { 02742 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'] as $classRef) { 02743 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef); 02744 } 02745 } 02746 02747 // 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) 02748 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid')) { 02749 $uid = $lookForLiveVersion['uid']; 02750 } 02751 02752 // Get workspace version of the source record, if any: 02753 $WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid'); 02754 02755 // Initialize: 02756 $sortRow = $TCA[$table]['ctrl']['sortby']; 02757 $destPid = intval($destPid); 02758 $origDestPid = $destPid; 02759 02760 $propArr = $this->getRecordProperties($table,$uid); // Get this before we change the pid (for logging) 02761 $moveRec = $this->getRecordProperties($table,$uid,TRUE); 02762 $resolvedPid = $this->resolvePid($table,$destPid); // This is the actual pid of the moving to destination 02763 02764 // 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. 02765 // 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. 02766 if ($table!='pages' || $resolvedPid==$moveRec['pid']) { 02767 $mayMoveAccess = $this->checkRecordUpdateAccess($table,$uid); // Edit rights for the record... 02768 } else { 02769 $mayMoveAccess = $this->doesRecordExist($table,$uid,'delete'); 02770 } 02771 02772 // 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 02773 if ($table!='pages' || $resolvedPid!=$moveRec['pid']) { 02774 $mayInsertAccess = $this->checkRecordInsertAccess($table,$resolvedPid,4); // Insert rights for the record... 02775 } else { 02776 $mayInsertAccess = $this->checkRecordUpdateAccess($table,$uid); 02777 } 02778 02779 // Check workspace permissions: 02780 $workspaceAccessBlocked = array(); 02781 $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... 02782 $destRes = $this->BE_USER->workspaceAllowLiveRecordsInPID($resolvedPid,$table); 02783 // Workspace source check: 02784 if ($errorCode = $this->BE_USER->workspaceCannotEditRecord($table, $WSversion['uid'] ? $WSversion['uid'] : $uid)) { 02785 $workspaceAccessBlocked['src1']='Record could not be edited in workspace: '.$errorCode.' '; 02786 } else { 02787 if (!$recIsNewVersion && $this->BE_USER->workspaceAllowLiveRecordsInPID($moveRec['pid'],$table)<=0) { 02788 $workspaceAccessBlocked['src2']='Could not remove record from table "'.$table.'" from its page "'.$moveRec['pid'].'" '; 02789 } 02790 } 02791 // Workspace destination check: 02792 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. 02793 $workspaceAccessBlocked['dest1']='Could not insert record from table "'.$table.'" in destination PID "'.$resolvedPid.'" '; 02794 } elseif ($destRes==1 && $WSversion['uid']) { 02795 $workspaceAccessBlocked['dest2']='Could not insert other versions in destination PID '; 02796 } 02797 02798 // 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...) 02799 if (($destPid<0 && !$sortRow) || $destPid>=0) { // $destPid>=0 because we must correct pid in case of versioning "page" types. 02800 $destPid = $resolvedPid; 02801 } 02802 02803 // Timestamp field: 02804 $updateFields = array(); 02805 if ($TCA[$table]['ctrl']['tstamp']) { 02806 $updateFields[$TCA[$table]['ctrl']['tstamp']] = time(); 02807 } 02808 02809 // If moving is allowed, begin the processing: 02810 if (!count($workspaceAccessBlocked)) { 02811 if ($mayMoveAccess) { 02812 if ($destPid>=0) { // insert as first element on page (where uid = $destPid) 02813 if ($mayInsertAccess) { 02814 if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid)) { 02815 $this->clear_cache($table,$uid); // clear cache before moving 02816 02817 $updateFields['pid'] = $destPid; // Setting PID 02818 02819 // table is sorted by 'sortby' 02820 if ($sortRow) { 02821 $sortNumber = $this->getSortNumber($table,$uid,$destPid); 02822 $updateFields[$sortRow] = $sortNumber; 02823 } 02824 // Create query for update: 02825 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields); 02826 02827 // Call post processing hooks: 02828 foreach($hookObjectsArr as $hookObj) { 02829 if (method_exists($hookObj, 'moveRecord_firstElementPostProcess')) { 02830 $hookObj->moveRecord_firstElementPostProcess($table, $uid, $destPid, $moveRec, $updateFields, $this); 02831 } 02832 } 02833 02834 // Logging... 02835 $newPropArr = $this->getRecordProperties($table,$uid); 02836 $oldpagePropArr = $this->getRecordProperties('pages',$propArr['pid']); 02837 $newpagePropArr = $this->getRecordProperties('pages',$destPid); 02838 02839 if ($destPid!=$propArr['pid']) { 02840 $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 02841 $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 02842 } else { 02843 $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 02844 } 02845 $this->clear_cache($table,$uid); // clear cache after moving 02846 $this->fixUniqueInPid($table,$uid); 02847 // fixCopyAfterDuplFields 02848 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. 02849 } else { 02850 $destPropArr = $this->getRecordProperties('pages',$destPid); 02851 $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']); 02852 } 02853 } 02854 } else { // Put after another record 02855 if ($sortRow) { // table is being sorted 02856 $sortInfo = $this->getSortNumber($table,$uid,$destPid); 02857 $destPid = $sortInfo['pid']; // Setting the destPid to the new pid of the record. 02858 if (is_array($sortInfo)) { // If not an array, there was an error (which is already logged) 02859 if ($mayInsertAccess) { 02860 if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid)) { 02861 $this->clear_cache($table,$uid); // clear cache before moving 02862 02863 // We now update the pid and sortnumber 02864 $updateFields['pid'] = $destPid; 02865 $updateFields[$sortRow] = $sortInfo['sortNumber']; 02866 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields); 02867 02868 // Call post processing hooks: 02869 foreach($hookObjectsArr as $hookObj) { 02870 if (method_exists($hookObj, 'moveRecord_afterAnotherElementPostProcess')) { 02871 $hookObj->moveRecord_afterAnotherElementPostProcess($table, $uid, $destPid, $origDestPid, $moveRec, $updateFields, $this); 02872 } 02873 } 02874 02875 // Logging... 02876 $newPropArr = $this->getRecordProperties($table,$uid); 02877 $oldpagePropArr = $this->getRecordProperties('pages',$propArr['pid']); 02878 if ($destPid!=$propArr['pid']) { 02879 $newpagePropArr = $this->getRecordProperties('pages',$destPid); 02880 $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 02881 $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 02882 } else { 02883 $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 02884 } 02885 02886 // clear cache after moving 02887 $this->clear_cache($table,$uid); 02888 02889 // fixUniqueInPid 02890 $this->fixUniqueInPid($table,$uid); 02891 02892 // fixCopyAfterDuplFields 02893 if ($origDestPid<0) {$this->fixCopyAfterDuplFields($table,$uid,abs($origDestPid),1);} 02894 } else { 02895 $destPropArr = $this->getRecordProperties('pages',$destPid); 02896 $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']); 02897 } 02898 } 02899 } 02900 } else { 02901 $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']); 02902 } 02903 } 02904 } else { 02905 $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']); 02906 } 02907 } else { 02908 $this->newlog("Move attempt failed due to workspace restrictions: ".implode(' ',$workspaceAccessBlocked),1); 02909 } 02910 } 02911 } 02912 02921 function localize($table,$uid,$language) { 02922 global $TCA; 02923 02924 $uid = intval($uid); 02925 02926 if ($TCA[$table] && $uid) { 02927 t3lib_div::loadTCA($table); 02928 02929 if ($TCA[$table]['ctrl']['languageField'] && $TCA[$table]['ctrl']['transOrigPointerField']) { 02930 if ($langRec = t3lib_BEfunc::getRecord('sys_language',intval($language),'uid,title')) { 02931 if ($this->doesRecordExist($table,$uid,'show')) { 02932 02933 $row = t3lib_BEfunc::getRecordWSOL($table,$uid); // Getting workspace overlay if possible - this will localize versions in workspace if any 02934 if (is_array($row)) { 02935 if ($row[$TCA[$table]['ctrl']['languageField']] <= 0) { 02936 if ($row[$TCA[$table]['ctrl']['transOrigPointerField']] == 0) { 02937 if (!t3lib_BEfunc::getRecordsByField($table,$TCA[$table]['ctrl']['transOrigPointerField'],$uid,'AND pid='.intval($row['pid']).' AND '.$TCA[$table]['ctrl']['languageField'].'='.intval($langRec['uid']))) { 02938 02939 // Initialize: 02940 $overrideValues = array(); 02941 $excludeFields = array(); 02942 02943 // Set override values: 02944 $overrideValues[$TCA[$table]['ctrl']['languageField']] = $langRec['uid']; 02945 $overrideValues[$TCA[$table]['ctrl']['transOrigPointerField']] = $uid; 02946 02947 // Set exclude Fields: 02948 foreach($TCA[$table]['columns'] as $fN => $fCfg) { 02949 if ($fCfg['l10n_mode']=='prefixLangTitle') { // Check if we are just prefixing: 02950 if (($fCfg['config']['type']=='text' || $fCfg['config']['type']=='input') && strlen($row[$fN])) { 02951 list($tscPID) = t3lib_BEfunc::getTSCpid($table,$uid,''); 02952 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 02953 02954 if (isset($TSConfig['translateToMessage']) && strlen($TSConfig['translateToMessage'])) { 02955 $translateToMsg = @sprintf($TSConfig['translateToMessage'], $langRec['title']); 02956 } 02957 if (!strlen($translateToMsg)) { 02958 $translateToMsg = 'Translate to '.$langRec['title'].':'; 02959 } 02960 02961 $overrideValues[$fN] = '['.$translateToMsg.'] '.$row[$fN]; 02962 } 02963 } elseif (t3lib_div::inList('exclude,noCopy,mergeIfNotBlank',$fCfg['l10n_mode']) && $fN!=$TCA[$table]['ctrl']['languageField'] && $fN!=$TCA[$table]['ctrl']['transOrigPointerField']) { // Otherwise, do not copy field (unless it is the language field or pointer to the original language) 02964 $excludeFields[] = $fN; 02965 } 02966 } 02967 // Execute the copy: 02968 $this->copyRecord($table,$uid,-$uid,1,$overrideValues,implode(',',$excludeFields)); 02969 } else $this->newlog('Localization failed; There already was a localization for this language of the record!',1); 02970 } else $this->newlog('Localization failed; Source record contained a reference to an original default record (which is strange)!',1); 02971 } else $this->newlog('Localization failed; Source record had another language than "Default" or "All" defined!',1); 02972 } else $this->newlog('Attempt to localize record that did not exist!',1); 02973 } else $this->newlog('Attempt to localize record without permission',1); 02974 } else $this->newlog('Sys language UID "'.$language.'" not found valid!',1); 02975 } else $this->newlog('Localization failed; "languageField" and "transOrigPointerField" must be defined for the table!',1); 02976 } 02977 } 02978 02979 02980 02981 02982 02983 02984 02985 02986 02987 02988 02989 02990 02991 02992 /********************************************* 02993 * 02994 * Cmd: Deleting 02995 * 02996 ********************************************/ 02997 03005 function deleteAction($table, $id) { 03006 global $TCA; 03007 03008 $delRec = t3lib_BEfunc::getRecord($table, $id); 03009 03010 if (is_array($delRec)) { // Record asked to be deleted was found: 03011 03012 // For Live version, try if there is a workspace version because if so, rather "delete" that instead 03013 if ($delRec['pid']!=-1) { // Look, if record is an offline version, then delete directly: 03014 if ($wsVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $id)) { 03015 $delRec = $wsVersion; 03016 $id = $delRec['uid']; 03017 } 03018 } 03019 03020 if ($delRec['pid']==-1) { // Look, if record is an offline version, then delete directly: 03021 if ($TCA[$table]['ctrl']['versioningWS']) { 03022 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. 03023 $liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'uid,t3ver_state'); 03024 03025 if ($delRec['t3ver_wsid']==0 || (int)$liveRec['t3ver_state']!==1) { // Delete those in WS 0 + if their live records state was not "Placeholder". 03026 $this->deleteEl($table, $id); 03027 } else { // If live record was placeholder, rather clear it from workspace (because it clears both version and placeholder). 03028 $this->version_clearWSID($table,$id); 03029 } 03030 } else $this->newlog('Tried to delete record from another workspace',1); 03031 } else $this->newlog('Versioning not enabled for record with PID = -1!',2); 03032 } elseif ($res = $this->BE_USER->workspaceAllowLiveRecordsInPID($delRec['pid'], $table)) { // Look, if record is "online" or in a versionized branch, then delete directly. 03033 if ($res>0) { 03034 $this->deleteEl($table, $id); 03035 } else $this->newlog('Stage of root point did not allow for deletion',1); 03036 } else { 03037 // Otherwise, try to delete by versionization: 03038 $this->versionizeRecord($table,$id,'DELETED!',TRUE); 03039 } 03040 } 03041 } 03042 03052 function deleteEl($table, $uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE) { 03053 if ($table == 'pages') { 03054 $this->deletePages($uid, $noRecordCheck, $forceHardDelete); 03055 } else { 03056 $this->deleteRecord($table, $uid, $noRecordCheck, $forceHardDelete); 03057 } 03058 } 03059 03067 function undeleteRecord($table,$uid) { 03068 $this->deleteRecord($table,$uid,TRUE,FALSE,TRUE); 03069 } 03070 03084 function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE,$undeleteRecord=FALSE) { 03085 global $TCA; 03086 03087 $uid = intval($uid); 03088 if ($TCA[$table] && $uid) { 03089 if ($noRecordCheck || $this->doesRecordExist($table,$uid,'delete')) { 03090 $this->clear_cache($table,$uid); // clear cache before deleting the record, else the correct page cannot be identified by clear_cache 03091 03092 $propArr = $this->getRecordProperties($table, $uid); 03093 $pagePropArr = $this->getRecordProperties('pages', $propArr['pid']); 03094 03095 $deleteRow = $TCA[$table]['ctrl']['delete']; 03096 if ($deleteRow && !$forceHardDelete) { 03097 $value = $undeleteRecord ? 0 : 1; 03098 $updateFields = array( 03099 $deleteRow => $value 03100 ); 03101 03102 if ($TCA[$table]['ctrl']['tstamp']) { 03103 $updateFields[$TCA[$table]['ctrl']['tstamp']] = time(); 03104 } 03105 03106 // If the table is sorted, then the sorting number is set very high 03107 if ($TCA[$table]['ctrl']['sortby'] && !$undeleteRecord) { 03108 $updateFields[$TCA[$table]['ctrl']['sortby']] = 1000000000; 03109 } 03110 03111 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields); 03112 } else { 03113 03114 // Fetches all fields that holds references to files 03115 $fileFieldArr = $this->extFileFields($table); 03116 if (count($fileFieldArr)) { 03117 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',',$fileFieldArr), $table, 'uid='.intval($uid)); 03118 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 03119 $fArray = $fileFieldArr; 03120 foreach($fArray as $theField) { // MISSING: Support for MM file relations! 03121 $this->extFileFunctions($table,$theField,$row[$theField],'deleteAll'); // This deletes files that belonged to this record. 03122 } 03123 } else { 03124 $this->log($table,$uid,3,0,100,'Delete: Zero rows in result when trying to read filenames from record which should be deleted'); 03125 } 03126 } 03127 03128 $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($uid)); 03129 } 03130 03131 $state = $undeleteRecord ? 1 : 3; // 1 means insert, 3 means delete 03132 if (!$GLOBALS['TYPO3_DB']->sql_error()) { 03133 if ($forceHardDelete) { 03134 $message = "Record '%s' (%s) was deleted unrecoverable from page '%s' (%s)"; 03135 } 03136 else { 03137 $message = $state == 1 ? 03138 "Record '%s' (%s) was restored on page '%s' (%s)" : 03139 "Record '%s' (%s) was deleted from page '%s' (%s)"; 03140 } 03141 $this->log($table, $uid, $state, 0, 0, 03142 $message, 0, 03143 array( 03144 $propArr['header'], 03145 $table.':'.$uid, 03146 $pagePropArr['header'], 03147 $propArr['pid'] 03148 ), 03149 $propArr['pid']); 03150 03151 } else { 03152 $this->log($table,$uid,$state,0,100,$GLOBALS['TYPO3_DB']->sql_error()); 03153 } 03154 03155 // Update reference index: 03156 $this->updateRefIndex($table,$uid); 03157 03158 } else $this->log($table,$uid,3,0,1,'Attempt to delete record without delete-permissions'); 03159 } 03160 } 03161 03170 function deletePages($uid,$force=FALSE,$forceHardDelete=FALSE) { 03171 // Getting list of pages to delete: 03172 if ($force) { 03173 $brExist = $this->doesBranchExist('',$uid,0,1); // returns the branch WITHOUT permission checks (0 secures that) 03174 $res = t3lib_div::trimExplode(',',$brExist.$uid,1); 03175 } else { 03176 $res = $this->canDeletePage($uid); 03177 } 03178 03179 // Perform deletion if not error: 03180 if (is_array($res)) { 03181 foreach($res as $deleteId) { 03182 $this->deleteSpecificPage($deleteId,$forceHardDelete); 03183 } 03184 } else { 03185 $this->newlog($res,1); 03186 } 03187 } 03188 03198 function deleteSpecificPage($uid,$forceHardDelete=FALSE) { 03199 global $TCA; 03200 reset ($TCA); 03201 $uid = intval($uid); 03202 if ($uid) { 03203 while (list($table)=each($TCA)) { 03204 if ($table!='pages') { 03205 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table)); 03206 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 03207 $this->deleteRecord($table,$row['uid'], TRUE, $forceHardDelete); 03208 } 03209 } 03210 } 03211 $this->deleteRecord('pages',$uid, TRUE, $forceHardDelete); 03212 } 03213 } 03214 03221 function canDeletePage($uid) { 03222 if ($this->doesRecordExist('pages',$uid,'delete')) { // If we may at all delete this page 03223 if ($this->deleteTree) { 03224 $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1); // returns the branch 03225 if ($brExist != -1) { // Checks if we had permissions 03226 if ($this->noRecordsFromUnallowedTables($brExist.$uid)) { 03227 return t3lib_div::trimExplode(',',$brExist.$uid,1); 03228 } else return 'Attempt to delete records from disallowed tables'; 03229 } else return 'Attempt to delete pages in branch without permissions'; 03230 } else { 03231 $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1); // returns the branch 03232 if ($brExist == '') { // Checks if branch exists 03233 if ($this->noRecordsFromUnallowedTables($uid)) { 03234 return array($uid); 03235 } else return 'Attempt to delete records from disallowed tables'; 03236 } else return 'Attempt to delete page which has subpages'; 03237 } 03238 } else return 'Attempt to delete page without permissions'; 03239 } 03240 03248 function cannotDeleteRecord($table,$id) { 03249 if ($table==='pages') { 03250 $res = $this->canDeletePage($id); 03251 return is_array($res) ? FALSE : $res; 03252 } else { 03253 return $this->doesRecordExist($table,$id,'delete') ? FALSE : 'No permission to delete record'; 03254 } 03255 } 03256 03257 03258 03259 03260 03261 03262 03263 03264 03265 03266 03267 03268 /********************************************* 03269 * 03270 * Cmd: Versioning 03271 * 03272 ********************************************/ 03273 03286 function versionizeRecord($table,$id,$label,$delete=FALSE,$versionizeTree=-1) { 03287 global $TCA; 03288 03289 $id = intval($id); 03290 03291 if ($TCA[$table] && $TCA[$table]['ctrl']['versioningWS'] && $id>0) { 03292 if ($this->doesRecordExist($table,$id,'show')) { 03293 if ($this->BE_USER->workspaceVersioningTypeAccess($versionizeTree)) { 03294 03295 // Select main record: 03296 $row = $this->recordInfo($table,$id,'pid,t3ver_id'); 03297 if (is_array($row)) { 03298 if ($row['pid']>=0) { // record must be online record 03299 if (!$delete || !$this->cannotDeleteRecord($table,$id)) { 03300 03301 // Look for next version number: 03302 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 03303 't3ver_id', 03304 $table, 03305 '(t3ver_oid='.$id.' OR uid='.$id.')'.$this->deleteClause($table), 03306 '', 03307 't3ver_id DESC', 03308 '1' 03309 ); 03310 list($highestVerNumber) = $GLOBALS['TYPO3_DB']->sql_fetch_row($res); 03311 03312 // Look for version number of the current: 03313 $subVer = $row['t3ver_id'].'.'.($highestVerNumber+1); 03314 03315 // Set up the values to override when making a raw-copy: 03316 $overrideArray = array( 03317 't3ver_id' => $highestVerNumber+1, 03318 't3ver_oid' => $id, 03319 't3ver_label' => ($label ? $label : $subVer.' / '.date('d-m-Y H:m:s')), 03320 't3ver_wsid' => $this->BE_USER->workspace, 03321 't3ver_state' => $delete ? 2 : 0, 03322 't3ver_count' => 0, 03323 't3ver_stage' => 0, 03324 't3ver_tstamp' => 0 03325 ); 03326 if ($TCA[$table]['ctrl']['editlock']) { 03327 $overrideArray[$TCA[$table]['ctrl']['editlock']] = 0; 03328 } 03329 if ($table==='pages') { 03330 $overrideArray['t3ver_swapmode'] = $versionizeTree; 03331 } 03332 03333 // Checking if the record already has a version in the current workspace of the backend user 03334 $workspaceCheck = TRUE; 03335 if ($this->BE_USER->workspace!==0) { 03336 // Look for version already in workspace: 03337 $workspaceCheck = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace,$table,$id,'uid') ? FALSE : TRUE; 03338 } 03339 03340 if ($workspaceCheck) { 03341 03342 // Create raw-copy and return result: 03343 return $this->copyRecord_raw($table,$id,-1,$overrideArray); 03344 } else $this->newlog('Record you wanted to versionize was already a version in the workspace (wsid='.$this->BE_USER->workspace.')!',1); 03345 } else $this->newlog('Record cannot be deleted: '.$this->cannotDeleteRecord($table,$id),1); 03346 } else $this->newlog('Record you wanted to versionize was already a version in archive (pid=-1)!',1); 03347 } else $this->newlog('Record you wanted to versionize did not exist!',1); 03348 } else $this->newlog('The versioning type '.$versionizeTree.' mode you requested was not allowed',1); 03349 } else $this->newlog('You didnt have correct permissions to make a new version (copy) of this record "'.$table.'" / '.$id,1); 03350 } else $this->newlog('Versioning is not supported for this table "'.$table.'" / '.$id,1); 03351 } 03352 03362 function versionizePages($uid,$label,$versionizeTree) { 03363 global $TCA; 03364 03365 $uid = intval($uid); 03366 $brExist = $this->doesBranchExist('',$uid,$this->pMap['show'],1); // returns the branch 03367 03368 if ($brExist != -1) { // Checks if we had permissions 03369 03370 // Finding list of tables ALLOWED to be copied 03371 $allowedTablesArray = $this->admin ? $this->compileAdminTables() : explode(',',$this->BE_USER->groupData['tables_modify']); // These are the tables, the user may modify 03372 $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. 03373 03374 // Make list of tables that should come along with a new version of the page: 03375 $verTablesArray = array(); 03376 $allTables = array_keys($TCA); 03377 foreach($allTables as $tN) { 03378 if ($tN!='pages' && ($versionizeTree>0 || $TCA[$tN]['ctrl']['versioning_followPages']) && ($this->admin || in_array($tN, $allowedTablesArray))) { 03379 $verTablesArray[] = $tN; 03380 } 03381 } 03382 03383 // Begin to copy pages if we're allowed to: 03384 if ($this->admin || in_array('pages',$allowedTablesArray)) { 03385 if ($this->BE_USER->workspaceVersioningTypeAccess($versionizeTree)) { 03386 // Versionize this page: 03387 $theNewRootID = $this->versionizeRecord('pages',$uid,$label,FALSE,$versionizeTree); 03388 if ($theNewRootID) { 03389 $this->rawCopyPageContent($uid,$theNewRootID,$verTablesArray); 03390 03391 // If we're going to copy recursively...: 03392 if ($versionizeTree>0) { 03393 03394 // Get ALL subpages to copy (read permissions respected - they should NOT be...): 03395 $CPtable = $this->int_pageTreeInfo(Array(), $uid, intval($versionizeTree), $theNewRootID); 03396 03397 // Now copying the subpages: 03398 foreach($CPtable as $thePageUid => $thePagePid) { 03399 $newPid = $this->copyMappingArray['pages'][$thePagePid]; 03400 if (isset($newPid)) { 03401 $theNewRootID = $this->copyRecord_raw('pages',$thePageUid,$newPid); 03402 $this->rawCopyPageContent($thePageUid,$theNewRootID,$verTablesArray); 03403 } else { 03404 $this->newlog('Something went wrong during copying branch (for versioning)',1); 03405 break; 03406 } 03407 } 03408 } // else the page was not copied. Too bad... 03409 } else $this->newlog('The root version could not be created!',1); 03410 } else $this->newlog('Versioning type "'.$versionizeTree.'" was not allowed in workspace',1); 03411 } else $this->newlog('Attempt to versionize page without permission to this table',1); 03412 } else $this->newlog('Could not read all subpages to versionize.',1); 03413 } 03414 03425 function version_swap($table,$id,$swapWith,$swapIntoWS=0) { 03426 global $TCA; 03427 03428 /* 03429 Version ID swapping principles: 03430 - 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 03431 03432 uid pid uid t3ver_oid pid 03433 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) 03434 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)) 03435 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.) 03436 03437 13 is online UID, 03438 247 is specific versions UID 03439 123 is the PID of the original record 03440 -1 is the versioning repository PID 03441 03442 Recovery Process: 03443 Search for negative UID (here "-13"): 03444 YES: Step 1 completed, but at least step 3 didn't. 03445 Search for the negativ UIDs positive (here: "13") 03446 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) 03447 NO: Only Step 1 completed! Rollback: Just change uid "-13" to "13" and "t3ver_oid" to "13" (not important) 03448 NO: No problems. 03449 */ 03450 03451 // First, check if we may actually edit the online record 03452 if ($this->checkRecordUpdateAccess($table,$id)) { 03453 03454 // Select the two versions: 03455 $curVersion = t3lib_BEfunc::getRecord($table,$id,'*'); 03456 $swapVersion = t3lib_BEfunc::getRecord($table,$swapWith,'*'); 03457 03458 if (is_array($curVersion) && is_array($swapVersion)) { 03459 if ($this->BE_USER->workspacePublishAccess($swapVersion['t3ver_wsid'])) { 03460 $wsAccess = $this->BE_USER->checkWorkspace($swapVersion['t3ver_wsid']); 03461 if ($swapVersion['t3ver_wsid']<=0 || !($wsAccess['publish_access']&1) || (int)$swapVersion['t3ver_stage']===10) { 03462 if ($this->doesRecordExist($table,$swapWith,'show') && $this->checkRecordUpdateAccess($table,$swapWith)) { 03463 if (!$swapIntoWS || $this->BE_USER->workspaceSwapAccess()) { 03464 03465 // Check if the swapWith record really IS a version of the original! 03466 if ((int)$swapVersion['pid']==-1 && (int)$curVersion['pid']>=0 && !strcmp($swapVersion['t3ver_oid'],$id)) { 03467 03468 // Lock file name: 03469 $lockFileName = PATH_site.'typo3temp/swap_locking/'.$table.':'.$id.'.ser'; 03470 03471 if (!@is_file($lockFileName)) { 03472 03473 // Write lock-file: 03474 t3lib_div::writeFileToTypo3tempDir($lockFileName,serialize(array( 03475 'tstamp'=>time(), 03476 'user'=>$GLOBALS['BE_USER']->user['username'], 03477 'curVersion'=>$curVersion, 03478 'swapVersion'=>$swapVersion 03479 ))); 03480 03481 // Find fields to keep 03482 $keepFields = $this->getUniqueFields($table); 03483 if ($TCA[$table]['ctrl']['sortby']) { 03484 $keepFields[] = $TCA[$table]['ctrl']['sortby']; 03485 } 03486 03487 // Swap "keepfields" 03488 foreach($keepFields as $fN) { 03489 $tmp = $swapVersion[$fN]; 03490 $swapVersion[$fN] = $curVersion[$fN]; 03491 $curVersion[$fN] = $tmp; 03492 } 03493 03494 // Preserve states: 03495 $t3ver_state = array(); 03496 $t3ver_state['swapVersion'] = $swapVersion['t3ver_state']; 03497 $t3ver_state['curVersion'] = $curVersion['t3ver_state']; 03498 03499 // Modify offline version to become online: 03500 $tmp_wsid = $swapVersion['t3ver_wsid']; 03501 unset($swapVersion['uid']); 03502 $swapVersion['pid'] = intval($curVersion['pid']); // Set pid for ONLINE 03503 $swapVersion['t3ver_oid'] = intval($id); 03504 $swapVersion['t3ver_wsid'] = $swapIntoWS ? intval($curVersion['t3ver_wsid']) : 0; 03505 $swapVersion['t3ver_tstamp'] = time(); 03506 $swapVersion['t3ver_stage'] = $swapVersion['t3ver_state'] = 0; 03507 03508 // Modify online version to become offline: 03509 unset($curVersion['uid']); 03510 $curVersion['pid'] = -1; // Set pid for OFFLINE 03511 $curVersion['t3ver_oid'] = intval($id); 03512 $curVersion['t3ver_wsid'] = $swapIntoWS ? intval($tmp_wsid) : 0; 03513 $curVersion['t3ver_tstamp'] = time(); 03514 $curVersion['t3ver_count'] = $curVersion['t3ver_count']+1; // Increment lifecycle counter 03515 $curVersion['t3ver_stage'] = $curVersion['t3ver_state'] = 0; 03516 03517 if ($table==='pages') { // Keeping the swapmode state 03518 $curVersion['t3ver_swapmode'] = $swapVersion['t3ver_swapmode']; 03519 } 03520 03521 // Execute swapping: 03522 $sqlErrors = array(); 03523 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($id),$swapVersion); 03524 if ($GLOBALS['TYPO3_DB']->sql_error()) { 03525 $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error(); 03526 } else { 03527 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($swapWith),$curVersion); 03528 if ($GLOBALS['TYPO3_DB']->sql_error()) { 03529 $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03530 } else { 03531 unlink($lockFileName); 03532 } 03533 } 03534 03535 if (!count($sqlErrors)) { 03536 03537 // Checking for delete: 03538 if ($t3ver_state['swapVersion']==2) { 03539 $this->deleteEl($table,$id,TRUE); // Force delete 03540 } 03541 03542 $this->newlog('Swapping successful for table "'.$table.'" uid '.$id.'=>'.$swapWith); 03543 03544 // Update reference index: 03545 $this->updateRefIndex($table,$id); 03546 $this->updateRefIndex($table,$swapWith); 03547 03548 // SWAPPING pids for subrecords: 03549 if ($table=='pages' && $swapVersion['t3ver_swapmode']>=0) { 03550 03551 // Collect table names that should be copied along with the tables: 03552 foreach($TCA as $tN => $tCfg) { 03553 if ($swapVersion['t3ver_swapmode']>0 || $TCA[$tN]['ctrl']['versioning_followPages']) { // For "Branch" publishing swap ALL, otherwise for "page" publishing, swap only "versioning_followPages" tables 03554 $temporaryPid = -($id+1000000); 03555 03556 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($id),array('pid'=>$temporaryPid)); 03557 if ($GLOBALS['TYPO3_DB']->sql_error()) $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03558 03559 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($swapWith),array('pid'=>$id)); 03560 if ($GLOBALS['TYPO3_DB']->sql_error()) $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03561 03562 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($temporaryPid),array('pid'=>$swapWith)); 03563 if ($GLOBALS['TYPO3_DB']->sql_error()) $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03564 03565 if (count($sqlErrors)) { 03566 $this->newlog('During Swapping: SQL errors happend: '.implode('; ',$sqlErrors),2); 03567 } 03568 } 03569 } 03570 } 03571 // Clear cache: 03572 $this->clear_cache($table,$id); 03573 03574 // Checking for "new-placeholder" and if found, delete it (BUT FIRST after swapping!): 03575 if ($t3ver_state['curVersion']==1) { 03576 $this->deleteEl($table, $swapWith, TRUE, TRUE); // For delete + completely delete! 03577 } 03578 } else $this->newlog('During Swapping: SQL errors happend: '.implode('; ',$sqlErrors),2); 03579 } 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); 03580 } 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); 03581 } else $this->newlog('Workspace #'.$swapVersion['t3ver_wsid'].' does not support swapping.',1); 03582 } else $this->newlog('You cannot publish a record you do not have edit and show permissions for',1); 03583 } else $this->newlog('Records in workspace #'.$swapVersion['t3ver_wsid'].' can only be published when in "Publish" stage.',1); 03584 } else $this->newlog('User could not publish records from workspace #'.$swapVersion['t3ver_wsid'],1); 03585 } else $this->newlog('Error: Either online or swap version could not be selected!',2); 03586 } else $this->newlog('Error: You cannot swap versions for a record you do not have access to edit!',1); 03587 } 03588 03596 function version_clearWSID($table,$id) { 03597 if ($errorCode = $this->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) { 03598 $this->newlog('Attempt to reset workspace for record failed: '.$errorCode,1); 03599 } elseif ($this->checkRecordUpdateAccess($table,$id)) { 03600 if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'uid,t3ver_state')) { 03601 // Clear workspace ID: 03602 $sArray = array(); 03603 $sArray['t3ver_wsid'] = 0; 03604 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($id),$sArray); 03605 03606 // Clear workspace ID for live version AND DELETE IT as well because it is a new record! 03607 if ((int)$liveRec['t3ver_state']===1) { 03608 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($liveRec['uid']),$sArray); 03609 $this->deleteEl($table, $liveRec['uid'], TRUE); // THIS assumes that the record was placeholder ONLY for ONE record (namely $id) 03610 } 03611 03612 // 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. 03613 $wsRec = t3lib_BEfunc::getRecord($table,$id); 03614 if ((int)$wsRec['t3ver_state']===2) { 03615 $this->deleteEl($table, $id, TRUE, TRUE); 03616 } 03617 } 03618 } else $this->newlog('Attempt to reset workspace for record failed because you do not have edit access',1); 03619 } 03620 03630 function version_setStage($table,$id,$stageId,$comment='') { 03631 if ($errorCode = $this->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) { 03632 $this->newlog('Attempt to set stage for record failed: '.$errorCode,1); 03633 } elseif ($this->checkRecordUpdateAccess($table,$id)) { 03634 $stat = $this->BE_USER->checkWorkspaceCurrent(); 03635 if (t3lib_div::inList('admin,online,offline,reviewer,owner', $stat['_ACCESS']) || ($stageId<=1 && $stat['_ACCESS']==='member')) { 03636 03637 // Set stage of record: 03638 $sArray = array(); 03639 $sArray['t3ver_stage'] = $stageId; 03640 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($id), $sArray); 03641 $this->newlog('Stage for record was changed to '.$stageId.'. Comment was: "'.substr($comment,0,100).'"'); 03642 // TEMPORARY, except 6-30 as action/detail number which is observed elsewhere! 03643 $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stage'=>$stageId)); 03644 03645 if ((int)$stat['stagechg_notification']>0) { 03646 $this->notifyStageChange($stat,$stageId,$table,$id,$comment); 03647 } 03648 } else $this->newlog('The member user tried to set a stage value "'.$stageId.'" that was not allowed',1); 03649 } else $this->newlog('Attempt to set stage for record failed because you do not have edit access',1); 03650 } 03651 03652 03653 03654 03655 03656 03657 03658 03659 03660 03661 03662 03663 03664 /********************************************* 03665 * 03666 * Cmd: Helper functions 03667 * 03668 ********************************************/ 03669 03675 function remapListedDBRecords() { 03676 global $TCA; 03677 03678 if (count($this->registerDBList)) { 03679 reset($this->registerDBList); 03680 while(list($table,$records)=each($this->registerDBList)) { 03681 t3lib_div::loadTCA($table); 03682 reset($records); 03683 while(list($uid,$fields)=each($records)) { 03684 $newData = array(); 03685 $theUidToUpdate = $this->copyMappingArray_merged[$table][$uid]; 03686 $theUidToUpdate_saveTo = t3lib_BEfunc::wsMapId($table,$theUidToUpdate); 03687 03688 foreach($fields as $fieldName => $value) { 03689 $conf = $TCA[$table]['columns'][$fieldName]['config']; 03690 03691 switch($conf['type']) { 03692 case 'group': 03693 case 'select': 03694 $vArray = $this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate); 03695 if (is_array($vArray)) { 03696 $newData[$fieldName] = implode(',',$vArray); 03697 } 03698 break; 03699 case 'flex': 03700 if ($value=='FlexForm_reference') { 03701 $origRecordRow = $this->recordInfo($table,$theUidToUpdate,'*'); // This will fetch the new row for the element 03702 03703 if (is_array($origRecordRow)) { 03704 t3lib_BEfunc::workspaceOL($table,$origRecordRow); 03705 03706 // Get current data structure and value array: 03707 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $origRecordRow, $table); 03708 $currentValueArray = t3lib_div::xml2array($origRecordRow[$fieldName]); 03709 03710 // Do recursive processing of the XML data: 03711 $currentValueArray['data'] = $this->checkValue_flex_procInData( 03712 $currentValueArray['data'], 03713 array(), // Not used. 03714 array(), // Not used. 03715 $dataStructArray, 03716 array($table,$theUidToUpdate,$fieldName), // Parameters. 03717 'remapListedDBRecords_flexFormCallBack' 03718 ); 03719 03720 // The return value should be compiled back into XML, ready to insert directly in the field (as we call updateDB() directly later): 03721 if (is_array($currentValueArray['data'])) { 03722 $newData[$fieldName] = 03723 $this->checkValue_flexArray2Xml($currentValueArray,TRUE); 03724 } 03725 } 03726 } 03727 break; 03728 default: 03729 debug('Field type should not appear here: '. $conf['type']); 03730 break; 03731 } 03732 } 03733 03734 if (count($newData)) { // If any fields were changed, those fields are updated! 03735 $this->updateDB($table,$theUidToUpdate_saveTo,$newData); 03736 } 03737 } 03738 } 03739 } 03740 } 03741 03753 function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2) { 03754 03755 // Extract parameters: 03756 list($table,$uid,$field) = $pParams; 03757 03758 // If references are set for this field, set flag so they can be corrected later: 03759 if ($this->isReferenceField($dsConf) && strlen($dataValue)) { 03760 $vArray = $this->remapListedDBRecords_procDBRefs($dsConf, $dataValue, $uid); 03761 if (is_array($vArray)) { 03762 $dataValue = implode(',',$vArray); 03763 } 03764 } 03765 03766 // Return 03767 return array('value' => $dataValue); 03768 } 03769 03779 function remapListedDBRecords_procDBRefs($conf, $value, $MM_localUid) { 03780 03781 // Initialize variables 03782 $set = FALSE; // Will be set true if an upgrade should be done... 03783 $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table']; // Allowed tables for references. 03784 $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : ''; // Table name to prepend the UID 03785 $dontRemapTables = t3lib_div::trimExplode(',',$conf['dontRemapTablesOnCopy'],1); // Which tables that should possibly not be remapped 03786 03787 // Convert value to list of references: 03788 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 03789 $dbAnalysis->registerNonTableValues = ($conf['type']=='select' && $conf['allowNonIdValues']) ? 1 : 0; 03790 $dbAnalysis->start($value, $allowedTables, $conf['MM'], $MM_localUid); 03791 03792 // Traverse those references and map IDs: 03793 foreach($dbAnalysis->itemArray as $k => $v) { 03794 $mapID = $this->copyMappingArray_merged[$v['table']][$v['id']]; 03795 if ($mapID && !in_array($v['table'],$dontRemapTables)) { 03796 $dbAnalysis->itemArray[$k]['id'] = $mapID; 03797 $set = TRUE; 03798 } 03799 } 03800 03801 // If a change has been done, set the new value(s) 03802 if ($set) { 03803 if ($conf['MM']) { 03804 // FIXME $theUidToUpdate is undefined 03805 $dbAnalysis->writeMM($conf['MM'], $theUidToUpdate, $prependName); 03806 } else { 03807 $vArray = $dbAnalysis->getValueArray($prependName); 03808 if ($conf['type']=='select') { 03809 $vArray = $dbAnalysis->convertPosNeg($vArray, $conf['foreign_table'], $conf['neg_foreign_table']); 03810 } 03811 return $vArray; 03812 } 03813 } 03814 } 03815 03816 03817 03818 03819 03820 03821 03822 03823 03824 03825 03826 03827 03828 03829 03830 03831 03832 /***************************** 03833 * 03834 * Access control / Checking functions 03835 * 03836 *****************************/ 03837 03844 function checkModifyAccessList($table) { 03845 $res = ($this->admin || (!$this->tableAdminOnly($table) && t3lib_div::inList($this->BE_USER->groupData['tables_modify'],$table))); 03846 return $res; 03847 } 03848 03856 function isRecordInWebMount($table,$id) { 03857 if (!isset($this->isRecordInWebMount_Cache[$table.':'.$id])) { 03858 $recP=$this->getRecordProperties($table,$id); 03859 $this->isRecordInWebMount_Cache[$table.':'.$id]=$this->isInWebMount($recP['event_pid']); 03860 } 03861 return $this->isRecordInWebMount_Cache[$table.':'.$id]; 03862 } 03863 03870 function isInWebMount($pid) { 03871 if (!isset($this->isInWebMount_Cache[$pid])) { 03872 $this->isInWebMount_Cache[$pid]=$this->BE_USER->isInWebMount($pid); 03873 } 03874 return $this->isInWebMount_Cache[$pid]; 03875 } 03876 03884 function checkRecordUpdateAccess($table,$id) { 03885 global $TCA; 03886 $res = 0; 03887 if ($TCA[$table] && intval($id)>0) { 03888 if (isset($this->recUpdateAccessCache[$table][$id])) { // If information is cached, return it 03889 return $this->recUpdateAccessCache[$table][$id]; 03890 // Check if record exists and 1) if 'pages' the page may be edited, 2) if page-content the page allows for editing 03891 } elseif ($this->doesRecordExist($table,$id,'edit')) { 03892 $res = 1; 03893 } 03894 $this->recUpdateAccessCache[$table][$id]=$res; // Cache the result 03895 } 03896 return $res; 03897 } 03898 03908 function checkRecordInsertAccess($insertTable,$pid,$action=1) { 03909 global $TCA; 03910 03911 $res = 0; 03912 $pid = intval($pid); 03913 if ($pid>=0) { 03914 if (isset($this->recInsertAccessCache[$insertTable][$pid])) { // If information is cached, return it 03915 return $this->recInsertAccessCache[$insertTable][$pid]; 03916 } else { 03917 // 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 03918 if ( (!$pid && $this->admin) || $this->doesRecordExist('pages',$pid,($insertTable=='pages'?$this->pMap['new']:$this->pMap['editcontent'])) ) { // Check permissions 03919 if ($this->isTableAllowedForThisPage($pid, $insertTable)) { 03920 $res = 1; 03921 $this->recInsertAccessCache[$insertTable][$pid]=$res; // Cache the result 03922 } else { 03923 $propArr = $this->getRecordProperties('pages',$pid); 03924 $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']); 03925 } 03926 } else { 03927 $propArr = $this->getRecordProperties('pages',$pid); 03928 $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']); 03929 } 03930 } 03931 } 03932 return $res; 03933 } 03934 03942 function isTableAllowedForThisPage($page_uid, $checkTable) { 03943 global $TCA, $PAGES_TYPES; 03944 $page_uid = intval($page_uid); 03945 03946 // 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. 03947 if (($TCA[$checkTable]['ctrl']['rootLevel'] xor !$page_uid) && $TCA[$checkTable]['ctrl']['rootLevel']!=-1 && $checkTable!='pages') { 03948 return false; 03949 } 03950 03951 // Check root-level 03952 if (!$page_uid) { 03953 if ($this->admin) { 03954 return true; 03955 } 03956 } else { 03957 // Check non-root-level 03958 $doktype = $this->pageInfo($page_uid,'doktype'); 03959 $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables']; 03960 $allowedArray = t3lib_div::trimExplode(',',$allowedTableList,1); 03961 if (strstr($allowedTableList,'*') || in_array($checkTable,$allowedArray)) { // If all tables or the table is listed as a allowed type, return true 03962 return true; 03963 } 03964 } 03965 } 03966 03975 function doesRecordExist($table,$id,$perms) { 03976 global $TCA; 03977 03978 $res = 0; 03979 $id = intval($id); 03980 03981 // Processing the incoming $perms (from possible string to integer that can be AND'ed) 03982 if (!t3lib_div::testInt($perms)) { 03983 if ($table!='pages') { 03984 switch($perms) { 03985 case 'edit': 03986 case 'delete': 03987 case 'new': 03988 $perms = 'editcontent'; // This holds it all in case the record is not page!! 03989 break; 03990 } 03991 } 03992 $perms = intval($this->pMap[$perms]); 03993 } else { 03994 $perms = intval($perms); 03995 } 03996 03997 if (!$perms) {die('Internal ERROR: no permissions to check for non-admin user.');} 03998 03999 // For all tables: Check if record exists: 04000 if (is_array($TCA[$table]) && $id>0 && ($this->isRecordInWebMount($table,$id) || $this->admin)) { 04001 if ($table != 'pages') { 04002 04003 // Find record without checking page: 04004 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid', $table, 'uid='.intval($id).$this->deleteClause($table)); // THIS SHOULD CHECK FOR editlock I think! 04005 $output = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres); 04006 t3lib_BEfunc::fixVersioningPid($table,$output,TRUE); 04007 04008 // If record found, check page as well: 04009 if (is_array($output)) { 04010 04011 // Looking up the page for record: 04012 $mres = $this->doesRecordExist_pageLookUp($output['pid'], $perms); 04013 $pageRec = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres); 04014 // 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): 04015 if (is_array($pageRec) || (!$output['pid'] && $this->admin)) { 04016 return TRUE; 04017 } 04018 } 04019 return FALSE; 04020 } else { 04021 $mres = $this->doesRecordExist_pageLookUp($id, $perms); 04022 return $GLOBALS['TYPO3_DB']->sql_num_rows($mres); 04023 } 04024 } 04025 } 04026 04036 function doesRecordExist_pageLookUp($id, $perms) { 04037 global $TCA; 04038 04039 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 04040 'uid', 04041 'pages', 04042 'uid='.intval($id). 04043 $this->deleteClause('pages'). 04044 ($perms && !$this->admin ? ' AND '.$this->BE_USER->getPagePermsClause($perms) : ''). 04045 (!$this->admin && $TCA['pages']['ctrl']['editlock'] && ($perms & (2+4+16)) ? ' AND '.$TCA['pages']['ctrl']['editlock'].'=0':'') // admin users don't need check 04046 ); 04047 } 04048 04062 function doesBranchExist($inList,$pid,$perms,$recurse) { 04063 global $TCA; 04064 $pid = intval($pid); 04065 $perms = intval($perms); 04066 04067 if ($pid>=0) { 04068 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 04069 'uid, perms_userid, perms_groupid, perms_user, perms_group, perms_everybody', 04070 'pages', 04071 'pid='.intval($pid).$this->deleteClause('pages'), 04072 '', 04073 'sorting' 04074 ); 04075 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 04076 if ($this->admin || $this->BE_USER->doesUserHaveAccess($row,$perms)) { // IF admin, then it's OK 04077 $inList.=$row['uid'].','; 04078 if ($recurse) { // Follow the subpages recursively... 04079 $inList = $this->doesBranchExist($inList, $row['uid'], $perms, $recurse); 04080 if ($inList == -1) {return -1;} // No permissions somewhere in the branch 04081 } 04082 } else { 04083 return -1; // No permissions 04084 } 04085 } 04086 } 04087 return $inList; 04088 } 04089 04096 function tableReadOnly($table) { 04097 // returns true if table is readonly 04098 global $TCA; 04099 return ($TCA[$table]['ctrl']['readOnly'] ? 1 : 0); 04100 } 04101 04108 function tableAdminOnly($table) { 04109 // returns true if table is admin-only 04110 global $TCA; 04111 return ($TCA[$table]['ctrl']['adminOnly'] ? 1 : 0); 04112 } 04113 04122 function destNotInsideSelf($dest,$id) { 04123 $loopCheck = 100; 04124 $dest = intval($dest); 04125 $id = intval($id); 04126 04127 if ($dest==$id) { 04128 return FALSE; 04129 } 04130 04131 while ($dest!=0 && $loopCheck>0) { 04132 $loopCheck--; 04133 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid, uid, t3ver_oid,t3ver_wsid', 'pages', 'uid='.intval($dest).$this->deleteClause('pages')); 04134 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 04135 t3lib_BEfunc::fixVersioningPid('pages',$row); 04136 if ($row['pid']==$id) { 04137 return FALSE; 04138 } else { 04139 $dest = $row['pid']; 04140 } 04141 } else { 04142 return FALSE; 04143 } 04144 } 04145 return TRUE; 04146 } 04147 04154 function getExcludeListArray() { 04155 global $TCA; 04156 04157 $list = array(); 04158 reset($TCA); 04159 while (list($table)=each($TCA)) { 04160 t3lib_div::loadTCA($table); 04161 while (list($field,$config)=each($TCA[$table]['columns'])) { 04162 if ($config['exclude'] && !t3lib_div::inList($this->BE_USER->groupData['non_exclude_fields'],$table.':'.$field)) { 04163 $list[]=$table.'-'.$field; 04164 } 04165 } 04166 } 04167 return $list; 04168 } 04169 04177 function doesPageHaveUnallowedTables($page_uid,$doktype) { 04178 global $TCA, $PAGES_TYPES; 04179 04180 $page_uid = intval($page_uid); 04181 if (!$page_uid) { 04182 return FALSE; // Not a number. Probably a new page 04183 } 04184 04185 $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables']; 04186 $allowedArray = t3lib_div::trimExplode(',',$allowedTableList,1); 04187 if (strstr($allowedTableList,'*')) { // If all tables is OK the return true 04188 return FALSE; // OK... 04189 } 04190 04191 reset ($TCA); 04192 $tableList = array(); 04193 while (list($table)=each($TCA)) { 04194 if (!in_array($table,$allowedArray)) { // If the table is not in the allowed list, check if there are records... 04195 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid='.intval($page_uid)); 04196 $count = $GLOBALS['TYPO3_DB']->sql_fetch_row($mres); 04197 if ($count[0]) { 04198 $tableList[]=$table; 04199 } 04200 } 04201 } 04202 return implode(',',$tableList); 04203 } 04204 04205 04206 04207 04208 04209 04210 04211 04212 /***************************** 04213 * 04214 * Information lookup 04215 * 04216 *****************************/ 04217 04226 function pageInfo($id,$field) { 04227 if (!isset($this->pageCache[$id])) { 04228 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'pages', 'uid='.intval($id)); 04229 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 04230 $this->pageCache[$id] = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 04231 } 04232 $GLOBALS['TYPO3_DB']->sql_free_result($res); 04233 } 04234 return $this->pageCache[$id][$field]; 04235 } 04236 04246 function recordInfo($table,$id,$fieldList) { 04247 global $TCA; 04248 if (is_array($TCA[$table])) { 04249 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fieldList, $table, 'uid='.intval($id)); 04250 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 04251 return $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 04252 } 04253 } 04254 } 04255 04267 function getRecordProperties($table,$id,$noWSOL=FALSE) { 04268 $row = ($table=='pages' && !$id) ? array('title'=>'[root-level]', 'uid' => 0, 'pid' => 0) :$this->recordInfo($table,$id,'*'); 04269 if (!$noWSOL) { 04270 t3lib_BEfunc::workspaceOL($table,$row); 04271 } 04272 t3lib_BEfunc::fixVersioningPid($table,$row); 04273 return $this->getRecordPropertiesFromRow($table,$row); 04274 } 04275 04283 function getRecordPropertiesFromRow($table,$row) { 04284 global $TCA; 04285 if ($TCA[$table]) { 04286 $out = array( 04287 'header' => $row[$TCA[$table]['ctrl']['label']], 04288 'pid' => $row['pid'], 04289 'event_pid' => ($table=='pages'?$row['uid']:$row['pid']), 04290 't3ver_state' => $TCA[$table]['ctrl']['versioningWS'] ? $row['t3ver_state'] : '', 04291 '_ORIG_pid' => $row['_ORIG_pid'] 04292 ); 04293 return $out; 04294 } 04295 } 04296 04297 04298 04299 04300 04301 04302 04303 04304 04305 04306 04307 04308 04309 04310 04311 /********************************************* 04312 * 04313 * Storing data to Database Layer 04314 * 04315 ********************************************/ 04316 04326 function updateDB($table,$id,$fieldArray) { 04327 global $TCA; 04328 04329 if (is_array($fieldArray) && is_array($TCA[$table]) && intval($id)) { 04330 unset($fieldArray['uid']); // Do NOT update the UID field, ever! 04331 04332 if (count($fieldArray)) { 04333 04334 // Execute the UPDATE query: 04335 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($id), $fieldArray); 04336 04337 // If succees, do...: 04338 if (!$GLOBALS['TYPO3_DB']->sql_error()) { 04339 04340 if ($this->checkStoredRecords) { 04341 $newRow = $this->checkStoredRecord($table,$id,$fieldArray,2); 04342 } 04343 04344 // Update reference index: 04345 $this->updateRefIndex($table,$id); 04346 04347 // Set log entry: 04348 $propArr = $this->getRecordPropertiesFromRow($table,$newRow); 04349 $theLogId = $this->log($table,$id,2,$propArr['pid'],0,"Record '%s' (%s) was updated.",10,array($propArr['header'],$table.':'.$id),$propArr['event_pid']); 04350 04351 // Set History data: 04352 $this->setHistory($table,$id,$theLogId); 04353 04354 // Clear cache for relevant pages: 04355 $this->clear_cache($table,$id); 04356 04357 // Unset the pageCache for the id if table was page. 04358 if ($table=='pages') unset($this->pageCache[$id]); 04359 } else { 04360 $this->log($table,$id,2,0,2,"SQL error: '%s' (%s)",12,array($GLOBALS['TYPO3_DB']->sql_error(),$table.':'.$id)); 04361 } 04362 } 04363 } 04364 } 04365 04378 function insertDB($table,$id,$fieldArray,$newVersion=FALSE,$suggestedUid=0,$dontSetNewIdIndex=FALSE) { 04379 global $TCA; 04380 04381 if (is_array($fieldArray) && is_array($TCA[$table]) && isset($fieldArray['pid'])) { 04382 unset($fieldArray['uid']); // Do NOT insert the UID field, ever! 04383 04384 if (count($fieldArray)) { 04385 04386 // Check for "suggestedUid". 04387 // This feature is used by the import functionality to force a new record to have a certain UID value. 04388 // This is only recommended for use when the destination server is a passive mirrow of another server. 04389 // As a security measure this feature is available only for Admin Users (for now) 04390 $suggestedUid = intval($suggestedUid); 04391 if ($this->BE_USER->isAdmin() && $suggestedUid && $this->suggestedInsertUids[$table.':'.$suggestedUid]) { 04392 // When the value of ->suggestedInsertUids[...] is "DELETE" it will try to remove the previous record 04393 if ($this->suggestedInsertUids[$table.':'.$suggestedUid]==='DELETE') { 04394 // DELETE: 04395 $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($suggestedUid)); 04396 } 04397 $fieldArray['uid'] = $suggestedUid; 04398 } 04399 04400 // Execute the INSERT query: 04401 $GLOBALS['TYPO3_DB']->exec_INSERTquery($table, $fieldArray); 04402 04403 // If succees, do...: 04404 if (!$GLOBALS['TYPO3_DB']->sql_error()) { 04405 04406 // Set mapping for NEW... -> real uid: 04407 $NEW_id = $id; // the NEW_id now holds the 'NEW....' -id 04408 $id = $GLOBALS['TYPO3_DB']->sql_insert_id(); 04409 if (!$dontSetNewIdIndex) { 04410 $this->substNEWwithIDs[$NEW_id] = $id; 04411 $this->substNEWwithIDs_table[$NEW_id] = $table; 04412 } 04413 04414 // Checking the record is properly saved and writing to log 04415 if ($this->checkStoredRecords) { 04416 $newRow = $this->checkStoredRecord($table,$id,$fieldArray,1); 04417 } 04418 04419 // Update reference index: 04420 $this->updateRefIndex($table,$id); 04421 04422 if ($newVersion) { 04423 $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); 04424 } else { 04425 $propArr = $this->getRecordPropertiesFromRow($table,$newRow); 04426 $page_propArr = $this->getRecordProperties('pages',$propArr['pid']); 04427 $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); 04428 04429 // Clear cache for relavant pages: 04430 $this->clear_cache($table,$id); 04431 } 04432 04433 return $id; 04434 } else { 04435 $this->log($table,$id,1,0,2,"SQL error: '%s' (%s)",12,array($GLOBALS['TYPO3_DB']->sql_error(),$table.':'.$id)); 04436 } 04437 } 04438 } 04439 } 04440 04451 function checkStoredRecord($table,$id,$fieldArray,$action) { 04452 global $TCA; 04453 04454 $id = intval($id); 04455 if (is_array($TCA[$table]) && $id) { 04456 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid='.intval($id)); 04457 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 04458 // Traverse array of values that was inserted into the database and compare with the actually stored value: 04459 $errorString = array(); 04460 foreach($fieldArray as $key => $value) { 04461 if ($this->checkStoredRecords_loose && !$value && !$row[$key]) { 04462 // Nothing... 04463 } elseif (strcmp($value,$row[$key])) { 04464 $errorString[] = $key; 04465 } 04466 } 04467 04468 // Set log message if there were fields with unmatching values: 04469 if (count($errorString)) { 04470 $this->log($table,$id,$action,0,102,'These fields are not properly updated in database: ('.implode(',',$errorString).') Probably value mismatch with fieldtype.'); 04471 } 04472 04473 // Return selected rows: 04474 return $row; 04475 } 04476 $GLOBALS['TYPO3_DB']->sql_free_result($res); 04477 } 04478 } 04479 04488 function setHistory($table,$id,$logId) { 04489 if (isset($this->historyRecords[$table.':'.$id])) { 04490 04491 // Initialize settings: 04492 list($tscPID) = t3lib_BEfunc::getTSCpid($table,$id,''); 04493 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 04494 04495 $tE = $this->getTableEntries($table,$TSConfig); 04496 $maxAgeSeconds = 60*60*24*(strcmp($tE['history.']['maxAgeDays'],'') ? t3lib_div::intInRange($tE['history.']['maxAgeDays'],0,365) : 30); // one month 04497 04498 // Garbage collect old entries: 04499 $this->clearHistory($maxAgeSeconds, $table); 04500 04501 // Set history data: 04502 $fields_values = array(); 04503 $fields_values['history_data'] = serialize($this->historyRecords[$table.':'.$id]); 04504 $fields_values['fieldlist'] = implode(',',array_keys($this->historyRecords[$table.':'.$id]['newRecord'])); 04505 $fields_values['tstamp'] = time(); 04506 $fields_values['tablename'] = $table; 04507 $fields_values['recuid'] = $id; 04508 $fields_values['sys_log_uid'] = $logId; 04509 04510 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_history', $fields_values); 04511 } 04512 } 04513 04521 function clearHistory($maxAgeSeconds=604800,$table) { 04522 $tstampLimit = $maxAgeSeconds ? time()-$maxAgeSeconds : 0; 04523 04524 $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_history', 'tstamp<'.intval($tstampLimit).' AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history')); 04525 } 04526 04535 function updateRefIndex($table,$id) { 04536 $refIndexObj = t3lib_div::makeInstance('t3lib_refindex'); 04537 $result = $refIndexObj->updateRefIndexTable($table,$id); 04538 } 04539 04540 04541 04542 04543 04544 04545 04546 04547 04548 04549 04550 04551 04552 /********************************************* 04553 * 04554 * Misc functions 04555 * 04556 ********************************************/ 04557 04567 function getSortNumber($table,$uid,$pid) { 04568 global $TCA; 04569 if ($TCA[$table] && $TCA[$table]['ctrl']['sortby']) { 04570 $sortRow = $TCA[$table]['ctrl']['sortby']; 04571 if ($pid>=0) { // Sorting number is in the top 04572 $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 04573 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { // There was an element 04574 if ($row['uid']==$uid) { // The top record was the record it self, so we return its current sortnumber 04575 return $row[$sortRow]; 04576 } 04577 if ($row[$sortRow] < 1) { // If the pages sortingnumber < 1 we must resort the records under this pid 04578 $this->resorting($table,$pid,$sortRow,0); 04579 return $this->sortIntervals; // First sorting number after resorting 04580 } else { 04581 return floor($row[$sortRow]/2); // Sorting number between current top element and zero 04582 } 04583 } else { // No pages, so we choose the default value as sorting-number 04584 return $this->sortIntervals; // First sorting number if no elements. 04585 } 04586 } else { // Sorting number is inside the list 04587 $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 04588 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { // There was a record 04589 04590 // 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. 04591 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$row['uid'],$sortRow.',pid,uid')) { 04592 $row = $lookForLiveVersion; 04593 } 04594 04595 // If the record happends to be it self 04596 if ($row['uid']==$uid) { 04597 $sortNumber = $row[$sortRow]; 04598 } else { 04599 $subres = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 04600 $sortRow.',pid,uid', 04601 $table, 04602 'pid='.intval($row['pid']).' AND '.$sortRow.'>='.intval($row[$sortRow]).$this->deleteClause($table), 04603 '', 04604 $sortRow.' ASC', 04605 '2' 04606 ); // Fetches the next record in order to calculate the in between sortNumber 04607 if ($GLOBALS['TYPO3_DB']->sql_num_rows($subres)==2) { // There was a record afterwards 04608 $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres); // Forward to the second result... 04609 $subrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres); // There was a record afterwards 04610 $sortNumber = $row[$sortRow]+ floor(($subrow[$sortRow]-$row[$sortRow])/2); // The sortNumber is found in between these values 04611 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 04612 $sortNumber = $this->resorting($table,$row['pid'],$sortRow, $row['uid']); // By this special param, resorting reserves and returns the sortnumber after the uid 04613 } 04614 } else { // If after the last record in the list, we just add the sortInterval to the last sortvalue 04615 $sortNumber = $row[$sortRow]+$this->sortIntervals; 04616 } 04617 } 04618 return Array('pid' => $row['pid'], 'sortNumber' => $sortNumber); 04619 } else { 04620 $propArr = $this->getRecordProperties($table,$uid); 04621 $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... 04622 return false; // There MUST be a page or else this cannot work 04623 } 04624 } 04625 } 04626 } 04627 04640 function resorting($table,$pid,$sortRow, $return_SortNumber_After_This_Uid) { 04641 global $TCA; 04642 if ($TCA[$table] && $sortRow && $TCA[$table]['ctrl']['sortby']==$sortRow) { 04643 $returnVal = 0; 04644 $intervals = $this->sortIntervals; 04645 $i = $intervals*2; 04646 04647 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($pid).$this->deleteClause($table), '', $sortRow.' ASC'); 04648 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 04649 $uid=intval($row['uid']); 04650 if ($uid) { 04651 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), array($sortRow=>$i)); 04652 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 04653 $i = $i+$intervals; 04654 $returnVal=$i; 04655 } 04656 } else {die ('Fatal ERROR!! No Uid at resorting.');} 04657 $i = $i+$intervals; 04658 } 04659 return $returnVal; 04660 } 04661 } 04662 04671 function setTSconfigPermissions($fieldArray,$TSConfig_p) { 04672 if (strcmp($TSConfig_p['userid'],'')) $fieldArray['perms_userid']=intval($TSConfig_p['userid']); 04673 if (strcmp($TSConfig_p['groupid'],'')) $fieldArray['perms_groupid']=intval($TSConfig_p['groupid']); 04674 if (strcmp($TSConfig_p['user'],'')) $fieldArray['perms_user']=t3lib_div::testInt($TSConfig_p['user']) ? $TSConfig_p['user'] : $this->assemblePermissions($TSConfig_p['user']); 04675 if (strcmp($TSConfig_p['group'],'')) $fieldArray['perms_group']=t3lib_div::testInt($TSConfig_p['group']) ? $TSConfig_p['group'] : $this->assemblePermissions($TSConfig_p['group']); 04676 if (strcmp($TSConfig_p['everybody'],'')) $fieldArray['perms_everybody']=t3lib_div::testInt($TSConfig_p['everybody']) ? $TSConfig_p['everybody'] : $this->assemblePermissions($TSConfig_p['everybody']); 04677 04678 return $fieldArray; 04679 } 04680 04688 function newFieldArray($table) { 04689 global $TCA; 04690 04691 t3lib_div::loadTCA($table); 04692 $fieldArray=Array(); 04693 if (is_array($TCA[$table]['columns'])) { 04694 reset ($TCA[$table]['columns']); 04695 while (list($field,$content)=each($TCA[$table]['columns'])) { 04696 if (isset($this->defaultValues[$table][$field])) { 04697 $fieldArray[$field] = $this->defaultValues[$table][$field]; 04698 } elseif (isset($content['config']['default'])) { 04699 $fieldArray[$field] = $content['config']['default']; 04700 } 04701 } 04702 } 04703 if ($table==='pages') { // Set default permissions for a page. 04704 $fieldArray['perms_userid'] = $this->userid; 04705 $fieldArray['perms_groupid'] = intval($this->BE_USER->firstMainGroup); 04706 $fieldArray['perms_user'] = $this->assemblePermissions($this->defaultPermissions['user']); 04707 $fieldArray['perms_group'] = $this->assemblePermissions($this->defaultPermissions['group']); 04708 $fieldArray['perms_everybody'] = $this->assemblePermissions($this->defaultPermissions['everybody']); 04709 } 04710 return $fieldArray; 04711 } 04712 04720 function addDefaultPermittedLanguageIfNotSet($table,&$incomingFieldArray) { 04721 global $TCA; 04722 04723 // Checking languages: 04724 if ($TCA[$table]['ctrl']['languageField']) { 04725 if (!isset($incomingFieldArray[$TCA[$table]['ctrl']['languageField']])) { // Language field must be found in input row - otherwise it does not make sense. 04726 $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))); 04727 foreach($rows as $r) { 04728 if ($this->BE_USER->checkLanguageAccess($r['uid'])) { 04729 $incomingFieldArray[$TCA[$table]['ctrl']['languageField']] = $r['uid']; 04730 break; 04731 } 04732 } 04733 } 04734 } 04735 } 04736 04744 function overrideFieldArray($table,$data) { 04745 if (is_array($this->overrideValues[$table])) { 04746 $data = array_merge($data,$this->overrideValues[$table]); 04747 } 04748 return $data; 04749 } 04750 04760 function compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray) { 04761 04762 // Fetch the original record: 04763 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid='.intval($id)); 04764 $currentRecord = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 04765 04766 // If the current record exists (which it should...), begin comparison: 04767 if (is_array($currentRecord)) { 04768 04769 // Read all field types: 04770 $c = 0; 04771 $cRecTypes = array(); 04772 foreach($currentRecord as $col => $val) { 04773 $cRecTypes[$col] = $GLOBALS['TYPO3_DB']->sql_field_type($res,$c); 04774 $c++; 04775 } 04776 04777 // Free result: 04778 $GLOBALS['TYPO3_DB']->sql_free_result($res); 04779 04780 // Unset the fields which are similar: 04781 foreach($fieldArray as $col => $val) { 04782 if ( 04783 !strcmp($val,$currentRecord[$col]) || // Unset fields which matched exactly. 04784 ($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. 04785 ) { 04786 unset($fieldArray[$col]); 04787 } else { 04788 $this->historyRecords[$table.':'.$id]['oldRecord'][$col] = $currentRecord[$col]; 04789 $this->historyRecords[$table.':'.$id]['newRecord'][$col] = $fieldArray[$col]; 04790 } 04791 } 04792 } else { // If the current record does not exist this is an error anyways and we just return an empty array here. 04793 $fieldArray = array(); 04794 } 04795 04796 return $fieldArray; 04797 } 04798 04806 function assemblePermissions($string) { 04807 $keyArr = t3lib_div::trimExplode(',',$string,1); 04808 $value=0; 04809 while(list(,$key)=each($keyArr)) { 04810 if ($key && isset($this->pMap[$key])) { 04811 $value |= $this->pMap[$key]; 04812 } 04813 } 04814 return $value; 04815 } 04816 04823 function rmComma($input) { 04824 return ereg_replace(',$','',$input); 04825 } 04826 04833 function convNumEntityToByteValue($input) { 04834 $token = md5(microtime()); 04835 $parts = explode($token,ereg_replace('(&#([0-9]+);)',$token.'\2'.$token,$input)); 04836 04837 foreach($parts as $k => $v) { 04838 if ($k%2) { 04839 $v = intval($v); 04840 if ($v > 32) { // Just to make sure that control bytes are not converted. 04841 $parts[$k] =chr(intval($v)); 04842 } 04843 } 04844 } 04845 04846 return implode('',$parts); 04847 } 04848 04855 function destPathFromUploadFolder($folder) { 04856 return PATH_site.$folder; 04857 } 04858 04865 function deleteClause($table) { 04866 // Returns the proper delete-clause if any for a table from TCA 04867 global $TCA; 04868 if ($TCA[$table]['ctrl']['delete']) { 04869 return ' AND '.$table.'.'.$TCA[$table]['ctrl']['delete'].'=0'; 04870 } else { 04871 return ''; 04872 } 04873 } 04874 04881 function getTCEMAIN_TSconfig($tscPID) { 04882 if (!isset($this->cachedTSconfig[$tscPID])) { 04883 $this->cachedTSconfig[$tscPID] = $this->BE_USER->getTSConfig('TCEMAIN',t3lib_BEfunc::getPagesTSconfig($tscPID)); 04884 } 04885 return $this->cachedTSconfig[$tscPID]['properties']; 04886 } 04887 04896 function getTableEntries($table,$TSconfig) { 04897 $tA = is_array($TSconfig['table.'][$table.'.']) ? $TSconfig['table.'][$table.'.'] : array();; 04898 $dA = is_array($TSconfig['default.']) ? $TSconfig['default.'] : array(); 04899 return t3lib_div::array_merge_recursive_overrule($dA,$tA); 04900 } 04901 04909 function getPID($table,$uid) { 04910 $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', $table, 'uid='.intval($uid)); 04911 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) { 04912 return $row['pid']; 04913 } 04914 } 04915 04922 function dbAnalysisStoreExec() { 04923 reset($this->dbAnalysisStore); 04924 while(list($k,$v)=each($this->dbAnalysisStore)) { 04925 $id = $this->substNEWwithIDs[$v[2]]; 04926 if ($id) { 04927 $v[2] = $id; 04928 $v[0]->writeMM($v[1],$v[2],$v[3]); 04929 } 04930 } 04931 } 04932 04938 function removeRegisteredFiles() { 04939 reset($this->removeFilesStore); 04940 while(list($k,$v)=each($this->removeFilesStore)) { 04941 unlink($v); 04942 } 04943 } 04944 04950 function removeCacheFiles() { 04951 return t3lib_extMgm::removeCacheFiles(); 04952 } 04953 04964 function int_pageTreeInfo($CPtable,$pid,$counter, $rootID) { 04965 if ($counter) { 04966 $addW = !$this->admin ? ' AND '.$this->BE_USER->getPagePermsClause($this->pMap['show']) : ''; 04967 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', 'pid='.intval($pid).$this->deleteClause('pages').$addW, '', 'sorting DESC'); 04968 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 04969 if ($row['uid']!=$rootID) { 04970 $CPtable[$row['uid']] = $pid; 04971 if ($counter-1) { // If the uid is NOT the rootID of the copyaction and if we are supposed to walk further down 04972 $CPtable = $this->int_pageTreeInfo($CPtable,$row['uid'],$counter-1, $rootID); 04973 } 04974 } 04975 } 04976 } 04977 return $CPtable; 04978 } 04979 04985 function compileAdminTables() { 04986 global $TCA; 04987 reset ($TCA); 04988 $listArr = array(); 04989 while (list($table)=each($TCA)) { 04990 $listArr[]=$table; 04991 } 04992 return $listArr; 04993 } 04994 05002 function fixUniqueInPid($table,$uid) { 05003 global $TCA; 05004 if ($TCA[$table]) { 05005 t3lib_div::loadTCA($table); 05006 reset ($TCA[$table]['columns']); 05007 $curData=$this->recordInfo($table,$uid,'*'); 05008 $newData=array(); 05009 while (list($field,$conf)=each($TCA[$table]['columns'])) { 05010 if ($conf['config']['type']=='input') { 05011 $evalCodesArray = t3lib_div::trimExplode(',',$conf['config']['eval'],1); 05012 if (in_array('uniqueInPid',$evalCodesArray)) { 05013 $newV = $this->getUnique($table,$field,$curData[$field],$uid,$curData['pid']); 05014 if (strcmp($newV,$curData[$field])) { 05015 $newData[$field]=$newV; 05016 } 05017 } 05018 } 05019 } 05020 // IF there are changed fields, then update the database 05021 if (count($newData)) { 05022 $this->updateDB($table,$uid,$newData); 05023 } 05024 } 05025 } 05026 05038 function fixCopyAfterDuplFields($table,$uid,$prevUid,$update, $newData=array()) { 05039 global $TCA; 05040 if ($TCA[$table] && $TCA[$table]['ctrl']['copyAfterDuplFields']) { 05041 t3lib_div::loadTCA($table); 05042 $prevData=$this->recordInfo($table,$prevUid,'*'); 05043 $theFields = t3lib_div::trimExplode(',',$TCA[$table]['ctrl']['copyAfterDuplFields'],1); 05044 reset($theFields); 05045 while(list(,$field)=each($theFields)) { 05046 if ($TCA[$table]['columns'][$field] && ($update || !isset($newData[$field]))) { 05047 $newData[$field]=$prevData[$field]; 05048 } 05049 } 05050 if ($update && count($newData)) { 05051 $this->updateDB($table,$uid,$newData); 05052 } 05053 } 05054 return $newData; 05055 } 05056 05063 function extFileFields($table) { 05064 global $TCA; 05065 $listArr=array(); 05066 t3lib_div::loadTCA($table); 05067 if ($TCA[$table]['columns']) { 05068 reset($TCA[$table]['columns']); 05069 while (list($field,$configArr)=each($TCA[$table]['columns'])) { 05070 if ($configArr['config']['type']=='group' && $configArr['config']['internal_type']=='file') { 05071 $listArr[]=$field; 05072 } 05073 } 05074 } 05075 return $listArr; 05076 } 05077 05084 function getUniqueFields($table) { 05085 global $TCA; 05086 05087 $listArr=array(); 05088 t3lib_div::loadTCA($table); 05089 if ($TCA[$table]['columns']) { 05090 reset($TCA[$table]['columns']); 05091 while (list($field,$configArr)=each($TCA[$table]['columns'])) { 05092 if ($configArr['config']['type']==='input') { 05093 $evalCodesArray = t3lib_div::trimExplode(',',$configArr['config']['eval'],1); 05094 if (in_array('uniqueInPid',$evalCodesArray) || in_array('unique',$evalCodesArray)) { 05095 $listArr[]=$field; 05096 } 05097 } 05098 } 05099 } 05100 return $listArr; 05101 } 05102 05109 function isReferenceField($conf) { 05110 return ($conf['type']=='group' && $conf['internal_type']=='db') || ($conf['type']=='select' && $conf['foreign_table']); 05111 } 05112 05124 function getCopyHeader($table,$pid,$field,$value,$count,$prevTitle='') { 05125 global $TCA; 05126 05127 // Set title value to check for: 05128 if ($count) { 05129 $checkTitle = $value.rtrim(' '.sprintf($this->prependLabel($table),$count)); 05130 } else { 05131 $checkTitle = $value; 05132 } 05133 05134 // Do check: 05135 if ($prevTitle != $checkTitle || $count<100) { 05136 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($pid).' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($checkTitle, $table).$this->deleteClause($table), '', '', '1'); 05137 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 05138 return $this->getCopyHeader($table,$pid,$field,$value,$count+1,$checkTitle); 05139 } 05140 } 05141 05142 // Default is to just return the current input title if no other was returned before: 05143 return $checkTitle; 05144 } 05145 05153 function prependLabel($table) { 05154 global $TCA; 05155 if (is_object($GLOBALS['LANG'])) { 05156 $label = $GLOBALS['LANG']->sL($TCA[$table]['ctrl']['prependAtCopy']); 05157 } else { 05158 list($label) = explode('|',$TCA[$table]['ctrl']['prependAtCopy']); 05159 } 05160 return $label; 05161 } 05162 05170 function resolvePid($table,$pid) { 05171 global $TCA; 05172 $pid = intval($pid); 05173 if ($pid < 0) { 05174 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', $table, 'uid='.abs($pid)); 05175 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 05176 05177 // 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. 05178 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,abs($pid),'pid')) { 05179 $row = $lookForLiveVersion; 05180 } 05181 05182 $pid = intval($row['pid']); 05183 } 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. 05184 if ($WSdestPage = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, 'pages', $pid, 'uid,t3ver_swapmode')) { // Looks for workspace version of page. 05185 if ($WSdestPage['t3ver_swapmode']==0) { // if swapmode is zero, then change pid value. 05186 $pid = $WSdestPage['uid']; 05187 } 05188 } 05189 } 05190 return $pid; 05191 } 05192 05200 function clearPrefixFromValue($table,$value) { 05201 global $TCA; 05202 $regex = sprintf(quotemeta($this->prependLabel($table)),'[0-9]*').'$'; 05203 return @ereg_replace($regex,'',$value); 05204 } 05205 05215 function extFileFunctions($table,$field,$filelist,$func) { 05216 global $TCA; 05217 t3lib_div::loadTCA($table); 05218 $uploadFolder = $TCA[$table]['columns'][$field]['config']['uploadfolder']; 05219 if ($uploadFolder && trim($filelist)) { 05220 $uploadPath = $this->destPathFromUploadFolder($uploadFolder); 05221 $fileArray = explode(',',$filelist); 05222 while (list(,$theFile)=each($fileArray)) { 05223 $theFile=trim($theFile); 05224 if ($theFile) { 05225 switch($func) { 05226 case 'deleteAll': 05227 if (@is_file($uploadPath.'/'.$theFile)) { 05228 unlink ($uploadPath.'/'.$theFile); 05229 } else { 05230 $this->log($table,0,3,0,100,"Delete: Referenced file that was supposed to be deleted together with it's record didn't exist"); 05231 } 05232 break; 05233 } 05234 } 05235 } 05236 } 05237 } 05238 05245 function noRecordsFromUnallowedTables($inList) { 05246 global $TCA; 05247 reset ($TCA); 05248 $inList = trim($this->rmComma(trim($inList))); 05249 if ($inList && !$this->admin) { 05250 while (list($table) = each($TCA)) { 05251 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid IN ('.$inList.')'.t3lib_BEfunc::deleteClause($table)); 05252 $count = $GLOBALS['TYPO3_DB']->sql_fetch_row($mres); 05253 if ($count[0] && ($this->tableReadOnly($table) || !$this->checkModifyAccessList($table))) { 05254 return FALSE; 05255 } 05256 } 05257 } 05258 return TRUE; 05259 } 05260 05271 function notifyStageChange($stat,$stageId,$table,$id,$comment) { 05272 $workspaceRec = t3lib_BEfunc::getRecord('sys_workspace', $stat['uid']); 05273 05274 if (is_array($workspaceRec)) { 05275 05276 // Compile label: 05277 switch((int)$stageId) { 05278 case 1: 05279 $newStage = 'Ready for review'; 05280 break; 05281 case 10: 05282 $newStage = 'Ready for publishing'; 05283 break; 05284 case -1: 05285 $newStage = 'Element was rejected!'; 05286 break; 05287 case 0: 05288 $newStage = 'Rejected element was noticed and edited'; 05289 break; 05290 default: 05291 $newStage = 'Unknown state change!?'; 05292 break; 05293 } 05294 05295 // Compile list of recipients: 05296 $emails = array(); 05297 switch((int)$stat['stagechg_notification']) { 05298 case 1: 05299 switch((int)$stageId) { 05300 case 1: 05301 $emails = $this->notifyStageChange_getEmails($workspaceRec['reviewers']); 05302 break; 05303 case 10: 05304 $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE); 05305 break; 05306 case -1: 05307 $emails = $this->notifyStageChange_getEmails($workspaceRec['reviewers']); 05308 $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['members'])); 05309 break; 05310 case 0: 05311 $emails = $this->notifyStageChange_getEmails($workspaceRec['members']); 05312 break; 05313 default: 05314 $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE); 05315 break; 05316 } 05317 break; 05318 case 10: 05319 $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE); 05320 $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['reviewers'])); 05321 $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['members'])); 05322 break; 05323 } 05324 $emails = array_unique($emails); 05325 05326 // Send email: 05327 if (count($emails)) { 05328 $message = sprintf(' 05329 At the TYPO3 site "%s" (%s) 05330 in workspace "%s" (#%s) 05331 the stage has changed for the element "%s": 05332 05333 ==> %s 05334 05335 User Comment: 05336 "%s" 05337 05338 State was change by %s (username: %s) 05339 ', 05340 $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], 05341 t3lib_div::getIndpEnv('TYPO3_SITE_URL').TYPO3_mainDir, 05342 $workspaceRec['title'], 05343 $workspaceRec['uid'], 05344 $table.':'.$id, 05345 $newStage, 05346 $comment, 05347 $this->BE_USER->user['realName'], 05348 $this->BE_USER->user['username']); 05349 05350 t3lib_div::plainMailEncoded( 05351 implode(',',$emails), 05352 'TYPO3 Workspace Note: Stage Change for '.$table.':'.$id, 05353 trim($message) 05354 ); 05355 } 05356 } 05357 } 05358 05366 function notifyStageChange_getEmails($listOfUsers,$noTablePrefix=FALSE) { 05367 $users = t3lib_div::trimExplode(',',$listOfUsers,1); 05368 $emails = array(); 05369 foreach($users as $userIdent) { 05370 if ($noTablePrefix) { 05371 $id = intval($userIdent); 05372 } else { 05373 list($table,$id) = t3lib_div::revExplode('_',$userIdent,2); 05374 } 05375 if ($table==='be_users' || $noTablePrefix) { 05376 if ($userRecord = t3lib_BEfunc::getRecord('be_users', $id, 'email')) { 05377 if (strlen(trim($userRecord['email']))) { 05378 $emails[$id] = $userRecord['email']; 05379 } 05380 } 05381 } 05382 } 05383 return $emails; 05384 } 05385 05386 05387 05388 05389 05390 05391 05392 05393 05394 05395 05396 05397 /****************************** 05398 * 05399 * Clearing cache 05400 * 05401 ******************************/ 05402 05412 function clear_cache($table,$uid) { 05413 global $TCA, $TYPO3_CONF_VARS; 05414 05415 $uid = intval($uid); 05416 if (is_array($TCA[$table]) && $uid > 0) { 05417 05418 // Get Page TSconfig relavant: 05419 list($tscPID) = t3lib_BEfunc::getTSCpid($table,$uid,''); 05420 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 05421 05422 if (!$TSConfig['clearCache_disable']) { 05423 05424 // If table is "pages": 05425 if (t3lib_extMgm::isLoaded('cms')) { 05426 $list_cache = array(); 05427 if ($table=='pages') { 05428 05429 // Builds list of pages on the SAME level as this page (siblings) 05430 $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05431 'A.pid AS pid, B.uid AS uid', 05432 'pages A, pages B', 05433 'A.uid='.intval($uid).' AND B.pid=A.pid AND B.deleted=0' 05434 ); 05435 05436 $pid_tmp = 0; 05437 while ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) { 05438 $list_cache[] = $row_tmp['uid']; 05439 $pid_tmp = $row_tmp['pid']; 05440 05441 // Add children as well: 05442 if ($TSConfig['clearCache_pageSiblingChildren']) { 05443 $res_tmp2 = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05444 'uid', 05445 'pages', 05446 'pid='.intval($row_tmp['uid']).' AND deleted=0' 05447 ); 05448 while ($row_tmp2 = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp2)) { 05449 $list_cache[] = $row_tmp2['uid']; 05450 } 05451 } 05452 } 05453 05454 // Finally, add the parent page as well: 05455 $list_cache[] = $pid_tmp; 05456 05457 // Add grand-parent as well: 05458 if ($TSConfig['clearCache_pageGrandParent']) { 05459 $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05460 'pid', 05461 'pages', 05462 'uid='.intval($pid_tmp) 05463 ); 05464 if ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) { 05465 $list_cache[] = $row_tmp['pid']; 05466 } 05467 } 05468 } else { // For other tables than "pages", delete cache for the records "parent page". 05469 $list_cache[] = intval($this->getPID($table,$uid)); 05470 } 05471 05472 // Call pre-processing function for clearing of cache for page ids: 05473 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) { 05474 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) { 05475 $_params = array('pageIdArray' => &$list_cache, 'table' => $table, 'uid' => $uid, 'functionID' => 'clear_cache()'); 05476 // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array! 05477 t3lib_div::callUserFunction($funcName,$_params,$this); 05478 } 05479 } 05480 05481 // Delete cache for selected pages: 05482 if (is_array($list_cache)) { 05483 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')'); 05484 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection', 'page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')'); 05485 } 05486 } 05487 } 05488 05489 // Clear cache for pages entered in TSconfig: 05490 if ($TSConfig['clearCacheCmd']) { 05491 $Commands = t3lib_div::trimExplode(',',strtolower($TSConfig['clearCacheCmd']),1); 05492 $Commands = array_unique($Commands); 05493 foreach($Commands as $cmdPart) { 05494 $this->clear_cacheCmd($cmdPart); 05495 } 05496 } 05497 05498 // Call post processing function for clear-cache: 05499 global $TYPO3_CONF_VARS; 05500 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) { 05501 // FIXME $uid_page is undefined 05502 $_params = array('table' => $table,'uid' => $uid,'uid_page' => $uid_page,'TSConfig' => $TSConfig); 05503 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) { 05504 t3lib_div::callUserFunction($_funcRef,$_params,$this); 05505 } 05506 } 05507 } 05508 } 05509 05522 function clear_cacheCmd($cacheCmd) { 05523 global $TYPO3_CONF_VARS; 05524 05525 // Clear cache for either ALL pages or ALL tables! 05526 switch($cacheCmd) { 05527 case 'pages': 05528 if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.pages')) { 05529 if (t3lib_extMgm::isLoaded('cms')) { 05530 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages',''); 05531 } 05532 } 05533 break; 05534 case 'all': 05535 if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.all')) { 05536 if (t3lib_extMgm::isLoaded('cms')) { 05537 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages',''); 05538 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection',''); 05539 } 05540 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_hash',''); 05541 05542 // Clearing additional cache tables: 05543 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'])) { 05544 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'] as $tableName) { 05545 if (!ereg('[^[:alnum:]_]',$tableName) && substr($tableName,-5)=='cache') { 05546 $GLOBALS['TYPO3_DB']->exec_DELETEquery($tableName,''); 05547 } else { 05548 die('Fatal Error: Trying to flush table "'.$tableName.'" with "Clear All Cache"'); 05549 } 05550 } 05551 } 05552 } 05553 break; 05554 case 'temp_CACHED': 05555 if ($this->admin && $TYPO3_CONF_VARS['EXT']['extCache']) { 05556 $this->removeCacheFiles(); 05557 } 05558 break; 05559 } 05560 05561 // Clear cache for a page ID! 05562 if (t3lib_div::testInt($cacheCmd)) { 05563 if (t3lib_extMgm::isLoaded('cms')) { 05564 05565 $list_cache = array($cacheCmd); 05566 05567 // Call pre-processing function for clearing of cache for page ids: 05568 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) { 05569 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) { 05570 $_params = array('pageIdArray' => &$list_cache, 'cacheCmd' => $cacheCmd, 'functionID' => 'clear_cacheCmd()'); 05571 // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array! 05572 t3lib_div::callUserFunction($funcName,$_params,$this); 05573 } 05574 } 05575 05576 // Delete cache for selected pages: 05577 if (is_array($list_cache)) { 05578 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')'); 05579 $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! 05580 } 05581 } 05582 } 05583 05584 // Call post processing function for clear-cache: 05585 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) { 05586 $_params = array('cacheCmd'=>$cacheCmd); 05587 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) { 05588 t3lib_div::callUserFunction($_funcRef,$_params,$this); 05589 } 05590 } 05591 } 05592 05593 05594 05595 05596 05597 05598 05599 05600 05601 05602 05603 05604 05605 05606 /***************************** 05607 * 05608 * Logging 05609 * 05610 *****************************/ 05611 05628 function log($table,$recuid,$action,$recpid,$error,$details,$details_nr=-1,$data=array(),$event_pid=-1,$NEWid='') { 05629 if ($this->enableLogging) { 05630 $type=1; // Type value for tce_db.php 05631 if (!$this->storeLogMessages) {$details='';} 05632 if ($error>0) $this->errorLog[] = '['.$type.'.'.$action.'.'.$details_nr.']: '.$details; 05633 return $this->BE_USER->writelog($type,$action,$error,$details_nr,$details,$data,$table,$recuid,$recpid,$event_pid,$NEWid); 05634 } 05635 } 05636 05645 function newlog($message, $error=0) { 05646 return $this->log('',0,0,0,$error,$message,-1); 05647 } 05648 05655 function printLogErrorMessages($redirect) { 05656 05657 $res_log = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05658 '*', 05659 'sys_log', 05660 'type=1 AND userid='.intval($this->BE_USER->user['uid']).' AND tstamp='.intval($GLOBALS['EXEC_TIME']).' AND error!=0' 05661 ); 05662 $errorJS = array(); 05663 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_log)) { 05664 $log_data = unserialize($row['log_data']); 05665 $errorJS[] = $row['error'].': '.sprintf($row['details'], $log_data[0],$log_data[1],$log_data[2],$log_data[3],$log_data[4]); 05666 } 05667 05668 if (count($errorJS)) { 05669 $error_doc = t3lib_div::makeInstance('template'); 05670 $error_doc->backPath = $GLOBALS['BACK_PATH']; 05671 05672 $content.= $error_doc->startPage('tce_db.php Error output'); 05673 05674 $lines[] = ' 05675 <tr class="bgColor5"> 05676 <td colspan="2" align="center"><strong>Errors:</strong></td> 05677 </tr>'; 05678 05679 foreach($errorJS as $line) { 05680 $lines[] = ' 05681 <tr class="bgColor4"> 05682 <td valign="top"><img'.t3lib_iconWorks::skinImg($error_doc->backPath,'gfx/icon_fatalerror.gif','width="18" height="16"').' alt="" /></td> 05683 <td>'.htmlspecialchars($line).'</td> 05684 </tr>'; 05685 } 05686 05687 $lines[] = ' 05688 <tr> 05689 <td colspan="2" align="center"><br />'. 05690 '<form action=""><input type="submit" value="Continue" onclick="'.htmlspecialchars('window.location.href=\''.$redirect.'\';return false;').'"></form>'. 05691 '</td> 05692 </tr>'; 05693 05694 $content.= ' 05695 <br/><br/> 05696 <table border="0" cellpadding="1" cellspacing="1" width="300" align="center"> 05697 '.implode('',$lines).' 05698 </table>'; 05699 05700 $content.= $error_doc->endPage(); 05701 echo $content; 05702 exit; 05703 } 05704 } 05705 } 05706 05707 05708 05709 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tcemain.php']) { 05710 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tcemain.php']); 05711 } 05712 ?>