Documentation TYPO3 par Ameos |
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 1999-2006 Kasper Skaarhoj (kasperYYYY@typo3.com) 00006 * All rights reserved 00007 * 00008 * This script is part of the TYPO3 project. The TYPO3 project is 00009 * free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * The GNU General Public License can be found at 00015 * http://www.gnu.org/copyleft/gpl.html. 00016 * A copy is found in the textfile GPL.txt and important notices to the license 00017 * from the author is found in LICENSE.txt distributed with these scripts. 00018 * 00019 * 00020 * This script is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00023 * GNU General Public License for more details. 00024 * 00025 * This copyright notice MUST APPEAR in all copies of the script! 00026 ***************************************************************/ 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 send 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 send 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 // This could be a good spot for parsing the array through a validation-function which checks if the values are allright (except that database references are not in their final form - but that is the point, isn't it?) 01287 // NOTE!!! Must check max-items of files before the later check because that check would just leave out filenames if there are too many!! 01288 01289 // Checking for select / authMode, removing elements from $valueArray if any of them is not allowed! 01290 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['authMode']) { 01291 $preCount = count($valueArray); 01292 foreach($valueArray as $kk => $vv) { 01293 if (!$this->BE_USER->checkAuthMode($table,$field,$vv,$tcaFieldConf['authMode'])) { 01294 unset($valueArray[$kk]); 01295 } 01296 } 01297 01298 // 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. 01299 if ($preCount && !count($valueArray)) { 01300 return array(); 01301 } 01302 } 01303 01304 // For group types: 01305 if ($tcaFieldConf['type']=='group') { 01306 switch($tcaFieldConf['internal_type']) { 01307 case 'file': 01308 $valueArray = $this->checkValue_group_select_file( 01309 $valueArray, 01310 $tcaFieldConf, 01311 $curValue, 01312 $uploadedFiles, 01313 $status, 01314 $table, 01315 $id, 01316 $recFID 01317 ); 01318 break; 01319 case 'db': 01320 $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'group'); 01321 break; 01322 } 01323 } 01324 // For select types which has a foreign table attached: 01325 if ($tcaFieldConf['type']=='select' && $tcaFieldConf['foreign_table']) { 01326 $valueArray = $this->checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,'select'); 01327 } 01328 01329 // 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... 01330 01331 // Checking the number of items, that it is correct. 01332 // If files, there MUST NOT be too many files in the list at this point, so check that prior to this code. 01333 $valueArrayC = count($valueArray); 01334 $minI = isset($tcaFieldConf['minitems']) ? intval($tcaFieldConf['minitems']):0; 01335 01336 // 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. 01337 $maxI = isset($tcaFieldConf['maxitems']) ? intval($tcaFieldConf['maxitems']):1; 01338 if ($valueArrayC > $maxI) {$valueArrayC=$maxI;} // Checking for not too many elements 01339 01340 // Dumping array to list 01341 $newVal=array(); 01342 foreach($valueArray as $nextVal) { 01343 if ($valueArrayC==0) {break;} 01344 $valueArrayC--; 01345 $newVal[]=$nextVal; 01346 } 01347 $res['value'] = implode(',',$newVal); 01348 01349 return $res; 01350 } 01351 01366 function checkValue_group_select_file($valueArray,$tcaFieldConf,$curValue,$uploadedFileArray,$status,$table,$id,$recFID) { 01367 01368 // If any files are uploaded: 01369 if (is_array($uploadedFileArray) && 01370 $uploadedFileArray['name'] && 01371 strcmp($uploadedFileArray['tmp_name'],'none')) { 01372 $valueArray[]=$uploadedFileArray['tmp_name']; 01373 $this->alternativeFileName[$uploadedFileArray['tmp_name']] = $uploadedFileArray['name']; 01374 } 01375 01376 // Creating fileFunc object. 01377 if (!$this->fileFunc) { 01378 $this->fileFunc = t3lib_div::makeInstance('t3lib_basicFileFunctions'); 01379 $this->include_filefunctions=1; 01380 } 01381 // Setting permitted extensions. 01382 $all_files = Array(); 01383 $all_files['webspace']['allow'] = $tcaFieldConf['allowed']; 01384 $all_files['webspace']['deny'] = $tcaFieldConf['disallowed'] ? $tcaFieldConf['disallowed'] : '*'; 01385 $all_files['ftpspace'] = $all_files['webspace']; 01386 $this->fileFunc->init('', $all_files); 01387 01388 // If there is an upload folder defined: 01389 if ($tcaFieldConf['uploadfolder']) { 01390 // For logging.. 01391 $propArr = $this->getRecordProperties($table,$id); 01392 01393 // Get destrination path: 01394 $dest = $this->destPathFromUploadFolder($tcaFieldConf['uploadfolder']); 01395 01396 // If we are updating: 01397 if ($status=='update') { 01398 01399 // Traverse the input values and convert to absolute filenames in case the update happens to an autoVersionized record. 01400 // 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! 01401 // 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_. 01402 // 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. 01403 // Illustration of the problem comes here: 01404 // 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. 01405 // 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. 01406 // 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. 01407 // 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. 01408 if ($this->autoVersioningUpdate===TRUE) { 01409 foreach($valueArray as $key => $theFile) { 01410 if ($theFile===basename($theFile)) { 01411 $valueArray[$key] = PATH_site.$tcaFieldConf['uploadfolder'].'/'.$theFile; 01412 } 01413 } 01414 } 01415 01416 // Finding the CURRENT files listed, either from MM or from the current record. 01417 $theFileValues=array(); 01418 if ($tcaFieldConf['MM']) { // If MM relations for the files also! 01419 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 01420 $dbAnalysis->start('','files',$tcaFieldConf['MM'],$id); 01421 reset($dbAnalysis->itemArray); 01422 while (list($somekey,$someval)=each($dbAnalysis->itemArray)) { 01423 if ($someval['id']) { 01424 $theFileValues[]=$someval['id']; 01425 } 01426 } 01427 } else { 01428 $theFileValues=t3lib_div::trimExplode(',',$curValue,1); 01429 } 01430 01431 // DELETE files: If existing files were found, traverse those and register files for deletion which has been removed: 01432 if (count($theFileValues)) { 01433 // 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!) 01434 foreach($valueArray as $key => $theFile) { 01435 if ($theFile && !strstr(t3lib_div::fixWindowsFilePath($theFile),'/')) { 01436 $theFileValues = t3lib_div::removeArrayEntryByValue($theFileValues,$theFile); 01437 } 01438 } 01439 01440 // This array contains the filenames in the uploadfolder that should be deleted: 01441 foreach($theFileValues as $key => $theFile) { 01442 $theFile = trim($theFile); 01443 if (@is_file($dest.'/'.$theFile)) { 01444 $this->removeFilesStore[]=$dest.'/'.$theFile; 01445 } elseif ($theFile) { 01446 $this->log($table,$id,5,0,1,"Could not delete file '%s' (does not exist). (%s)",10,array($dest.'/'.$theFile, $recFID),$propArr['event_pid']); 01447 } 01448 } 01449 } 01450 } 01451 01452 // Traverse the submitted values: 01453 foreach($valueArray as $key => $theFile) { 01454 // 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) 01455 if (strstr(t3lib_div::fixWindowsFilePath($theFile),'/')) { 01456 // Init: 01457 $maxSize = intval($tcaFieldConf['max_size']); 01458 $cmd=''; 01459 $theDestFile=''; // Must be cleared. Else a faulty fileref may be inserted if the below code returns an error!! (Change: 22/12/2000) 01460 01461 // Check various things before copying file: 01462 if (@is_dir($dest) && (@is_file($theFile) || @is_uploaded_file($theFile))) { // File and destination must exist 01463 01464 // Finding size. For safe_mode we have to rely on the size in the upload array if the file is uploaded. 01465 if (is_uploaded_file($theFile) && $theFile==$uploadedFileArray['tmp_name']) { 01466 $fileSize = $uploadedFileArray['size']; 01467 } else { 01468 $fileSize = filesize($theFile); 01469 } 01470 01471 if (!$maxSize || $fileSize<=($maxSize*1024)) { // Check file size: 01472 // Prepare filename: 01473 $theEndFileName = isset($this->alternativeFileName[$theFile]) ? $this->alternativeFileName[$theFile] : $theFile; 01474 $fI = t3lib_div::split_fileref($theEndFileName); 01475 01476 // Check for allowed extension: 01477 if ($this->fileFunc->checkIfAllowed($fI['fileext'], $dest, $theEndFileName)) { 01478 $theDestFile = $this->fileFunc->getUniqueName($this->fileFunc->cleanFileName($fI['file']), $dest); 01479 01480 // If we have a unique destination filename, then write the file: 01481 if ($theDestFile) { 01482 t3lib_div::upload_copy_move($theFile,$theDestFile); 01483 $this->copiedFileMap[$theFile] = $theDestFile; 01484 clearstatcache(); 01485 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']); 01486 } 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']); 01487 } else $this->log($table,$id,5,0,1,"Fileextension '%s' not allowed. (%s)",12,array($fI['fileext'], $recFID),$propArr['event_pid']); 01488 } 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']); 01489 } 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']); 01490 01491 // If the destination file was created, we will set the new filename in the value array, otherwise unset the entry in the value array! 01492 if (@is_file($theDestFile)) { 01493 $info = t3lib_div::split_fileref($theDestFile); 01494 $valueArray[$key]=$info['file']; // The value is set to the new filename 01495 } else { 01496 unset($valueArray[$key]); // The value is set to the new filename 01497 } 01498 } 01499 } 01500 01501 // 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! 01502 if ($tcaFieldConf['MM']) { 01503 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 01504 $dbAnalysis->tableArray['files']=array(); // dummy 01505 01506 reset($valueArray); 01507 while (list($key,$theFile)=each($valueArray)) { 01508 // explode files 01509 $dbAnalysis->itemArray[]['id']=$theFile; 01510 } 01511 if ($status=='update') { 01512 $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,0); 01513 } else { 01514 $this->dbAnalysisStore[] = array($dbAnalysis, $tcaFieldConf['MM'], $id, 0); // This will be traversed later to execute the actions 01515 } 01516 $cc=count($dbAnalysis->itemArray); 01517 $valueArray = array($cc); 01518 } 01519 } 01520 01521 return $valueArray; 01522 } 01523 01536 function checkValue_flex($res,$value,$tcaFieldConf,$PP,$uploadedFiles,$field) { 01537 list($table,$id,$curValue,$status,$realPid,$recFID) = $PP; 01538 01539 if (is_array($value)) { 01540 01541 // Get current value array: 01542 $dataStructArray = t3lib_BEfunc::getFlexFormDS($tcaFieldConf,$this->checkValue_currentRecord,$table); 01543 #debug($this->checkValue_currentRecord); 01544 $currentValueArray = t3lib_div::xml2array($curValue); 01545 if (!is_array($currentValueArray)) $currentValueArray = array(); 01546 if (is_array($currentValueArray['meta']['currentLangId'])) unset($currentValueArray['meta']['currentLangId']); // Remove all old meta for languages... 01547 01548 // Evaluation of input values: 01549 $value['data'] = $this->checkValue_flex_procInData($value['data'],$currentValueArray['data'],$uploadedFiles['data'],$dataStructArray,$PP); 01550 01551 // Create XML and convert charsets from input value: 01552 $xmlValue = $this->checkValue_flexArray2Xml($value,TRUE); 01553 01554 // If we wanted to set UTF fixed: 01555 // $storeInCharset='utf-8'; 01556 // $currentCharset=$GLOBALS['LANG']->charSet; 01557 // $xmlValue = $GLOBALS['LANG']->csConvObj->conv($xmlValue,$currentCharset,$storeInCharset,1); 01558 $storeInCharset=$GLOBALS['LANG']->charSet; 01559 01560 // Merge them together IF they are both arrays: 01561 // 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). 01562 if (is_array($currentValueArray)) { 01563 $arrValue = t3lib_div::xml2array($xmlValue); 01564 $arrValue = t3lib_div::array_merge_recursive_overrule($currentValueArray,$arrValue); 01565 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01566 } 01567 01568 // Temporary fix to delete flex form elements: 01569 $deleteCMDs = t3lib_div::_GP('_DELETE_FLEX_FORMdata'); 01570 if (is_array($deleteCMDs[$table][$id][$field]['data'])) { 01571 $arrValue = t3lib_div::xml2array($xmlValue); 01572 $this->_DELETE_FLEX_FORMdata($arrValue['data'],$deleteCMDs[$table][$id][$field]['data']); 01573 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01574 } 01575 01576 // Temporary fix to move flex form elements up: 01577 $moveCMDs = t3lib_div::_GP('_MOVEUP_FLEX_FORMdata'); 01578 if (is_array($moveCMDs[$table][$id][$field]['data'])) { 01579 $arrValue = t3lib_div::xml2array($xmlValue); 01580 $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'up'); 01581 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01582 } 01583 01584 // Temporary fix to move flex form elements down: 01585 $moveCMDs = t3lib_div::_GP('_MOVEDOWN_FLEX_FORMdata'); 01586 if (is_array($moveCMDs[$table][$id][$field]['data'])) { 01587 $arrValue = t3lib_div::xml2array($xmlValue); 01588 $this->_MOVE_FLEX_FORMdata($arrValue['data'],$moveCMDs[$table][$id][$field]['data'], 'down'); 01589 $xmlValue = $this->checkValue_flexArray2Xml($arrValue,TRUE); 01590 } 01591 01592 // Create the value XML: 01593 $res['value']=''; 01594 $res['value'].=$xmlValue; 01595 } else { // Passthrough...: 01596 $res['value']=$value; 01597 } 01598 01599 return $res; 01600 } 01601 01609 function checkValue_flexArray2Xml($array, $addPrologue=FALSE) { 01610 $flexObj = t3lib_div::makeInstance('t3lib_flexformtools'); 01611 return $flexObj->flexArray2Xml($array, $addPrologue); 01612 } 01613 01621 function _DELETE_FLEX_FORMdata(&$valueArrayToRemoveFrom,$deleteCMDS) { 01622 if (is_array($valueArrayToRemoveFrom) && is_array($deleteCMDS)) { 01623 foreach($deleteCMDS as $key => $value) { 01624 if (is_array($deleteCMDS[$key])) { 01625 $this->_DELETE_FLEX_FORMdata($valueArrayToRemoveFrom[$key],$deleteCMDS[$key]); 01626 } else { 01627 unset($valueArrayToRemoveFrom[$key]); 01628 } 01629 } 01630 } 01631 } 01632 01643 function _MOVE_FLEX_FORMdata(&$valueArrayToMoveIn, $moveCMDS, $direction) { 01644 if (is_array($valueArrayToMoveIn) && is_array($moveCMDS)) { 01645 01646 // Only execute the first move command: 01647 list ($key, $value) = each ($moveCMDS); 01648 01649 if (is_array($moveCMDS[$key])) { 01650 $this->_MOVE_FLEX_FORMdata($valueArrayToMoveIn[$key],$moveCMDS[$key], $direction); 01651 } else { 01652 switch ($direction) { 01653 case 'up': 01654 if ($key > 1) { 01655 $tmpArr = $valueArrayToMoveIn[$key]; 01656 $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key-1]; 01657 $valueArrayToMoveIn[$key-1] = $tmpArr; 01658 } 01659 break; 01660 case 'down': 01661 if ($key < count($valueArrayToMoveIn)) { 01662 $tmpArr = $valueArrayToMoveIn[$key]; 01663 $valueArrayToMoveIn[$key] = $valueArrayToMoveIn[$key+1]; 01664 $valueArrayToMoveIn[$key+1] = $tmpArr; 01665 } 01666 break; 01667 } 01668 } 01669 } 01670 } 01671 01672 01673 01674 01675 01676 01677 01678 01679 01680 01681 01682 01683 01684 01685 01686 01687 01688 01689 /********************************************* 01690 * 01691 * Helper functions for evaluation functions. 01692 * 01693 ********************************************/ 01694 01705 function getUnique($table,$field,$value,$id,$newPid=0) { 01706 global $TCA; 01707 01708 // Initialize: 01709 t3lib_div::loadTCA($table); 01710 $whereAdd=''; 01711 $newValue=''; 01712 if (intval($newPid)) { $whereAdd.=' AND pid='.intval($newPid); } else { $whereAdd.=' AND pid>=0'; } // "AND pid>=0" for versioning 01713 $whereAdd.=$this->deleteClause($table); 01714 01715 // If the field is configured in TCA, proceed: 01716 if (is_array($TCA[$table]) && is_array($TCA[$table]['columns'][$field])) { 01717 01718 // Look for a record which might already have the value: 01719 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($value, $table).' AND uid!='.intval($id).$whereAdd); 01720 $counter = 0; 01721 01722 // For as long as records with the test-value existing, try again (with incremented numbers appended). 01723 while ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 01724 $newValue = $value.$counter; 01725 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, $field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($newValue, $table).' AND uid!='.intval($id).$whereAdd); 01726 $counter++; 01727 if ($counter>100) { break; } // At "100" it will give up and accept a duplicate - should probably be fixed to a small hash string instead...! 01728 } 01729 // If the new value is there: 01730 $value = strlen($newValue) ? $newValue : $value; 01731 } 01732 return $value; 01733 } 01734 01743 function checkValue_input_Eval($value,$evalArray,$is_in) { 01744 $res = Array(); 01745 $newValue = $value; 01746 $set = true; 01747 01748 foreach($evalArray as $func) { 01749 switch($func) { 01750 case 'int': 01751 case 'year': 01752 case 'date': 01753 case 'datetime': 01754 case 'time': 01755 case 'timesec': 01756 $value = intval($value); 01757 break; 01758 case 'double2': 01759 $theDec = 0; 01760 for ($a=strlen($value); $a>0; $a--) { 01761 if (substr($value,$a-1,1)=='.' || substr($value,$a-1,1)==',') { 01762 $theDec = substr($value,$a); 01763 $value = substr($value,0,$a-1); 01764 break; 01765 } 01766 } 01767 $theDec = ereg_replace('[^0-9]','',$theDec).'00'; 01768 $value = intval(str_replace(' ','',$value)).'.'.substr($theDec,0,2); 01769 break; 01770 case 'md5': 01771 if (strlen($value)!=32){$set=false;} 01772 break; 01773 case 'trim': 01774 $value = trim($value); 01775 break; 01776 case 'upper': 01777 $value = strtoupper($value); 01778 # $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. 01779 break; 01780 case 'lower': 01781 $value = strtolower($value); 01782 # $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. 01783 break; 01784 case 'required': 01785 if (!$value) {$set=0;} 01786 break; 01787 case 'is_in': 01788 $c=strlen($value); 01789 if ($c) { 01790 $newVal = ''; 01791 for ($a=0;$a<$c;$a++) { 01792 $char = substr($value,$a,1); 01793 if (strstr($is_in,$char)) { 01794 $newVal.=$char; 01795 } 01796 } 01797 $value = $newVal; 01798 } 01799 break; 01800 case 'nospace': 01801 $value = str_replace(' ','',$value); 01802 break; 01803 case 'alpha': 01804 $value = ereg_replace('[^a-zA-Z]','',$value); 01805 break; 01806 case 'num': 01807 $value = ereg_replace('[^0-9]','',$value); 01808 break; 01809 case 'alphanum': 01810 $value = ereg_replace('[^a-zA-Z0-9]','',$value); 01811 break; 01812 case 'alphanum_x': 01813 $value = ereg_replace('[^a-zA-Z0-9_-]','',$value); 01814 break; 01815 default: 01816 if (substr($func, 0, 3) == 'tx_') { 01817 $evalObj = t3lib_div::getUserObj($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tce']['formevals'][$func].':&'.$func); 01818 if(is_object($evalObj) && method_exists($evalObj, 'evaluateFieldValue')) { 01819 $value = $evalObj->evaluateFieldValue($value, $is_in, $set); 01820 } 01821 } 01822 break; 01823 } 01824 } 01825 if ($set) {$res['value'] = $value;} 01826 return $res; 01827 } 01828 01839 function checkValue_group_select_processDBdata($valueArray,$tcaFieldConf,$id,$status,$type) { 01840 $tables = $type=='group'?$tcaFieldConf['allowed']:$tcaFieldConf['foreign_table'].','.$tcaFieldConf['neg_foreign_table']; 01841 $prep = $type=='group'?$tcaFieldConf['prepend_tname']:$tcaFieldConf['neg_foreign_table']; 01842 01843 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 01844 $dbAnalysis->registerNonTableValues=$tcaFieldConf['allowNonIdValues'] ? 1 : 0; 01845 $dbAnalysis->start(implode(',',$valueArray),$tables); 01846 01847 if ($tcaFieldConf['MM']) { 01848 if ($status=='update') { 01849 $dbAnalysis->writeMM($tcaFieldConf['MM'],$id,$prep); 01850 } else { 01851 $this->dbAnalysisStore[] = array($dbAnalysis,$tcaFieldConf['MM'],$id,$prep); // This will be traversed later to execute the actions 01852 } 01853 $cc=count($dbAnalysis->itemArray); 01854 $valueArray = array($cc); 01855 } else { 01856 $valueArray = $dbAnalysis->getValueArray($prep); 01857 if ($type=='select' && $prep) { 01858 $valueArray = $dbAnalysis->convertPosNeg($valueArray,$tcaFieldConf['foreign_table'],$tcaFieldConf['neg_foreign_table']); 01859 } 01860 } 01861 01862 // 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. 01863 return $valueArray; 01864 } 01865 01872 function checkValue_group_select_explodeSelectGroupValue($value) { 01873 $valueArray = t3lib_div::trimExplode(',',$value,1); 01874 reset($valueArray); 01875 while(list($key,$newVal)=each($valueArray)) { 01876 $temp=explode('|',$newVal,2); 01877 $valueArray[$key] = str_replace(',','',str_replace('|','',rawurldecode($temp[0]))); 01878 } 01879 return $valueArray; 01880 } 01881 01896 function checkValue_flex_procInData($dataPart,$dataPart_current,$uploadedFiles,$dataStructArray,$pParams,$callBackFunc='') { 01897 #debug(array($dataPart,$dataPart_current,$dataStructArray)); 01898 if (is_array($dataPart)) { 01899 foreach($dataPart as $sKey => $sheetDef) { 01900 list ($dataStruct,$actualSheet) = t3lib_div::resolveSheetDefInDS($dataStructArray,$sKey); 01901 #debug(array($dataStruct,$actualSheet,$sheetDef,$actualSheet,$sKey)); 01902 if (is_array($dataStruct) && $actualSheet==$sKey && is_array($sheetDef)) { 01903 foreach($sheetDef as $lKey => $lData) { 01904 $this->checkValue_flex_procInData_travDS( 01905 $dataPart[$sKey][$lKey], 01906 $dataPart_current[$sKey][$lKey], 01907 $uploadedFiles[$sKey][$lKey], 01908 $dataStruct['ROOT']['el'], 01909 $pParams, 01910 $callBackFunc, 01911 $sKey.'/'.$lKey.'/' 01912 ); 01913 } 01914 } 01915 } 01916 } 01917 01918 return $dataPart; 01919 } 01920 01935 function checkValue_flex_procInData_travDS(&$dataValues,$dataValues_current,$uploadedFiles,$DSelements,$pParams,$callBackFunc,$structurePath) { 01936 if (is_array($DSelements)) { 01937 01938 // For each DS element: 01939 foreach($DSelements as $key => $dsConf) { 01940 01941 // Array/Section: 01942 if ($DSelements[$key]['type']=='array') { 01943 if (is_array($dataValues[$key]['el'])) { 01944 if ($DSelements[$key]['section']) { 01945 foreach($dataValues[$key]['el'] as $ik => $el) { 01946 $theKey = key($el); 01947 if (is_array($dataValues[$key]['el'][$ik][$theKey]['el'])) { 01948 $this->checkValue_flex_procInData_travDS( 01949 $dataValues[$key]['el'][$ik][$theKey]['el'], 01950 $dataValues_current[$key]['el'][$ik][$theKey]['el'], 01951 $uploadedFiles[$key]['el'][$ik][$theKey]['el'], 01952 $DSelements[$key]['el'][$theKey]['el'], 01953 $pParams, 01954 $callBackFunc, 01955 $structurePath.$key.'/el/'.$ik.'/'.$theKey.'/el/' 01956 ); 01957 } 01958 } 01959 } else { 01960 if (!isset($dataValues[$key]['el'])) $dataValues[$key]['el']=array(); 01961 $this->checkValue_flex_procInData_travDS( 01962 $dataValues[$key]['el'], 01963 $dataValues_current[$key]['el'], 01964 $uploadedFiles[$key]['el'], 01965 $DSelements[$key]['el'], 01966 $pParams, 01967 $callBackFunc, 01968 $structurePath.$key.'/el/' 01969 ); 01970 } 01971 } 01972 } else { 01973 if (is_array($dsConf['TCEforms']['config']) && is_array($dataValues[$key])) { 01974 foreach($dataValues[$key] as $vKey => $data) { 01975 01976 if ($callBackFunc) { 01977 if (is_object($this->callBackObj)) { 01978 $res = $this->callBackObj->$callBackFunc( 01979 $pParams, 01980 $dsConf['TCEforms']['config'], 01981 $dataValues[$key][$vKey], 01982 $dataValues_current[$key][$vKey], 01983 $uploadedFiles[$key][$vKey], 01984 $structurePath.$key.'/'.$vKey.'/' 01985 ); 01986 } else { 01987 $res = $this->$callBackFunc( 01988 $pParams, 01989 $dsConf['TCEforms']['config'], 01990 $dataValues[$key][$vKey], 01991 $dataValues_current[$key][$vKey], 01992 $uploadedFiles[$key][$vKey] 01993 ); 01994 } 01995 } else { // Default 01996 list($CVtable,$CVid,$CVcurValue,$CVstatus,$CVrealPid,$CVrecFID,$CVtscPID) = $pParams; 01997 01998 $res = $this->checkValue_SW( 01999 array(), 02000 $dataValues[$key][$vKey], 02001 $dsConf['TCEforms']['config'], 02002 $CVtable, 02003 $CVid, 02004 $dataValues_current[$key][$vKey], 02005 $CVstatus, 02006 $CVrealPid, 02007 $CVrecFID, 02008 '', 02009 $uploadedFiles[$key][$vKey], 02010 array(), 02011 $CVtscPID 02012 ); 02013 02014 // Look for RTE transformation of field: 02015 if ($dataValues[$key]['_TRANSFORM_'.$vKey] == 'RTE' && !$this->dontProcessTransformations) { 02016 02017 // Unsetting trigger field - we absolutely don't want that into the data storage! 02018 unset($dataValues[$key]['_TRANSFORM_'.$vKey]); 02019 02020 if (isset($res['value'])) { 02021 02022 // Calculating/Retrieving some values here: 02023 list(,,$recFieldName) = explode(':', $CVrecFID); 02024 $theTypeString = t3lib_BEfunc::getTCAtypeValue($CVtable,$this->checkValue_currentRecord); 02025 $specConf = t3lib_BEfunc::getSpecConfParts('',$dsConf['TCEforms']['defaultExtras']); 02026 02027 // Find, thisConfig: 02028 $RTEsetup = $this->BE_USER->getTSConfig('RTE',t3lib_BEfunc::getPagesTSconfig($CVtscPID)); 02029 $thisConfig = t3lib_BEfunc::RTEsetup($RTEsetup['properties'],$CVtable,$recFieldName,$theTypeString); 02030 02031 // Get RTE object, draw form and set flag: 02032 $RTEobj = &t3lib_BEfunc::RTEgetObj(); 02033 if (is_object($RTEobj)) { 02034 $res['value'] = $RTEobj->transformContent('db',$res['value'],$CVtable,$recFieldName,$this->checkValue_currentRecord,$specConf,$thisConfig,'',$CVrealPid); 02035 } else { 02036 debug('NO RTE OBJECT FOUND!'); 02037 } 02038 } 02039 } 02040 } 02041 02042 // Adding the value: 02043 if (isset($res['value'])) { 02044 $dataValues[$key][$vKey] = $res['value']; 02045 } 02046 } 02047 } 02048 } 02049 } 02050 } 02051 } 02052 02053 02054 02055 02056 02057 02058 02059 02060 02061 02062 02063 02064 02065 02066 02067 02068 02069 /********************************************* 02070 * 02071 * PROCESSING COMMANDS 02072 * 02073 ********************************************/ 02074 02081 function process_cmdmap() { 02082 global $TCA, $TYPO3_CONF_VARS; 02083 02084 // Editing frozen: 02085 if ($this->BE_USER->workspace!==0 && $this->BE_USER->workspaceRec['freeze']) { 02086 $this->newlog('All editing in this workspace has been frozen!',1); 02087 return FALSE; 02088 } 02089 02090 // Hook initialization: 02091 $hookObjectsArr = array(); 02092 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'])) { 02093 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'] as $classRef) { 02094 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef); 02095 } 02096 } 02097 02098 // Traverse command map: 02099 reset($this->cmdmap); 02100 while(list($table,) = each($this->cmdmap)) { 02101 02102 // Check if the table may be modified! 02103 $modifyAccessList = $this->checkModifyAccessList($table); 02104 if (!$modifyAccessList) { 02105 $id = 0; 02106 $this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table)); 02107 } // FIXME: $id not set here (Comment added by Sebastian Kurfuerst) 02108 02109 // Check basic permissions and circumstances: 02110 if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->cmdmap[$table]) && $modifyAccessList) { 02111 02112 // Traverse the command map: 02113 foreach($this->cmdmap[$table] as $id => $incomingCmdArray) { 02114 if (is_array($incomingCmdArray)) { // have found a command. 02115 02116 // Get command and value (notice, only one command is observed at a time!): 02117 reset($incomingCmdArray); 02118 $command = key($incomingCmdArray); 02119 $value = current($incomingCmdArray); 02120 02121 foreach($hookObjectsArr as $hookObj) { 02122 if (method_exists($hookObj, 'processCmdmap_preProcess')) { 02123 $hookObj->processCmdmap_preProcess($command, $table, $id, $value, $this); 02124 } 02125 } 02126 02127 // Init copyMapping array: 02128 $this->copyMappingArray = Array(); // Must clear this array before call from here to those functions: Contains mapping information between new and old id numbers. 02129 02130 // Branch, based on command 02131 switch ($command) { 02132 case 'move': 02133 $this->moveRecord($table,$id,$value); 02134 break; 02135 case 'copy': 02136 if ($table === 'pages') { 02137 $this->copyPages($id,$value); 02138 } else { 02139 $this->copyRecord($table,$id,$value,1); 02140 } 02141 break; 02142 case 'localize': 02143 $this->localize($table,$id,$value); 02144 break; 02145 case 'version': 02146 switch ((string)$value['action']) { 02147 case 'new': 02148 $versionizeTree = t3lib_div::intInRange(!isset($value['treeLevels'])?-1:$value['treeLevels'],-1,100); 02149 if ($table == 'pages' && $versionizeTree>=0) { 02150 $this->versionizePages($id,$value['label'],$versionizeTree); 02151 } else { 02152 $this->versionizeRecord($table,$id,$value['label']); 02153 } 02154 break; 02155 case 'swap': 02156 $this->version_swap($table,$id,$value['swapWith'],$value['swapIntoWS']); 02157 break; 02158 case 'clearWSID': 02159 $this->version_clearWSID($table,$id); 02160 break; 02161 case 'setStage': 02162 $idList = t3lib_div::trimExplode(',',$id,1); 02163 foreach($idList as $id) { 02164 $this->version_setStage($table,$id,$value['stageId'],$value['comment']?$value['comment']:$this->generalComment); 02165 } 02166 break; 02167 } 02168 break; 02169 case 'delete': 02170 $this->deleteAction($table, $id); 02171 break; 02172 case 'undelete': 02173 $this->undeleteRecord($table, $id); 02174 break; 02175 } 02176 02177 foreach($hookObjectsArr as $hookObj) { 02178 if (method_exists($hookObj, 'processCmdmap_postProcess')) { 02179 $hookObj->processCmdmap_postProcess($command, $table, $id, $value, $this); 02180 } 02181 } 02182 02183 // Merging the copy-array info together for remapping purposes. 02184 $this->copyMappingArray_merged= t3lib_div::array_merge_recursive_overrule($this->copyMappingArray_merged,$this->copyMappingArray); 02185 } 02186 } 02187 } 02188 } 02189 02190 // Finally, before exit, check if there are ID references to remap. This might be the case if versioning or copying has taken place! 02191 $this->remapListedDBRecords(); 02192 } 02193 02194 02195 02196 02197 02198 02199 02200 02201 02202 02203 02204 /********************************************* 02205 * 02206 * Cmd: Copying 02207 * 02208 ********************************************/ 02209 02221 function copyRecord($table,$uid,$destPid,$first=0,$overrideValues=array(),$excludeFields='') { 02222 global $TCA; 02223 02224 $uid = $origUid = intval($uid); 02225 if ($TCA[$table] && $uid) { 02226 t3lib_div::loadTCA($table); 02227 /* 02228 // 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) 02229 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid')) { 02230 $uid = $lookForLiveVersion['uid']; 02231 } 02232 // Get workspace version of the source record, if any: Then we will copy workspace version instead: 02233 if ($WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid')) { 02234 $uid = $WSversion['uid']; 02235 } 02236 // 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. 02237 */ 02238 if ($this->doesRecordExist($table,$uid,'show')) { // This checks if the record can be selected which is all that a copy action requires. 02239 $data = Array(); 02240 02241 $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)); 02242 02243 #$row = $this->recordInfo($table,$uid,'*'); 02244 $row = t3lib_BEfunc::getRecordWSOL($table,$uid); // So it copies (and localized) content from workspace... 02245 if (is_array($row)) { 02246 02247 // Initializing: 02248 $theNewID = uniqid('NEW'); 02249 $enableField = isset($TCA[$table]['ctrl']['enablecolumns']) ? $TCA[$table]['ctrl']['enablecolumns']['disabled'] : ''; 02250 $headerField = $TCA[$table]['ctrl']['label']; 02251 02252 // Getting default data: 02253 $defaultData = $this->newFieldArray($table); 02254 02255 // Getting "copy-after" fields if applicable: 02256 // 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. 02257 $copyAfterFields = $destPid<0 ? $this->fixCopyAfterDuplFields($table,$uid,abs($destPid),0) : array(); 02258 02259 // Page TSconfig related: 02260 $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... 02261 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 02262 $tE = $this->getTableEntries($table,$TSConfig); 02263 02264 // Traverse ALL fields of the selected record: 02265 foreach($row as $field => $value) { 02266 if (!in_array($field,$nonFields)) { 02267 02268 // Get TCA configuration for the field: 02269 $conf = $TCA[$table]['columns'][$field]['config']; 02270 02271 // Preparation/Processing of the value: 02272 if ($field=='pid') { // "pid" is hardcoded of course: 02273 $value = $destPid; 02274 } elseif (isset($overrideValues[$field])) { // Override value... 02275 $value = $overrideValues[$field]; 02276 } elseif (isset($copyAfterFields[$field])) { // Copy-after value if available: 02277 $value = $copyAfterFields[$field]; 02278 } elseif ($TCA[$table]['ctrl']['setToDefaultOnCopy'] && t3lib_div::inList($TCA[$table]['ctrl']['setToDefaultOnCopy'],$field)) { // Revert to default for some fields: 02279 $value = $defaultData[$field]; 02280 } else { 02281 // Hide at copy may override: 02282 if ($first && $field==$enableField && $TCA[$table]['ctrl']['hideAtCopy'] && !$this->neverHideAtCopy && !$tE['disableHideAtCopy']) { 02283 $value=1; 02284 } 02285 // Prepend label on copy: 02286 if ($first && $field==$headerField && $TCA[$table]['ctrl']['prependAtCopy'] && !$tE['disablePrependAtCopy']) { 02287 $value = $this->getCopyHeader($table,$this->resolvePid($table,$destPid),$field,$this->clearPrefixFromValue($table,$value),0); 02288 } 02289 // Processing based on the TCA config field type (files, references, flexforms...) 02290 $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf); 02291 } 02292 02293 // Add value to array. 02294 $data[$table][$theNewID][$field] = $value; 02295 } 02296 } 02297 02298 // Overriding values: 02299 if ($TCA[$table]['ctrl']['editlock']) { 02300 $data[$table][$theNewID][$TCA[$table]['ctrl']['editlock']] = 0; 02301 } 02302 02303 // Setting original UID: 02304 if ($TCA[$table]['ctrl']['origUid']) { 02305 $data[$table][$theNewID][$TCA[$table]['ctrl']['origUid']] = $uid; 02306 } 02307 02308 // Do the copy by simply submitting the array through TCEmain: 02309 $copyTCE = t3lib_div::makeInstance('t3lib_TCEmain'); 02310 $copyTCE->stripslashes_values = 0; 02311 $copyTCE->copyTree = $this->copyTree; 02312 $copyTCE->cachedTSconfig = $this->cachedTSconfig; // Copy forth the cached TSconfig 02313 $copyTCE->dontProcessTransformations=1; // Transformations should NOT be carried out during copy 02314 02315 $copyTCE->start($data,'',$this->BE_USER); 02316 $copyTCE->process_datamap(); 02317 02318 // Getting the new UID: 02319 $theNewSQLID = $copyTCE->substNEWwithIDs[$theNewID]; 02320 if ($theNewSQLID) { 02321 $this->copyMappingArray[$table][$origUid] = $theNewSQLID; 02322 } 02323 02324 // Copy back the cached TSconfig 02325 $this->cachedTSconfig = $copyTCE->cachedTSconfig; 02326 $this->errorLog = array_merge($this->errorLog,$copyTCE->errorLog); 02327 unset($copyTCE); 02328 02329 return $theNewSQLID; 02330 } else $this->log($table,$uid,3,0,1,'Attempt to copy record that did not exist!'); 02331 } else $this->log($table,$uid,3,0,1,'Attempt to copy record without permission'); 02332 } 02333 } 02334 02343 function copyPages($uid,$destPid) { 02344 02345 // Initialize: 02346 $uid = intval($uid); 02347 $destPid = intval($destPid); 02348 02349 // Finding list of tables to copy. 02350 $copyTablesArray = $this->admin ? $this->compileAdminTables() : explode(',',$this->BE_USER->groupData['tables_modify']); // These are the tables, the user may modify 02351 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 02352 foreach($copyTablesArray as $k => $table) { 02353 if (!$table || !t3lib_div::inList($this->copyWhichTables.',pages',$table)) { // pages are always going... 02354 unset($copyTablesArray[$k]); 02355 } 02356 } 02357 } 02358 $copyTablesArray = array_unique($copyTablesArray); 02359 02360 // Begin to copy pages if we're allowed to: 02361 if ($this->admin || in_array('pages',$copyTablesArray)) { 02362 02363 // Copy this page we're on. And set first-flag (this will trigger that the record is hidden if that is configured)! 02364 $theNewRootID = $this->copySpecificPage($uid,$destPid,$copyTablesArray,1); 02365 02366 // If we're going to copy recursively...: 02367 if ($theNewRootID && $this->copyTree) { 02368 02369 // Get ALL subpages to copy (read-permissions are respected!): 02370 $CPtable = $this->int_pageTreeInfo(Array(), $uid, intval($this->copyTree), $theNewRootID); 02371 02372 // Now copying the subpages: 02373 foreach($CPtable as $thePageUid => $thePagePid) { 02374 $newPid = $this->copyMappingArray['pages'][$thePagePid]; 02375 if (isset($newPid)) { 02376 $this->copySpecificPage($thePageUid,$newPid,$copyTablesArray); 02377 } else { 02378 $this->log('pages',$uid,5,0,1,'Something went wrong during copying branch'); 02379 break; 02380 } 02381 } 02382 } // else the page was not copied. Too bad... 02383 } else { 02384 $this->log('pages',$uid,5,0,1,'Attempt to copy page without permission to this table'); 02385 } 02386 } 02387 02397 function copySpecificPage($uid,$destPid,$copyTablesArray,$first=0) { 02398 global $TCA; 02399 02400 // Copy the page itself: 02401 $theNewRootID = $this->copyRecord('pages',$uid,$destPid,$first); 02402 02403 // If a new page was created upon the copy operation we will proceed with all the tables ON that page: 02404 if ($theNewRootID) { 02405 foreach($copyTablesArray as $table) { 02406 if ($table && is_array($TCA[$table]) && $table!='pages') { // all records under the page is copied. 02407 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table), '', ($TCA[$table]['ctrl']['sortby'] ? $TCA[$table]['ctrl']['sortby'].' DESC' : '')); 02408 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 02409 $this->copyRecord($table,$row['uid'], $theNewRootID); // Copying each of the underlying records... 02410 } 02411 } 02412 } 02413 return $theNewRootID; 02414 } 02415 } 02416 02431 function copyRecord_raw($table,$uid,$pid,$overrideArray=array()) { 02432 global $TCA; 02433 02434 $uid = intval($uid); 02435 if ($TCA[$table] && $uid) { 02436 t3lib_div::loadTCA($table); 02437 if ($this->doesRecordExist($table,$uid,'show')) { 02438 02439 // Set up fields which should not be processed. They are still written - just passed through no-questions-asked! 02440 $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'); 02441 02442 // Select main record: 02443 $row = $this->recordInfo($table,$uid,'*'); 02444 if (is_array($row)) { 02445 02446 // Merge in override array. 02447 $row = array_merge($row,$overrideArray); 02448 02449 // Traverse ALL fields of the selected record: 02450 foreach($row as $field => $value) { 02451 if (!in_array($field,$nonFields)) { 02452 02453 // Get TCA configuration for the field: 02454 $conf = $TCA[$table]['columns'][$field]['config']; 02455 if (is_array($conf)) { 02456 // Processing based on the TCA config field type (files, references, flexforms...) 02457 $value = $this->copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf); 02458 } 02459 02460 // Add value to array. 02461 $row[$field] = $value; 02462 } 02463 } 02464 02465 // Force versioning related fields: 02466 $row['pid'] = $pid; 02467 02468 // Setting original UID: 02469 if ($TCA[$table]['ctrl']['origUid']) { 02470 $row[$TCA[$table]['ctrl']['origUid']] = $uid; 02471 } 02472 02473 // Do the copy by internal function 02474 $theNewSQLID = $this->insertNewCopyVersion($table,$row,$pid); 02475 if ($theNewSQLID) { 02476 $this->dbAnalysisStoreExec(); 02477 $this->dbAnalysisStore = array(); 02478 return $this->copyMappingArray[$table][$uid] = $theNewSQLID; 02479 } 02480 } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record that did not exist!'); 02481 } else $this->log($table,$uid,3,0,1,'Attempt to rawcopy/versionize record without copy permission'); 02482 } 02483 } 02484 02495 function rawCopyPageContent($old_pid,$new_pid,$copyTablesArray) { 02496 global $TCA; 02497 02498 if ($new_pid) { 02499 foreach($copyTablesArray as $table) { 02500 if ($table && is_array($TCA[$table]) && $table!='pages') { // all records under the page is copied. 02501 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($old_pid).$this->deleteClause($table)); 02502 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 02503 $this->copyRecord_raw($table,$row['uid'],$new_pid); // Copying each of the underlying records (method RAW) 02504 } 02505 } 02506 } 02507 } 02508 } 02509 02519 function insertNewCopyVersion($table,$fieldArray,$realPid) { 02520 global $TCA; 02521 02522 $id = uniqid('NEW'); 02523 02524 // $fieldArray is set as current record. 02525 // 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... 02526 $this->checkValue_currentRecord = $fieldArray; 02527 02528 // Traverse record and input-process each value: 02529 foreach($fieldArray as $field => $fieldValue) { 02530 if (isset($TCA[$table]['columns'][$field])) { 02531 // Evaluating the value. 02532 $res = $this->checkValue($table,$field,$fieldValue,$id,'new',$realPid,0); 02533 if (isset($res['value'])) { 02534 $fieldArray[$field] = $res['value']; 02535 } 02536 } 02537 } 02538 02539 // System fields being set: 02540 if ($TCA[$table]['ctrl']['crdate']) { 02541 $fieldArray[$TCA[$table]['ctrl']['crdate']]=time(); 02542 } 02543 if ($TCA[$table]['ctrl']['cruser_id']) { 02544 $fieldArray[$TCA[$table]['ctrl']['cruser_id']]=$this->userid; 02545 } 02546 if ($TCA[$table]['ctrl']['tstamp']) { 02547 $fieldArray[$TCA[$table]['ctrl']['tstamp']]=time(); 02548 } 02549 02550 // Finally, insert record: 02551 $this->insertDB($table,$id,$fieldArray, TRUE); 02552 02553 // Return new id: 02554 return $this->substNEWwithIDs[$id]; 02555 } 02556 02570 function copyRecord_procBasedOnFieldType($table,$uid,$field,$value,$row,$conf) { 02571 global $TCA; 02572 02573 // 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) 02574 $value = $this->copyRecord_procFilesRefs($conf, $uid, $value); 02575 02576 02577 // Register if there are references to take care of (no change to value): 02578 if ($this->isReferenceField($conf)) { 02579 $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table']; 02580 $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : $conf['neg_foreign_table']; 02581 if ($conf['MM']) { 02582 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 02583 $dbAnalysis->start('',$allowedTables,$conf['MM'],$uid); 02584 $value = implode(',',$dbAnalysis->getValueArray($prependName)); 02585 } 02586 if ($value) { // Setting the value in this array will notify the remapListedDBRecords() function that this field MAY need references to be corrected 02587 $this->registerDBList[$table][$uid][$field] = $value; 02588 } 02589 } 02590 02591 // 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()) 02592 if ($conf['type']=='flex') { 02593 02594 // Get current value array: 02595 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $row, $table); 02596 $currentValueArray = t3lib_div::xml2array($value); 02597 02598 // Traversing the XML structure, processing files: 02599 if (is_array($currentValueArray)) { 02600 $currentValueArray['data'] = $this->checkValue_flex_procInData( 02601 $currentValueArray['data'], 02602 array(), // Not used. 02603 array(), // Not used. 02604 $dataStructArray, 02605 array($table,$uid,$field), // Parameters. 02606 'copyRecord_flexFormCallBack' 02607 ); 02608 $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. 02609 } 02610 } 02611 02612 return $value; 02613 } 02614 02626 function copyRecord_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2) { 02627 02628 // Extract parameters: 02629 list($table, $uid, $field) = $pParams; 02630 02631 // Process references and files, currently that means only the files, prepending absolute paths: 02632 $dataValue = $this->copyRecord_procFilesRefs($dsConf, $uid, $dataValue); 02633 02634 // If references are set for this field, set flag so they can be corrected later (in ->remapListedDBRecords()) 02635 if ($this->isReferenceField($dsConf) && strlen($dataValue)) { 02636 $this->registerDBList[$table][$uid][$field] = 'FlexForm_reference'; 02637 } 02638 02639 // Return 02640 return array('value' => $dataValue); 02641 } 02642 02654 function copyRecord_procFilesRefs($conf, $uid, $value) { 02655 02656 // Prepend absolute paths to files: 02657 if ($conf['type']=='group' && $conf['internal_type']=='file') { 02658 02659 // Get an array with files as values: 02660 if ($conf['MM']) { 02661 $theFileValues = array(); 02662 02663 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 02664 $dbAnalysis->start('', 'files', $conf['MM'], $uid); 02665 02666 foreach($dbAnalysis->itemArray as $somekey => $someval) { 02667 if ($someval['id']) { 02668 $theFileValues[] = $someval['id']; 02669 } 02670 } 02671 } else { 02672 $theFileValues = t3lib_div::trimExplode(',',$value,1); 02673 } 02674 02675 // Traverse this array of files: 02676 $uploadFolder = $conf['uploadfolder']; 02677 $dest = $this->destPathFromUploadFolder($uploadFolder); 02678 $newValue = array(); 02679 02680 foreach($theFileValues as $file) { 02681 if (trim($file)) { 02682 $realFile = $dest.'/'.trim($file); 02683 if (@is_file($realFile)) { 02684 $newValue[] = $realFile; 02685 } 02686 } 02687 } 02688 02689 // 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...) 02690 $value = implode(',',$newValue); 02691 } 02692 02693 // Return the new value: 02694 return $value; 02695 } 02696 02697 02698 02699 02700 02701 02702 02703 02704 02705 02706 02707 02708 02709 /********************************************* 02710 * 02711 * Cmd: Moving, Localizing 02712 * 02713 ********************************************/ 02714 02723 function moveRecord($table,$uid,$destPid) { 02724 global $TCA, $TYPO3_CONF_VARS; 02725 02726 if ($TCA[$table]) { 02727 02728 // Prepare user defined objects (if any) for hooks which extend this function: 02729 $hookObjectsArr = array(); 02730 if (is_array ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'])) { 02731 foreach ($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['moveRecordClass'] as $classRef) { 02732 $hookObjectsArr[] = &t3lib_div::getUserObj($classRef); 02733 } 02734 } 02735 02736 // 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) 02737 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$uid,'uid')) { 02738 $uid = $lookForLiveVersion['uid']; 02739 } 02740 02741 // Get workspace version of the source record, if any: 02742 $WSversion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $uid, 'uid,t3ver_oid'); 02743 02744 // Initialize: 02745 $sortRow = $TCA[$table]['ctrl']['sortby']; 02746 $destPid = intval($destPid); 02747 $origDestPid = $destPid; 02748 02749 $propArr = $this->getRecordProperties($table,$uid); // Get this before we change the pid (for logging) 02750 $moveRec = $this->getRecordProperties($table,$uid,TRUE); 02751 $resolvedPid = $this->resolvePid($table,$destPid); // This is the actual pid of the moving to destination 02752 02753 // 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. 02754 // 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. 02755 if ($table!='pages' || $resolvedPid==$moveRec['pid']) { 02756 $mayMoveAccess = $this->checkRecordUpdateAccess($table,$uid); // Edit rights for the record... 02757 } else { 02758 $mayMoveAccess = $this->doesRecordExist($table,$uid,'delete'); 02759 } 02760 02761 // 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 02762 if ($table!='pages' || $resolvedPid!=$moveRec['pid']) { 02763 $mayInsertAccess = $this->checkRecordInsertAccess($table,$resolvedPid,4); // Insert rights for the record... 02764 } else { 02765 $mayInsertAccess = $this->checkRecordUpdateAccess($table,$uid); 02766 } 02767 02768 // Check workspace permissions: 02769 $workspaceAccessBlocked = array(); 02770 $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... 02771 $destRes = $this->BE_USER->workspaceAllowLiveRecordsInPID($resolvedPid,$table); 02772 // Workspace source check: 02773 if ($errorCode = $this->BE_USER->workspaceCannotEditRecord($table, $WSversion['uid'] ? $WSversion['uid'] : $uid)) { 02774 $workspaceAccessBlocked['src1']='Record could not be edited in workspace: '.$errorCode.' '; 02775 } else { 02776 if (!$recIsNewVersion && $this->BE_USER->workspaceAllowLiveRecordsInPID($moveRec['pid'],$table)<=0) { 02777 $workspaceAccessBlocked['src2']='Could not remove record from table "'.$table.'" from its page "'.$moveRec['pid'].'" '; 02778 } 02779 } 02780 // Workspace destination check: 02781 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. 02782 $workspaceAccessBlocked['dest1']='Could not insert record from table "'.$table.'" in destination PID "'.$resolvedPid.'" '; 02783 } elseif ($destRes==1 && $WSversion['uid']) { 02784 $workspaceAccessBlocked['dest2']='Could not insert other versions in destination PID '; 02785 } 02786 02787 // 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...) 02788 if (($destPid<0 && !$sortRow) || $destPid>=0) { // $destPid>=0 because we must correct pid in case of versioning "page" types. 02789 $destPid = $resolvedPid; 02790 } 02791 02792 // Timestamp field: 02793 $updateFields = array(); 02794 if ($TCA[$table]['ctrl']['tstamp']) { 02795 $updateFields[$TCA[$table]['ctrl']['tstamp']] = time(); 02796 } 02797 02798 // If moving is allowed, begin the processing: 02799 if (!count($workspaceAccessBlocked)) { 02800 if ($mayMoveAccess) { 02801 if ($destPid>=0) { // insert as first element on page (where uid = $destPid) 02802 if ($mayInsertAccess) { 02803 if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid)) { 02804 $this->clear_cache($table,$uid); // clear cache before moving 02805 02806 $updateFields['pid'] = $destPid; // Setting PID 02807 02808 // table is sorted by 'sortby' 02809 if ($sortRow) { 02810 $sortNumber = $this->getSortNumber($table,$uid,$destPid); 02811 $updateFields[$sortRow] = $sortNumber; 02812 } 02813 // Create query for update: 02814 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields); 02815 02816 // Call post processing hooks: 02817 foreach($hookObjectsArr as $hookObj) { 02818 if (method_exists($hookObj, 'moveRecord_firstElementPostProcess')) { 02819 $hookObj->moveRecord_firstElementPostProcess($table, $uid, $destPid, $moveRec, $updateFields, $this); 02820 } 02821 } 02822 02823 // Logging... 02824 $newPropArr = $this->getRecordProperties($table,$uid); 02825 $oldpagePropArr = $this->getRecordProperties('pages',$propArr['pid']); 02826 $newpagePropArr = $this->getRecordProperties('pages',$destPid); 02827 02828 if ($destPid!=$propArr['pid']) { 02829 $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 02830 $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 02831 } else { 02832 $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 02833 } 02834 $this->clear_cache($table,$uid); // clear cache after moving 02835 $this->fixUniqueInPid($table,$uid); 02836 // fixCopyAfterDuplFields 02837 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. 02838 } else { 02839 $destPropArr = $this->getRecordProperties('pages',$destPid); 02840 $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']); 02841 } 02842 } 02843 } else { // Put after another record 02844 if ($sortRow) { // table is being sorted 02845 $sortInfo = $this->getSortNumber($table,$uid,$destPid); 02846 $destPid = $sortInfo['pid']; // Setting the destPid to the new pid of the record. 02847 if (is_array($sortInfo)) { // If not an array, there was an error (which is already logged) 02848 if ($mayInsertAccess) { 02849 if ($table!='pages' || $this->destNotInsideSelf($destPid,$uid)) { 02850 $this->clear_cache($table,$uid); // clear cache before moving 02851 02852 // We now update the pid and sortnumber 02853 $updateFields['pid'] = $destPid; 02854 $updateFields[$sortRow] = $sortInfo['sortNumber']; 02855 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields); 02856 02857 // Call post processing hooks: 02858 foreach($hookObjectsArr as $hookObj) { 02859 if (method_exists($hookObj, 'moveRecord_afterAnotherElementPostProcess')) { 02860 $hookObj->moveRecord_afterAnotherElementPostProcess($table, $uid, $destPid, $origDestPid, $moveRec, $updateFields, $this); 02861 } 02862 } 02863 02864 // Logging... 02865 $newPropArr = $this->getRecordProperties($table,$uid); 02866 $oldpagePropArr = $this->getRecordProperties('pages',$propArr['pid']); 02867 if ($destPid!=$propArr['pid']) { 02868 $newpagePropArr = $this->getRecordProperties('pages',$destPid); 02869 $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 02870 $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 02871 } else { 02872 $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 02873 } 02874 02875 // clear cache after moving 02876 $this->clear_cache($table,$uid); 02877 02878 // fixUniqueInPid 02879 $this->fixUniqueInPid($table,$uid); 02880 02881 // fixCopyAfterDuplFields 02882 if ($origDestPid<0) {$this->fixCopyAfterDuplFields($table,$uid,abs($origDestPid),1);} 02883 } else { 02884 $destPropArr = $this->getRecordProperties('pages',$destPid); 02885 $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']); 02886 } 02887 } 02888 } 02889 } else { 02890 $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']); 02891 } 02892 } 02893 } else { 02894 $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']); 02895 } 02896 } else { 02897 $this->newlog("Move attempt failed due to workspace restrictions: ".implode(' ',$workspaceAccessBlocked),1); 02898 } 02899 } 02900 } 02901 02910 function localize($table,$uid,$language) { 02911 global $TCA; 02912 02913 $uid = intval($uid); 02914 02915 if ($TCA[$table] && $uid) { 02916 t3lib_div::loadTCA($table); 02917 02918 if ($TCA[$table]['ctrl']['languageField'] && $TCA[$table]['ctrl']['transOrigPointerField']) { 02919 if ($langRec = t3lib_BEfunc::getRecord('sys_language',intval($language),'uid,title')) { 02920 if ($this->doesRecordExist($table,$uid,'show')) { 02921 02922 $row = t3lib_BEfunc::getRecordWSOL($table,$uid); // Getting workspace overlay if possible - this will localize versions in workspace if any 02923 if (is_array($row)) { 02924 if ($row[$TCA[$table]['ctrl']['languageField']] <= 0) { 02925 if ($row[$TCA[$table]['ctrl']['transOrigPointerField']] == 0) { 02926 if (!t3lib_BEfunc::getRecordsByField($table,$TCA[$table]['ctrl']['transOrigPointerField'],$uid,'AND pid='.intval($row['pid']).' AND '.$TCA[$table]['ctrl']['languageField'].'='.intval($langRec['uid']))) { 02927 02928 // Initialize: 02929 $overrideValues = array(); 02930 $excludeFields = array(); 02931 02932 // Set override values: 02933 $overrideValues[$TCA[$table]['ctrl']['languageField']] = $langRec['uid']; 02934 $overrideValues[$TCA[$table]['ctrl']['transOrigPointerField']] = $uid; 02935 02936 // Set exclude Fields: 02937 foreach($TCA[$table]['columns'] as $fN => $fCfg) { 02938 if ($fCfg['l10n_mode']=='prefixLangTitle') { // Check if we are just prefixing: 02939 if (($fCfg['config']['type']=='text' || $fCfg['config']['type']=='input') && strlen($row[$fN])) { 02940 list($tscPID) = t3lib_BEfunc::getTSCpid($table,$uid,''); 02941 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 02942 02943 if (isset($TSConfig['translateToMessage']) && strlen($TSConfig['translateToMessage'])) { 02944 $translateToMsg = @sprintf($TSConfig['translateToMessage'], $langRec['title']); 02945 } 02946 if (!strlen($translateToMsg)) { 02947 $translateToMsg = 'Translate to '.$langRec['title'].':'; 02948 } 02949 02950 $overrideValues[$fN] = '['.$translateToMsg.'] '.$row[$fN]; 02951 } 02952 } 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) 02953 $excludeFields[] = $fN; 02954 } 02955 } 02956 // Execute the copy: 02957 $this->copyRecord($table,$uid,-$uid,1,$overrideValues,implode(',',$excludeFields)); 02958 } else $this->newlog('Localization failed; There already was a localization for this language of the record!',1); 02959 } else $this->newlog('Localization failed; Source record contained a reference to an original default record (which is strange)!',1); 02960 } else $this->newlog('Localization failed; Source record had another language than "Default" or "All" defined!',1); 02961 } else $this->newlog('Attempt to localize record that did not exist!',1); 02962 } else $this->newlog('Attempt to localize record without permission',1); 02963 } else $this->newlog('Sys language UID "'.$language.'" not found valid!',1); 02964 } else $this->newlog('Localization failed; "languageField" and "transOrigPointerField" must be defined for the table!',1); 02965 } 02966 } 02967 02968 02969 02970 02971 02972 02973 02974 02975 02976 02977 02978 02979 02980 02981 /********************************************* 02982 * 02983 * Cmd: Deleting 02984 * 02985 ********************************************/ 02986 02994 function deleteAction($table, $id) { 02995 global $TCA; 02996 02997 $delRec = t3lib_BEfunc::getRecord($table, $id); 02998 02999 if (is_array($delRec)) { // Record asked to be deleted was found: 03000 03001 // For Live version, try if there is a workspace version because if so, rather "delete" that instead 03002 if ($delRec['pid']!=-1) { // Look, if record is an offline version, then delete directly: 03003 if ($wsVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, $table, $id)) { 03004 $delRec = $wsVersion; 03005 $id = $delRec['uid']; 03006 } 03007 } 03008 03009 if ($delRec['pid']==-1) { // Look, if record is an offline version, then delete directly: 03010 if ($TCA[$table]['ctrl']['versioningWS']) { 03011 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. 03012 $liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'uid,t3ver_state'); 03013 03014 if ($delRec['t3ver_wsid']==0 || (int)$liveRec['t3ver_state']!==1) { // Delete those in WS 0 + if their live records state was not "Placeholder". 03015 $this->deleteEl($table, $id); 03016 } else { // If live record was placeholder, rather clear it from workspace (because it clears both version and placeholder). 03017 $this->version_clearWSID($table,$id); 03018 } 03019 } else $this->newlog('Tried to delete record from another workspace',1); 03020 } else $this->newlog('Versioning not enabled for record with PID = -1!',2); 03021 } elseif ($res = $this->BE_USER->workspaceAllowLiveRecordsInPID($delRec['pid'], $table)) { // Look, if record is "online" or in a versionized branch, then delete directly. 03022 if ($res>0) { 03023 $this->deleteEl($table, $id); 03024 } else $this->newlog('Stage of root point did not allow for deletion',1); 03025 } else { 03026 // Otherwise, try to delete by versionization: 03027 $this->versionizeRecord($table,$id,'DELETED!',TRUE); 03028 } 03029 } 03030 } 03031 03041 function deleteEl($table, $uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE) { 03042 if ($table == 'pages') { 03043 $this->deletePages($uid, $noRecordCheck, $forceHardDelete); 03044 } else { 03045 $this->deleteRecord($table, $uid, $noRecordCheck, $forceHardDelete); 03046 } 03047 } 03048 03056 function undeleteRecord($table,$uid) { 03057 $this->deleteRecord($table,$uid,TRUE,FALSE,TRUE); 03058 } 03059 03073 function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE,$undeleteRecord=FALSE) { 03074 global $TCA; 03075 03076 $uid = intval($uid); 03077 if ($TCA[$table] && $uid) { 03078 if ($noRecordCheck || $this->doesRecordExist($table,$uid,'delete')) { 03079 $this->clear_cache($table,$uid); // clear cache before deleting the record, else the correct page cannot be identified by clear_cache 03080 03081 $propArr = $this->getRecordProperties($table, $uid); 03082 $pagePropArr = $this->getRecordProperties('pages', $propArr['pid']); 03083 03084 $deleteRow = $TCA[$table]['ctrl']['delete']; 03085 if ($deleteRow && !$forceHardDelete) { 03086 $value = $undeleteRecord ? 0 : 1; 03087 $updateFields = array( 03088 $deleteRow => $value 03089 ); 03090 03091 if ($TCA[$table]['ctrl']['tstamp']) { 03092 $updateFields[$TCA[$table]['ctrl']['tstamp']] = time(); 03093 } 03094 03095 // If the table is sorted, then the sorting number is set very high 03096 if ($TCA[$table]['ctrl']['sortby'] && !$undeleteRecord) { 03097 $updateFields[$TCA[$table]['ctrl']['sortby']] = 1000000000; 03098 } 03099 03100 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), $updateFields); 03101 } else { 03102 03103 // Fetches all fields that holds references to files 03104 $fileFieldArr = $this->extFileFields($table); 03105 if (count($fileFieldArr)) { 03106 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery(implode(',',$fileFieldArr), $table, 'uid='.intval($uid)); 03107 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 03108 $fArray = $fileFieldArr; 03109 foreach($fArray as $theField) { // MISSING: Support for MM file relations! 03110 $this->extFileFunctions($table,$theField,$row[$theField],'deleteAll'); // This deletes files that belonged to this record. 03111 } 03112 } else { 03113 $this->log($table,$uid,3,0,100,'Delete: Zero rows in result when trying to read filenames from record which should be deleted'); 03114 } 03115 } 03116 03117 $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($uid)); 03118 } 03119 03120 $state = $undeleteRecord ? 1 : 3; // 1 means insert, 3 means delete 03121 if (!$GLOBALS['TYPO3_DB']->sql_error()) { 03122 if ($forceHardDelete) { 03123 $message = "Record '%s' (%s) was deleted unrecoverable from page '%s' (%s)"; 03124 } 03125 else { 03126 $message = $state == 1 ? 03127 "Record '%s' (%s) was restored on page '%s' (%s)" : 03128 "Record '%s' (%s) was deleted from page '%s' (%s)"; 03129 } 03130 $this->log($table, $uid, $state, 0, 0, 03131 $message, 0, 03132 array( 03133 $propArr['header'], 03134 $table.':'.$uid, 03135 $pagePropArr['header'], 03136 $propArr['pid'] 03137 ), 03138 $propArr['pid']); 03139 03140 } else { 03141 $this->log($table,$uid,$state,0,100,$GLOBALS['TYPO3_DB']->sql_error()); 03142 } 03143 03144 // Update reference index: 03145 $this->updateRefIndex($table,$uid); 03146 03147 } else $this->log($table,$uid,3,0,1,'Attempt to delete record without delete-permissions'); 03148 } 03149 } 03150 03159 function deletePages($uid,$force=FALSE,$forceHardDelete=FALSE) { 03160 // Getting list of pages to delete: 03161 if ($force) { 03162 $brExist = $this->doesBranchExist('',$uid,0,1); // returns the branch WITHOUT permission checks (0 secures that) 03163 $res = t3lib_div::trimExplode(',',$brExist.$uid,1); 03164 } else { 03165 $res = $this->canDeletePage($uid); 03166 } 03167 03168 // Perform deletion if not error: 03169 if (is_array($res)) { 03170 foreach($res as $deleteId) { 03171 $this->deleteSpecificPage($deleteId,$forceHardDelete); 03172 } 03173 } else { 03174 $this->newlog($res,1); 03175 } 03176 } 03177 03187 function deleteSpecificPage($uid,$forceHardDelete=FALSE) { 03188 global $TCA; 03189 reset ($TCA); 03190 $uid = intval($uid); 03191 if ($uid) { 03192 while (list($table)=each($TCA)) { 03193 if ($table!='pages') { 03194 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($uid).$this->deleteClause($table)); 03195 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 03196 $this->deleteRecord($table,$row['uid'], TRUE, $forceHardDelete); 03197 } 03198 } 03199 } 03200 $this->deleteRecord('pages',$uid, TRUE, $forceHardDelete); 03201 } 03202 } 03203 03210 function canDeletePage($uid) { 03211 if ($this->doesRecordExist('pages',$uid,'delete')) { // If we may at all delete this page 03212 if ($this->deleteTree) { 03213 $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1); // returns the branch 03214 if ($brExist != -1) { // Checks if we had permissions 03215 if ($this->noRecordsFromUnallowedTables($brExist.$uid)) { 03216 return t3lib_div::trimExplode(',',$brExist.$uid,1); 03217 } else return 'Attempt to delete records from disallowed tables'; 03218 } else return 'Attempt to delete pages in branch without permissions'; 03219 } else { 03220 $brExist = $this->doesBranchExist('',$uid,$this->pMap['delete'],1); // returns the branch 03221 if ($brExist == '') { // Checks if branch exists 03222 if ($this->noRecordsFromUnallowedTables($uid)) { 03223 return array($uid); 03224 } else return 'Attempt to delete records from disallowed tables'; 03225 } else return 'Attempt to delete page which has subpages'; 03226 } 03227 } else return 'Attempt to delete page without permissions'; 03228 } 03229 03237 function cannotDeleteRecord($table,$id) { 03238 if ($table==='pages') { 03239 $res = $this->canDeletePage($id); 03240 return is_array($res) ? FALSE : $res; 03241 } else { 03242 return $this->doesRecordExist($table,$id,'delete') ? FALSE : 'No permission to delete record'; 03243 } 03244 } 03245 03246 03247 03248 03249 03250 03251 03252 03253 03254 03255 03256 03257 /********************************************* 03258 * 03259 * Cmd: Versioning 03260 * 03261 ********************************************/ 03262 03275 function versionizeRecord($table,$id,$label,$delete=FALSE,$versionizeTree=-1) { 03276 global $TCA; 03277 03278 $id = intval($id); 03279 03280 if ($TCA[$table] && $TCA[$table]['ctrl']['versioningWS'] && $id>0) { 03281 if ($this->doesRecordExist($table,$id,'show')) { 03282 if ($this->BE_USER->workspaceVersioningTypeAccess($versionizeTree)) { 03283 03284 // Select main record: 03285 $row = $this->recordInfo($table,$id,'pid,t3ver_id'); 03286 if (is_array($row)) { 03287 if ($row['pid']>=0) { // record must be online record 03288 if (!$delete || !$this->cannotDeleteRecord($table,$id)) { 03289 03290 // Look for next version number: 03291 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 03292 't3ver_id', 03293 $table, 03294 '(t3ver_oid='.$id.' || uid='.$id.')'.$this->deleteClause($table), 03295 '', 03296 't3ver_id DESC', 03297 '1' 03298 ); 03299 list($highestVerNumber) = $GLOBALS['TYPO3_DB']->sql_fetch_row($res); 03300 03301 // Look for version number of the current: 03302 $subVer = $row['t3ver_id'].'.'.($highestVerNumber+1); 03303 03304 // Set up the values to override when making a raw-copy: 03305 $overrideArray = array( 03306 't3ver_id' => $highestVerNumber+1, 03307 't3ver_oid' => $id, 03308 't3ver_label' => ($label ? $label : $subVer.' / '.date('d-m-Y H:m:s')), 03309 't3ver_wsid' => $this->BE_USER->workspace, 03310 't3ver_state' => $delete ? 2 : 0, 03311 't3ver_count' => 0, 03312 't3ver_stage' => 0, 03313 't3ver_tstamp' => 0 03314 ); 03315 if ($TCA[$table]['ctrl']['editlock']) { 03316 $overrideArray[$TCA[$table]['ctrl']['editlock']] = 0; 03317 } 03318 if ($table==='pages') { 03319 $overrideArray['t3ver_swapmode'] = $versionizeTree; 03320 } 03321 03322 // Checking if the record already has a version in the current workspace of the backend user 03323 $workspaceCheck = TRUE; 03324 if ($this->BE_USER->workspace!==0) { 03325 // Look for version already in workspace: 03326 $workspaceCheck = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace,$table,$id,'uid') ? FALSE : TRUE; 03327 } 03328 03329 if ($workspaceCheck) { 03330 03331 // Create raw-copy and return result: 03332 return $this->copyRecord_raw($table,$id,-1,$overrideArray); 03333 } else $this->newlog('Record you wanted to versionize was already a version in the workspace (wsid='.$this->BE_USER->workspace.')!',1); 03334 } else $this->newlog('Record cannot be deleted: '.$this->cannotDeleteRecord($table,$id),1); 03335 } else $this->newlog('Record you wanted to versionize was already a version in archive (pid=-1)!',1); 03336 } else $this->newlog('Record you wanted to versionize did not exist!',1); 03337 } else $this->newlog('The versioning type '.$versionizeTree.' mode you requested was not allowed',1); 03338 } else $this->newlog('You didnt have correct permissions to make a new version (copy) of this record "'.$table.'" / '.$id,1); 03339 } else $this->newlog('Versioning is not supported for this table "'.$table.'" / '.$id,1); 03340 } 03341 03351 function versionizePages($uid,$label,$versionizeTree) { 03352 global $TCA; 03353 03354 $uid = intval($uid); 03355 $brExist = $this->doesBranchExist('',$uid,$this->pMap['show'],1); // returns the branch 03356 03357 if ($brExist != -1) { // Checks if we had permissions 03358 03359 // Finding list of tables ALLOWED to be copied 03360 $allowedTablesArray = $this->admin ? $this->compileAdminTables() : explode(',',$this->BE_USER->groupData['tables_modify']); // These are the tables, the user may modify 03361 $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. 03362 03363 // Make list of tables that should come along with a new version of the page: 03364 $verTablesArray = array(); 03365 $allTables = array_keys($TCA); 03366 foreach($allTables as $tN) { 03367 if ($tN!='pages' && ($versionizeTree>0 || $TCA[$tN]['ctrl']['versioning_followPages']) && ($this->admin || in_array($tN, $allowedTablesArray))) { 03368 $verTablesArray[] = $tN; 03369 } 03370 } 03371 03372 // Begin to copy pages if we're allowed to: 03373 if ($this->admin || in_array('pages',$allowedTablesArray)) { 03374 if ($this->BE_USER->workspaceVersioningTypeAccess($versionizeTree)) { 03375 // Versionize this page: 03376 $theNewRootID = $this->versionizeRecord('pages',$uid,$label,FALSE,$versionizeTree); 03377 if ($theNewRootID) { 03378 $this->rawCopyPageContent($uid,$theNewRootID,$verTablesArray); 03379 03380 // If we're going to copy recursively...: 03381 if ($versionizeTree>0) { 03382 03383 // Get ALL subpages to copy (read permissions respected - they should NOT be...): 03384 $CPtable = $this->int_pageTreeInfo(Array(), $uid, intval($versionizeTree), $theNewRootID); 03385 03386 // Now copying the subpages: 03387 foreach($CPtable as $thePageUid => $thePagePid) { 03388 $newPid = $this->copyMappingArray['pages'][$thePagePid]; 03389 if (isset($newPid)) { 03390 $theNewRootID = $this->copyRecord_raw('pages',$thePageUid,$newPid); 03391 $this->rawCopyPageContent($thePageUid,$theNewRootID,$verTablesArray); 03392 } else { 03393 $this->newlog('Something went wrong during copying branch (for versioning)',1); 03394 break; 03395 } 03396 } 03397 } // else the page was not copied. Too bad... 03398 } else $this->newlog('The root version could not be created!',1); 03399 } else $this->newlog('Versioning type "'.$versionizeTree.'" was not allowed in workspace',1); 03400 } else $this->newlog('Attempt to versionize page without permission to this table',1); 03401 } else $this->newlog('Could not read all subpages to versionize.',1); 03402 } 03403 03414 function version_swap($table,$id,$swapWith,$swapIntoWS=0) { 03415 global $TCA; 03416 03417 /* 03418 Version ID swapping principles: 03419 - 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 03420 03421 uid pid uid t3ver_oid pid 03422 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) 03423 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)) 03424 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.) 03425 03426 13 is online UID, 03427 247 is specific versions UID 03428 123 is the PID of the original record 03429 -1 is the versioning repository PID 03430 03431 Recovery Process: 03432 Search for negative UID (here "-13"): 03433 YES: Step 1 completed, but at least step 3 didn't. 03434 Search for the negativ UIDs positive (here: "13") 03435 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) 03436 NO: Only Step 1 completed! Rollback: Just change uid "-13" to "13" and "t3ver_oid" to "13" (not important) 03437 NO: No problems. 03438 */ 03439 03440 // First, check if we may actually edit the online record 03441 if ($this->checkRecordUpdateAccess($table,$id)) { 03442 03443 // Select the two versions: 03444 $curVersion = t3lib_BEfunc::getRecord($table,$id,'*'); 03445 $swapVersion = t3lib_BEfunc::getRecord($table,$swapWith,'*'); 03446 03447 if (is_array($curVersion) && is_array($swapVersion)) { 03448 if ($this->BE_USER->workspacePublishAccess($swapVersion['t3ver_wsid'])) { 03449 $wsAccess = $this->BE_USER->checkWorkspace($swapVersion['t3ver_wsid']); 03450 if ($swapVersion['t3ver_wsid']<=0 || !($wsAccess['publish_access']&1) || (int)$swapVersion['t3ver_stage']===10) { 03451 if ($this->doesRecordExist($table,$swapWith,'show') && $this->checkRecordUpdateAccess($table,$swapWith)) { 03452 if (!$swapIntoWS || $this->BE_USER->workspaceSwapAccess()) { 03453 03454 // Check if the swapWith record really IS a version of the original! 03455 if ((int)$swapVersion['pid']==-1 && (int)$curVersion['pid']>=0 && !strcmp($swapVersion['t3ver_oid'],$id)) { 03456 03457 // Lock file name: 03458 $lockFileName = PATH_site.'typo3temp/swap_locking/'.$table.':'.$id.'.ser'; 03459 03460 if (!@is_file($lockFileName)) { 03461 03462 // Write lock-file: 03463 t3lib_div::writeFileToTypo3tempDir($lockFileName,serialize(array( 03464 'tstamp'=>time(), 03465 'user'=>$GLOBALS['BE_USER']->user['username'], 03466 'curVersion'=>$curVersion, 03467 'swapVersion'=>$swapVersion 03468 ))); 03469 03470 // Find fields to keep 03471 $keepFields = $this->getUniqueFields($table); 03472 if ($TCA[$table]['ctrl']['sortby']) { 03473 $keepFields[] = $TCA[$table]['ctrl']['sortby']; 03474 } 03475 03476 // Swap "keepfields" 03477 foreach($keepFields as $fN) { 03478 $tmp = $swapVersion[$fN]; 03479 $swapVersion[$fN] = $curVersion[$fN]; 03480 $curVersion[$fN] = $tmp; 03481 } 03482 03483 // Preserve states: 03484 $t3ver_state = array(); 03485 $t3ver_state['swapVersion'] = $swapVersion['t3ver_state']; 03486 $t3ver_state['curVersion'] = $curVersion['t3ver_state']; 03487 03488 // Modify offline version to become online: 03489 $tmp_wsid = $swapVersion['t3ver_wsid']; 03490 unset($swapVersion['uid']); 03491 $swapVersion['pid'] = intval($curVersion['pid']); // Set pid for ONLINE 03492 $swapVersion['t3ver_oid'] = intval($id); 03493 $swapVersion['t3ver_wsid'] = $swapIntoWS ? intval($curVersion['t3ver_wsid']) : 0; 03494 $swapVersion['t3ver_tstamp'] = time(); 03495 $swapVersion['t3ver_stage'] = $swapVersion['t3ver_state'] = 0; 03496 03497 // Modify online version to become offline: 03498 unset($curVersion['uid']); 03499 $curVersion['pid'] = -1; // Set pid for OFFLINE 03500 $curVersion['t3ver_oid'] = intval($id); 03501 $curVersion['t3ver_wsid'] = $swapIntoWS ? intval($tmp_wsid) : 0; 03502 $curVersion['t3ver_tstamp'] = time(); 03503 $curVersion['t3ver_count'] = $curVersion['t3ver_count']+1; // Increment lifecycle counter 03504 $curVersion['t3ver_stage'] = $curVersion['t3ver_state'] = 0; 03505 03506 if ($table==='pages') { // Keeping the swapmode state 03507 $curVersion['t3ver_swapmode'] = $swapVersion['t3ver_swapmode']; 03508 } 03509 03510 // Execute swapping: 03511 $sqlErrors = array(); 03512 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($id),$swapVersion); 03513 if ($GLOBALS['TYPO3_DB']->sql_error()) { 03514 $sqlErrors[] = $GLOBALS['TYPO3_DB']->sql_error(); 03515 } else { 03516 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($swapWith),$curVersion); 03517 if ($GLOBALS['TYPO3_DB']->sql_error()) { 03518 $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03519 } else { 03520 unlink($lockFileName); 03521 } 03522 } 03523 03524 if (!count($sqlErrors)) { 03525 03526 // Checking for delete: 03527 if ($t3ver_state['swapVersion']==2) { 03528 $this->deleteEl($table,$id,TRUE); // Force delete 03529 } 03530 03531 $this->newlog('Swapping successful for table "'.$table.'" uid '.$id.'=>'.$swapWith); 03532 03533 // Update reference index: 03534 $this->updateRefIndex($table,$id); 03535 $this->updateRefIndex($table,$swapWith); 03536 03537 // SWAPPING pids for subrecords: 03538 if ($table=='pages' && $swapVersion['t3ver_swapmode']>=0) { 03539 03540 // Collect table names that should be copied along with the tables: 03541 foreach($TCA as $tN => $tCfg) { 03542 if ($swapVersion['t3ver_swapmode']>0 || $TCA[$tN]['ctrl']['versioning_followPages']) { // For "Branch" publishing swap ALL, otherwise for "page" publishing, swap only "versioning_followPages" tables 03543 $temporaryPid = -($id+1000000); 03544 03545 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($id),array('pid'=>$temporaryPid)); 03546 if ($GLOBALS['TYPO3_DB']->sql_error()) $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03547 03548 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($swapWith),array('pid'=>$id)); 03549 if ($GLOBALS['TYPO3_DB']->sql_error()) $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03550 03551 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($tN,'pid='.intval($temporaryPid),array('pid'=>$swapWith)); 03552 if ($GLOBALS['TYPO3_DB']->sql_error()) $sqlErrors[]=$GLOBALS['TYPO3_DB']->sql_error(); 03553 03554 if (count($sqlErrors)) { 03555 $this->newlog('During Swapping: SQL errors happend: '.implode('; ',$sqlErrors),2); 03556 } 03557 } 03558 } 03559 } 03560 // Clear cache: 03561 $this->clear_cache($table,$id); 03562 03563 // Checking for "new-placeholder" and if found, delete it (BUT FIRST after swapping!): 03564 if ($t3ver_state['curVersion']==1) { 03565 $this->deleteEl($table, $swapWith, TRUE, TRUE); // For delete + completely delete! 03566 } 03567 } else $this->newlog('During Swapping: SQL errors happend: '.implode('; ',$sqlErrors),2); 03568 } 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); 03569 } 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); 03570 } else $this->newlog('Workspace #'.$swapVersion['t3ver_wsid'].' does not support swapping.',1); 03571 } else $this->newlog('You cannot publish a record you do not have edit and show permissions for',1); 03572 } else $this->newlog('Records in workspace #'.$swapVersion['t3ver_wsid'].' can only be published when in "Publish" stage.',1); 03573 } else $this->newlog('User could not publish records from workspace #'.$swapVersion['t3ver_wsid'],1); 03574 } else $this->newlog('Error: Either online or swap version could not be selected!',2); 03575 } else $this->newlog('Error: You cannot swap versions for a record you do not have access to edit!',1); 03576 } 03577 03585 function version_clearWSID($table,$id) { 03586 if ($errorCode = $this->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) { 03587 $this->newlog('Attempt to reset workspace for record failed: '.$errorCode,1); 03588 } elseif ($this->checkRecordUpdateAccess($table,$id)) { 03589 if ($liveRec = t3lib_BEfunc::getLiveVersionOfRecord($table,$id,'uid,t3ver_state')) { 03590 // Clear workspace ID: 03591 $sArray = array(); 03592 $sArray['t3ver_wsid'] = 0; 03593 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($id),$sArray); 03594 03595 // Clear workspace ID for live version AND DELETE IT as well because it is a new record! 03596 if ((int)$liveRec['t3ver_state']===1) { 03597 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table,'uid='.intval($liveRec['uid']),$sArray); 03598 $this->deleteEl($table, $liveRec['uid'], TRUE); // THIS assumes that the record was placeholder ONLY for ONE record (namely $id) 03599 } 03600 03601 // 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. 03602 $wsRec = t3lib_BEfunc::getRecord($table,$id); 03603 if ((int)$wsRec['t3ver_state']===2) { 03604 $this->deleteEl($table, $id, TRUE, TRUE); 03605 } 03606 } 03607 } else $this->newlog('Attempt to reset workspace for record failed because you do not have edit access',1); 03608 } 03609 03619 function version_setStage($table,$id,$stageId,$comment='') { 03620 if ($errorCode = $this->BE_USER->workspaceCannotEditOfflineVersion($table, $id)) { 03621 $this->newlog('Attempt to set stage for record failed: '.$errorCode,1); 03622 } elseif ($this->checkRecordUpdateAccess($table,$id)) { 03623 $stat = $this->BE_USER->checkWorkspaceCurrent(); 03624 if (t3lib_div::inList('admin,online,offline,reviewer,owner', $stat['_ACCESS']) || ($stageId<=1 && $stat['_ACCESS']==='member')) { 03625 03626 // Set stage of record: 03627 $sArray = array(); 03628 $sArray['t3ver_stage'] = $stageId; 03629 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($id), $sArray); 03630 $this->newlog('Stage for record was changed to '.$stageId.'. Comment was: "'.substr($comment,0,100).'"'); 03631 // TEMPORARY, except 6-30 as action/detail number which is observed elsewhere! 03632 $this->log($table,$id,6,0,0,'Stage raised...',30,array('comment'=>$comment,'stage'=>$stageId)); 03633 03634 if ((int)$stat['stagechg_notification']>0) { 03635 $this->notifyStageChange($stat,$stageId,$table,$id,$comment); 03636 } 03637 } else $this->newlog('The member user tried to set a stage value "'.$stageId.'" that was not allowed',1); 03638 } else $this->newlog('Attempt to set stage for record failed because you do not have edit access',1); 03639 } 03640 03641 03642 03643 03644 03645 03646 03647 03648 03649 03650 03651 03652 03653 /********************************************* 03654 * 03655 * Cmd: Helper functions 03656 * 03657 ********************************************/ 03658 03664 function remapListedDBRecords() { 03665 global $TCA; 03666 03667 if (count($this->registerDBList)) { 03668 reset($this->registerDBList); 03669 while(list($table,$records)=each($this->registerDBList)) { 03670 t3lib_div::loadTCA($table); 03671 reset($records); 03672 while(list($uid,$fields)=each($records)) { 03673 $newData = array(); 03674 $theUidToUpdate = $this->copyMappingArray_merged[$table][$uid]; 03675 $theUidToUpdate_saveTo = t3lib_BEfunc::wsMapId($table,$theUidToUpdate); 03676 03677 foreach($fields as $fieldName => $value) { 03678 $conf = $TCA[$table]['columns'][$fieldName]['config']; 03679 03680 switch($conf['type']) { 03681 case 'group': 03682 case 'select': 03683 $vArray = $this->remapListedDBRecords_procDBRefs($conf, $value, $theUidToUpdate); 03684 if (is_array($vArray)) { 03685 $newData[$fieldName] = implode(',',$vArray); 03686 } 03687 break; 03688 case 'flex': 03689 if ($value=='FlexForm_reference') { 03690 $origRecordRow = $this->recordInfo($table,$theUidToUpdate,'*'); // This will fetch the new row for the element 03691 03692 if (is_array($origRecordRow)) { 03693 t3lib_BEfunc::workspaceOL($table,$origRecordRow); 03694 03695 // Get current data structure and value array: 03696 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $origRecordRow, $table); 03697 $currentValueArray = t3lib_div::xml2array($origRecordRow[$fieldName]); 03698 03699 // Do recursive processing of the XML data: 03700 $currentValueArray['data'] = $this->checkValue_flex_procInData( 03701 $currentValueArray['data'], 03702 array(), // Not used. 03703 array(), // Not used. 03704 $dataStructArray, 03705 array($table,$theUidToUpdate,$fieldName), // Parameters. 03706 'remapListedDBRecords_flexFormCallBack' 03707 ); 03708 03709 // The return value should be compiled back into XML, ready to insert directly in the field (as we call updateDB() directly later): 03710 if (is_array($currentValueArray['data'])) { 03711 $newData[$fieldName] = 03712 $this->checkValue_flexArray2Xml($currentValueArray,TRUE); 03713 } 03714 } 03715 } 03716 break; 03717 default: 03718 debug('Field type should not appear here: '. $conf['type']); 03719 break; 03720 } 03721 } 03722 03723 if (count($newData)) { // If any fields were changed, those fields are updated! 03724 $this->updateDB($table,$theUidToUpdate_saveTo,$newData); 03725 } 03726 } 03727 } 03728 } 03729 } 03730 03742 function remapListedDBRecords_flexFormCallBack($pParams, $dsConf, $dataValue, $dataValue_ext1, $dataValue_ext2) { 03743 03744 // Extract parameters: 03745 list($table,$uid,$field) = $pParams; 03746 03747 // If references are set for this field, set flag so they can be corrected later: 03748 if ($this->isReferenceField($dsConf) && strlen($dataValue)) { 03749 $vArray = $this->remapListedDBRecords_procDBRefs($dsConf, $dataValue, $uid); 03750 if (is_array($vArray)) { 03751 $dataValue = implode(',',$vArray); 03752 } 03753 } 03754 03755 // Return 03756 return array('value' => $dataValue); 03757 } 03758 03768 function remapListedDBRecords_procDBRefs($conf, $value, $MM_localUid) { 03769 03770 // Initialize variables 03771 $set = FALSE; // Will be set true if an upgrade should be done... 03772 $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table']; // Allowed tables for references. 03773 $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : ''; // Table name to prepend the UID 03774 $dontRemapTables = t3lib_div::trimExplode(',',$conf['dontRemapTablesOnCopy'],1); // Which tables that should possibly not be remapped 03775 03776 // Convert value to list of references: 03777 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 03778 $dbAnalysis->registerNonTableValues = ($conf['type']=='select' && $conf['allowNonIdValues']) ? 1 : 0; 03779 $dbAnalysis->start($value, $allowedTables, $conf['MM'], $MM_localUid); 03780 03781 // Traverse those references and map IDs: 03782 foreach($dbAnalysis->itemArray as $k => $v) { 03783 $mapID = $this->copyMappingArray_merged[$v['table']][$v['id']]; 03784 if ($mapID && !in_array($v['table'],$dontRemapTables)) { 03785 $dbAnalysis->itemArray[$k]['id'] = $mapID; 03786 $set = TRUE; 03787 } 03788 } 03789 03790 // If a change has been done, set the new value(s) 03791 if ($set) { 03792 if ($conf['MM']) { 03793 // FIXME $theUidToUpdate is undefined 03794 $dbAnalysis->writeMM($conf['MM'], $theUidToUpdate, $prependName); 03795 } else { 03796 $vArray = $dbAnalysis->getValueArray($prependName); 03797 if ($conf['type']=='select') { 03798 $vArray = $dbAnalysis->convertPosNeg($vArray, $conf['foreign_table'], $conf['neg_foreign_table']); 03799 } 03800 return $vArray; 03801 } 03802 } 03803 } 03804 03805 03806 03807 03808 03809 03810 03811 03812 03813 03814 03815 03816 03817 03818 03819 03820 03821 /***************************** 03822 * 03823 * Access control / Checking functions 03824 * 03825 *****************************/ 03826 03833 function checkModifyAccessList($table) { 03834 $res = ($this->admin || (!$this->tableAdminOnly($table) && t3lib_div::inList($this->BE_USER->groupData['tables_modify'],$table))); 03835 return $res; 03836 } 03837 03845 function isRecordInWebMount($table,$id) { 03846 if (!isset($this->isRecordInWebMount_Cache[$table.':'.$id])) { 03847 $recP=$this->getRecordProperties($table,$id); 03848 $this->isRecordInWebMount_Cache[$table.':'.$id]=$this->isInWebMount($recP['event_pid']); 03849 } 03850 return $this->isRecordInWebMount_Cache[$table.':'.$id]; 03851 } 03852 03859 function isInWebMount($pid) { 03860 if (!isset($this->isInWebMount_Cache[$pid])) { 03861 $this->isInWebMount_Cache[$pid]=$this->BE_USER->isInWebMount($pid); 03862 } 03863 return $this->isInWebMount_Cache[$pid]; 03864 } 03865 03873 function checkRecordUpdateAccess($table,$id) { 03874 global $TCA; 03875 $res = 0; 03876 if ($TCA[$table] && intval($id)>0) { 03877 if (isset($this->recUpdateAccessCache[$table][$id])) { // If information is cached, return it 03878 return $this->recUpdateAccessCache[$table][$id]; 03879 // Check if record exists and 1) if 'pages' the page may be edited, 2) if page-content the page allows for editing 03880 } elseif ($this->doesRecordExist($table,$id,'edit')) { 03881 $res = 1; 03882 } 03883 $this->recUpdateAccessCache[$table][$id]=$res; // Cache the result 03884 } 03885 return $res; 03886 } 03887 03897 function checkRecordInsertAccess($insertTable,$pid,$action=1) { 03898 global $TCA; 03899 03900 $res = 0; 03901 $pid = intval($pid); 03902 if ($pid>=0) { 03903 if (isset($this->recInsertAccessCache[$insertTable][$pid])) { // If information is cached, return it 03904 return $this->recInsertAccessCache[$insertTable][$pid]; 03905 } else { 03906 // 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 03907 if ( (!$pid && $this->admin) || $this->doesRecordExist('pages',$pid,($insertTable=='pages'?$this->pMap['new']:$this->pMap['editcontent'])) ) { // Check permissions 03908 if ($this->isTableAllowedForThisPage($pid, $insertTable)) { 03909 $res = 1; 03910 $this->recInsertAccessCache[$insertTable][$pid]=$res; // Cache the result 03911 } else { 03912 $propArr = $this->getRecordProperties('pages',$pid); 03913 $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']); 03914 } 03915 } else { 03916 $propArr = $this->getRecordProperties('pages',$pid); 03917 $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']); 03918 } 03919 } 03920 } 03921 return $res; 03922 } 03923 03931 function isTableAllowedForThisPage($page_uid, $checkTable) { 03932 global $TCA, $PAGES_TYPES; 03933 $page_uid = intval($page_uid); 03934 03935 // 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. 03936 if (($TCA[$checkTable]['ctrl']['rootLevel'] xor !$page_uid) && $TCA[$checkTable]['ctrl']['rootLevel']!=-1 && $checkTable!='pages') { 03937 return false; 03938 } 03939 03940 // Check root-level 03941 if (!$page_uid) { 03942 if ($this->admin) { 03943 return true; 03944 } 03945 } else { 03946 // Check non-root-level 03947 $doktype = $this->pageInfo($page_uid,'doktype'); 03948 $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables']; 03949 $allowedArray = t3lib_div::trimExplode(',',$allowedTableList,1); 03950 if (strstr($allowedTableList,'*') || in_array($checkTable,$allowedArray)) { // If all tables or the table is listed as a allowed type, return true 03951 return true; 03952 } 03953 } 03954 } 03955 03964 function doesRecordExist($table,$id,$perms) { 03965 global $TCA; 03966 03967 $res = 0; 03968 $id = intval($id); 03969 03970 // Processing the incoming $perms (from possible string to integer that can be AND'ed) 03971 if (!t3lib_div::testInt($perms)) { 03972 if ($table!='pages') { 03973 switch($perms) { 03974 case 'edit': 03975 case 'delete': 03976 case 'new': 03977 $perms = 'editcontent'; // This holds it all in case the record is not page!! 03978 break; 03979 } 03980 } 03981 $perms = intval($this->pMap[$perms]); 03982 } else { 03983 $perms = intval($perms); 03984 } 03985 03986 if (!$perms) {die('Internal ERROR: no permissions to check for non-admin user.');} 03987 03988 // For all tables: Check if record exists: 03989 if (is_array($TCA[$table]) && $id>0 && ($this->isRecordInWebMount($table,$id) || $this->admin)) { 03990 if ($table != 'pages') { 03991 03992 // Find record without checking page: 03993 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid,pid', $table, 'uid='.intval($id).$this->deleteClause($table)); // THIS SHOULD CHECK FOR editlock I think! 03994 $output = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres); 03995 t3lib_BEfunc::fixVersioningPid($table,$output,TRUE); 03996 03997 // If record found, check page as well: 03998 if (is_array($output)) { 03999 04000 // Looking up the page for record: 04001 $mres = $this->doesRecordExist_pageLookUp($output['pid'], $perms); 04002 $pageRec = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres); 04003 // 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): 04004 if (is_array($pageRec) || (!$output['pid'] && $this->admin)) { 04005 return TRUE; 04006 } 04007 } 04008 return FALSE; 04009 } else { 04010 $mres = $this->doesRecordExist_pageLookUp($id, $perms); 04011 return $GLOBALS['TYPO3_DB']->sql_num_rows($mres); 04012 } 04013 } 04014 } 04015 04025 function doesRecordExist_pageLookUp($id, $perms) { 04026 global $TCA; 04027 04028 return $GLOBALS['TYPO3_DB']->exec_SELECTquery( 04029 'uid', 04030 'pages', 04031 'uid='.intval($id). 04032 $this->deleteClause('pages'). 04033 ($perms && !$this->admin ? ' AND '.$this->BE_USER->getPagePermsClause($perms) : ''). 04034 (!$this->admin && $TCA['pages']['ctrl']['editlock'] && ($perms & (2+4+16)) ? ' AND '.$TCA['pages']['ctrl']['editlock'].'=0':'') // admin users don't need check 04035 ); 04036 } 04037 04051 function doesBranchExist($inList,$pid,$perms,$recurse) { 04052 global $TCA; 04053 $pid = intval($pid); 04054 $perms = intval($perms); 04055 04056 if ($pid>=0) { 04057 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 04058 'uid, perms_userid, perms_groupid, perms_user, perms_group, perms_everybody', 04059 'pages', 04060 'pid='.intval($pid).$this->deleteClause('pages'), 04061 '', 04062 'sorting' 04063 ); 04064 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 04065 if ($this->admin || $this->BE_USER->doesUserHaveAccess($row,$perms)) { // IF admin, then it's OK 04066 $inList.=$row['uid'].','; 04067 if ($recurse) { // Follow the subpages recursively... 04068 $inList = $this->doesBranchExist($inList, $row['uid'], $perms, $recurse); 04069 if ($inList == -1) {return -1;} // No permissions somewhere in the branch 04070 } 04071 } else { 04072 return -1; // No permissions 04073 } 04074 } 04075 } 04076 return $inList; 04077 } 04078 04085 function tableReadOnly($table) { 04086 // returns true if table is readonly 04087 global $TCA; 04088 return ($TCA[$table]['ctrl']['readOnly'] ? 1 : 0); 04089 } 04090 04097 function tableAdminOnly($table) { 04098 // returns true if table is admin-only 04099 global $TCA; 04100 return ($TCA[$table]['ctrl']['adminOnly'] ? 1 : 0); 04101 } 04102 04111 function destNotInsideSelf($dest,$id) { 04112 $loopCheck = 100; 04113 $dest = intval($dest); 04114 $id = intval($id); 04115 04116 if ($dest==$id) { 04117 return FALSE; 04118 } 04119 04120 while ($dest!=0 && $loopCheck>0) { 04121 $loopCheck--; 04122 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid, uid, t3ver_oid,t3ver_wsid', 'pages', 'uid='.intval($dest).$this->deleteClause('pages')); 04123 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 04124 t3lib_BEfunc::fixVersioningPid('pages',$row); 04125 if ($row['pid']==$id) { 04126 return FALSE; 04127 } else { 04128 $dest = $row['pid']; 04129 } 04130 } else { 04131 return FALSE; 04132 } 04133 } 04134 return TRUE; 04135 } 04136 04143 function getExcludeListArray() { 04144 global $TCA; 04145 04146 $list = array(); 04147 reset($TCA); 04148 while (list($table)=each($TCA)) { 04149 t3lib_div::loadTCA($table); 04150 while (list($field,$config)=each($TCA[$table]['columns'])) { 04151 if ($config['exclude'] && !t3lib_div::inList($this->BE_USER->groupData['non_exclude_fields'],$table.':'.$field)) { 04152 $list[]=$table.'-'.$field; 04153 } 04154 } 04155 } 04156 return $list; 04157 } 04158 04166 function doesPageHaveUnallowedTables($page_uid,$doktype) { 04167 global $TCA, $PAGES_TYPES; 04168 04169 $page_uid = intval($page_uid); 04170 if (!$page_uid) { 04171 return FALSE; // Not a number. Probably a new page 04172 } 04173 04174 $allowedTableList = isset($PAGES_TYPES[$doktype]['allowedTables']) ? $PAGES_TYPES[$doktype]['allowedTables'] : $PAGES_TYPES['default']['allowedTables']; 04175 $allowedArray = t3lib_div::trimExplode(',',$allowedTableList,1); 04176 if (strstr($allowedTableList,'*')) { // If all tables is OK the return true 04177 return FALSE; // OK... 04178 } 04179 04180 reset ($TCA); 04181 $tableList = array(); 04182 while (list($table)=each($TCA)) { 04183 if (!in_array($table,$allowedArray)) { // If the table is not in the allowed list, check if there are records... 04184 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid='.intval($page_uid)); 04185 $count = $GLOBALS['TYPO3_DB']->sql_fetch_row($mres); 04186 if ($count[0]) { 04187 $tableList[]=$table; 04188 } 04189 } 04190 } 04191 return implode(',',$tableList); 04192 } 04193 04194 04195 04196 04197 04198 04199 04200 04201 /***************************** 04202 * 04203 * Information lookup 04204 * 04205 *****************************/ 04206 04215 function pageInfo($id,$field) { 04216 if (!isset($this->pageCache[$id])) { 04217 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'pages', 'uid='.intval($id)); 04218 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 04219 $this->pageCache[$id] = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 04220 } 04221 $GLOBALS['TYPO3_DB']->sql_free_result($res); 04222 } 04223 return $this->pageCache[$id][$field]; 04224 } 04225 04235 function recordInfo($table,$id,$fieldList) { 04236 global $TCA; 04237 if (is_array($TCA[$table])) { 04238 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery($fieldList, $table, 'uid='.intval($id)); 04239 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 04240 return $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 04241 } 04242 } 04243 } 04244 04256 function getRecordProperties($table,$id,$noWSOL=FALSE) { 04257 $row = ($table=='pages' && !$id) ? array('title'=>'[root-level]', 'uid' => 0, 'pid' => 0) :$this->recordInfo($table,$id,'*'); 04258 if (!$noWSOL) { 04259 t3lib_BEfunc::workspaceOL($table,$row); 04260 } 04261 t3lib_BEfunc::fixVersioningPid($table,$row); 04262 return $this->getRecordPropertiesFromRow($table,$row); 04263 } 04264 04272 function getRecordPropertiesFromRow($table,$row) { 04273 global $TCA; 04274 if ($TCA[$table]) { 04275 $out = array( 04276 'header' => $row[$TCA[$table]['ctrl']['label']], 04277 'pid' => $row['pid'], 04278 'event_pid' => ($table=='pages'?$row['uid']:$row['pid']), 04279 't3ver_state' => $TCA[$table]['ctrl']['versioningWS'] ? $row['t3ver_state'] : '', 04280 '_ORIG_pid' => $row['_ORIG_pid'] 04281 ); 04282 return $out; 04283 } 04284 } 04285 04286 04287 04288 04289 04290 04291 04292 04293 04294 04295 04296 04297 04298 04299 04300 /********************************************* 04301 * 04302 * Storing data to Database Layer 04303 * 04304 ********************************************/ 04305 04315 function updateDB($table,$id,$fieldArray) { 04316 global $TCA; 04317 04318 if (is_array($fieldArray) && is_array($TCA[$table]) && intval($id)) { 04319 unset($fieldArray['uid']); // Do NOT update the UID field, ever! 04320 04321 if (count($fieldArray)) { 04322 04323 // Execute the UPDATE query: 04324 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($id), $fieldArray); 04325 04326 // If succees, do...: 04327 if (!$GLOBALS['TYPO3_DB']->sql_error()) { 04328 04329 if ($this->checkStoredRecords) { 04330 $newRow = $this->checkStoredRecord($table,$id,$fieldArray,2); 04331 } 04332 04333 // Update reference index: 04334 $this->updateRefIndex($table,$id); 04335 04336 // Set log entry: 04337 $propArr = $this->getRecordPropertiesFromRow($table,$newRow); 04338 $theLogId = $this->log($table,$id,2,$propArr['pid'],0,"Record '%s' (%s) was updated.",10,array($propArr['header'],$table.':'.$id),$propArr['event_pid']); 04339 04340 // Set History data: 04341 $this->setHistory($table,$id,$theLogId); 04342 04343 // Clear cache for relevant pages: 04344 $this->clear_cache($table,$id); 04345 04346 // Unset the pageCache for the id if table was page. 04347 if ($table=='pages') unset($this->pageCache[$id]); 04348 } else { 04349 $this->log($table,$id,2,0,2,"SQL error: '%s' (%s)",12,array($GLOBALS['TYPO3_DB']->sql_error(),$table.':'.$id)); 04350 } 04351 } 04352 } 04353 } 04354 04367 function insertDB($table,$id,$fieldArray,$newVersion=FALSE,$suggestedUid=0,$dontSetNewIdIndex=FALSE) { 04368 global $TCA; 04369 04370 if (is_array($fieldArray) && is_array($TCA[$table]) && isset($fieldArray['pid'])) { 04371 unset($fieldArray['uid']); // Do NOT insert the UID field, ever! 04372 04373 if (count($fieldArray)) { 04374 04375 // Check for "suggestedUid". 04376 // This feature is used by the import functionality to force a new record to have a certain UID value. 04377 // This is only recommended for use when the destination server is a passive mirrow of another server. 04378 // As a security measure this feature is available only for Admin Users (for now) 04379 $suggestedUid = intval($suggestedUid); 04380 if ($this->BE_USER->isAdmin() && $suggestedUid && $this->suggestedInsertUids[$table.':'.$suggestedUid]) { 04381 // When the value of ->suggestedInsertUids[...] is "DELETE" it will try to remove the previous record 04382 if ($this->suggestedInsertUids[$table.':'.$suggestedUid]==='DELETE') { 04383 // DELETE: 04384 $GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($suggestedUid)); 04385 } 04386 $fieldArray['uid'] = $suggestedUid; 04387 } 04388 04389 // Execute the INSERT query: 04390 $GLOBALS['TYPO3_DB']->exec_INSERTquery($table, $fieldArray); 04391 04392 // If succees, do...: 04393 if (!$GLOBALS['TYPO3_DB']->sql_error()) { 04394 04395 // Set mapping for NEW... -> real uid: 04396 $NEW_id = $id; // the NEW_id now holds the 'NEW....' -id 04397 $id = $GLOBALS['TYPO3_DB']->sql_insert_id(); 04398 if (!$dontSetNewIdIndex) { 04399 $this->substNEWwithIDs[$NEW_id] = $id; 04400 $this->substNEWwithIDs_table[$NEW_id] = $table; 04401 } 04402 04403 // Checking the record is properly saved and writing to log 04404 if ($this->checkStoredRecords) { 04405 $newRow = $this->checkStoredRecord($table,$id,$fieldArray,1); 04406 } 04407 04408 // Update reference index: 04409 $this->updateRefIndex($table,$id); 04410 04411 if ($newVersion) { 04412 $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); 04413 } else { 04414 $propArr = $this->getRecordPropertiesFromRow($table,$newRow); 04415 $page_propArr = $this->getRecordProperties('pages',$propArr['pid']); 04416 $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); 04417 04418 // Clear cache for relavant pages: 04419 $this->clear_cache($table,$id); 04420 } 04421 04422 return $id; 04423 } else { 04424 $this->log($table,$id,1,0,2,"SQL error: '%s' (%s)",12,array($GLOBALS['TYPO3_DB']->sql_error(),$table.':'.$id)); 04425 } 04426 } 04427 } 04428 } 04429 04440 function checkStoredRecord($table,$id,$fieldArray,$action) { 04441 global $TCA; 04442 04443 $id = intval($id); 04444 if (is_array($TCA[$table]) && $id) { 04445 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid='.intval($id)); 04446 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 04447 // Traverse array of values that was inserted into the database and compare with the actually stored value: 04448 $errorString = array(); 04449 foreach($fieldArray as $key => $value) { 04450 if ($this->checkStoredRecords_loose && !$value && !$row[$key]) { 04451 // Nothing... 04452 } elseif (strcmp($value,$row[$key])) { 04453 $errorString[] = $key; 04454 } 04455 } 04456 04457 // Set log message if there were fields with unmatching values: 04458 if (count($errorString)) { 04459 $this->log($table,$id,$action,0,102,'These fields are not properly updated in database: ('.implode(',',$errorString).') Probably value mismatch with fieldtype.'); 04460 } 04461 04462 // Return selected rows: 04463 return $row; 04464 } 04465 $GLOBALS['TYPO3_DB']->sql_free_result($res); 04466 } 04467 } 04468 04477 function setHistory($table,$id,$logId) { 04478 if (isset($this->historyRecords[$table.':'.$id])) { 04479 04480 // Initialize settings: 04481 list($tscPID) = t3lib_BEfunc::getTSCpid($table,$id,''); 04482 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 04483 04484 $tE = $this->getTableEntries($table,$TSConfig); 04485 $maxAgeSeconds = 60*60*24*(strcmp($tE['history.']['maxAgeDays'],'') ? t3lib_div::intInRange($tE['history.']['maxAgeDays'],0,365) : 30); // one month 04486 04487 // Garbage collect old entries: 04488 $this->clearHistory($maxAgeSeconds, $table); 04489 04490 // Set history data: 04491 $fields_values = array(); 04492 $fields_values['history_data'] = serialize($this->historyRecords[$table.':'.$id]); 04493 $fields_values['fieldlist'] = implode(',',array_keys($this->historyRecords[$table.':'.$id]['newRecord'])); 04494 $fields_values['tstamp'] = time(); 04495 $fields_values['tablename'] = $table; 04496 $fields_values['recuid'] = $id; 04497 $fields_values['sys_log_uid'] = $logId; 04498 04499 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_history', $fields_values); 04500 } 04501 } 04502 04510 function clearHistory($maxAgeSeconds=604800,$table) { 04511 $tstampLimit = $maxAgeSeconds ? time()-$maxAgeSeconds : 0; 04512 04513 $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_history', 'tstamp<'.intval($tstampLimit).' AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history')); 04514 } 04515 04524 function updateRefIndex($table,$id) { 04525 $refIndexObj = t3lib_div::makeInstance('t3lib_refindex'); 04526 $result = $refIndexObj->updateRefIndexTable($table,$id); 04527 } 04528 04529 04530 04531 04532 04533 04534 04535 04536 04537 04538 04539 04540 04541 /********************************************* 04542 * 04543 * Misc functions 04544 * 04545 ********************************************/ 04546 04556 function getSortNumber($table,$uid,$pid) { 04557 global $TCA; 04558 if ($TCA[$table] && $TCA[$table]['ctrl']['sortby']) { 04559 $sortRow = $TCA[$table]['ctrl']['sortby']; 04560 if ($pid>=0) { // Sorting number is in the top 04561 $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 04562 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { // There was an element 04563 if ($row['uid']==$uid) { // The top record was the record it self, so we return its current sortnumber 04564 return $row[$sortRow]; 04565 } 04566 if ($row[$sortRow] < 1) { // If the pages sortingnumber < 1 we must resort the records under this pid 04567 $this->resorting($table,$pid,$sortRow,0); 04568 return $this->sortIntervals; // First sorting number after resorting 04569 } else { 04570 return floor($row[$sortRow]/2); // Sorting number between current top element and zero 04571 } 04572 } else { // No pages, so we choose the default value as sorting-number 04573 return $this->sortIntervals; // First sorting number if no elements. 04574 } 04575 } else { // Sorting number is inside the list 04576 $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 04577 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { // There was a record 04578 04579 // 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. 04580 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,$row['uid'],$sortRow.',pid,uid')) { 04581 $row = $lookForLiveVersion; 04582 } 04583 04584 // If the record happends to be it self 04585 if ($row['uid']==$uid) { 04586 $sortNumber = $row[$sortRow]; 04587 } else { 04588 $subres = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 04589 $sortRow.',pid,uid', 04590 $table, 04591 'pid='.intval($row['pid']).' AND '.$sortRow.'>='.intval($row[$sortRow]).$this->deleteClause($table), 04592 '', 04593 $sortRow.' ASC', 04594 '2' 04595 ); // Fetches the next record in order to calculate the in between sortNumber 04596 if ($GLOBALS['TYPO3_DB']->sql_num_rows($subres)==2) { // There was a record afterwards 04597 $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres); // Forward to the second result... 04598 $subrow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($subres); // There was a record afterwards 04599 $sortNumber = $row[$sortRow]+ floor(($subrow[$sortRow]-$row[$sortRow])/2); // The sortNumber is found in between these values 04600 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 04601 $sortNumber = $this->resorting($table,$row['pid'],$sortRow, $row['uid']); // By this special param, resorting reserves and returns the sortnumber after the uid 04602 } 04603 } else { // If after the last record in the list, we just add the sortInterval to the last sortvalue 04604 $sortNumber = $row[$sortRow]+$this->sortIntervals; 04605 } 04606 } 04607 return Array('pid' => $row['pid'], 'sortNumber' => $sortNumber); 04608 } else { 04609 $propArr = $this->getRecordProperties($table,$uid); 04610 $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... 04611 return false; // There MUST be a page or else this cannot work 04612 } 04613 } 04614 } 04615 } 04616 04629 function resorting($table,$pid,$sortRow, $return_SortNumber_After_This_Uid) { 04630 global $TCA; 04631 if ($TCA[$table] && $sortRow && $TCA[$table]['ctrl']['sortby']==$sortRow) { 04632 $returnVal = 0; 04633 $intervals = $this->sortIntervals; 04634 $i = $intervals*2; 04635 04636 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($pid).$this->deleteClause($table), '', $sortRow.' ASC'); 04637 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { 04638 $uid=intval($row['uid']); 04639 if ($uid) { 04640 $GLOBALS['TYPO3_DB']->exec_UPDATEquery($table, 'uid='.intval($uid), array($sortRow=>$i)); 04641 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 04642 $i = $i+$intervals; 04643 $returnVal=$i; 04644 } 04645 } else {die ('Fatal ERROR!! No Uid at resorting.');} 04646 $i = $i+$intervals; 04647 } 04648 return $returnVal; 04649 } 04650 } 04651 04660 function setTSconfigPermissions($fieldArray,$TSConfig_p) { 04661 if (strcmp($TSConfig_p['userid'],'')) $fieldArray['perms_userid']=intval($TSConfig_p['userid']); 04662 if (strcmp($TSConfig_p['groupid'],'')) $fieldArray['perms_groupid']=intval($TSConfig_p['groupid']); 04663 if (strcmp($TSConfig_p['user'],'')) $fieldArray['perms_user']=t3lib_div::testInt($TSConfig_p['user']) ? $TSConfig_p['user'] : $this->assemblePermissions($TSConfig_p['user']); 04664 if (strcmp($TSConfig_p['group'],'')) $fieldArray['perms_group']=t3lib_div::testInt($TSConfig_p['group']) ? $TSConfig_p['group'] : $this->assemblePermissions($TSConfig_p['group']); 04665 if (strcmp($TSConfig_p['everybody'],'')) $fieldArray['perms_everybody']=t3lib_div::testInt($TSConfig_p['everybody']) ? $TSConfig_p['everybody'] : $this->assemblePermissions($TSConfig_p['everybody']); 04666 04667 return $fieldArray; 04668 } 04669 04677 function newFieldArray($table) { 04678 global $TCA; 04679 04680 t3lib_div::loadTCA($table); 04681 $fieldArray=Array(); 04682 if (is_array($TCA[$table]['columns'])) { 04683 reset ($TCA[$table]['columns']); 04684 while (list($field,$content)=each($TCA[$table]['columns'])) { 04685 if (isset($this->defaultValues[$table][$field])) { 04686 $fieldArray[$field] = $this->defaultValues[$table][$field]; 04687 } elseif (isset($content['config']['default'])) { 04688 $fieldArray[$field] = $content['config']['default']; 04689 } 04690 } 04691 } 04692 if ($table==='pages') { // Set default permissions for a page. 04693 $fieldArray['perms_userid'] = $this->userid; 04694 $fieldArray['perms_groupid'] = intval($this->BE_USER->firstMainGroup); 04695 $fieldArray['perms_user'] = $this->assemblePermissions($this->defaultPermissions['user']); 04696 $fieldArray['perms_group'] = $this->assemblePermissions($this->defaultPermissions['group']); 04697 $fieldArray['perms_everybody'] = $this->assemblePermissions($this->defaultPermissions['everybody']); 04698 } 04699 return $fieldArray; 04700 } 04701 04709 function addDefaultPermittedLanguageIfNotSet($table,&$incomingFieldArray) { 04710 global $TCA; 04711 04712 // Checking languages: 04713 if ($TCA[$table]['ctrl']['languageField']) { 04714 if (!isset($incomingFieldArray[$TCA[$table]['ctrl']['languageField']])) { // Language field must be found in input row - otherwise it does not make sense. 04715 $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))); 04716 foreach($rows as $r) { 04717 if ($this->BE_USER->checkLanguageAccess($r['uid'])) { 04718 $incomingFieldArray[$TCA[$table]['ctrl']['languageField']] = $r['uid']; 04719 break; 04720 } 04721 } 04722 } 04723 } 04724 } 04725 04733 function overrideFieldArray($table,$data) { 04734 if (is_array($this->overrideValues[$table])) { 04735 $data = array_merge($data,$this->overrideValues[$table]); 04736 } 04737 return $data; 04738 } 04739 04749 function compareFieldArrayWithCurrentAndUnset($table,$id,$fieldArray) { 04750 04751 // Fetch the original record: 04752 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $table, 'uid='.intval($id)); 04753 $currentRecord = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 04754 04755 // If the current record exists (which it should...), begin comparison: 04756 if (is_array($currentRecord)) { 04757 04758 // Read all field types: 04759 $c = 0; 04760 $cRecTypes = array(); 04761 foreach($currentRecord as $col => $val) { 04762 $cRecTypes[$col] = $GLOBALS['TYPO3_DB']->sql_field_type($res,$c); 04763 $c++; 04764 } 04765 04766 // Free result: 04767 $GLOBALS['TYPO3_DB']->sql_free_result($res); 04768 04769 // Unset the fields which are similar: 04770 foreach($fieldArray as $col => $val) { 04771 if ( 04772 !strcmp($val,$currentRecord[$col]) || // Unset fields which matched exactly. 04773 ($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. 04774 ) { 04775 unset($fieldArray[$col]); 04776 } else { 04777 $this->historyRecords[$table.':'.$id]['oldRecord'][$col] = $currentRecord[$col]; 04778 $this->historyRecords[$table.':'.$id]['newRecord'][$col] = $fieldArray[$col]; 04779 } 04780 } 04781 } else { // If the current record does not exist this is an error anyways and we just return an empty array here. 04782 $fieldArray = array(); 04783 } 04784 04785 return $fieldArray; 04786 } 04787 04795 function assemblePermissions($string) { 04796 $keyArr = t3lib_div::trimExplode(',',$string,1); 04797 $value=0; 04798 while(list(,$key)=each($keyArr)) { 04799 if ($key && isset($this->pMap[$key])) { 04800 $value |= $this->pMap[$key]; 04801 } 04802 } 04803 return $value; 04804 } 04805 04812 function rmComma($input) { 04813 return ereg_replace(',$','',$input); 04814 } 04815 04822 function convNumEntityToByteValue($input) { 04823 $token = md5(microtime()); 04824 $parts = explode($token,ereg_replace('(&#([0-9]+);)',$token.'\2'.$token,$input)); 04825 04826 foreach($parts as $k => $v) { 04827 if ($k%2) { 04828 $v = intval($v); 04829 if ($v > 32) { // Just to make sure that control bytes are not converted. 04830 $parts[$k] =chr(intval($v)); 04831 } 04832 } 04833 } 04834 04835 return implode('',$parts); 04836 } 04837 04844 function destPathFromUploadFolder($folder) { 04845 return PATH_site.$folder; 04846 } 04847 04854 function deleteClause($table) { 04855 // Returns the proper delete-clause if any for a table from TCA 04856 global $TCA; 04857 if ($TCA[$table]['ctrl']['delete']) { 04858 return ' AND '.$table.'.'.$TCA[$table]['ctrl']['delete'].'=0'; 04859 } else { 04860 return ''; 04861 } 04862 } 04863 04870 function getTCEMAIN_TSconfig($tscPID) { 04871 if (!isset($this->cachedTSconfig[$tscPID])) { 04872 $this->cachedTSconfig[$tscPID] = $this->BE_USER->getTSConfig('TCEMAIN',t3lib_BEfunc::getPagesTSconfig($tscPID)); 04873 } 04874 return $this->cachedTSconfig[$tscPID]['properties']; 04875 } 04876 04885 function getTableEntries($table,$TSconfig) { 04886 $tA = is_array($TSconfig['table.'][$table.'.']) ? $TSconfig['table.'][$table.'.'] : array();; 04887 $dA = is_array($TSconfig['default.']) ? $TSconfig['default.'] : array(); 04888 return t3lib_div::array_merge_recursive_overrule($dA,$tA); 04889 } 04890 04898 function getPID($table,$uid) { 04899 $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', $table, 'uid='.intval($uid)); 04900 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) { 04901 return $row['pid']; 04902 } 04903 } 04904 04911 function dbAnalysisStoreExec() { 04912 reset($this->dbAnalysisStore); 04913 while(list($k,$v)=each($this->dbAnalysisStore)) { 04914 $id = $this->substNEWwithIDs[$v[2]]; 04915 if ($id) { 04916 $v[2] = $id; 04917 $v[0]->writeMM($v[1],$v[2],$v[3]); 04918 } 04919 } 04920 } 04921 04927 function removeRegisteredFiles() { 04928 reset($this->removeFilesStore); 04929 while(list($k,$v)=each($this->removeFilesStore)) { 04930 unlink($v); 04931 } 04932 } 04933 04939 function removeCacheFiles() { 04940 return t3lib_extMgm::removeCacheFiles(); 04941 } 04942 04953 function int_pageTreeInfo($CPtable,$pid,$counter, $rootID) { 04954 if ($counter) { 04955 $addW = !$this->admin ? ' AND '.$this->BE_USER->getPagePermsClause($this->pMap['show']) : ''; 04956 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'pages', 'pid='.intval($pid).$this->deleteClause('pages').$addW, '', 'sorting DESC'); 04957 while($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($mres)) { 04958 if ($row['uid']!=$rootID) { 04959 $CPtable[$row['uid']] = $pid; 04960 if ($counter-1) { // If the uid is NOT the rootID of the copyaction and if we are supposed to walk further down 04961 $CPtable = $this->int_pageTreeInfo($CPtable,$row['uid'],$counter-1, $rootID); 04962 } 04963 } 04964 } 04965 } 04966 return $CPtable; 04967 } 04968 04974 function compileAdminTables() { 04975 global $TCA; 04976 reset ($TCA); 04977 $listArr = array(); 04978 while (list($table)=each($TCA)) { 04979 $listArr[]=$table; 04980 } 04981 return $listArr; 04982 } 04983 04991 function fixUniqueInPid($table,$uid) { 04992 global $TCA; 04993 if ($TCA[$table]) { 04994 t3lib_div::loadTCA($table); 04995 reset ($TCA[$table]['columns']); 04996 $curData=$this->recordInfo($table,$uid,'*'); 04997 $newData=array(); 04998 while (list($field,$conf)=each($TCA[$table]['columns'])) { 04999 if ($conf['config']['type']=='input') { 05000 $evalCodesArray = t3lib_div::trimExplode(',',$conf['config']['eval'],1); 05001 if (in_array('uniqueInPid',$evalCodesArray)) { 05002 $newV = $this->getUnique($table,$field,$curData[$field],$uid,$curData['pid']); 05003 if (strcmp($newV,$curData[$field])) { 05004 $newData[$field]=$newV; 05005 } 05006 } 05007 } 05008 } 05009 // IF there are changed fields, then update the database 05010 if (count($newData)) { 05011 $this->updateDB($table,$uid,$newData); 05012 } 05013 } 05014 } 05015 05027 function fixCopyAfterDuplFields($table,$uid,$prevUid,$update, $newData=array()) { 05028 global $TCA; 05029 if ($TCA[$table] && $TCA[$table]['ctrl']['copyAfterDuplFields']) { 05030 t3lib_div::loadTCA($table); 05031 $prevData=$this->recordInfo($table,$prevUid,'*'); 05032 $theFields = t3lib_div::trimExplode(',',$TCA[$table]['ctrl']['copyAfterDuplFields'],1); 05033 reset($theFields); 05034 while(list(,$field)=each($theFields)) { 05035 if ($TCA[$table]['columns'][$field] && ($update || !isset($newData[$field]))) { 05036 $newData[$field]=$prevData[$field]; 05037 } 05038 } 05039 if ($update && count($newData)) { 05040 $this->updateDB($table,$uid,$newData); 05041 } 05042 } 05043 return $newData; 05044 } 05045 05052 function extFileFields($table) { 05053 global $TCA; 05054 $listArr=array(); 05055 t3lib_div::loadTCA($table); 05056 if ($TCA[$table]['columns']) { 05057 reset($TCA[$table]['columns']); 05058 while (list($field,$configArr)=each($TCA[$table]['columns'])) { 05059 if ($configArr['config']['type']=='group' && $configArr['config']['internal_type']=='file') { 05060 $listArr[]=$field; 05061 } 05062 } 05063 } 05064 return $listArr; 05065 } 05066 05073 function getUniqueFields($table) { 05074 global $TCA; 05075 05076 $listArr=array(); 05077 t3lib_div::loadTCA($table); 05078 if ($TCA[$table]['columns']) { 05079 reset($TCA[$table]['columns']); 05080 while (list($field,$configArr)=each($TCA[$table]['columns'])) { 05081 if ($configArr['config']['type']==='input') { 05082 $evalCodesArray = t3lib_div::trimExplode(',',$configArr['config']['eval'],1); 05083 if (in_array('uniqueInPid',$evalCodesArray) || in_array('unique',$evalCodesArray)) { 05084 $listArr[]=$field; 05085 } 05086 } 05087 } 05088 } 05089 return $listArr; 05090 } 05091 05098 function isReferenceField($conf) { 05099 return ($conf['type']=='group' && $conf['internal_type']=='db') || ($conf['type']=='select' && $conf['foreign_table']); 05100 } 05101 05113 function getCopyHeader($table,$pid,$field,$value,$count,$prevTitle='') { 05114 global $TCA; 05115 05116 // Set title value to check for: 05117 if ($count) { 05118 $checkTitle = $value.rtrim(' '.sprintf($this->prependLabel($table),$count)); 05119 } else { 05120 $checkTitle = $value; 05121 } 05122 05123 // Do check: 05124 if ($prevTitle != $checkTitle || $count<100) { 05125 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', $table, 'pid='.intval($pid).' AND '.$field.'='.$GLOBALS['TYPO3_DB']->fullQuoteStr($checkTitle, $table).$this->deleteClause($table), '', '', '1'); 05126 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res)) { 05127 return $this->getCopyHeader($table,$pid,$field,$value,$count+1,$checkTitle); 05128 } 05129 } 05130 05131 // Default is to just return the current input title if no other was returned before: 05132 return $checkTitle; 05133 } 05134 05142 function prependLabel($table) { 05143 global $TCA; 05144 if (is_object($GLOBALS['LANG'])) { 05145 $label = $GLOBALS['LANG']->sL($TCA[$table]['ctrl']['prependAtCopy']); 05146 } else { 05147 list($label) = explode('|',$TCA[$table]['ctrl']['prependAtCopy']); 05148 } 05149 return $label; 05150 } 05151 05159 function resolvePid($table,$pid) { 05160 global $TCA; 05161 $pid = intval($pid); 05162 if ($pid < 0) { 05163 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('pid', $table, 'uid='.abs($pid)); 05164 $row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res); 05165 05166 // 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. 05167 if ($lookForLiveVersion = t3lib_BEfunc::getLiveVersionOfRecord($table,abs($pid),'pid')) { 05168 $row = $lookForLiveVersion; 05169 } 05170 05171 $pid = intval($row['pid']); 05172 } 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. 05173 if ($WSdestPage = t3lib_BEfunc::getWorkspaceVersionOfRecord($this->BE_USER->workspace, 'pages', $pid, 'uid,t3ver_swapmode')) { // Looks for workspace version of page. 05174 if ($WSdestPage['t3ver_swapmode']==0) { // if swapmode is zero, then change pid value. 05175 $pid = $WSdestPage['uid']; 05176 } 05177 } 05178 } 05179 return $pid; 05180 } 05181 05189 function clearPrefixFromValue($table,$value) { 05190 global $TCA; 05191 $regex = sprintf(quotemeta($this->prependLabel($table)),'[0-9]*').'$'; 05192 return @ereg_replace($regex,'',$value); 05193 } 05194 05204 function extFileFunctions($table,$field,$filelist,$func) { 05205 global $TCA; 05206 t3lib_div::loadTCA($table); 05207 $uploadFolder = $TCA[$table]['columns'][$field]['config']['uploadfolder']; 05208 if ($uploadFolder && trim($filelist)) { 05209 $uploadPath = $this->destPathFromUploadFolder($uploadFolder); 05210 $fileArray = explode(',',$filelist); 05211 while (list(,$theFile)=each($fileArray)) { 05212 $theFile=trim($theFile); 05213 if ($theFile) { 05214 switch($func) { 05215 case 'deleteAll': 05216 if (@is_file($uploadPath.'/'.$theFile)) { 05217 unlink ($uploadPath.'/'.$theFile); 05218 } else { 05219 $this->log($table,0,3,0,100,"Delete: Referenced file that was supposed to be deleted together with it's record didn't exist"); 05220 } 05221 break; 05222 } 05223 } 05224 } 05225 } 05226 } 05227 05234 function noRecordsFromUnallowedTables($inList) { 05235 global $TCA; 05236 reset ($TCA); 05237 $inList = trim($this->rmComma(trim($inList))); 05238 if ($inList && !$this->admin) { 05239 while (list($table) = each($TCA)) { 05240 $mres = $GLOBALS['TYPO3_DB']->exec_SELECTquery('count(*)', $table, 'pid IN ('.$inList.')'.t3lib_BEfunc::deleteClause($table)); 05241 $count = $GLOBALS['TYPO3_DB']->sql_fetch_row($mres); 05242 if ($count[0] && ($this->tableReadOnly($table) || !$this->checkModifyAccessList($table))) { 05243 return FALSE; 05244 } 05245 } 05246 } 05247 return TRUE; 05248 } 05249 05260 function notifyStageChange($stat,$stageId,$table,$id,$comment) { 05261 $workspaceRec = t3lib_BEfunc::getRecord('sys_workspace', $stat['uid']); 05262 05263 if (is_array($workspaceRec)) { 05264 05265 // Compile label: 05266 switch((int)$stageId) { 05267 case 1: 05268 $newStage = 'Ready for review'; 05269 break; 05270 case 10: 05271 $newStage = 'Ready for publishing'; 05272 break; 05273 case -1: 05274 $newStage = 'Element was rejected!'; 05275 break; 05276 case 0: 05277 $newStage = 'Rejected element was noticed and edited'; 05278 break; 05279 default: 05280 $newStage = 'Unknown state change!?'; 05281 break; 05282 } 05283 05284 // Compile list of recipients: 05285 $emails = array(); 05286 switch((int)$stat['stagechg_notification']) { 05287 case 1: 05288 switch((int)$stageId) { 05289 case 1: 05290 $emails = $this->notifyStageChange_getEmails($workspaceRec['reviewers']); 05291 break; 05292 case 10: 05293 $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE); 05294 break; 05295 case -1: 05296 $emails = $this->notifyStageChange_getEmails($workspaceRec['reviewers']); 05297 $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['members'])); 05298 break; 05299 case 0: 05300 $emails = $this->notifyStageChange_getEmails($workspaceRec['members']); 05301 break; 05302 default: 05303 $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE); 05304 break; 05305 } 05306 break; 05307 case 10: 05308 $emails = $this->notifyStageChange_getEmails($workspaceRec['adminusers'], TRUE); 05309 $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['reviewers'])); 05310 $emails = array_merge($emails,$this->notifyStageChange_getEmails($workspaceRec['members'])); 05311 break; 05312 } 05313 $emails = array_unique($emails); 05314 05315 // Send email: 05316 if (count($emails)) { 05317 $message = sprintf(' 05318 At the TYPO3 site "%s" (%s) 05319 in workspace "%s" (#%s) 05320 the stage has changed for the element "%s": 05321 05322 ==> %s 05323 05324 User Comment: 05325 "%s" 05326 05327 State was change by %s (username: %s) 05328 ', 05329 $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], 05330 t3lib_div::getIndpEnv('TYPO3_SITE_URL').TYPO3_mainDir, 05331 $workspaceRec['title'], 05332 $workspaceRec['uid'], 05333 $table.':'.$id, 05334 $newStage, 05335 $comment, 05336 $this->BE_USER->user['realName'], 05337 $this->BE_USER->user['username']); 05338 05339 t3lib_div::plainMailEncoded( 05340 implode(',',$emails), 05341 'TYPO3 Workspace Note: Stage Change for '.$table.':'.$id, 05342 trim($message) 05343 ); 05344 } 05345 } 05346 } 05347 05355 function notifyStageChange_getEmails($listOfUsers,$noTablePrefix=FALSE) { 05356 $users = t3lib_div::trimExplode(',',$listOfUsers,1); 05357 $emails = array(); 05358 foreach($users as $userIdent) { 05359 if ($noTablePrefix) { 05360 $id = intval($userIdent); 05361 } else { 05362 list($table,$id) = t3lib_div::revExplode('_',$userIdent,2); 05363 } 05364 if ($table==='be_users' || $noTablePrefix) { 05365 if ($userRecord = t3lib_BEfunc::getRecord('be_users', $id, 'email')) { 05366 if (strlen(trim($userRecord['email']))) { 05367 $emails[$id] = $userRecord['email']; 05368 } 05369 } 05370 } 05371 } 05372 return $emails; 05373 } 05374 05375 05376 05377 05378 05379 05380 05381 05382 05383 05384 05385 05386 /****************************** 05387 * 05388 * Clearing cache 05389 * 05390 ******************************/ 05391 05401 function clear_cache($table,$uid) { 05402 global $TCA, $TYPO3_CONF_VARS; 05403 05404 $uid = intval($uid); 05405 if (is_array($TCA[$table]) && $uid > 0) { 05406 05407 // Get Page TSconfig relavant: 05408 list($tscPID) = t3lib_BEfunc::getTSCpid($table,$uid,''); 05409 $TSConfig = $this->getTCEMAIN_TSconfig($tscPID); 05410 05411 if (!$TSConfig['clearCache_disable']) { 05412 05413 // If table is "pages": 05414 if (t3lib_extMgm::isLoaded('cms')) { 05415 $list_cache = array(); 05416 if ($table=='pages') { 05417 05418 // Builds list of pages on the SAME level as this page (siblings) 05419 $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05420 'A.pid AS pid, B.uid AS uid', 05421 'pages A, pages B', 05422 'A.uid='.intval($uid).' AND B.pid=A.pid AND B.deleted=0' 05423 ); 05424 05425 $pid_tmp = 0; 05426 while ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) { 05427 $list_cache[] = $row_tmp['uid']; 05428 $pid_tmp = $row_tmp['pid']; 05429 05430 // Add children as well: 05431 if ($TSConfig['clearCache_pageSiblingChildren']) { 05432 $res_tmp2 = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05433 'uid', 05434 'pages', 05435 'pid='.intval($row_tmp['uid']).' AND deleted=0' 05436 ); 05437 while ($row_tmp2 = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp2)) { 05438 $list_cache[] = $row_tmp2['uid']; 05439 } 05440 } 05441 } 05442 05443 // Finally, add the parent page as well: 05444 $list_cache[] = $pid_tmp; 05445 05446 // Add grand-parent as well: 05447 if ($TSConfig['clearCache_pageGrandParent']) { 05448 $res_tmp = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05449 'pid', 05450 'pages', 05451 'uid='.intval($pid_tmp) 05452 ); 05453 if ($row_tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_tmp)) { 05454 $list_cache[] = $row_tmp['pid']; 05455 } 05456 } 05457 } else { // For other tables than "pages", delete cache for the records "parent page". 05458 $list_cache[] = intval($this->getPID($table,$uid)); 05459 } 05460 05461 // Call pre-processing function for clearing of cache for page ids: 05462 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) { 05463 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) { 05464 $_params = array('pageIdArray' => &$list_cache, 'table' => $table, 'uid' => $uid, 'functionID' => 'clear_cache()'); 05465 // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array! 05466 t3lib_div::callUserFunction($funcName,$_params,$this); 05467 } 05468 } 05469 05470 // Delete cache for selected pages: 05471 if (is_array($list_cache)) { 05472 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')'); 05473 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection', 'page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')'); 05474 } 05475 } 05476 } 05477 05478 // Clear cache for pages entered in TSconfig: 05479 if ($TSConfig['clearCacheCmd']) { 05480 $Commands = t3lib_div::trimExplode(',',strtolower($TSConfig['clearCacheCmd']),1); 05481 $Commands = array_unique($Commands); 05482 foreach($Commands as $cmdPart) { 05483 $this->clear_cacheCmd($cmdPart); 05484 } 05485 } 05486 05487 // Call post processing function for clear-cache: 05488 global $TYPO3_CONF_VARS; 05489 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) { 05490 // FIXME $uid_page is undefined 05491 $_params = array('table' => $table,'uid' => $uid,'uid_page' => $uid_page,'TSConfig' => $TSConfig); 05492 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) { 05493 t3lib_div::callUserFunction($_funcRef,$_params,$this); 05494 } 05495 } 05496 } 05497 } 05498 05511 function clear_cacheCmd($cacheCmd) { 05512 global $TYPO3_CONF_VARS; 05513 05514 // Clear cache for either ALL pages or ALL tables! 05515 switch($cacheCmd) { 05516 case 'pages': 05517 if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.pages')) { 05518 if (t3lib_extMgm::isLoaded('cms')) { 05519 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages',''); 05520 } 05521 } 05522 break; 05523 case 'all': 05524 if ($this->admin || $this->BE_USER->getTSConfigVal('options.clearCache.all')) { 05525 if (t3lib_extMgm::isLoaded('cms')) { 05526 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages',''); 05527 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pagesection',''); 05528 } 05529 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_hash',''); 05530 05531 // Clearing additional cache tables: 05532 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'])) { 05533 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearAllCache_additionalTables'] as $tableName) { 05534 if (!ereg('[^[:alnum:]_]',$tableName) && substr($tableName,-5)=='cache') { 05535 $GLOBALS['TYPO3_DB']->exec_DELETEquery($tableName,''); 05536 } else { 05537 die('Fatal Error: Trying to flush table "'.$tableName.'" with "Clear All Cache"'); 05538 } 05539 } 05540 } 05541 } 05542 break; 05543 case 'temp_CACHED': 05544 if ($this->admin && $TYPO3_CONF_VARS['EXT']['extCache']) { 05545 $this->removeCacheFiles(); 05546 } 05547 break; 05548 } 05549 05550 // Clear cache for a page ID! 05551 if (t3lib_div::testInt($cacheCmd)) { 05552 if (t3lib_extMgm::isLoaded('cms')) { 05553 05554 $list_cache = array($cacheCmd); 05555 05556 // Call pre-processing function for clearing of cache for page ids: 05557 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'])) { 05558 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearPageCacheEval'] as $funcName) { 05559 $_params = array('pageIdArray' => &$list_cache, 'cacheCmd' => $cacheCmd, 'functionID' => 'clear_cacheCmd()'); 05560 // Returns the array of ids to clear, false if nothing should be cleared! Never an empty array! 05561 t3lib_div::callUserFunction($funcName,$_params,$this); 05562 } 05563 } 05564 05565 // Delete cache for selected pages: 05566 if (is_array($list_cache)) { 05567 $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_pages','page_id IN ('.implode(',',$GLOBALS['TYPO3_DB']->cleanIntArray($list_cache)).')'); 05568 $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! 05569 } 05570 } 05571 } 05572 05573 // Call post processing function for clear-cache: 05574 if (is_array($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'])) { 05575 $_params = array('cacheCmd'=>$cacheCmd); 05576 foreach($TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['clearCachePostProc'] as $_funcRef) { 05577 t3lib_div::callUserFunction($_funcRef,$_params,$this); 05578 } 05579 } 05580 } 05581 05582 05583 05584 05585 05586 05587 05588 05589 05590 05591 05592 05593 05594 05595 /***************************** 05596 * 05597 * Logging 05598 * 05599 *****************************/ 05600 05617 function log($table,$recuid,$action,$recpid,$error,$details,$details_nr=-1,$data=array(),$event_pid=-1,$NEWid='') { 05618 if ($this->enableLogging) { 05619 $type=1; // Type value for tce_db.php 05620 if (!$this->storeLogMessages) {$details='';} 05621 if ($error>0) $this->errorLog[] = '['.$type.'.'.$action.'.'.$details_nr.']: '.$details; 05622 return $this->BE_USER->writelog($type,$action,$error,$details_nr,$details,$data,$table,$recuid,$recpid,$event_pid,$NEWid); 05623 } 05624 } 05625 05634 function newlog($message, $error=0) { 05635 return $this->log('',0,0,0,$error,$message,-1); 05636 } 05637 05644 function printLogErrorMessages($redirect) { 05645 05646 $res_log = $GLOBALS['TYPO3_DB']->exec_SELECTquery( 05647 '*', 05648 'sys_log', 05649 'type=1 AND userid='.intval($this->BE_USER->user['uid']).' AND tstamp='.intval($GLOBALS['EXEC_TIME']).' AND error!=0' 05650 ); 05651 $errorJS = array(); 05652 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res_log)) { 05653 $log_data = unserialize($row['log_data']); 05654 $errorJS[] = $row['error'].': '.sprintf($row['details'], $log_data[0],$log_data[1],$log_data[2],$log_data[3],$log_data[4]); 05655 } 05656 05657 if (count($errorJS)) { 05658 $error_doc = t3lib_div::makeInstance('template'); 05659 $error_doc->backPath = $GLOBALS['BACK_PATH']; 05660 05661 $content.= $error_doc->startPage('tce_db.php Error output'); 05662 05663 $lines[] = ' 05664 <tr class="bgColor5"> 05665 <td colspan="2" align="center"><strong>Errors:</strong></td> 05666 </tr>'; 05667 05668 foreach($errorJS as $line) { 05669 $lines[] = ' 05670 <tr class="bgColor4"> 05671 <td valign="top"><img'.t3lib_iconWorks::skinImg($error_doc->backPath,'gfx/icon_fatalerror.gif','width="18" height="16"').' alt="" /></td> 05672 <td>'.htmlspecialchars($line).'</td> 05673 </tr>'; 05674 } 05675 05676 $lines[] = ' 05677 <tr> 05678 <td colspan="2" align="center"><br />'. 05679 '<form action=""><input type="submit" value="Continue" onclick="'.htmlspecialchars('window.location.href=\''.$redirect.'\';return false;').'"></form>'. 05680 '</td> 05681 </tr>'; 05682 05683 $content.= ' 05684 <br/><br/> 05685 <table border="0" cellpadding="1" cellspacing="1" width="300" align="center"> 05686 '.implode('',$lines).' 05687 </table>'; 05688 05689 $content.= $error_doc->endPage(); 05690 echo $content; 05691 exit; 05692 } 05693 } 05694 } 05695 05696 05697 05698 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tcemain.php']) { 05699 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_tcemain.php']); 05700 } 05701 ?>