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