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