|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 1999-2011 Kasper Skårhøj (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 ***************************************************************/ 00027 /** 00028 * [CLASS/FUNCTION INDEX of SCRIPT] 00029 * 00030 * 00031 * 00032 * 89: class t3lib_refindex 00033 * 107: function updateRefIndexTable($table,$uid,$testOnly=FALSE) 00034 * 178: function generateRefIndexData($table,$uid) 00035 * 255: function createEntryData($table,$uid,$field,$flexpointer,$deleted,$ref_table,$ref_uid,$ref_string='',$sort=-1,$softref_key='',$softref_id='') 00036 * 282: function createEntryData_dbRels($table,$uid,$fieldname,$flexpointer,$deleted,$items) 00037 * 299: function createEntryData_fileRels($table,$uid,$fieldname,$flexpointer,$deleted,$items) 00038 * 320: function createEntryData_softreferences($table,$uid,$fieldname,$flexpointer,$deleted,$keys) 00039 * 00040 * SECTION: Get relations from table row 00041 * 376: function getRelations($table,$row,$onlyField='') 00042 * 473: function getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj) 00043 * 523: function getRelations_procFiles($value, $conf, $uid) 00044 * 573: function getRelations_procDB($value, $conf, $uid) 00045 * 00046 * SECTION: Setting values 00047 * 616: function setReferenceValue($hash,$newValue,$returnDataArray=FALSE) 00048 * 699: function setReferenceValue_dbRels($refRec,$itemArray,$newValue,&$dataArray,$flexpointer='') 00049 * 737: function setReferenceValue_fileRels($refRec,$itemArray,$newValue,&$dataArray,$flexpointer='') 00050 * 775: function setReferenceValue_softreferences($refRec,$softref,$newValue,&$dataArray,$flexpointer='') 00051 * 00052 * SECTION: Helper functions 00053 * 822: function isReferenceField($conf) 00054 * 832: function destPathFromUploadFolder($folder) 00055 * 842: function error($msg) 00056 * 853: function updateIndex($testOnly,$cli_echo=FALSE) 00057 * 00058 * TOTAL FUNCTIONS: 18 00059 * (This index is automatically created/updated by the extension "extdeveval") 00060 * 00061 */ 00062 00063 00064 /** 00065 * Reference index processing and relation extraction 00066 * 00067 * NOTICE: When the reference index is updated for an offline version the results may not be correct. 00068 * First, lets assumed that the reference update happens in LIVE workspace (ALWAYS update from Live workspace if you analyse whole database!) 00069 * Secondly, lets assume that in a Draft workspace you have changed the data structure of a parent page record - this is (in TemplaVoila) inherited by subpages. 00070 * When in the LIVE workspace the data structure for the records/pages in the offline workspace will not be evaluated to the right one simply because the data structure is taken from a rootline traversal and in the Live workspace that will NOT include the changed DataSTructure! Thus the evaluation will be based on the Data Structure set in the Live workspace! 00071 * Somehow this scenario is rarely going to happen. Yet, it is an inconsistency and I see now practical way to handle it - other than simply ignoring maintaining the index for workspace records. Or we can say that the index is precise for all Live elements while glitches might happen in an offline workspace? 00072 * Anyway, I just wanted to document this finding - I don't think we can find a solution for it. And its very TemplaVoila specific. 00073 * 00074 * @author Kasper Skårhøj <kasperYYYY@typo3.com> 00075 * @package TYPO3 00076 * @subpackage t3lib 00077 */ 00078 class t3lib_refindex { 00079 00080 var $temp_flexRelations = array(); 00081 var $errorLog = array(); 00082 var $WSOL = FALSE; 00083 var $relations = array(); 00084 00085 var $words_strings = array(); 00086 var $words = array(); 00087 00088 var $hashVersion = 1; // Number which we can increase if a change in the code means we will have to force a re-generation of the index. 00089 00090 00091 /** 00092 * Call this function to update the sys_refindex table for a record (even one just deleted) 00093 * NOTICE: Currently, references updated for a deleted-flagged record will not include those from within flexform fields in some cases where the data structure is defined by another record since the resolving process ignores deleted records! This will also result in bad cleaning up in tcemain I think... Anyway, thats the story of flexforms; as long as the DS can change, lots of references can get lost in no time. 00094 * 00095 * @param string Table name 00096 * @param integer UID of record 00097 * @param boolean If set, nothing will be written to the index but the result value will still report statistics on what is added, deleted and kept. Can be used for mere analysis. 00098 * @return array Array with statistics about how many index records were added, deleted and not altered plus the complete reference set for the record. 00099 */ 00100 function updateRefIndexTable($table, $uid, $testOnly = FALSE) { 00101 00102 // First, secure that the index table is not updated with workspace tainted relations: 00103 $this->WSOL = FALSE; 00104 00105 // Init: 00106 $result = array( 00107 'keptNodes' => 0, 00108 'deletedNodes' => 0, 00109 'addedNodes' => 0 00110 ); 00111 00112 // Get current index from Database: 00113 $currentRels = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( 00114 '*', 00115 'sys_refindex', 00116 'tablename=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_refindex') . 00117 ' AND recuid=' . intval($uid), 00118 '', '', '', 'hash' 00119 ); 00120 00121 // First, test to see if the record exists (including deleted-flagged) 00122 if (t3lib_BEfunc::getRecordRaw($table, 'uid=' . intval($uid), 'uid')) { 00123 00124 // Then, get relations: 00125 $relations = $this->generateRefIndexData($table, $uid); 00126 00127 if (is_array($relations)) { 00128 00129 // Traverse the generated index: 00130 foreach ($relations as $k => $datRec) { 00131 $relations[$k]['hash'] = md5(implode('///', $relations[$k]) . '///' . $this->hashVersion); 00132 00133 // First, check if already indexed and if so, unset that row (so in the end we know which rows to remove!) 00134 if (isset($currentRels[$relations[$k]['hash']])) { 00135 unset($currentRels[$relations[$k]['hash']]); 00136 $result['keptNodes']++; 00137 $relations[$k]['_ACTION'] = 'KEPT'; 00138 } else { 00139 // If new, add it: 00140 if (!$testOnly) { 00141 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_refindex', $relations[$k]); 00142 } 00143 $result['addedNodes']++; 00144 $relations[$k]['_ACTION'] = 'ADDED'; 00145 } 00146 } 00147 00148 $result['relations'] = $relations; 00149 } else { 00150 return FALSE; 00151 } // Weird mistake I would say... 00152 } 00153 00154 // If any old are left, remove them: 00155 if (count($currentRels)) { 00156 $hashList = array_keys($currentRels); 00157 if (count($hashList)) { 00158 $result['deletedNodes'] = count($hashList); 00159 $result['deletedNodes_hashList'] = implode(',', $hashList); 00160 if (!$testOnly) { 00161 $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_refindex', 'hash IN (' . implode(',', $GLOBALS['TYPO3_DB']->fullQuoteArray($hashList, 'sys_refindex')) . ')'); 00162 } 00163 } 00164 } 00165 00166 return $result; 00167 } 00168 00169 /** 00170 * Returns array of arrays with an index of all references found in record from table/uid 00171 * If the result is used to update the sys_refindex table then ->WSOL must NOT be true (no workspace overlay anywhere!) 00172 * 00173 * @param string Table name from $TCA 00174 * @param integer Record UID 00175 * @return array Index Rows 00176 */ 00177 function generateRefIndexData($table, $uid) { 00178 global $TCA; 00179 00180 if (isset($TCA[$table])) { 00181 // Get raw record from DB: 00182 $record = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', $table, 'uid=' . intval($uid)); 00183 00184 if (is_array($record)) { 00185 00186 // Initialize: 00187 $this->words_strings = array(); 00188 $this->words = array(); 00189 00190 // Deleted: 00191 $deleted = $TCA[$table]['ctrl']['delete'] ? ($record[$TCA[$table]['ctrl']['delete']] ? 1 : 0) : 0; 00192 00193 // Get all relations from record: 00194 $dbrels = $this->getRelations($table, $record); 00195 00196 // Traverse those relations, compile records to insert in table: 00197 $this->relations = array(); 00198 foreach ($dbrels as $fieldname => $dat) { 00199 00200 // Based on type, 00201 switch ((string) $dat['type']) { 00202 case 'db': 00203 $this->createEntryData_dbRels($table, $uid, $fieldname, '', $deleted, $dat['itemArray']); 00204 break; 00205 case 'file_reference': 00206 case 'file': 00207 $this->createEntryData_fileRels($table, $uid, $fieldname, '', $deleted, $dat['newValueFiles']); 00208 break; 00209 case 'flex': 00210 // DB references: 00211 if (is_array($dat['flexFormRels']['db'])) { 00212 foreach ($dat['flexFormRels']['db'] as $flexpointer => $subList) { 00213 $this->createEntryData_dbRels($table, $uid, $fieldname, $flexpointer, $deleted, $subList); 00214 } 00215 } 00216 // File references (NOT TESTED!) 00217 if (is_array($dat['flexFormRels']['file'])) { // Not tested 00218 foreach ($dat['flexFormRels']['file'] as $flexpointer => $subList) { 00219 $this->createEntryData_fileRels($table, $uid, $fieldname, $flexpointer, $deleted, $subList); 00220 } 00221 } 00222 // Soft references in flexforms (NOT TESTED!) 00223 if (is_array($dat['flexFormRels']['softrefs'])) { 00224 foreach ($dat['flexFormRels']['softrefs'] as $flexpointer => $subList) { 00225 $this->createEntryData_softreferences($table, $uid, $fieldname, $flexpointer, $deleted, $subList['keys']); 00226 } 00227 } 00228 break; 00229 } 00230 00231 // Softreferences in the field: 00232 if (is_array($dat['softrefs'])) { 00233 $this->createEntryData_softreferences($table, $uid, $fieldname, '', $deleted, $dat['softrefs']['keys']); 00234 } 00235 } 00236 00237 // Word indexing: 00238 t3lib_div::loadTCA($table); 00239 foreach ($TCA[$table]['columns'] as $field => $conf) { 00240 if (t3lib_div::inList('input,text', $conf['config']['type']) && strcmp($record[$field], '') && !t3lib_div::testInt($record[$field])) { 00241 $this->words_strings[$field] = $record[$field]; 00242 } 00243 } 00244 00245 return $this->relations; 00246 } 00247 } 00248 } 00249 00250 /** 00251 * Create array with field/value pairs ready to insert in database. 00252 * The "hash" field is a fingerprint value across this table. 00253 * 00254 * @param string Tablename of source record (where reference is located) 00255 * @param integer UID of source record (where reference is located) 00256 * @param string Fieldname of source record (where reference is located) 00257 * @param string Pointer to location inside flexform structure where reference is located in [field] 00258 * @param integer Whether record is deleted-flagged or not 00259 * @param string For database references; the tablename the reference points to. Special keyword "_FILE" indicates that "ref_string" is a file reference either absolute or relative to PATH_site. Special keyword "_STRING" indicates some special usage (typ. softreference) where "ref_string" is used for the value. 00260 * @param integer For database references; The UID of the record (zero "ref_table" is "_FILE" or "_STRING") 00261 * @param string For "_FILE" or "_STRING" references: The filepath (relative to PATH_site or absolute) or other string. 00262 * @param integer The sorting order of references if many (the "group" or "select" TCA types). -1 if no sorting order is specified. 00263 * @param string If the reference is a soft reference, this is the soft reference parser key. Otherwise empty. 00264 * @param string Soft reference ID for key. Might be useful for replace operations. 00265 * @return array Array record to insert into table. 00266 */ 00267 function createEntryData($table, $uid, $field, $flexpointer, $deleted, $ref_table, $ref_uid, $ref_string = '', $sort = -1, $softref_key = '', $softref_id = '') { 00268 return array( 00269 'tablename' => $table, 00270 'recuid' => $uid, 00271 'field' => $field, 00272 'flexpointer' => $flexpointer, 00273 'softref_key' => $softref_key, 00274 'softref_id' => $softref_id, 00275 'sorting' => $sort, 00276 'deleted' => $deleted, 00277 'ref_table' => $ref_table, 00278 'ref_uid' => $ref_uid, 00279 'ref_string' => $ref_string, 00280 ); 00281 } 00282 00283 /** 00284 * Enter database references to ->relations array 00285 * 00286 * @param string [See createEntryData, arg 1] 00287 * @param integer [See createEntryData, arg 2] 00288 * @param string [See createEntryData, arg 3] 00289 * @param string [See createEntryData, arg 4] 00290 * @param string [See createEntryData, arg 5] 00291 * @param array Data array with databaes relations (table/id) 00292 * @return void 00293 */ 00294 function createEntryData_dbRels($table, $uid, $fieldname, $flexpointer, $deleted, $items) { 00295 foreach ($items as $sort => $i) { 00296 $this->relations[] = $this->createEntryData($table, $uid, $fieldname, $flexpointer, $deleted, $i['table'], $i['id'], '', $sort); 00297 } 00298 } 00299 00300 /** 00301 * Enter file references to ->relations array 00302 * 00303 * @param string [See createEntryData, arg 1] 00304 * @param integer [See createEntryData, arg 2] 00305 * @param string [See createEntryData, arg 3] 00306 * @param string [See createEntryData, arg 4] 00307 * @param string [See createEntryData, arg 5] 00308 * @param array Data array with file relations 00309 * @return void 00310 */ 00311 function createEntryData_fileRels($table, $uid, $fieldname, $flexpointer, $deleted, $items) { 00312 foreach ($items as $sort => $i) { 00313 $filePath = $i['ID_absFile']; 00314 if (t3lib_div::isFirstPartOfStr($filePath, PATH_site)) { 00315 $filePath = substr($filePath, strlen(PATH_site)); 00316 } 00317 $this->relations[] = $this->createEntryData($table, $uid, $fieldname, $flexpointer, $deleted, '_FILE', 0, $filePath, $sort); 00318 } 00319 } 00320 00321 /** 00322 * Enter softref references to ->relations array 00323 * 00324 * @param string [See createEntryData, arg 1] 00325 * @param integer [See createEntryData, arg 2] 00326 * @param string [See createEntryData, arg 3] 00327 * @param string [See createEntryData, arg 4] 00328 * @param string [See createEntryData, arg 5] 00329 * @param array Data array with soft reference keys 00330 * @return void 00331 */ 00332 function createEntryData_softreferences($table, $uid, $fieldname, $flexpointer, $deleted, $keys) { 00333 if (is_array($keys)) { 00334 foreach ($keys as $spKey => $elements) { 00335 if (is_array($elements)) { 00336 foreach ($elements as $subKey => $el) { 00337 if (is_array($el['subst'])) { 00338 switch ((string) $el['subst']['type']) { 00339 case 'db': 00340 list($tableName, $recordId) = explode(':', $el['subst']['recordRef']); 00341 $this->relations[] = $this->createEntryData($table, $uid, $fieldname, $flexpointer, $deleted, $tableName, $recordId, '', -1, $spKey, $subKey); 00342 break; 00343 case 'file_reference': 00344 case 'file': 00345 $this->relations[] = $this->createEntryData($table, $uid, $fieldname, $flexpointer, $deleted, '_FILE', 0, $el['subst']['relFileName'], -1, $spKey, $subKey); 00346 break; 00347 case 'string': 00348 $this->relations[] = $this->createEntryData($table, $uid, $fieldname, $flexpointer, $deleted, '_STRING', 0, $el['subst']['tokenValue'], -1, $spKey, $subKey); 00349 break; 00350 } 00351 } 00352 } 00353 } 00354 } 00355 } 00356 } 00357 00358 00359 /******************************* 00360 * 00361 * Get relations from table row 00362 * 00363 *******************************/ 00364 00365 /** 00366 * Returns relation information for a $table/$row-array 00367 * Traverses all fields in input row which are configured in TCA/columns 00368 * It looks for hard relations to files and records in the TCA types "select" and "group" 00369 * 00370 * @param string Table name 00371 * @param array Row from table 00372 * @param string Specific field to fetch for. 00373 * @return array Array with information about relations 00374 * @see export_addRecord() 00375 */ 00376 function getRelations($table, $row, $onlyField = '') { 00377 global $TCA; 00378 00379 // Load full table description 00380 t3lib_div::loadTCA($table); 00381 00382 // Initialize: 00383 $uid = $row['uid']; 00384 $nonFields = explode(',', 'uid,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,pid'); 00385 00386 $outRow = array(); 00387 foreach ($row as $field => $value) { 00388 if (!in_array($field, $nonFields) && is_array($TCA[$table]['columns'][$field]) && (!$onlyField || $onlyField === $field)) { 00389 $conf = $TCA[$table]['columns'][$field]['config']; 00390 00391 // Add files 00392 if ($result = $this->getRelations_procFiles($value, $conf, $uid)) { 00393 // Creates an entry for the field with all the files: 00394 $outRow[$field] = array( 00395 'type' => 'file', 00396 'newValueFiles' => $result, 00397 ); 00398 } 00399 00400 // Add DB: 00401 if ($result = $this->getRelations_procDB($value, $conf, $uid, $table)) { 00402 // Create an entry for the field with all DB relations: 00403 $outRow[$field] = array( 00404 'type' => 'db', 00405 'itemArray' => $result, 00406 ); 00407 } 00408 00409 // For "flex" fieldtypes we need to traverse the structure looking for file and db references of course! 00410 if ($conf['type'] == 'flex') { 00411 00412 // Get current value array: 00413 // NOTICE: failure to resolve Data Structures can lead to integrity problems with the reference index. Please look up the note in the JavaDoc documentation for the function t3lib_BEfunc::getFlexFormDS() 00414 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $row, $table, '', $this->WSOL); 00415 $currentValueArray = t3lib_div::xml2array($value); 00416 00417 // Traversing the XML structure, processing files: 00418 if (is_array($currentValueArray)) { 00419 $this->temp_flexRelations = array( 00420 'db' => array(), 00421 'file' => array(), 00422 'softrefs' => array() 00423 ); 00424 00425 // Create and call iterator object: 00426 $flexObj = t3lib_div::makeInstance('t3lib_flexformtools'); 00427 $flexObj->traverseFlexFormXMLData($table, $field, $row, $this, 'getRelations_flexFormCallBack'); 00428 00429 // Create an entry for the field: 00430 $outRow[$field] = array( 00431 'type' => 'flex', 00432 'flexFormRels' => $this->temp_flexRelations, 00433 ); 00434 } 00435 } 00436 00437 // Soft References: 00438 if (strlen($value) && $softRefs = t3lib_BEfunc::explodeSoftRefParserList($conf['softref'])) { 00439 $softRefValue = $value; 00440 foreach ($softRefs as $spKey => $spParams) { 00441 $softRefObj = t3lib_BEfunc::softRefParserObj($spKey); 00442 if (is_object($softRefObj)) { 00443 $resultArray = $softRefObj->findRef($table, $field, $uid, $softRefValue, $spKey, $spParams); 00444 if (is_array($resultArray)) { 00445 $outRow[$field]['softrefs']['keys'][$spKey] = $resultArray['elements']; 00446 if (strlen($resultArray['content'])) { 00447 $softRefValue = $resultArray['content']; 00448 } 00449 } 00450 } 00451 } 00452 00453 if (is_array($outRow[$field]['softrefs']) && count($outRow[$field]['softrefs']) && strcmp($value, $softRefValue) && strstr($softRefValue, '{softref:')) { 00454 $outRow[$field]['softrefs']['tokenizedContent'] = $softRefValue; 00455 } 00456 } 00457 } 00458 } 00459 00460 return $outRow; 00461 } 00462 00463 /** 00464 * Callback function for traversing the FlexForm structure in relation to finding file and DB references! 00465 * 00466 * @param array Data structure for the current value 00467 * @param mixed Current value 00468 * @param array Additional configuration used in calling function 00469 * @param string Path of value in DS structure 00470 * @param object Object reference to caller 00471 * @return void 00472 * @see t3lib_TCEmain::checkValue_flex_procInData_travDS() 00473 */ 00474 function getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, $pObj) { 00475 $structurePath = substr($structurePath, 5) . '/'; // removing "data/" in the beginning of path (which points to location in data array) 00476 00477 $dsConf = $dsArr['TCEforms']['config']; 00478 00479 // Implode parameter values: 00480 list($table, $uid, $field) = array($PA['table'], $PA['uid'], $PA['field']); 00481 00482 // Add files 00483 if ($result = $this->getRelations_procFiles($dataValue, $dsConf, $uid)) { 00484 00485 // Creates an entry for the field with all the files: 00486 $this->temp_flexRelations['file'][$structurePath] = $result; 00487 } 00488 00489 // Add DB: 00490 if ($result = $this->getRelations_procDB($dataValue, $dsConf, $uid)) { 00491 00492 // Create an entry for the field with all DB relations: 00493 $this->temp_flexRelations['db'][$structurePath] = $result; 00494 } 00495 00496 // Soft References: 00497 if (strlen($dataValue) && $softRefs = t3lib_BEfunc::explodeSoftRefParserList($dsConf['softref'])) { 00498 $softRefValue = $dataValue; 00499 foreach ($softRefs as $spKey => $spParams) { 00500 $softRefObj = t3lib_BEfunc::softRefParserObj($spKey); 00501 if (is_object($softRefObj)) { 00502 $resultArray = $softRefObj->findRef($table, $field, $uid, $softRefValue, $spKey, $spParams, $structurePath); 00503 if (is_array($resultArray) && is_array($resultArray['elements'])) { 00504 $this->temp_flexRelations['softrefs'][$structurePath]['keys'][$spKey] = $resultArray['elements']; 00505 if (strlen($resultArray['content'])) { 00506 $softRefValue = $resultArray['content']; 00507 } 00508 } 00509 } 00510 } 00511 00512 if (count($this->temp_flexRelations['softrefs']) && strcmp($dataValue, $softRefValue)) { 00513 $this->temp_flexRelations['softrefs'][$structurePath]['tokenizedContent'] = $softRefValue; 00514 } 00515 } 00516 } 00517 00518 /** 00519 * Check field configuration if it is a file relation field and extract file relations if any 00520 * 00521 * @param string Field value 00522 * @param array Field configuration array of type "TCA/columns" 00523 * @param integer Field uid 00524 * @return array If field type is OK it will return an array with the files inside. Else false 00525 */ 00526 function getRelations_procFiles($value, $conf, $uid) { 00527 // Take care of files... 00528 if ($conf['type'] == 'group' && ($conf['internal_type'] == 'file' || $conf['internal_type'] == 'file_reference')) { 00529 00530 // Collect file values in array: 00531 if ($conf['MM']) { 00532 $theFileValues = array(); 00533 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 00534 $dbAnalysis->start('', 'files', $conf['MM'], $uid); 00535 00536 foreach ($dbAnalysis->itemArray as $somekey => $someval) { 00537 if ($someval['id']) { 00538 $theFileValues[] = $someval['id']; 00539 } 00540 } 00541 } else { 00542 $theFileValues = explode(',', $value); 00543 } 00544 00545 // Traverse the files and add them: 00546 $uploadFolder = $conf['internal_type'] == 'file' ? $conf['uploadfolder'] : ''; 00547 $dest = $this->destPathFromUploadFolder($uploadFolder); 00548 $newValue = array(); 00549 $newValueFiles = array(); 00550 00551 foreach ($theFileValues as $file) { 00552 if (trim($file)) { 00553 $realFile = $dest . '/' . trim($file); 00554 # if (@is_file($realFile)) { // Now, the refernece index should NOT look if files exist - just faithfully include them if they are in the records! 00555 $newValueFiles[] = array( 00556 'filename' => basename($file), 00557 'ID' => md5($realFile), 00558 'ID_absFile' => $realFile 00559 ); // the order should be preserved here because.. (?) 00560 # } else $this->error('Missing file: '.$realFile); 00561 } 00562 } 00563 00564 return $newValueFiles; 00565 } 00566 } 00567 00568 /** 00569 * Check field configuration if it is a DB relation field and extract DB relations if any 00570 * 00571 * @param string Field value 00572 * @param array Field configuration array of type "TCA/columns" 00573 * @param integer Field uid 00574 * @param string Table name 00575 * @return array If field type is OK it will return an array with the database relations. Else false 00576 */ 00577 function getRelations_procDB($value, $conf, $uid, $table = '') { 00578 00579 // DB record lists: 00580 if ($this->isReferenceField($conf)) { 00581 $allowedTables = $conf['type'] == 'group' ? $conf['allowed'] : $conf['foreign_table'] . ',' . $conf['neg_foreign_table']; 00582 $prependName = $conf['type'] == 'group' ? $conf['prepend_tname'] : $conf['neg_foreign_table']; 00583 00584 if ($conf['MM_opposite_field']) { 00585 return array(); 00586 } 00587 00588 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup'); 00589 $dbAnalysis->start($value, $allowedTables, $conf['MM'], $uid, $table, $conf); 00590 00591 return $dbAnalysis->itemArray; 00592 } 00593 } 00594 00595 00596 /******************************* 00597 * 00598 * Setting values 00599 * 00600 *******************************/ 00601 00602 /** 00603 * Setting the value of a reference or removing it completely. 00604 * Usage: For lowlevel clean up operations! 00605 * WARNING: With this you can set values that are not allowed in the database since it will bypass all checks for validity! Hence it is targetted at clean-up operations. Please use TCEmain in the usual ways if you wish to manipulate references. 00606 * Since this interface allows updates to soft reference values (which TCEmain does not directly) you may like to use it for that as an exception to the warning above. 00607 * Notice; If you want to remove multiple references from the same field, you MUST start with the one having the highest sorting number. If you don't the removal of a reference with a lower number will recreate an index in which the remaining references in that field has new hash-keys due to new sorting numbers - and you will get errors for the remaining operations which cannot find the hash you feed it! 00608 * To ensure proper working only admin-BE_USERS in live workspace should use this function 00609 * 00610 * @param string 32-byte hash string identifying the record from sys_refindex which you wish to change the value for 00611 * @param mixed Value you wish to set for reference. If NULL, the reference is removed (unless a soft-reference in which case it can only be set to a blank string). If you wish to set a database reference, use the format "[table]:[uid]". Any other case, the input value is set as-is 00612 * @param boolean Return $dataArray only, do not submit it to database. 00613 * @param boolean If set, it will bypass check for workspace-zero and admin user 00614 * @return string If a return string, that carries an error message, otherwise false (=OK) (except if $returnDataArray is set!) 00615 */ 00616 function setReferenceValue($hash, $newValue, $returnDataArray = FALSE, $bypassWorkspaceAdminCheck = FALSE) { 00617 00618 if (($GLOBALS['BE_USER']->workspace === 0 && $GLOBALS['BE_USER']->isAdmin()) || $bypassWorkspaceAdminCheck) { 00619 00620 // Get current index from Database: 00621 $refRec = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow( 00622 '*', 00623 'sys_refindex', 00624 'hash=' . $GLOBALS['TYPO3_DB']->fullQuoteStr($hash, 'sys_refindex') 00625 ); 00626 00627 // Check if reference existed. 00628 if (is_array($refRec)) { 00629 if ($GLOBALS['TCA'][$refRec['tablename']]) { 00630 00631 // Get that record from database: 00632 $record = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow('*', $refRec['tablename'], 'uid=' . intval($refRec['recuid'])); 00633 00634 if (is_array($record)) { 00635 00636 // Get all relations from record, filter with fieldname: 00637 $dbrels = $this->getRelations($refRec['tablename'], $record, $refRec['field']); 00638 if ($dat = $dbrels[$refRec['field']]) { 00639 00640 // Initialize data array that is to be sent to TCEmain afterwards: 00641 $dataArray = array(); 00642 00643 // Based on type, 00644 switch ((string) $dat['type']) { 00645 case 'db': 00646 $error = $this->setReferenceValue_dbRels($refRec, $dat['itemArray'], $newValue, $dataArray); 00647 if ($error) { 00648 return $error; 00649 } 00650 break; 00651 case 'file_reference': 00652 case 'file': 00653 $this->setReferenceValue_fileRels($refRec, $dat['newValueFiles'], $newValue, $dataArray); 00654 if ($error) { 00655 return $error; 00656 } 00657 break; 00658 case 'flex': 00659 // DB references: 00660 if (is_array($dat['flexFormRels']['db'][$refRec['flexpointer']])) { 00661 $error = $this->setReferenceValue_dbRels($refRec, $dat['flexFormRels']['db'][$refRec['flexpointer']], $newValue, $dataArray, $refRec['flexpointer']); 00662 if ($error) { 00663 return $error; 00664 } 00665 } 00666 // File references 00667 if (is_array($dat['flexFormRels']['file'][$refRec['flexpointer']])) { 00668 $this->setReferenceValue_fileRels($refRec, $dat['flexFormRels']['file'][$refRec['flexpointer']], $newValue, $dataArray, $refRec['flexpointer']); 00669 if ($error) { 00670 return $error; 00671 } 00672 } 00673 // Soft references in flexforms 00674 if ($refRec['softref_key'] && is_array($dat['flexFormRels']['softrefs'][$refRec['flexpointer']]['keys'][$refRec['softref_key']])) { 00675 $error = $this->setReferenceValue_softreferences($refRec, $dat['flexFormRels']['softrefs'][$refRec['flexpointer']], $newValue, $dataArray, $refRec['flexpointer']); 00676 if ($error) { 00677 return $error; 00678 } 00679 } 00680 break; 00681 } 00682 00683 // Softreferences in the field: 00684 if ($refRec['softref_key'] && is_array($dat['softrefs']['keys'][$refRec['softref_key']])) { 00685 $error = $this->setReferenceValue_softreferences($refRec, $dat['softrefs'], $newValue, $dataArray); 00686 if ($error) { 00687 return $error; 00688 } 00689 00690 } 00691 00692 // Data Array, now ready to sent to TCEmain 00693 if ($returnDataArray) { 00694 return $dataArray; 00695 } else { 00696 00697 // Execute CMD array: 00698 $tce = t3lib_div::makeInstance('t3lib_TCEmain'); 00699 $tce->stripslashes_values = FALSE; 00700 $tce->dontProcessTransformations = TRUE; 00701 $tce->bypassWorkspaceRestrictions = TRUE; 00702 $tce->bypassFileHandling = TRUE; 00703 $tce->bypassAccessCheckForRecords = TRUE; // Otherwise this cannot update things in deleted records... 00704 00705 $tce->start($dataArray, array()); // check has been done previously that there is a backend user which is Admin and also in live workspace 00706 $tce->process_datamap(); 00707 00708 // Return errors if any: 00709 if (count($tce->errorLog)) { 00710 return LF . 'TCEmain:' . implode(LF . 'TCEmain:', $tce->errorLog); 00711 } 00712 } 00713 } 00714 } 00715 } else { 00716 return 'ERROR: Tablename "' . $refRec['tablename'] . '" was not in TCA!'; 00717 } 00718 } else { 00719 return 'ERROR: No reference record with hash="' . $hash . '" was found!'; 00720 } 00721 } else { 00722 return 'ERROR: BE_USER object is not admin OR not in workspace 0 (Live)'; 00723 } 00724 } 00725 00726 /** 00727 * Setting a value for a reference for a DB field: 00728 * 00729 * @param array sys_refindex record 00730 * @param array Array of references from that field 00731 * @param string Value to substitute current value with (or NULL to unset it) 00732 * @param array data array in which the new value is set (passed by reference) 00733 * @param string Flexform pointer, if in a flex form field. 00734 * @return string Error message if any, otherwise false = OK 00735 */ 00736 function setReferenceValue_dbRels($refRec, $itemArray, $newValue, &$dataArray, $flexpointer = '') { 00737 if (!strcmp($itemArray[$refRec['sorting']]['id'], $refRec['ref_uid']) && !strcmp($itemArray[$refRec['sorting']]['table'], $refRec['ref_table'])) { 00738 00739 // Setting or removing value: 00740 if ($newValue === NULL) { // Remove value: 00741 unset($itemArray[$refRec['sorting']]); 00742 } else { 00743 list($itemArray[$refRec['sorting']]['table'], $itemArray[$refRec['sorting']]['id']) = explode(':', $newValue); 00744 } 00745 00746 // Traverse and compile new list of records: 00747 $saveValue = array(); 00748 foreach ($itemArray as $pair) { 00749 $saveValue[] = $pair['table'] . '_' . $pair['id']; 00750 } 00751 00752 // Set in data array: 00753 if ($flexpointer) { 00754 $flexToolObj = t3lib_div::makeInstance('t3lib_flexformtools'); 00755 $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = array(); 00756 $flexToolObj->setArrayValueByPath(substr($flexpointer, 0, -1), $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'], implode(',', $saveValue)); 00757 } else { 00758 $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = implode(',', $saveValue); 00759 } 00760 00761 } else { 00762 return 'ERROR: table:id pair "' . $refRec['ref_table'] . ':' . $refRec['ref_uid'] . '" did not match that of the record ("' . $itemArray[$refRec['sorting']]['table'] . ':' . $itemArray[$refRec['sorting']]['id'] . '") in sorting index "' . $refRec['sorting'] . '"'; 00763 } 00764 } 00765 00766 /** 00767 * Setting a value for a reference for a FILE field: 00768 * 00769 * @param array sys_refindex record 00770 * @param array Array of references from that field 00771 * @param string Value to substitute current value with (or NULL to unset it) 00772 * @param array data array in which the new value is set (passed by reference) 00773 * @param string Flexform pointer, if in a flex form field. 00774 * @return string Error message if any, otherwise false = OK 00775 */ 00776 function setReferenceValue_fileRels($refRec, $itemArray, $newValue, &$dataArray, $flexpointer = '') { 00777 if (!strcmp(substr($itemArray[$refRec['sorting']]['ID_absFile'], strlen(PATH_site)), $refRec['ref_string']) && !strcmp('_FILE', $refRec['ref_table'])) { 00778 00779 // Setting or removing value: 00780 if ($newValue === NULL) { // Remove value: 00781 unset($itemArray[$refRec['sorting']]); 00782 } else { 00783 $itemArray[$refRec['sorting']]['filename'] = $newValue; 00784 } 00785 00786 // Traverse and compile new list of records: 00787 $saveValue = array(); 00788 foreach ($itemArray as $fileInfo) { 00789 $saveValue[] = $fileInfo['filename']; 00790 } 00791 00792 // Set in data array: 00793 if ($flexpointer) { 00794 $flexToolObj = t3lib_div::makeInstance('t3lib_flexformtools'); 00795 $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = array(); 00796 $flexToolObj->setArrayValueByPath(substr($flexpointer, 0, -1), $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'], implode(',', $saveValue)); 00797 } else { 00798 $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = implode(',', $saveValue); 00799 } 00800 00801 } else { 00802 return 'ERROR: either "' . $refRec['ref_table'] . '" was not "_FILE" or file PATH_site+"' . $refRec['ref_string'] . '" did not match that of the record ("' . $itemArray[$refRec['sorting']]['ID_absFile'] . '") in sorting index "' . $refRec['sorting'] . '"'; 00803 } 00804 } 00805 00806 /** 00807 * Setting a value for a soft reference token 00808 * 00809 * @param array sys_refindex record 00810 * @param array Array of soft reference occurencies 00811 * @param string Value to substitute current value with 00812 * @param array data array in which the new value is set (passed by reference) 00813 * @param string Flexform pointer, if in a flex form field. 00814 * @return string Error message if any, otherwise false = OK 00815 */ 00816 function setReferenceValue_softreferences($refRec, $softref, $newValue, &$dataArray, $flexpointer = '') { 00817 if (is_array($softref['keys'][$refRec['softref_key']][$refRec['softref_id']])) { 00818 00819 // Set new value: 00820 $softref['keys'][$refRec['softref_key']][$refRec['softref_id']]['subst']['tokenValue'] = '' . $newValue; 00821 00822 // Traverse softreferences and replace in tokenized content to rebuild it with new value inside: 00823 foreach ($softref['keys'] as $sfIndexes) { 00824 foreach ($sfIndexes as $data) { 00825 $softref['tokenizedContent'] = str_replace('{softref:' . $data['subst']['tokenID'] . '}', $data['subst']['tokenValue'], $softref['tokenizedContent']); 00826 } 00827 } 00828 00829 // Set in data array: 00830 if (!strstr($softref['tokenizedContent'], '{softref:')) { 00831 if ($flexpointer) { 00832 $flexToolObj = t3lib_div::makeInstance('t3lib_flexformtools'); 00833 $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'] = array(); 00834 $flexToolObj->setArrayValueByPath(substr($flexpointer, 0, -1), $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']]['data'], $softref['tokenizedContent']); 00835 } else { 00836 $dataArray[$refRec['tablename']][$refRec['recuid']][$refRec['field']] = $softref['tokenizedContent']; 00837 } 00838 } else { 00839 return 'ERROR: After substituting all found soft references there were still soft reference tokens in the text. (theoretically this does not have to be an error if the string "{softref:" happens to be in the field for another reason.)'; 00840 } 00841 } else { 00842 return 'ERROR: Soft reference parser key "' . $refRec['softref_key'] . '" or the index "' . $refRec['softref_id'] . '" was not found.'; 00843 } 00844 } 00845 00846 00847 /******************************* 00848 * 00849 * Helper functions 00850 * 00851 *******************************/ 00852 00853 /** 00854 * Returns true if the TCA/columns field type is a DB reference field 00855 * 00856 * @param array config array for TCA/columns field 00857 * @return boolean True if DB reference field (group/db or select with foreign-table) 00858 */ 00859 function isReferenceField($conf) { 00860 return ($conf['type'] == 'group' && $conf['internal_type'] == 'db') || (($conf['type'] == 'select' || $conf['type'] == 'inline') && $conf['foreign_table']); 00861 } 00862 00863 /** 00864 * Returns destination path to an upload folder given by $folder 00865 * 00866 * @param string Folder relative to PATH_site 00867 * @return string Input folder prefixed with PATH_site. No checking for existence is done. Output must be a folder without trailing slash. 00868 */ 00869 function destPathFromUploadFolder($folder) { 00870 if (!$folder) { 00871 return substr(PATH_site, 0, -1); 00872 } 00873 return PATH_site . $folder; 00874 } 00875 00876 /** 00877 * Sets error message in the internal error log 00878 * 00879 * @param string Error message 00880 * @return void 00881 */ 00882 function error($msg) { 00883 $this->errorLog[] = $msg; 00884 } 00885 00886 /** 00887 * Updating Index (External API) 00888 * 00889 * @param boolean If set, only a test 00890 * @param boolean If set, output CLI status 00891 * @return array Header and body status content 00892 */ 00893 function updateIndex($testOnly, $cli_echo = FALSE) { 00894 global $TCA, $TYPO3_DB; 00895 00896 $errors = array(); 00897 $tableNames = array(); 00898 $recCount = 0; 00899 $tableCount = 0; 00900 00901 $headerContent = $testOnly ? 'Reference Index being TESTED (nothing written, use "-e" to update)' : 'Reference Index being Updated'; 00902 if ($cli_echo) { 00903 echo 00904 '*******************************************' . LF . 00905 $headerContent . LF . 00906 '*******************************************' . LF; 00907 } 00908 00909 // Traverse all tables: 00910 foreach ($TCA as $tableName => $cfg) { 00911 $tableNames[] = $tableName; 00912 $tableCount++; 00913 00914 // Traverse all records in tables, including deleted records: 00915 $allRecs = $TYPO3_DB->exec_SELECTgetRows('uid', $tableName, '1=1'); //.t3lib_BEfunc::deleteClause($tableName) 00916 $uidList = array(0); 00917 foreach ($allRecs as $recdat) { 00918 $refIndexObj = t3lib_div::makeInstance('t3lib_refindex'); 00919 $result = $refIndexObj->updateRefIndexTable($tableName, $recdat['uid'], $testOnly); 00920 $uidList[] = $recdat['uid']; 00921 $recCount++; 00922 00923 if ($result['addedNodes'] || $result['deletedNodes']) { 00924 $Err = 'Record ' . $tableName . ':' . $recdat['uid'] . ' had ' . $result['addedNodes'] . ' added indexes and ' . $result['deletedNodes'] . ' deleted indexes'; 00925 $errors[] = $Err; 00926 if ($cli_echo) { 00927 echo $Err . LF; 00928 } 00929 } 00930 } 00931 00932 // Searching lost indexes for this table: 00933 $where = 'tablename=' . $TYPO3_DB->fullQuoteStr($tableName, 'sys_refindex') . ' AND recuid NOT IN (' . implode(',', $uidList) . ')'; 00934 $lostIndexes = $TYPO3_DB->exec_SELECTgetRows('hash', 'sys_refindex', $where); 00935 if (count($lostIndexes)) { 00936 $Err = 'Table ' . $tableName . ' has ' . count($lostIndexes) . ' lost indexes which are now deleted'; 00937 $errors[] = $Err; 00938 if ($cli_echo) { 00939 echo $Err . LF; 00940 } 00941 if (!$testOnly) { 00942 $TYPO3_DB->exec_DELETEquery('sys_refindex', $where); 00943 } 00944 } 00945 } 00946 00947 // Searching lost indexes for non-existing tables: 00948 $where = 'tablename NOT IN (' . implode(',', $TYPO3_DB->fullQuoteArray($tableNames, 'sys_refindex')) . ')'; 00949 $lostTables = $TYPO3_DB->exec_SELECTgetRows('hash', 'sys_refindex', $where); 00950 if (count($lostTables)) { 00951 $Err = 'Index table hosted ' . count($lostTables) . ' indexes for non-existing tables, now removed'; 00952 $errors[] = $Err; 00953 if ($cli_echo) { 00954 echo $Err . LF; 00955 } 00956 if (!$testOnly) { 00957 $TYPO3_DB->exec_DELETEquery('sys_refindex', $where); 00958 } 00959 } 00960 00961 $testedHowMuch = $recCount . ' records from ' . $tableCount . ' tables were checked/updated.' . LF; 00962 00963 $bodyContent = $testedHowMuch . (count($errors) ? implode(LF, $errors) : 'Index Integrity was perfect!'); 00964 if ($cli_echo) { 00965 echo $testedHowMuch . (count($errors) ? 'Updates: ' . count($errors) : 'Index Integrity was perfect!') . LF; 00966 } 00967 00968 if (!$testOnly) { 00969 $registry = t3lib_div::makeInstance('t3lib_Registry'); 00970 $registry->set('core', 'sys_refindex_lastUpdate', $GLOBALS['EXEC_TIME']); 00971 } 00972 00973 return array($headerContent, $bodyContent, count($errors)); 00974 } 00975 } 00976 00977 00978 if (defined('TYPO3_MODE') && isset($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_refindex.php'])) { 00979 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_refindex.php']); 00980 } 00981 00982 ?>
1.8.0