[API][!!!] Fix API compatibility break
[Packages/TYPO3.CMS.git] / typo3 / class.show_rechis.inc
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 1999-2009 Kasper Skårhøj (kasperYYYY@typo3.com)
6 * (c) 2006-2009 Sebastian Kurfürst (sebastian@garbage-group.de)
7 * All rights reserved
8 *
9 * This script is part of the TYPO3 project. The TYPO3 project is
10 * free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * The GNU General Public License can be found at
16 * http://www.gnu.org/copyleft/gpl.html.
17 * A copy is found in the textfile GPL.txt and important notices to the license
18 * from the author is found in LICENSE.txt distributed with these scripts.
19 *
20 *
21 * This script is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * This copyright notice MUST APPEAR in all copies of the script!
27 ***************************************************************/
28 /**
29 * Class for the record history display script (show_rechis.php)
30 *
31 * XHTML Compliant
32 *
33 * @author Sebastian Kurfürst <sebastian@garbage-group.de>
34 */
35
36
37 /**
38 * Class for the record history display script (show_rechis.php)
39 *
40 * @author Sebastian Kurfürst <sebastian@garbage-group.de>
41 * @package TYPO3
42 * @subpackage core
43 */
44 class recordHistory {
45 // External, static:
46 var $maxSteps=20; // Maximum number of sys_history steps to show.
47 var $showDiff=1; // display diff or not (0-no diff, 1-inline)
48 var $showSubElements=1; // on a pages table - show sub elements as well.
49 var $showInsertDelete=1; // show inserts and deletes as well
50
51 // Internal, GPvars
52 var $element; // Element reference, syntax [tablename]:[uid]
53 var $lastSyslogId; // syslog ID which is not shown anymore
54 var $returnUrl;
55
56 // Internal
57 var $changeLog;
58 var $showMarked=FALSE;
59 /**
60 * Constructor for the class
61 *
62 * @return void
63 */
64 function __construct() {
65 // GPvars:
66 $this->element = t3lib_div::_GP('element');
67 $this->returnUrl = t3lib_div::sanitizeLocalUrl(t3lib_div::_GP('returnUrl'));
68 $this->lastSyslogId = t3lib_div::_GP('diff');
69 $this->rollbackFields = t3lib_div::_GP('rollbackFields');
70 // resolve sh_uid if set
71 $this->resolveShUid();
72 }
73
74 /**
75 * Compatibility constructor.
76 *
77 * @deprecated since TYPO3 4.6 and will be removed in TYPO3 4.8. Use __construct() instead.
78 */
79 public function recordHistory() {
80 t3lib_div::logDeprecatedFunction();
81 // Note: we cannot call $this->__construct() here because it would call the derived class constructor and cause recursion
82 // This code uses official PHP behavior (http://www.php.net/manual/en/language.oop5.basic.php) when $this in the
83 // statically called non-static method inherits $this from the caller's scope.
84 recordHistory::__construct();
85 }
86
87 /**
88 * Main function for the listing of history.
89 * It detects incoming variables like element reference, history element uid etc. and renders the correct screen.
90 *
91 * @return HTML content for the module
92 */
93 function main() {
94 $content = '';
95
96 // single-click rollback
97 if (t3lib_div::_GP('revert') && t3lib_div::_GP('sumUp')) {
98 $this->rollbackFields = t3lib_div::_GP('revert');
99 $this->showInsertDelete = 0;
100 $this->showSubElements = 0;
101
102 $element = explode(':',$this->element);
103 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*','sys_history', 'tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($element[0], 'sys_history').' AND recuid='.intval($element[1]), '', 'uid DESC', '1');
104 $record = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
105 $this->lastSyslogId = $record['sys_log_uid'];
106
107 $this->createChangeLog();
108 $completeDiff = $this->createMultipleDiff();
109 $this->performRollback($completeDiff);
110 t3lib_utility_Http::redirect($this->returnUrl);
111 }
112
113 // save snapshot
114 if (t3lib_div::_GP('highlight') && !t3lib_div::_GP('settings')) {
115 $this->toggleHighlight(t3lib_div::_GP('highlight'));
116 }
117
118 $content .= $this->displaySettings();
119 if ($this->createChangeLog()) {
120 if ($this->rollbackFields) {
121 $completeDiff = $this->createMultipleDiff();
122 $content .= $this->performRollback($completeDiff);
123
124 }
125 if ($this->lastSyslogId) {
126 $completeDiff = $this->createMultipleDiff();
127 $content .= $this->displayMultipleDiff($completeDiff);
128 }
129 if ($this->element) {
130 $content .= $this->displayHistory();
131 }
132 }
133 return $content;
134 }
135
136 /*******************************
137 *
138 * database actions
139 *
140 *******************************/
141
142 /**
143 * toggles highlight state of record
144 *
145 * @param integer uid of sys_history entry
146 * @return [type] ...
147 */
148 function toggleHighlight($uid) {
149 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('snapshot','sys_history','uid='.intval($uid));
150 $tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
151 if ($tmp['snapshot']) {
152 $tmp = 0;
153 } else {
154 $tmp = 1;
155 }
156 $updateFields = array('snapshot' => $tmp);
157 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_history','uid='.intval($uid),$updateFields);
158 }
159
160 /**
161 * perform rollback
162 *
163 * @param array diff array to rollback
164 * @return void
165 * @access private
166 */
167 function performRollback($diff) {
168 if (!$this->rollbackFields) {
169 return 0;
170 }
171
172 $reloadPageFrame=0;
173 $rollbackData = explode(':',$this->rollbackFields);
174
175 // PROCESS INSERTS AND DELETES
176 // rewrite inserts and deletes
177 $cmdmapArray = array();
178 if ($diff['insertsDeletes']) {
179
180 switch (count($rollbackData)) {
181 case 1: // all tables
182 $data = $diff['insertsDeletes'];
183 break;
184 case 2: // one record
185 if ($diff['insertsDeletes'][$this->rollbackFields]) {
186 $data[$this->rollbackFields] = $diff['insertsDeletes'][$this->rollbackFields];
187 }
188 break;
189 case 3: // one field in one record -- ignore!
190 break;
191 }
192 if ($data) {
193 foreach ($data as $key => $action) {
194 $elParts = explode(':',$key);
195 if ($action == 1) { // inserted records should be deleted
196 $cmdmapArray[$elParts[0]][$elParts[1]]['delete'] = 1;
197 // when the record is deleted, the contents of the record do not need to be updated
198 unset($diff['oldData'][$key]);
199 unset($diff['newData'][$key]);
200 } elseif ($action == -1) { // deleted records should be inserted again
201 $cmdmapArray[$elParts[0]][$elParts[1]]['undelete'] = 1;
202 }
203 }
204 }
205 }
206 // Writes the data:
207 if ($cmdmapArray) {
208 $tce = t3lib_div::makeInstance('t3lib_TCEmain');
209 $tce->stripslashes_values=0;
210 $tce->debug=0;
211 $tce->dontProcessTransformations=1;
212 $tce->start(array(),$cmdmapArray);
213 $tce->process_cmdmap();
214 unset($tce);
215 if (isset($cmdmapArray['pages'])) {
216 $reloadPageFrame=1;
217 }
218 }
219
220 // PROCESS CHANGES
221 // create an array for process_datamap
222 $diff_modified = array();
223 foreach ($diff['oldData'] as $key => $value) {
224 $splitKey = explode(':',$key);
225 $diff_modified[$splitKey[0]][$splitKey[1]] = $value;
226 }
227 switch (count($rollbackData)) {
228 case 1: // all tables
229 $data = $diff_modified;
230 break;
231 case 2: // one record
232 $data[$rollbackData[0]][$rollbackData[1]] = $diff_modified[$rollbackData[0]][$rollbackData[1]];
233 break;
234 case 3: // one field in one record
235 $data[$rollbackData[0]][$rollbackData[1]][$rollbackData[2]] = $diff_modified[$rollbackData[0]][$rollbackData[1]][$rollbackData[2]];
236 break;
237 }
238 // Removing fields:
239 $data = $this->removeFilefields($rollbackData[0],$data);
240
241 // Writes the data:
242 $tce = t3lib_div::makeInstance('t3lib_TCEmain');
243 $tce->stripslashes_values=0;
244 $tce->debug=0;
245 $tce->dontProcessTransformations=1;
246 $tce->start($data,array());
247 $tce->process_datamap();
248 unset($tce);
249 if (isset($data['pages'])) {
250 $reloadPageFrame=1;
251 }
252
253 // return to normal operation
254 $this->lastSyslogId = FALSE;
255 $this->rollbackFields = FALSE;
256 $this->createChangeLog();
257
258 // reload page frame if necessary
259 if ($reloadPageFrame) {
260 return '<script type="text/javascript">
261 /*<![CDATA[*/
262 if (top.content && top.content.nav_frame && top.content.nav_frame.refresh_nav) {
263 top.content.nav_frame.refresh_nav();
264 }
265 /*]]>*/
266 </script>';
267 }
268 }
269
270 /*******************************
271 *
272 * Display functions
273 *
274 *******************************/
275
276 /**
277 * Displays settings
278 *
279 * @return string HTML code to modify settings
280 */
281 function displaySettings() {
282 // get current selection from UC, merge data, write it back to UC
283 $currentSelection = is_array($GLOBALS['BE_USER']->uc['moduleData']['history']) ? $GLOBALS['BE_USER']->uc['moduleData']['history'] : array('maxSteps' => '', 'showDiff' => 1, 'showSubElements' => 1, 'showInsertDelete' => 1);
284
285 $currentSelectionOverride = t3lib_div::_GP('settings');
286 if ($currentSelectionOverride) {
287 $currentSelection = array_merge($currentSelection,$currentSelectionOverride);
288 $GLOBALS['BE_USER']->uc['moduleData']['history'] = $currentSelection;
289 $GLOBALS['BE_USER']->writeUC($GLOBALS['BE_USER']->uc);
290 }
291
292 // display selector for number of history entries
293 $selector['maxSteps'] = array(
294 10 => 10,
295 20 => 20,
296 50 => 50,
297 100 => 100,
298 '' => 'maxSteps_all',
299 'marked' => 'maxSteps_marked'
300 );
301 $selector['showDiff'] = array(
302 0 => 'showDiff_no',
303 1 => 'showDiff_inline'
304 );
305 $selector['showSubElements'] = array(
306 0 => 'no',
307 1 => 'yes',
308 );
309 $selector['showInsertDelete'] = array(
310 0 => 'no',
311 1 => 'yes',
312 );
313 // render selectors
314 $displayCode = '';
315 foreach ($selector as $key => $values) {
316 $displayCode .= '<tr><td>'.$GLOBALS['LANG']->getLL($key,1).'</td><td><select name="settings['.$key.']" onChange="document.settings.submit()" style="width:100px">';
317 foreach ($values as $singleKey => $singleVal) {
318 $caption = $GLOBALS['LANG']->getLL($singleVal,1)?$GLOBALS['LANG']->getLL($singleVal,1):$singleVal;
319 $displayCode .= '<option value="'.$singleKey.'" '.(($singleKey == $currentSelection[$key])?'selected':'').'> '.$caption.'</option>';
320 }
321 $displayCode .= '</select></td></tr>';
322 }
323 // set values correctly
324 if ($currentSelection['maxSteps'] != 'marked') {
325 $this->maxSteps = $currentSelection['maxSteps']?intval($currentSelection['maxSteps']):'';
326 } else {
327 $this->showMarked = TRUE;
328 $this->maxSteps = FALSE;
329 }
330 $this->showDiff = intval($currentSelection['showDiff']);
331 $this->showSubElements = intval($currentSelection['showSubElements']);
332 $this->showInsertDelete = intval($currentSelection['showInsertDelete']);
333
334 $content = '';
335 // get link to page history if the element history is shown
336 $elParts = explode(':',$this->element);
337 if ($elParts[0] != 'pages') {
338 $content .= '<strong>'.$GLOBALS['LANG']->getLL('elementHistory',1).'</strong><br />';
339 $pid = t3lib_BEfunc::getRecordRaw($elParts[0],'uid='.intval($elParts[1]));
340 $content .= $this->linkPage($GLOBALS['LANG']->getLL('elementHistory_link',1),array('element' => 'pages:'.$pid['pid']));
341 }
342 $content .= '<form name="settings" action="'.t3lib_div::getIndpEnv('TYPO3_REQUEST_URL').'" method="post"><table>'.$displayCode.'</table></form>';
343 return $GLOBALS['SOBE']->doc->section($GLOBALS['LANG']->getLL('settings',1),$content,0,1,0,0);
344
345 }
346
347 /**
348 * Shows the full change log
349 *
350 * @return string HTML for list, wrapped in a table.
351 */
352 function displayHistory() {
353 $lines=array();
354
355 // Initialize:
356 $lines[] = '<tr class="t3-row-header">
357 <td> </td>
358 <td>'.$GLOBALS['LANG']->getLL('time',1).'</td>
359 <td>'.$GLOBALS['LANG']->getLL('age',1).'</td>
360 <td>'.$GLOBALS['LANG']->getLL('user',1).'</td>
361 <td>'.$GLOBALS['LANG']->getLL('tableUid',1).'</td>
362 <td>'.$GLOBALS['LANG']->getLL('differences',1).'</td>
363 <td>&nbsp;</td>
364 </tr>';
365
366 // get default page TSconfig expiration time
367 $elParts = explode(':',$this->element);
368 if ($elParts[0] != 'pages') {
369 $tmp = t3lib_BEfunc::getRecordRaw($elParts[0],'uid='.intval($elParts[1]));
370 $pid = $tmp['pid'];
371 } else {
372 $pid = $elParts[1];
373 }
374 $tmpTsConfig = $GLOBALS['BE_USER']->getTSConfig('TCEMAIN',t3lib_BEfunc::getPagesTSconfig($pid));
375 $expirationTime = isset($tmpTsConfig['properties']['default.']['history.']['maxAgeDays']) ? $tmpTsConfig['properties']['default.']['history.']['maxAgeDays'] : 30;
376
377 $expirationTimestamp = $expirationTime ? ($GLOBALS['EXEC_TIME'] - 60 * 60 * 24 * $expirationTime) : 0;
378 $expirationWarning = 0;
379
380 $be_user_array = t3lib_BEfunc::getUserNames();
381
382 // Traverse changelog array:
383 if (!$this->changeLog) {
384 return 0;
385 }
386 $i = 0;
387 foreach ($this->changeLog as $sysLogUid => $entry) {
388 // stop after maxSteps
389 if ($i > $this->maxSteps && $this->maxSteps) {
390 break;
391 }
392
393 // display inconsistency warning
394 if ($entry['tstamp'] < $expirationTimestamp && !$expirationWarning) {
395 $expirationWarning = 1;
396
397 $lines[] = '
398 <tr class="c-headLine">
399 <td colspan="7"><strong>'.$GLOBALS['LANG']->getLL('consistenceWarning',1).'</strong></td>
400 </tr>';
401 }
402
403 // show only marked states
404 if (!$entry['snapshot'] && $this->showMarked) {
405 continue;
406 }
407 $i++;
408 // get user names
409 $userName = ($entry['user']?$be_user_array[$entry['user']]['username']:$GLOBALS['LANG']->getLL('externalChange',1));
410
411 // build up single line
412 $singleLine = array();
413
414 // diff link
415 $image = t3lib_iconWorks::getSpriteIcon('actions-view-go-forward', array('title' => $GLOBALS['LANG']->getLL('sumUpChanges', TRUE)));
416 $singleLine[] = '<span>'.$this->linkPage($image,array('diff' => $sysLogUid)).'</span>'; // remove first link
417
418 $singleLine[] = htmlspecialchars(t3lib_BEfunc::datetime($entry['tstamp'])); // add time
419 $singleLine[] = htmlspecialchars(t3lib_BEfunc::calcAge($GLOBALS['EXEC_TIME'] - $entry['tstamp'], $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears'))); // add age
420 $singleLine[] = htmlspecialchars($userName); // add user name
421 $singleLine[] = $this->linkPage($this->generateTitle($entry['tablename'],$entry['recuid']),array('element' => $entry['tablename'].':'.$entry['recuid']),'',$GLOBALS['LANG']->getLL('linkRecordHistory',1)); // add record UID
422
423 // show insert/delete/diff/changed field names
424 if ($entry['action']) { // insert or delete of element
425 $singleLine[] = '<strong>'.htmlspecialchars($GLOBALS['LANG']->getLL($entry['action'],1)).'</strong>';
426 } else {
427 if (!$this->showDiff) { // display field names instead of full diff
428 // re-write field names with labels
429 $tmpFieldList = explode(',',$entry['fieldlist']);
430 foreach ($tmpFieldList as $key => $value) {
431 $tmp = str_replace(':','',$GLOBALS['LANG']->sl(t3lib_BEfunc::getItemLabel($entry['tablename'],$value),1));
432 if($tmp) $tmpFieldList[$key] = $tmp;
433 else unset($tmpFieldList[$key]); // remove fields if no label available
434 }
435 $singleLine[] = htmlspecialchars(implode(',',$tmpFieldList));
436 } else { // display diff
437 $diff = $this->renderDiff($entry,$entry['tablename']);
438 $singleLine[] = $diff;
439 }
440 }
441 // show link to mark/unmark state
442 if (!$entry['action']) {
443 if ($entry['snapshot']) {
444 $image = '<img'.t3lib_iconWorks::skinImg('','gfx/unmarkstate.gif').' align="top" alt="'.$GLOBALS['LANG']->getLL('unmarkState',1).'" title="'.$GLOBALS['LANG']->getLL('unmarkState',1).'" />';
445 } else {
446 $image = '<img'.t3lib_iconWorks::skinImg('','gfx/markstate.gif').' align="top" alt="'.$GLOBALS['LANG']->getLL('markState',1).'" title="'.$GLOBALS['LANG']->getLL('markState',1).'" />';
447 }
448 $singleLine[] = $this->linkPage($image,array('highlight' => $entry['uid']));
449 } else {
450 $singleLine[] = '';
451 }
452
453 // put line together
454 $lines[] = '
455 <tr class="db_list_normal">
456 <td>' . implode('</td><td>', $singleLine) . '</td>
457 </tr>';
458 }
459
460 // Finally, put it all together:
461 $theCode = '
462 <!--
463 History (list):
464 -->
465 <table class="typo3-dblist" border="0" cellpadding="0" cellspacing="0" id="typo3-history">
466 ' . implode('', $lines) . '
467 </table>';
468
469 if ($this->lastSyslogId) {
470 $theCode .= '<br />' . $this->linkPage(t3lib_iconWorks::getSpriteIcon('actions-move-to-bottom', array('title' => $GLOBALS['LANG']->getLL('fullView', TRUE))), array('diff' => ''));
471 }
472
473 // Add message about the difference view.
474 $flashMessage = t3lib_div::makeInstance(
475 't3lib_FlashMessage',
476 $GLOBALS['LANG']->getLL('differenceMsg'),
477 '',
478 t3lib_FlashMessage::INFO
479 );
480
481 $theCode .= '<br /><br />' . $flashMessage->render() . '<br />';
482
483 // Add CSH:
484 $theCode .= t3lib_BEfunc::cshItem('xMOD_csh_corebe', 'history_'.($this->sumUp ? 'sum' : 'log'), $GLOBALS['BACK_PATH'],'');
485
486 // Add the whole content as a module section:
487 return $GLOBALS['SOBE']->doc->section($GLOBALS['LANG']->getLL('changes'),$theCode,0,1);
488 }
489
490 /**
491 * Displays a diff over multiple fields including rollback links
492 *
493 * @param array difference array
494 * @return string HTML output
495 */
496 function displayMultipleDiff($diff) {
497 $content = '';
498
499 // get all array keys needed
500 $arrayKeys = array_merge(array_keys($diff['newData']),array_keys($diff['insertsDeletes']),array_keys($diff['oldData']));
501 $arrayKeys = array_unique($arrayKeys);
502
503 if ($arrayKeys) {
504 foreach ($arrayKeys as $key) {
505 $record = '';
506 $elParts = explode(':',$key);
507 // turn around diff because it should be a "rollback preview"
508 if ($diff['insertsDeletes'][$key] == 1) { // insert
509 $record .= '<strong>'.$GLOBALS['LANG']->getLL('delete',1).'</strong>';
510 $record .= '<br />';
511 } elseif ($diff['insertsDeletes'][$key] == -1) {
512 $record .= '<strong>'.$GLOBALS['LANG']->getLL('insert',1).'</strong>';
513 $record .= '<br />';
514 }
515 // build up temporary diff array
516 // turn around diff because it should be a "rollback preview"
517 if ($diff['newData'][$key]) {
518 $tmpArr['newRecord'] = $diff['oldData'][$key];
519 $tmpArr['oldRecord'] = $diff['newData'][$key];
520 $record .= $this->renderDiff($tmpArr, $elParts[0],$elParts[1]);
521 }
522
523 $elParts = explode(':',$key);
524 $titleLine = $this->createRollbackLink($key, $GLOBALS['LANG']->getLL('revertRecord',1),1) . $this->generateTitle($elParts[0],$elParts[1]);
525 $record = '<div style="margin-left:10px;padding-left:5px;border-left:1px solid black;border-bottom:1px dotted black;padding-bottom:2px;">'.$record.'</div>';
526
527 $content .= $GLOBALS['SOBE']->doc->section($titleLine,$record,0,0,0,1);
528 }
529 $content = $this->createRollbackLink('ALL', $GLOBALS['LANG']->getLL('revertAll',1),0) . '<div style="margin-left:10px;padding-left:5px;border-left:1px solid black;border-bottom:1px dotted black;padding-bottom:2px;">'.$content.'</div>';
530 } else {
531 $content = $GLOBALS['LANG']->getLL('noDifferences',1);
532 }
533 return $GLOBALS['SOBE']->doc->section($GLOBALS['LANG']->getLL('mergedDifferences',1),$content,0,1,0,1);
534 }
535
536 /**
537 * Renders HTML table-rows with the comparison information of an sys_history entry record
538 *
539 * @param array sys_history entry record.
540 * @param string The table name
541 * @param integer If set to UID of record, display rollback links
542 * @return string HTML table
543 * @access private
544 */
545 function renderDiff($entry,$table,$rollbackUid=0) {
546 $lines=array();
547 if (is_array($entry['newRecord'])) {
548
549 $t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff');
550
551 $fieldsToDisplay = array_keys($entry['newRecord']);
552 foreach($fieldsToDisplay as $fN) {
553 t3lib_div::loadTCA($table);
554 if (is_array($GLOBALS['TCA'][$table]['columns'][$fN]) && $GLOBALS['TCA'][$table]['columns'][$fN]['config']['type']!='passthrough') {
555
556 // Create diff-result:
557 $diffres = $t3lib_diff_Obj->makeDiffDisplay(
558 t3lib_BEfunc::getProcessedValue($table,$fN,$entry['oldRecord'][$fN],0,1),
559 t3lib_BEfunc::getProcessedValue($table,$fN,$entry['newRecord'][$fN],0,1)
560 );
561 $lines[]='
562 <tr class="bgColor4">
563 '.($rollbackUid?'<td style="width:33px">'.$this->createRollbackLink($table.':'.$rollbackUid.':'.$fN, $GLOBALS['LANG']->getLL('revertField',1),2).'</td>':'').'
564 <td style="width:90px"><em>'.$GLOBALS['LANG']->sl(t3lib_BEfunc::getItemLabel($table,$fN),1).'</em></td>
565 <td style="width:300px">'.nl2br($diffres).'</td>
566 </tr>';
567 }
568 }
569 }
570 if ($lines) {
571 $content = '<table border="0" cellpadding="2" cellspacing="2" id="typo3-history-item">
572 '.implode('',$lines).'
573 </table>';
574 return $content;
575 }
576 return NULL; // error fallback
577 }
578
579 /*******************************
580 *
581 * build up history
582 *
583 *******************************/
584
585 /**
586 * Creates a diff between the current version of the records and the selected version
587 *
588 * @return array diff for many elements
589 */
590 function createMultipleDiff() {
591 $insertsDeletes = array();
592 $newArr = array();
593 $differences = array();
594 if (!$this->changeLog) {
595 return 0;
596 }
597
598 // traverse changelog array
599 foreach ($this->changeLog as $key => $value) {
600 $field = $value['tablename'].':'.$value['recuid'];
601 // inserts / deletes
602 if ($value['action']) {
603 if (!$insertsDeletes[$field]) {
604 $insertsDeletes[$field] = 0;
605 }
606 if ($value['action'] == 'insert') {
607 $insertsDeletes[$field]++;
608 } else {
609 $insertsDeletes[$field]--;
610 }
611 // unset not needed fields
612 if ($insertsDeletes[$field] == 0) {
613 unset($insertsDeletes[$field]);
614 }
615 } else {
616 // update fields
617 if (!isset($newArr[$field])) { // first row of field
618 $newArr[$field] = $value['newRecord'];
619 $differences[$field] = $value['oldRecord'];
620 } else { // standard
621 $differences[$field] = array_merge($differences[$field],$value['oldRecord']);
622 }
623 }
624 }
625
626 // remove entries where there were no changes effectively
627 foreach ($newArr as $record => $value) {
628 foreach ($value as $key => $innerVal) {
629 if ($newArr[$record][$key] == $differences[$record][$key]) {
630 unset($newArr[$record][$key]);
631 unset($differences[$record][$key]);
632 }
633 }
634 if (empty($newArr[$record]) && empty($differences[$record])) {
635 unset($newArr[$record]);
636 unset($differences[$record]);
637 }
638 }
639 return array(
640 'newData' => $newArr,
641 'oldData' => $differences,
642 'insertsDeletes' => $insertsDeletes
643 );
644 }
645
646 /**
647 * Creates change log including sub-elements, filling $this->changeLog
648 *
649 * @return [type] ...
650 */
651 function createChangeLog() {
652 $elParts = explode(':',$this->element);
653 $changeLog = $this->getHistoryData($elParts[0],$elParts[1]);
654
655 // get history of tables of this page and merge it into changelog
656 if ($elParts[0] == 'pages' && $this->showSubElements) {
657 foreach ($GLOBALS['TCA'] as $tablename => $value) {
658 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid',$tablename,'pid='.intval($elParts[1])); // check if there are records on the page
659 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
660 if ($newChangeLog = $this->getHistoryData($tablename, $row['uid'])) { // if there is history data available, merge it into changelog
661 foreach ($newChangeLog as $key => $value) {
662 $changeLog[$key] = $value;
663 }
664 }
665 }
666 }
667 }
668 if(!$changeLog) {
669 return 0;
670 }
671
672 krsort($changeLog);
673 $this->changeLog = $changeLog;
674
675 return 1;
676 }
677
678 /**
679 * Gets history and delete/insert data from sys_log and sys_history
680 *
681 * @param string DB table name
682 * @param integer UID of record
683 * @return array history data of the record
684 */
685 function getHistoryData($table,$uid) {
686 $uid = $this->resolveElement($table,$uid);
687 // If table is found in $GLOBALS['TCA']:
688 if ($GLOBALS['TCA'][$table]) {
689 // Selecting the $this->maxSteps most recent states:
690 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
691 'sys_history.*,sys_log.userid',
692 'sys_history,sys_log',
693 'sys_history.sys_log_uid=sys_log.uid
694 AND sys_history.tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history').'
695 AND sys_history.recuid='.intval($uid),
696 '',
697 'sys_log.uid DESC',
698 $this->maxSteps
699 );
700
701 // Traversing the result, building up changesArray / changeLog:
702 #$changesArray=array(); // used temporarily to track intermedia changes
703 $changeLog=array();
704 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
705 // only history until a certain syslog ID needed
706 if ($row['sys_log_uid'] < $this->lastSyslogId && $this->lastSyslogId) {
707 continue;
708 }
709 $hisDat = unserialize($row['history_data']);
710 if (is_array($hisDat['newRecord']) && is_array($hisDat['oldRecord'])) {
711
712 // Add hisDat to the changeLog
713 $hisDat['uid']=$row['uid'];
714 $hisDat['tstamp']=$row['tstamp'];
715 $hisDat['user']=$row['userid'];
716 $hisDat['snapshot']=$row['snapshot'];
717 $hisDat['fieldlist']=$row['fieldlist'];
718 $hisDat['tablename']=$row['tablename'];
719 $hisDat['recuid']=$row['recuid'];
720
721 $changeLog[$row['sys_log_uid']]=$hisDat;
722
723 // Update change array
724 // This is used to detect if any intermedia changes have been made.
725 #$changesArray = array_merge($changesArray,$hisDat['oldRecord']);
726 } else {
727 debug('ERROR: [getHistoryData]');
728 return 0; // error fallback
729 }
730 }
731 // SELECT INSERTS/DELETES
732 if ($this->showInsertDelete) {
733 // Select most recent inserts and deletes // WITHOUT snapshots
734 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
735 'uid,userid,action,tstamp',
736 'sys_log',
737 'type=1
738 AND ( action=1 OR action=3 )
739 AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_log').'
740 AND recuid='.intval($uid),
741 '',
742 'uid DESC',
743 $this->maxSteps
744 );
745 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
746
747 if ($row['uid'] < $this->lastSyslogId && $this->lastSyslogId) {
748 continue;
749 }
750 $hisDat = array();
751
752 switch ($row['action']) {
753 case 1: // Insert
754 $hisDat['action'] = 'insert';
755 break;
756 case 3: // Delete
757 $hisDat['action'] = 'delete';
758 break;
759 }
760 $hisDat['tstamp']=$row['tstamp'];
761 $hisDat['user']=$row['userid'];
762 $hisDat['tablename'] = $table;
763 $hisDat['recuid'] = $uid;
764 $changeLog[$row['uid']] = $hisDat;
765 }
766 }
767 return $changeLog;
768 }
769 return 0; // error fallback
770 }
771
772
773 /*******************************
774 *
775 * Various helper functions
776 *
777 *******************************/
778
779 /**
780 * generates the title and puts the record title behind
781 *
782 * @param [type] $table: ...
783 * @param [type] $uid: ...
784 * @return [type] ...
785 */
786 function generateTitle($table, $uid) {
787 $out = $table.':'.$uid;
788 if ($labelField = $GLOBALS['TCA'][$table]['ctrl']['label']) {
789 $record = t3lib_BEfunc::getRecordRaw($table, 'uid='.intval($uid));
790 $out .= ' ('.t3lib_BEfunc::getRecordTitle($table, $record, TRUE).')';
791 }
792 return $out;
793 }
794 /**
795 * creates a link for the rollback
796 *
797 * @param sting parameter which is set to rollbackFields
798 * @param string optional, alternative label and title tag of image
799 * @param integer optional, type of rollback: 0 - ALL; 1 - element; 2 - field
800 * @return string HTML output
801 */
802 function createRollbackLink($key, $alt='', $type=0) {
803 return $this->linkPage('<img '.t3lib_iconWorks::skinImg('','gfx/revert_'.$type.'.gif','width="33" height="33"').' alt="'.$alt.'" title="'.$alt.'" align="middle" />',array('rollbackFields'=>$key));
804 }
805
806 /**
807 * Creates a link to the same page.
808 *
809 * @param string String to wrap in <a> tags (must be htmlspecialchars()'ed prior to calling function)
810 * @param array Array of key/value pairs to override the default values with.
811 * @param string Possible anchor value.
812 * @param string Possible title.
813 * @return string Link.
814 * @access private
815 */
816 function linkPage($str,$inparams=array(),$anchor='',$title='') {
817
818 // Setting default values based on GET parameters:
819 $params['element']=$this->element;
820 $params['returnUrl']=$this->returnUrl;
821 $params['diff']=$this->lastSyslogId;
822 // Mergin overriding values:
823 $params = array_merge($params,$inparams);
824
825 // Make the link:
826 $Ahref = 'show_rechis.php?'.t3lib_div::implodeArrayForUrl('',$params).($anchor?'#'.$anchor:'');
827 $link = '<a href="'.htmlspecialchars($Ahref).'"'.($title?' title="'.$title.'"':'').'>'.$str.'</a>';
828
829 // Return link:
830 return $link;
831 }
832
833 /**
834 * Will traverse the field names in $dataArray and look in $GLOBALS['TCA'] if the fields are of types which cannot be handled by the sys_history (that is currently group types with internal_type set to "file")
835 *
836 * @param string Table name
837 * @param array The data array
838 * @return array The modified data array
839 * @access private
840 */
841 function removeFilefields($table,$dataArray) {
842 if ($GLOBALS['TCA'][$table]) {
843 t3lib_div::loadTCA($table);
844
845 foreach($GLOBALS['TCA'][$table]['columns'] as $field => $config) {
846 if ($config['config']['type']=='group' && $config['config']['internal_type']=='file') {
847 unset($dataArray[$field]);
848 }
849 }
850 }
851 return $dataArray;
852 }
853
854 /**
855 * Convert input element reference to workspace version if any.
856 *
857 * @param string table of input element
858 * @param integer UID of record
859 * @return integer converted UID of record
860 */
861 function resolveElement($table,$uid) {
862 if (isset($GLOBALS['TCA'][$table])) {
863 if ($workspaceVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $table, $uid, 'uid')) {
864 $uid = $workspaceVersion['uid'];
865 }
866 }
867 return $uid;
868 }
869
870 /**
871 * resolve sh_uid (used from log)
872 *
873 * @return [type] ...
874 */
875 function resolveShUid() {
876 if (t3lib_div::_GP('sh_uid')) {
877 $sh_uid = t3lib_div::_GP('sh_uid');
878 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*','sys_history', 'uid='.intval($sh_uid));
879 $record = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
880 $this->element = $record['tablename'].':'.$record['recuid'];
881 $this->lastSyslogId = $record['sys_log_uid']-1;
882 }
883 }
884 }
885 ?>