* Merging all changes from TYPO3_4-0 branch back into HEAD
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_refindex.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2006 Kasper Skaarhoj (kasperYYYY@typo3.com)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Reference index processing
29 *
30 * $Id$
31 *
32 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
33 */
34 /**
35 * [CLASS/FUNCTION INDEX of SCRIPT]
36 *
37 *
38 *
39 * 76: class t3lib_refindex
40 * 94: function updateRefIndexTable($table,$uid,$testOnly=FALSE)
41 * 165: function generateRefIndexData($table,$uid)
42 * 242: function createEntryData($table,$uid,$field,$flexpointer,$deleted,$ref_table,$ref_uid,$ref_string='',$sort=-1,$softref_key='',$softref_id='')
43 * 269: function createEntryData_dbRels($table,$uid,$fieldname,$flexpointer,$deleted,$items)
44 * 286: function createEntryData_fileRels($table,$uid,$fieldname,$flexpointer,$deleted,$items)
45 * 307: function createEntryData_softreferences($table,$uid,$fieldname,$flexpointer,$deleted,$keys)
46 *
47 * SECTION: Get relations from table row
48 * 362: function getRelations($table,$row)
49 * 459: function getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj)
50 * 509: function getRelations_procFiles($value, $conf, $uid)
51 * 559: function getRelations_procDB($value, $conf, $uid)
52 *
53 * SECTION: Helper functions
54 * 596: function isReferenceField($conf)
55 * 606: function destPathFromUploadFolder($folder)
56 * 616: function error($msg)
57 * 627: function updateIndex($testOnly,$cli_echo=FALSE)
58 *
59 * TOTAL FUNCTIONS: 14
60 * (This index is automatically created/updated by the extension "extdeveval")
61 *
62 */
63
64 require_once(PATH_t3lib.'class.t3lib_tcemain.php');
65 require_once(PATH_t3lib.'class.t3lib_flexformtools.php');
66
67
68
69 /**
70 * Reference index processing and relation extraction
71 *
72 * NOTICE: When the reference index is updated for an offline version the results may not be correct.
73 * First, lets assumed that the reference update happens in LIVE workspace (ALWAYS update from Live workspace if you analyse whole database!)
74 * 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.
75 * 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!
76 * 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?
77 * Anyway, I just wanted to document this finding - I don't think we can find a solution for it. And its very TemplaVoila specific.
78 *
79 * @author Kasper Skaarhoj <kasperYYYY@typo3.com>
80 * @package TYPO3
81 * @subpackage t3lib
82 */
83 class t3lib_refindex {
84
85 var $temp_flexRelations = array();
86 var $errorLog = array();
87 var $WSOL = FALSE;
88 var $relations = array();
89 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.
90
91
92 /**
93 * Call this function to update the sys_refindex table for a record.
94 * 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.
95 *
96 * @param string Table name
97 * @param integer UID of record
98 * @param boolean If set, nothing will be written to the index but the result value will still report statistics on what as added, deleted and kept. Can be used for mere analysis.
99 * @return array Array with statistics about how many index records were added, deleted and not altered plus the complete reference set for the record.
100 */
101 function updateRefIndexTable($table,$uid,$testOnly=FALSE) {
102
103 // First, secure that the index table is not updated with workspace tainted relations:
104 $this->WSOL = FALSE;
105
106 // Init:
107 $result = array(
108 'keptNodes' => 0,
109 'deletedNodes' => 0,
110 'addedNodes' => 0
111 );
112
113 // Get current index from Database:
114 $currentRels = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
115 '*',
116 'sys_refindex',
117 'tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table,'sys_refindex').
118 ' AND recuid='.intval($uid),
119 '','','','hash'
120 );
121
122 // First, test to see if the record exists (being deleted also means it doesn't exist!)
123 if (t3lib_BEfunc::getRecordRaw($table,'uid='.intval($uid),'uid')) {
124
125 // Then, get relations:
126 $relations = $this->generateRefIndexData($table,$uid);
127
128 if (is_array($relations)) {
129
130 // Traverse the generated index:
131 foreach($relations as $k => $datRec) {
132 $relations[$k]['hash'] = md5(implode('///',$relations[$k]).'///'.$this->hashVersion);
133
134 // First, check if already indexed and if so, unset that row (so in the end we know which rows to remove!)
135 if (isset($currentRels[$relations[$k]['hash']])) {
136 unset($currentRels[$relations[$k]['hash']]);
137 $result['keptNodes']++;
138 $relations[$k]['_ACTION'] = 'KEPT';
139 } else {
140 // If new, add it:
141 if (!$testOnly) $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_refindex',$relations[$k]);
142 $result['addedNodes']++;
143 $relations[$k]['_ACTION'] = 'ADDED';
144 }
145 }
146
147 $result['relations'] = $relations;
148 } else return FALSE; // Weird mistake I would say...
149 }
150
151 // If any old are left, remove them:
152 if (count($currentRels)) {
153 $hashList = array_keys($currentRels);
154 if (count($hashList)) {
155 $result['deletedNodes'] = count($hashList);
156 $result['deletedNodes_hashList'] = implode(',',$hashList);
157 if (!$testOnly) $GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_refindex','hash IN ('.implode(',',$GLOBALS['TYPO3_DB']->fullQuoteArray($hashList,'sys_refindex')).')');
158 }
159 }
160
161 return $result;
162 }
163
164 /**
165 * Returns array of arrays with an index of all references found in record from table/uid
166 * If the result is used to update the sys_refindex table then ->WSOL must NOT be true (no workspace overlay anywhere!)
167 *
168 * @param string Table name from $TCA
169 * @param integer Record UID
170 * @return array Index Rows
171 */
172 function generateRefIndexData($table,$uid) {
173 global $TCA;
174
175 if (isset($TCA[$table])) {
176 // Get raw record from DB:
177 list($record) = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('*',$table,'uid='.intval($uid));
178
179 if (is_array($record)) {
180
181 // Deleted:
182 $deleted = $TCA[$table]['ctrl']['delete'] ? ($record[$TCA[$table]['ctrl']['delete']]?1:0) : 0;
183
184 // Get all relations from record:
185 $dbrels = $this->getRelations($table,$record);
186
187 // Traverse those relations, compile records to insert in table:
188 $this->relations = array();
189 foreach($dbrels as $fieldname => $dat) {
190
191 // Based on type,
192 switch((string)$dat['type']) {
193 case 'db':
194 $this->createEntryData_dbRels($table,$uid,$fieldname,'',$deleted,$dat['itemArray']);
195 break;
196 case 'file':
197 $this->createEntryData_fileRels($table,$uid,$fieldname,'',$deleted,$dat['newValueFiles']);
198 break;
199 case 'flex':
200 // DB references:
201 if (is_array($dat['flexFormRels']['db'])) {
202 foreach($dat['flexFormRels']['db'] as $flexpointer => $subList) {
203 $this->createEntryData_dbRels($table,$uid,$fieldname,$flexpointer,$deleted,$subList);
204 }
205 }
206 // File references (NOT TESTED!)
207 if (is_array($dat['flexFormRels']['file'])) { // Not tested
208 foreach($dat['flexFormRels']['file'] as $flexpointer => $subList) {
209 $this->createEntryData_fileRels($table,$uid,$fieldname,$flexpointer,$deleted,$subList);
210 }
211 }
212 // Soft references in flexforms (NOT TESTED!)
213 if (is_array($dat['flexFormRels']['softrefs'])) {
214 foreach($dat['flexFormRels']['softrefs'] as $flexpointer => $subList) {
215 $this->createEntryData_softreferences($table,$uid,$fieldname,$flexpointer,$deleted,$subList['keys']);
216 }
217 }
218 break;
219 }
220
221 // Softreferences in the field:
222 if (is_array($dat['softrefs'])) {
223 $this->createEntryData_softreferences($table,$uid,$fieldname,'',$deleted,$dat['softrefs']['keys']);
224 }
225 }
226
227 return $this->relations;
228 }
229 }
230 }
231
232 /**
233 * Create array with field/value pairs ready to insert in database.
234 * The "hash" field is a fingerprint value across this table.
235 *
236 * @param string Tablename of source record (where reference is located)
237 * @param integer UID of source record (where reference is located)
238 * @param string Fieldname of source record (where reference is located)
239 * @param string Pointer to location inside flexform structure where reference is located in [field]
240 * @param integer Whether record is deleted-flagged or not
241 * @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.
242 * @param integer For database references; The UID of the record (zero "ref_table" is "_FILE" or "_STRING")
243 * @param string For "_FILE" or "_STRING" references: The filepath (relative to PATH_site or absolute) or other string.
244 * @param integer The sorting order of references if many (the "group" or "select" TCA types). -1 if no sorting order is specified.
245 * @param string If the reference is a soft reference, this is the soft reference parser key. Otherwise empty.
246 * @param string Soft reference ID for key. Might be useful for replace operations.
247 * @return array Array record to insert into table.
248 */
249 function createEntryData($table,$uid,$field,$flexpointer,$deleted,$ref_table,$ref_uid,$ref_string='',$sort=-1,$softref_key='',$softref_id='') {
250 return array(
251 'tablename' => $table,
252 'recuid' => $uid,
253 'field' => $field,
254 'flexpointer' => $flexpointer,
255 'softref_key' => $softref_key,
256 'softref_id' => $softref_id,
257 'sorting' => $sort,
258 'deleted' => $deleted,
259 'ref_table' => $ref_table,
260 'ref_uid' => $ref_uid,
261 'ref_string' => $ref_string,
262 );
263 }
264
265 /**
266 * Enter database references to ->relations array
267 *
268 * @param string [See createEntryData, arg 1]
269 * @param integer [See createEntryData, arg 2]
270 * @param string [See createEntryData, arg 3]
271 * @param string [See createEntryData, arg 4]
272 * @param string [See createEntryData, arg 5]
273 * @param array Data array with databaes relations (table/id)
274 * @return void
275 */
276 function createEntryData_dbRels($table,$uid,$fieldname,$flexpointer,$deleted,$items) {
277 foreach($items as $sort => $i) {
278 $this->relations[] = $this->createEntryData($table,$uid,$fieldname,$flexpointer,$deleted,$i['table'],$i['id'],'',$sort);
279 }
280 }
281
282 /**
283 * Enter file references to ->relations array
284 *
285 * @param string [See createEntryData, arg 1]
286 * @param integer [See createEntryData, arg 2]
287 * @param string [See createEntryData, arg 3]
288 * @param string [See createEntryData, arg 4]
289 * @param string [See createEntryData, arg 5]
290 * @param array Data array with file relations
291 * @return void
292 */
293 function createEntryData_fileRels($table,$uid,$fieldname,$flexpointer,$deleted,$items) {
294 foreach($items as $sort => $i) {
295 $filePath = $i['ID_absFile'];
296 if (t3lib_div::isFirstPartOfStr($filePath,PATH_site)) {
297 $filePath = substr($filePath,strlen(PATH_site));
298 }
299 $this->relations[] = $this->createEntryData($table,$uid,$fieldname,$flexpointer,$deleted,'_FILE',0,$filePath,$sort);
300 }
301 }
302
303 /**
304 * Enter softref references to ->relations array
305 *
306 * @param string [See createEntryData, arg 1]
307 * @param integer [See createEntryData, arg 2]
308 * @param string [See createEntryData, arg 3]
309 * @param string [See createEntryData, arg 4]
310 * @param string [See createEntryData, arg 5]
311 * @param array Data array with soft reference keys
312 * @return void
313 */
314 function createEntryData_softreferences($table,$uid,$fieldname,$flexpointer,$deleted,$keys) {
315 if (is_array($keys)) {
316 foreach($keys as $spKey => $elements) {
317 if (is_array($elements)) {
318 foreach($elements as $subKey => $el) {
319 if (is_array($el['subst'])) {
320 switch((string)$el['subst']['type']) {
321 case 'db':
322 list($tableName,$recordId) = explode(':',$el['subst']['recordRef']);
323 $this->relations[] = $this->createEntryData($table,$uid,$fieldname,$flexpointer,$deleted,$tableName,$recordId,'',-1,$spKey,$subKey);
324 break;
325 case 'file':
326 $this->relations[] = $this->createEntryData($table,$uid,$fieldname,$flexpointer,$deleted,'_FILE',0,$el['subst']['relFileName'],-1,$spKey,$subKey);
327 break;
328 case 'string':
329 $this->relations[] = $this->createEntryData($table,$uid,$fieldname,$flexpointer,$deleted,'_STRING',0,$el['subst']['tokenValue'],-1,$spKey,$subKey);
330 break;
331 }
332 }
333 }
334 }
335 }
336 }
337 }
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353 /*******************************
354 *
355 * Get relations from table row
356 *
357 *******************************/
358
359 /**
360 * Returns relation information for a $table/$row-array
361 * Traverses all fields in input row which are configured in TCA/columns
362 * It looks for hard relations to files and records in the TCA types "select" and "group"
363 *
364 * @param string Table name
365 * @param array Row from table
366 * @return array Array with information about relations
367 * @see export_addRecord()
368 */
369 function getRelations($table,$row) {
370 global $TCA;
371
372 // Load full table description
373 t3lib_div::loadTCA($table);
374
375 // Initialize:
376 $uid = $row['uid'];
377 $nonFields = explode(',','uid,perms_userid,perms_groupid,perms_user,perms_group,perms_everybody,pid');
378
379 $outRow = array();
380 foreach($row as $field => $value) {
381 if (!in_array($field,$nonFields) && is_array($TCA[$table]['columns'][$field])) {
382 $conf = $TCA[$table]['columns'][$field]['config'];
383
384 // Add files
385 if ($result = $this->getRelations_procFiles($value, $conf, $uid)) {
386 // Creates an entry for the field with all the files:
387 $outRow[$field] = array(
388 'type' => 'file',
389 'newValueFiles' => $result,
390 );
391 }
392
393 // Add DB:
394 if ($result = $this->getRelations_procDB($value, $conf, $uid)) {
395 // Create an entry for the field with all DB relations:
396 $outRow[$field] = array(
397 'type' => 'db',
398 'itemArray' => $result,
399 );
400 }
401
402 // For "flex" fieldtypes we need to traverse the structure looking for file and db references of course!
403 if ($conf['type']=='flex') {
404
405 // Get current value array:
406 $dataStructArray = t3lib_BEfunc::getFlexFormDS($conf, $row, $table,'',$this->WSOL);
407 $currentValueArray = t3lib_div::xml2array($value);
408
409 // Traversing the XML structure, processing files:
410 if (is_array($currentValueArray)) {
411 $this->temp_flexRelations = array(
412 'db' => array(),
413 'file' => array(),
414 'softrefs' => array()
415 );
416
417 // Create and call iterator object:
418 $flexObj = t3lib_div::makeInstance('t3lib_flexformtools');
419 $flexObj->traverseFlexFormXMLData($table,$field,$row,$this,'getRelations_flexFormCallBack');
420
421 // Create an entry for the field:
422 $outRow[$field] = array(
423 'type' => 'flex',
424 'flexFormRels' => $this->temp_flexRelations,
425 );
426 }
427 }
428
429 // Soft References:
430 if (strlen($value) && $softRefs = t3lib_BEfunc::explodeSoftRefParserList($conf['softref'])) {
431 $softRefValue = $value;
432 foreach($softRefs as $spKey => $spParams) {
433 $softRefObj = &t3lib_BEfunc::softRefParserObj($spKey);
434 if (is_object($softRefObj)) {
435 $resultArray = $softRefObj->findRef($table, $field, $uid, $softRefValue, $spKey, $spParams);
436 if (is_array($resultArray)) {
437 $outRow[$field]['softrefs']['keys'][$spKey] = $resultArray['elements'];
438 if (strlen($resultArray['content'])) {
439 $softRefValue = $resultArray['content'];
440 }
441 }
442 }
443 }
444
445 if (is_array($outRow[$field]['softrefs']) && count($outRow[$field]['softrefs']) && strcmp($value,$softRefValue) && strstr($softRefValue,'{softref:')) {
446 $outRow[$field]['softrefs']['tokenizedContent'] = $softRefValue;
447 }
448 }
449 }
450 }
451
452 return $outRow;
453 }
454
455 /**
456 * Callback function for traversing the FlexForm structure in relation to finding file and DB references!
457 *
458 * @param array Data structure for the current value
459 * @param mixed Current value
460 * @param array Additional configuration used in calling function
461 * @param string Path of value in DS structure
462 * @param object Object reference to caller
463 * @return void
464 * @see t3lib_TCEmain::checkValue_flex_procInData_travDS()
465 */
466 function getRelations_flexFormCallBack($dsArr, $dataValue, $PA, $structurePath, &$pObj) {
467 $structurePath = substr($structurePath,5).'/'; // removing "data/" in the beginning of path (which points to location in data array)
468
469 $dsConf = $dsArr['TCEforms']['config'];
470
471 // Implode parameter values:
472 list($table, $uid, $field) = array($PA['table'],$PA['uid'],$PA['field']);
473
474 // Add files
475 if ($result = $this->getRelations_procFiles($dataValue, $dsConf, $uid)) {
476
477 // Creates an entry for the field with all the files:
478 $this->temp_flexRelations['file'][$structurePath] = $result;
479 }
480
481 // Add DB:
482 if ($result = $this->getRelations_procDB($dataValue, $dsConf, $uid)) {
483
484 // Create an entry for the field with all DB relations:
485 $this->temp_flexRelations['db'][$structurePath] = $result;
486 }
487
488 // Soft References:
489 if (strlen($dataValue) && $softRefs = t3lib_BEfunc::explodeSoftRefParserList($dsConf['softref'])) {
490 $softRefValue = $dataValue;
491 foreach($softRefs as $spKey => $spParams) {
492 $softRefObj = &t3lib_BEfunc::softRefParserObj($spKey);
493 if (is_object($softRefObj)) {
494 $resultArray = $softRefObj->findRef($table, $field, $uid, $softRefValue, $spKey, $spParams, $structurePath);
495 if (is_array($resultArray) && is_array($resultArray['elements'])) {
496 $this->temp_flexRelations['softrefs'][$structurePath]['keys'][$spKey] = $resultArray['elements'];
497 if (strlen($resultArray['content'])) $softRefValue = $resultArray['content'];
498 }
499 }
500 }
501
502 if (count($this->temp_flexRelations['softrefs']) && strcmp($dataValue,$softRefValue)) {
503 $this->temp_flexRelations['softrefs'][$structurePath]['tokenizedContent'] = $softRefValue;
504 }
505 }
506 }
507
508 /**
509 * Check field configuration if it is a file relation field and extract file relations if any
510 *
511 * @param string Field value
512 * @param array Field configuration array of type "TCA/columns"
513 * @param integer Field uid
514 * @return array If field type is OK it will return an array with the files inside. Else false
515 */
516 function getRelations_procFiles($value, $conf, $uid) {
517 // Take care of files...
518 if ($conf['type']=='group' && $conf['internal_type']=='file') {
519
520 // Collect file values in array:
521 if ($conf['MM']) {
522 $theFileValues = array();
523 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
524 $dbAnalysis->start('', 'files', $conf['MM'], $uid);
525
526 foreach($dbAnalysis->itemArray as $somekey => $someval) {
527 if ($someval['id']) {
528 $theFileValues[] = $someval['id'];
529 }
530 }
531 } else {
532 $theFileValues = explode(',',$value);
533 }
534
535 // Traverse the files and add them:
536 $uploadFolder = $conf['uploadfolder'];
537 $dest = $this->destPathFromUploadFolder($uploadFolder);
538 $newValue = array();
539 $newValueFiles = array();
540
541 foreach($theFileValues as $file) {
542 if (trim($file)) {
543 $realFile = $dest.'/'.trim($file);
544 if (@is_file($realFile)) {
545 $newValueFiles[] = array(
546 'filename' => $file,
547 'ID' => md5($realFile),
548 'ID_absFile' => $realFile
549 ); // the order should be preserved here because.. (?)
550 } else $this->error('Missing file: '.$realFile);
551 }
552 }
553
554 return $newValueFiles;
555 }
556 }
557
558 /**
559 * Check field configuration if it is a DB relation field and extract DB relations if any
560 *
561 * @param string Field value
562 * @param array Field configuration array of type "TCA/columns"
563 * @param integer Field uid
564 * @return array If field type is OK it will return an array with the database relations. Else false
565 */
566 function getRelations_procDB($value, $conf, $uid) {
567
568 // DB record lists:
569 if ($this->isReferenceField($conf)) {
570 $allowedTables = $conf['type']=='group' ? $conf['allowed'] : $conf['foreign_table'].','.$conf['neg_foreign_table'];
571 $prependName = $conf['type']=='group' ? $conf['prepend_tname'] : $conf['neg_foreign_table'];
572
573 $dbAnalysis = t3lib_div::makeInstance('t3lib_loadDBGroup');
574 $dbAnalysis->start($value,$allowedTables,$conf['MM'],$uid);
575
576 return $dbAnalysis->itemArray;
577 }
578 }
579
580
581
582
583
584
585
586
587
588
589
590
591 /*******************************
592 *
593 * Helper functions
594 *
595 *******************************/
596
597 /**
598 * Returns true if the TCA/columns field type is a DB reference field
599 *
600 * @param array config array for TCA/columns field
601 * @return boolean True if DB reference field (group/db or select with foreign-table)
602 */
603 function isReferenceField($conf) {
604 return ($conf['type']=='group' && $conf['internal_type']=='db') || ($conf['type']=='select' && $conf['foreign_table']);
605 }
606
607 /**
608 * Returns destination path to an upload folder given by $folder
609 *
610 * @param string Folder relative to PATH_site
611 * @return string Input folder prefixed with PATH_site. No checking for existence is done. Output must be a folder without trailing slash.
612 */
613 function destPathFromUploadFolder($folder) {
614 return PATH_site.$folder;
615 }
616
617 /**
618 * Sets error message in the internal error log
619 *
620 * @param string Error message
621 * @return void
622 */
623 function error($msg) {
624 $this->errorLog[]=$msg;
625 }
626
627 /**
628 * Updating Index (External API)
629 *
630 * @param boolean If set, only a test
631 * @param boolean If set, output CLI status
632 * @return array Header and body status content
633 */
634 function updateIndex($testOnly,$cli_echo=FALSE) {
635 global $TCA, $TYPO3_DB;
636
637 $errors = array();
638 $tableNames = array();
639 $recCount=0;
640 $tableCount=0;
641
642 $headerContent = $testOnly ? 'Reference Index TESTED (nothing written)' : 'Reference Index Updated';
643 if ($cli_echo) echo
644 '*******************************************'.chr(10).
645 $headerContent.chr(10).
646 '*******************************************'.chr(10);
647
648 // Traverse all tables:
649 foreach ($TCA as $tableName => $cfg) {
650 $tableNames[] = $tableName;
651 $tableCount++;
652
653 // Traverse all non-deleted records in tables:
654 $allRecs = $TYPO3_DB->exec_SELECTgetRows('uid',$tableName,'1=1'); //.t3lib_BEfunc::deleteClause($tableName)
655 $uidList = array(0);
656 foreach ($allRecs as $recdat) {
657 $refIndexObj = t3lib_div::makeInstance('t3lib_refindex');
658 $result = $refIndexObj->updateRefIndexTable($tableName,$recdat['uid'],$testOnly);
659 $uidList[]= $recdat['uid'];
660 $recCount++;
661
662 if ($result['addedNodes'] || $result['deletedNodes']) {
663 $Err = 'Record '.$tableName.':'.$recdat['uid'].' had '.$result['addedNodes'].' added indexes and '.$result['deletedNodes'].' deleted indexes';
664 $errors[]= $Err;
665 if ($cli_echo) echo $Err.chr(10);
666 #$errors[] = t3lib_div::view_array($result);
667 }
668 }
669
670 // Searching lost indexes for this table:
671 $where = 'tablename='.$TYPO3_DB->fullQuoteStr($tableName,'sys_refindex').' AND recuid NOT IN ('.implode(',',$uidList).')';
672 $lostIndexes = $TYPO3_DB->exec_SELECTgetRows('hash','sys_refindex',$where);
673 if (count($lostIndexes)) {
674 $Err = 'Table '.$tableName.' has '.count($lostIndexes).' lost indexes which are now deleted';
675 $errors[]= $Err;
676 if ($cli_echo) echo $Err.chr(10);
677 if (!$testOnly) $TYPO3_DB->exec_DELETEquery('sys_refindex',$where);
678 }
679 }
680
681 // Searching lost indexes for non-existing tables:
682 $where = 'tablename NOT IN ('.implode(',',$TYPO3_DB->fullQuoteArray($tableNames,'sys_refindex')).')';
683 $lostTables = $TYPO3_DB->exec_SELECTgetRows('hash','sys_refindex',$where);
684 if (count($lostTables)) {
685 $Err = 'Index table hosted '.count($lostTables).' indexes for non-existing tables, now removed';
686 $errors[]= $Err;
687 if ($cli_echo) echo $Err.chr(10);
688 if (!$testOnly) $TYPO3_DB->exec_DELETEquery('sys_refindex',$where);
689 }
690
691 $testedHowMuch = $recCount.' records from '.$tableCount.' tables were checked/updated.'.chr(10);
692
693 $bodyContent = $testedHowMuch.(count($errors)?implode(chr(10),$errors):'Index Integrity was perfect!');
694 if ($cli_echo) echo $testedHowMuch.(count($errors)?'Updates: '.count($errors):'Index Integrity was perfect!').chr(10);
695
696 return array($headerContent,$bodyContent);
697 }
698 }
699
700
701 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_refindex.php']) {
702 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_refindex.php']);
703 }
704 ?>