Feature #15090 » bug_1650_undohistory_2.patch
TYPO3core_testing/t3lib/class.t3lib_tcemain.php 2005-12-09 20:17:08.000000000 +0100 | ||
---|---|---|
$modifyAccessList = $this->checkModifyAccessList($table);
|
||
if (!$modifyAccessList) {
|
||
$this->log($table,$id,2,0,1,"Attempt to modify table '%s' without permission",1,array($table));
|
||
}
|
||
} // FIXME: $id not set here (Comment added by Sebastian Kurfuerst)
|
||
// Check basic permissions and circumstances:
|
||
if (isset($TCA[$table]) && !$this->tableReadOnly($table) && is_array($this->cmdmap[$table]) && $modifyAccessList) {
|
||
... | ... | |
case 'delete':
|
||
$this->deleteAction($table, $id);
|
||
break;
|
||
case 'undelete':
|
||
$this->undeleteRecord($table, $id);
|
||
break;
|
||
}
|
||
foreach($hookObjectsArr as $hookObj) {
|
||
... | ... | |
}
|
||
/**
|
||
* Deleting a record
|
||
* Undeleting a record
|
||
*
|
||
* @param string Table name
|
||
* @param integer Record UID
|
||
* @return void
|
||
*/
|
||
function undeleteRecord($table,$uid) {
|
||
$this->deleteRecord($table,$uid,TRUE,FALSE,TRUE);
|
||
}
|
||
/**
|
||
* Deleting/Undeleting a record
|
||
* This function may not be used to delete pages-records unless the underlying records are already deleted
|
||
* Deletes a record regardless of versioning state (live of offline, doesn't matter, the uid decides)
|
||
* If both $noRecordCheck and $forceHardDelete are set it could even delete a "deleted"-flagged record!
|
||
... | ... | |
* @param integer Record UID
|
||
* @param boolean Flag: If $noRecordCheck is set, then the function does not check permission to delete record
|
||
* @param boolean If TRUE, the "deleted" flag is ignored if applicable for record and the record is deleted COMPLETELY!
|
||
* @param boolean If TRUE, the "deleted" flag is set to 0 again and thus, the item is undeleted.
|
||
* @return void
|
||
*/
|
||
function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE) {
|
||
function deleteRecord($table,$uid, $noRecordCheck=FALSE, $forceHardDelete=FALSE,$undeleteRecord=FALSE) {
|
||
global $TCA;
|
||
$uid = intval($uid);
|
||
... | ... | |
$deleteRow = $TCA[$table]['ctrl']['delete'];
|
||
if ($deleteRow && !$forceHardDelete) {
|
||
$value = $undeleteRecord ? 0 : 1;
|
||
$updateFields = array(
|
||
$deleteRow => 1
|
||
$deleteRow => $value
|
||
);
|
||
// If the table is sorted, then the sorting number is set very high
|
||
if ($TCA[$table]['ctrl']['sortby']) {
|
||
if ($TCA[$table]['ctrl']['sortby'] && !$undeleteRecord) {
|
||
$updateFields[$TCA[$table]['ctrl']['sortby']] = 1000000000;
|
||
}
|
||
... | ... | |
$GLOBALS['TYPO3_DB']->exec_DELETEquery($table, 'uid='.intval($uid));
|
||
}
|
||
$state = $undeleteRecord ? 1 : 3;
|
||
if (!$GLOBALS['TYPO3_DB']->sql_error()) {
|
||
$this->log($table,$uid,3,0,0,'');
|
||
$this->log($table,$uid,$state,0,0,'');
|
||
} else {
|
||
$this->log($table,$uid,3,0,100,$GLOBALS['TYPO3_DB']->sql_error());
|
||
$this->log($table,$uid,$state,0,100,$GLOBALS['TYPO3_DB']->sql_error());
|
||
}
|
||
} else $this->log($table,$uid,3,0,1,'Attempt to delete record without delete-permissions');
|
||
... | ... | |
* @return void
|
||
*/
|
||
function clearHistory($table,$id,$keepEntries=10,$maxAgeSeconds=604800) {
|
||
$tstampLimit = $maxAgeSeconds ? time()-$maxAgeSeconds : 0;
|
||
/* $tstampLimit = $maxAgeSeconds ? time()-$maxAgeSeconds : 0;
|
||
$where = '
|
||
tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history').'
|
||
... | ... | |
$GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_history', $where.' AND uid<='.intval($resRow['uid']));
|
||
} elseif (is_array($resRow)) {
|
||
$GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_history', $where.' AND uid<='.intval($resRow['uid']));
|
||
}
|
||
}*/
|
||
}
|
TYPO3core_testing/typo3/class.show_rechis_new.inc 2005-12-09 22:42:25.000000000 +0100 | ||
---|---|---|
<?php
|
||
/***************************************************************
|
||
* Copyright notice
|
||
*
|
||
* (c) 1999-2005 Kasper Skaarhoj (kasperYYYY@typo3.com)
|
||
* All rights reserved
|
||
*
|
||
* This script is part of the TYPO3 project. The TYPO3 project is
|
||
* free software; you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation; either version 2 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* The GNU General Public License can be found at
|
||
* http://www.gnu.org/copyleft/gpl.html.
|
||
* A copy is found in the textfile GPL.txt and important notices to the license
|
||
* from the author is found in LICENSE.txt distributed with these scripts.
|
||
*
|
||
*
|
||
* This script is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* This copyright notice MUST APPEAR in all copies of the script!
|
||
***************************************************************/
|
||
/**
|
||
* Class for the record history display script (show_rechis.php)
|
||
*
|
||
* $Id: class.show_rechis.inc,v 1.17 2005/11/02 22:51:52 typo3 Exp $
|
||
* XHTML Compliant
|
||
*
|
||
* @author Sebastian Kurfuerst <sebastian@garbage-group.de>
|
||
*/
|
||
/**
|
||
* Class for the record history display script (show_rechis.php)
|
||
*
|
||
* @author Sebastian Kurfuerst <sebastian@garbage-group.de>
|
||
* @package TYPO3
|
||
* @subpackage core
|
||
*/
|
||
class recordHistory {
|
||
// External, static:
|
||
var $maxSteps=20; // Maximum number of sys_history steps to show.
|
||
var $showDiff=1; // display diff or not (0-no diff, 1-inline, 2-popup)
|
||
// TODO: $displayDiff=2
|
||
var $showSubElements=1; // on a pages table - show sub elements as well.
|
||
var $showInsertDelete=1; // show inserts and deletes as well
|
||
// Internal, GPvars
|
||
var $element; // Element reference, syntax [tablename]:[uid]
|
||
var $lastSyslogId; // syslog ID which is not shown anymore
|
||
var $returnUrl;
|
||
// Internal
|
||
var $changeLog;
|
||
var $addWhere = '';
|
||
/**
|
||
* Constructor for the class
|
||
*
|
||
* @return void
|
||
*/
|
||
function recordHistory() {
|
||
// GPvars:
|
||
$this->element = t3lib_div::_GP('element');
|
||
$this->returnUrl = t3lib_div::_GP('returnUrl');
|
||
$this->lastSyslogId = t3lib_div::_GP('diff');
|
||
$this->rollbackFields = t3lib_div::_GP('rollbackFields');
|
||
// resolve sh_uid if set
|
||
$this->resolveShUid();
|
||
}
|
||
/**
|
||
* Main function for the listing of history.
|
||
* It detects incoming variables like element reference, history element uid etc. and renders the correct screen.
|
||
*
|
||
* @return void
|
||
*/
|
||
function main() {
|
||
$content = '';
|
||
// save snapshot
|
||
if (t3lib_div::_GP('saveSnapshot') && !t3lib_div::_GP('settings')) {
|
||
$this->toggleSnapshot(t3lib_div::_GP('saveSnapshot'));
|
||
}
|
||
$content .= $this->displaySettings();
|
||
if ($this->createChangeLog()) {
|
||
if ($this->rollbackFields) {
|
||
$completeDiff = $this->createMultipleDiff();
|
||
$this->performRollback($completeDiff);
|
||
}
|
||
if ($this->lastSyslogId) {
|
||
$completeDiff = $this->createMultipleDiff();
|
||
$content .= $this->displayMultipleDiff($completeDiff);
|
||
}
|
||
if ($this->element) {
|
||
$content .= $this->displayHistory();
|
||
}
|
||
}
|
||
return $content;
|
||
}
|
||
/*******************************
|
||
*
|
||
* database actions
|
||
*
|
||
*******************************/
|
||
/**
|
||
* toggles snapshot state of record
|
||
*
|
||
* @param integer uid of sys_history entry
|
||
*/
|
||
function toggleSnapshot($uid) {
|
||
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('snapshot','sys_history','uid='.intval($uid));
|
||
$tmp = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
|
||
if ($tmp['snapshot']) {
|
||
$tmp = 0;
|
||
} else {
|
||
$tmp = 1;
|
||
}
|
||
$updateFields = array('snapshot' => $tmp);
|
||
$GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_history','uid='.intval($uid),$updateFields);
|
||
}
|
||
/**
|
||
* perform rollback
|
||
*
|
||
* @param array diff array to rollback
|
||
* @return void
|
||
* @access private
|
||
*/
|
||
function performRollback($diff) {
|
||
if (!$this->rollbackFields) {
|
||
return 0;
|
||
}
|
||
// PROCESS INSERTS AND DELETES
|
||
// rewrite inserts and deletes
|
||
$cmdmapArray = array();
|
||
if ($diff['insertsDeletes']) {
|
||
foreach ($diff['insertsDeletes'] as $key => $action) {
|
||
$elParts = explode(':',$key);
|
||
if ($action == 1) { // inserted records should be deleted
|
||
$cmdmapArray[$elParts[0]][$elParts[1]]['delete'] = 1;
|
||
// when the record is deleted, the contents of the record do not need to be updated
|
||
unset($diff['oldData'][$key]);
|
||
unset($diff['newData'][$key]);
|
||
} else { // deleted records should be inserted again
|
||
$cmdmapArray[$elParts[0]][$elParts[1]]['undelete'] = 1;
|
||
}
|
||
}
|
||
}
|
||
// Writes the data:
|
||
if ($cmdmapArray) {
|
||
$tce = t3lib_div::makeInstance('t3lib_TCEmain');
|
||
$tce->stripslashes_values=0;
|
||
$tce->debug=0;
|
||
$tce->dontProcessTransformations=1;
|
||
$tce->start(array(),$cmdmapArray);
|
||
$tce->process_cmdmap();
|
||
unset($tce);
|
||
}
|
||
// PROCESS CHANGES
|
||
// create an array for process_datamap
|
||
$diff_modified = array();
|
||
foreach ($diff['oldData'] as $key => $value) {
|
||
$splitKey = explode(':',$key);
|
||
$diff_modified[$splitKey[0]][$splitKey[1]] = $value;
|
||
}
|
||
$rollbackData = explode(':',$this->rollbackFields);
|
||
switch (count($rollbackData)) {
|
||
case 1: // TODO: implement all tables / one table
|
||
$data = $diff_modified;
|
||
break;
|
||
case 2: // one record
|
||
$data[$rollbackData[0]][$rollbackData[1]] = $diff_modified[$rollbackData[0]][$rollbackData[1]];
|
||
break;
|
||
case 3: // one field in one record
|
||
$data[$rollbackData[0]][$rollbackData[1]][$rollbackData[2]] = $diff_modified[$rollbackData[0]][$rollbackData[1]][$rollbackData[2]];
|
||
break;
|
||
}
|
||
// Removing fields:
|
||
$data = $this->removeFilefields($rollbackData[0],$data);
|
||
// Writes the data:
|
||
$tce = t3lib_div::makeInstance('t3lib_TCEmain');
|
||
$tce->stripslashes_values=0;
|
||
$tce->debug=0;
|
||
$tce->dontProcessTransformations=1;
|
||
$tce->start($data,array());
|
||
$tce->process_datamap();
|
||
unset($tce);
|
||
// return to normal operation
|
||
$this->lastSyslogId = FALSE;
|
||
$this->rollbackFields = FALSE;
|
||
$this->createChangeLog();
|
||
}
|
||
/*******************************
|
||
*
|
||
* Display functions
|
||
*
|
||
*******************************/
|
||
/**
|
||
* Displays settings
|
||
*
|
||
* @return string HTML code to modify settings
|
||
*/
|
||
function displaySettings() {
|
||
global $BE_USER, $LANG, $SOBE;
|
||
// get current selection from UC, merge data, write it back to UC
|
||
$currentSelection = is_array($BE_USER->uc['moduleData']['history']) ? $BE_USER->uc['moduleData']['history'] : array('maxSteps' => '', 'showDiff' => 1, 'showSubElements' => 1, 'showInsertDelete' => 1);
|
||
$currentSelectionOverride = t3lib_div::_GP('settings');
|
||
if ($currentSelectionOverride) {
|
||
$currentSelection = array_merge($currentSelection,$currentSelectionOverride);
|
||
$BE_USER->uc['moduleData']['history'] = $currentSelection;
|
||
$BE_USER->writeUC($BE_USER->uc);
|
||
}
|
||
// display selector for number of history entries
|
||
$selector['maxSteps'] = array(
|
||
10 => 10,
|
||
20 => 20,
|
||
50 => 50,
|
||
100 => 100,
|
||
'' => 'maxSteps_all',
|
||
'snapshots' => 'maxSteps_snapshots'
|
||
);
|
||
$selector['showDiff'] = array(
|
||
0 => 'showDiff_no',
|
||
1 => 'showDiff_inline',
|
||
2 => 'showDiff_popup',
|
||
);
|
||
$selector['showSubElements'] = array(
|
||
0 => 'no',
|
||
1 => 'yes',
|
||
);
|
||
$selector['showInsertDelete'] = array(
|
||
0 => 'no',
|
||
1 => 'yes',
|
||
);
|
||
// render selectors
|
||
$displayCode = '';
|
||
foreach ($selector as $key => $values) {
|
||
$displayCode .= '<tr><td>'.$LANG->getLL($key,1).'</td><td><select name="settings['.$key.']" onChange="document.settings.submit()" style="width:100px">';
|
||
foreach ($values as $singleKey => $singleVal) {
|
||
$caption = $LANG->getLL($singleVal,1)?$LANG->getLL($singleVal,1):$singleVal;
|
||
$displayCode .= '<option value="'.$singleKey.'" '.(($singleKey == $currentSelection[$key])?'selected':'').'> '.$caption.'</option>';
|
||
}
|
||
$displayCode .= '</select></td></tr>';
|
||
}
|
||
// set values correctly
|
||
if ($currentSelection['maxSteps'] != 'snapshots') {
|
||
$this->maxSteps = $currentSelection['maxSteps']?intval($currentSelection['maxSteps']):'';
|
||
} else {
|
||
$this->addWhere = ' AND sys_history.snapshot=1';
|
||
$this->maxSteps = '';
|
||
}
|
||
$this->showDiff = intval($currentSelection['showDiff']);
|
||
$this->showSubElements = intval($currentSelection['showSubElements']);
|
||
$this->showInsertDelete = intval($currentSelection['showInsertDelete']);
|
||
$content = '<form name="settings" action="'.t3lib_div::getIndpEnv('TYPO3_REQUEST_URL').'" method="post"><table>'.$displayCode.'</table></form>';
|
||
return $SOBE->doc->section($LANG->getLL('settings',1),$content,0,1,0,0);
|
||
}
|
||
/**
|
||
* Shows the full change log
|
||
*
|
||
* @return string HTML for list, wrapped in a table.
|
||
*/
|
||
function displayHistory() {
|
||
global $LANG;
|
||
global $SOBE;
|
||
global $TCA;
|
||
$changeLog = $this->changeLog;
|
||
$lines=array();
|
||
// Initialize:
|
||
$lines[] = '<tr class="bgColor5 c-head">
|
||
<td> </td>
|
||
<td>'.$LANG->getLL('time',1).'</td>
|
||
<td>'.$LANG->getLL('age',1).'</td>
|
||
<td>'.$LANG->getLL('user',1).'</td>
|
||
<td>'.$LANG->getLL('tableUid',1).'</td>
|
||
<td>'.$LANG->getLL('differences',1).'</td>
|
||
<td> </td>
|
||
</tr>';
|
||
// TODO: Maybe user/groupname blinding necessary
|
||
#$be_group_Array=t3lib_BEfunc::getListGroupNames('title,uid');
|
||
#$groupArray=array_keys($be_group_Array);
|
||
$be_user_array = t3lib_BEfunc::getUserNames();
|
||
#if (!$GLOBALS['BE_USER']->isAdmin()) $be_user_Array = t3lib_BEfunc::blindUserNames($be_user_Array,$groupArray,1);
|
||
// Traverse changelog array:
|
||
if (!$changeLog) {
|
||
return 0;
|
||
}
|
||
$i = 0;
|
||
foreach ($changeLog as $sysLogUid => $entry) {
|
||
// stop after maxSteps
|
||
if ($i > $this->maxSteps && $this->maxSteps) {
|
||
break;
|
||
}
|
||
$i++;
|
||
// get user name
|
||
$userName = ($entry['user']?$be_user_array[$entry['user']]['username']:$LANG->getLL('externalChange',1));
|
||
// build up single line
|
||
$singleLine = array();
|
||
// diff link
|
||
$image = '<img'.t3lib_iconWorks::skinImg('','gfx/pilright_n.gif').' align="top" alt="'.$LANG->getLL('sumUpChanges',1).'" title="'.$LANG->getLL('sumUpChanges',1).'" />';
|
||
$singleLine[] = '<span style="position:relative;top:-10px">'.$this->linkPage($image,array('diff' => $sysLogUid)).'</span>';
|
||
$singleLine[] = htmlspecialchars(t3lib_BEfunc::datetime($entry['tstamp'])); // add time
|
||
$singleLine[] = htmlspecialchars(t3lib_BEfunc::calcAge(time()-$entry['tstamp'],$GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.php:labels.minutesHoursDaysYears'))); // add age
|
||
$singleLine[] = htmlspecialchars($userName); // add user name
|
||
$singleLine[] = htmlspecialchars($entry['tablename'].':'.$entry['recuid']); // add record UID
|
||
// show insert/delete/diff/changed field names
|
||
if ($entry['action']) { // insert or delete of element
|
||
$singleLine[] = '<strong>'.htmlspecialchars($LANG->getLL($entry['action'],1)).'</strong>';
|
||
} else {
|
||
if (!$this->showDiff) { // display field names instead of full diff
|
||
// re-write field names with labels
|
||
$tmpFieldList = explode(',',$entry['fieldlist']);
|
||
foreach ($tmpFieldList as $key => $value) {
|
||
$tmpFieldList[$key] = str_replace(':','',$LANG->sl(t3lib_BEfunc::getItemLabel($entry['tablename'],$value),1)); // FIXME: Ugly hack for removing the :
|
||
}
|
||
$singleLine[] = htmlspecialchars(implode(',',$tmpFieldList));
|
||
} else { // display diff
|
||
$diff = $this->renderDiff($entry,$entry['tablename']);
|
||
$singleLine[] = $diff;
|
||
}
|
||
}
|
||
// show link to save snapshot
|
||
$image = '<img'.t3lib_iconWorks::skinImg('','gfx/savesnapshot.gif').' align="top" alt="'.$LANG->getLL('saveSnapshot',1).'" title="'.$LANG->getLL('saveSnapshot',1).'" />';
|
||
$singleLine[] = $this->linkPage($image,array('saveSnapshot' => $entry['uid']));
|
||
$bgColorClass = $entry['snapshot'] ? 'bgColor2' : 'bgColor4-20';
|
||
// put line together
|
||
$lines[] = '
|
||
<tr class="'.$bgColorClass.'">
|
||
<td>'.implode('</td><td>',$singleLine).'</td>
|
||
</tr>';
|
||
}
|
||
// Finally, put it all together:
|
||
$theCode = '
|
||
<!--
|
||
History (list):
|
||
-->
|
||
<table border="0" cellpadding="2" cellspacing="2" id="typo3-history">
|
||
'.implode('',$lines).'
|
||
</table>';
|
||
if ($this->lastSyslogId) {
|
||
$theCode .= $this->linkPage('<img'.t3lib_iconWorks::skinImg('','gfx/group_tobottom.gif').' alt="'.$LANG->getLL('fullView',1).'" title="'.$LANG->getLL('fullView',1).'" />',array('diff' => ''));
|
||
}
|
||
// Add message about the difference view.
|
||
$theCode .= '<br /><img'.t3lib_iconWorks::skinImg('','gfx/icon_note.gif','width="18" height="16"').' align="top" alt="" />'.$LANG->getLL('differenceMsg').'<br /><br />';
|
||
// Add CSH:
|
||
// TODO: FIX CSH
|
||
$theCode .= t3lib_BEfunc::cshItem('xMOD_csh_corebe', 'history_'.($this->sumUp ? 'sum' : 'log'), $GLOBALS['BACK_PATH'],'');
|
||
// Add the whole content as a module section:
|
||
return $SOBE->doc->section($LANG->getLL('changes'),$theCode,0,1);
|
||
}
|
||
/**
|
||
* Displays a diff over multiple fields including rollback links
|
||
*
|
||
* @param array difference array
|
||
* @return string HTML output
|
||
*/
|
||
function displayMultipleDiff($diff) {
|
||
global $SOBE, $LANG;
|
||
$content = '';
|
||
// create diffs for all changed records
|
||
$arrayKeys = array_merge(array_keys($diff['newData']),array_keys($diff['insertsDeletes']));
|
||
$arrayKeys = array_unique($arrayKeys);
|
||
foreach ($arrayKeys as $key) {
|
||
$record = '';
|
||
$elParts = explode(':',$key);
|
||
if ($diff['insertsDeletes'][$key] == 1) { // insert
|
||
$record .= $LANG->getLL('insert',1);
|
||
$record .= '<br/>';
|
||
} elseif ($diff['insertsDeletes'][$key] == -1) {
|
||
$record .= $LANG->getLL('delete',1);
|
||
$record .= '<br/>';
|
||
}
|
||
// build up temporary diff array
|
||
if ($diff['newData'][$key]) {
|
||
$tmpArr['newRecord'] = $diff['newData'][$key];
|
||
$tmpArr['oldRecord'] = $diff['oldData'][$key];
|
||
$record .= $this->renderDiff($tmpArr, $elParts[0],$elParts[1]);
|
||
}
|
||
$titleLine = $key.$this->createRollbackLink($key, $LANG->getLL('revertRecord',1));
|
||
$content .= $SOBE->doc->section($titleLine,$record,0,0,0,1);
|
||
}
|
||
$titleLine = $LANG->getLL('mergedDifferences',1) . $this->createRollbackLink('ALL', $LANG->getLL('revertAll',1));
|
||
return $SOBE->doc->section($titleLine,$content,0,1,0,1);
|
||
}
|
||
/**
|
||
* Renders HTML table-rows with the comparison information of an sys_history entry record
|
||
*
|
||
* @param array sys_history entry record.
|
||
* @param string The table name
|
||
* @param integer If set to UID of record, display rollback links
|
||
* @return string HTML table
|
||
* @access private
|
||
*/
|
||
function renderDiff($entry,$table,$rollbackUid=0) {
|
||
global $SOBE, $LANG, $TCA;
|
||
$lines=array();
|
||
if (is_array($entry['newRecord'])) {
|
||
$t3lib_diff_Obj = t3lib_div::makeInstance('t3lib_diff');
|
||
$fieldsToDisplay = array_keys($entry['newRecord']);
|
||
foreach($fieldsToDisplay as $fN) {
|
||
t3lib_div::loadTCA($table);
|
||
if (is_array($TCA[$table]['columns'][$fN]) && $TCA[$table]['columns'][$fN]['config']['type']!='passthrough') {
|
||
// Create diff-result:
|
||
$diffres = $t3lib_diff_Obj->makeDiffDisplay(
|
||
t3lib_BEfunc::getProcessedValue($table,$fN,$entry['oldRecord'][$fN],0,1),
|
||
t3lib_BEfunc::getProcessedValue($table,$fN,$entry['newRecord'][$fN],0,1)
|
||
);
|
||
$lines[]='
|
||
<tr class="bgColor4">
|
||
<td><em>'.$LANG->sl(t3lib_BEfunc::getItemLabel($table,$fN),1).($rollbackUid?$this->createRollbackLink($table.':'.$rollbackUid.':'.$fN, $LANG->getLL('revertField',1)):'').'</em></td>
|
||
<td>'.nl2br($diffres).'</td>
|
||
</tr>';
|
||
}
|
||
}
|
||
}
|
||
if ($lines) {
|
||
$content = '<table border="0" cellpadding="2" cellspacing="2" id="typo3-history-item">
|
||
'.implode('',$lines).'
|
||
</table>';
|
||
return $content;
|
||
}
|
||
return NULL; // error fallback
|
||
}
|
||
/*******************************
|
||
*
|
||
* build up history
|
||
*
|
||
*******************************/
|
||
/**
|
||
* Creates a diff between the current version of the records and the selected version
|
||
*
|
||
* @return array diff for many elements
|
||
*/
|
||
function createMultipleDiff() {
|
||
$insertsDeletes = array();
|
||
$newArr = array();
|
||
$differences = array();
|
||
if (!$this->changeLog) {
|
||
return 0;
|
||
}
|
||
$i = array();
|
||
foreach ($this->changeLog as $key => $value) {
|
||
$field = $value['tablename'].':'.$value['recuid'];
|
||
if ($value['action']) {
|
||
if (!$insertsDeletes[$field]) {
|
||
$insertsDeletes[$field] = 0;
|
||
}
|
||
if ($value['action'] == 'insert') {
|
||
$insertsDeletes[$field]++;
|
||
} else {
|
||
$insertsDeletes[$field]--;
|
||
}
|
||
if ($insertsDeletes[$field] == 0) {
|
||
unset($insertsDeletes[$field]);
|
||
}
|
||
} else {
|
||
if ($i[$field] == 0) {
|
||
$newArr[$field] = $value['newRecord'];
|
||
}
|
||
$i[$field]++;
|
||
$differences[$field] = array_merge($differences[$field],$value['oldRecord']);
|
||
}
|
||
}
|
||
return array(
|
||
'newData' => $newArr,
|
||
'oldData' => $differences,
|
||
'insertsDeletes' => $insertsDeletes
|
||
);
|
||
}
|
||
/**
|
||
* Creates change log including sub-elements, filling $this->changeLog
|
||
*
|
||
*/
|
||
function createChangeLog() {
|
||
global $TCA;
|
||
$elParts = explode(':',$this->element);
|
||
$changeLog = $this->getHistoryData($elParts[0],$elParts[1]);
|
||
// get history of tables of this page and merge it into changelog
|
||
if ($elParts[0] == 'pages' && $this->showSubElements) {
|
||
foreach ($TCA as $tablename => $value) {
|
||
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid',$tablename,'pid='.intval($elParts[1])); // check if there are records on the page
|
||
while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
|
||
if ($newChangeLog = $this->getHistoryData($tablename, $row['uid'])) { // if there is history data available, merge it into changelog
|
||
foreach ($newChangeLog as $key => $value) {
|
||
$changeLog[$key] = $value;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if(!$changeLog) {
|
||
return 0;
|
||
}
|
||
krsort($changeLog);
|
||
$this->changeLog = $changeLog;
|
||
return 1;
|
||
}
|
||
/**
|
||
* Gets history and delete/insert data from sys_log and sys_history
|
||
*
|
||
* @param string DB table name
|
||
* @param integer UID of record
|
||
* @return array history data of the record
|
||
*/
|
||
function getHistoryData($table,$uid) {
|
||
global $TCA;
|
||
$uid = $this->resolveElement($table,$uid);
|
||
// If table is found in $TCA:
|
||
if ($TCA[$table]) {
|
||
// Selecting the $this->maxSteps most recent states:
|
||
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
|
||
'sys_history.*,sys_log.userid',
|
||
'sys_history,sys_log',
|
||
'sys_history.sys_log_uid=sys_log.uid
|
||
AND sys_history.tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_history').'
|
||
AND sys_history.recuid='.intval($uid).$this->addWhere,
|
||
'',
|
||
'sys_log.uid DESC',
|
||
$this->maxSteps
|
||
);
|
||
// Traversing the result, building up changesArray / changeLog:
|
||
$changesArray=array(); // used temporarily to track intermedia changes
|
||
$changeLog=array();
|
||
while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
|
||
// only history until a certain syslog ID needed
|
||
if ($row['sys_log_uid'] <= $this->lastSyslogId && $this->lastSyslogId) {
|
||
continue;
|
||
}
|
||
$hisDat = unserialize($row['history_data']);
|
||
if (is_array($hisDat['newRecord']) && is_array($hisDat['oldRecord'])) {
|
||
// If intermedia changes:
|
||
$intermediaChanges = $this->cmp($changesArray,$hisDat['newRecord']);
|
||
if (count($intermediaChanges)) { // && !$row['snapshot']
|
||
$changeLog[]=$intermediaChanges;
|
||
// FIXME: ARRAY INDEX MIGHT HAVE AN ERROR!!!!!
|
||
}
|
||
// Add hisDat to the changeLog
|
||
// TODO: Possible to overload it with $row?
|
||
$hisDat['uid']=$row['uid'];
|
||
$hisDat['tstamp']=$row['tstamp'];
|
||
$hisDat['user']=$row['userid'];
|
||
$hisDat['snapshot']=$row['snapshot'];
|
||
$hisDat['fieldlist']=$row['fieldlist'];
|
||
$hisDat['tablename']=$row['tablename'];
|
||
$hisDat['recuid']=$row['recuid'];
|
||
$changeLog[$row['sys_log_uid']]=$hisDat;
|
||
// Update change array
|
||
// This is used to detect if any intermedia changes have been made.
|
||
$changesArray = array_merge($changesArray,$hisDat['oldRecord']);
|
||
} else {
|
||
debug('ERROR: [getHistoryData]');
|
||
return 0; // error fallback
|
||
}
|
||
}
|
||
// SELECT INSERTS/DELETES
|
||
if ($this->showInsertDelete && !$this->addWhere) {
|
||
// Select most recent inserts and deletes // WITHOUT snapshots
|
||
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery(
|
||
'uid,userid,action,tstamp',
|
||
'sys_log',
|
||
'type=1
|
||
AND ( action=1 OR action=3 )
|
||
AND tablename='.$GLOBALS['TYPO3_DB']->fullQuoteStr($table, 'sys_log').'
|
||
AND recuid='.intval($uid),
|
||
'',
|
||
'uid DESC',
|
||
$this->maxSteps
|
||
);
|
||
while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
|
||
if ($row['uid'] <= $this->lastSyslogId && $this->lastSyslogId) {
|
||
continue;
|
||
}
|
||
$hisDat = array();
|
||
switch ($row['action']) {
|
||
case 1: // Insert
|
||
$hisDat['action'] = 'insert';
|
||
break;
|
||
case 3: // Delete
|
||
$hisDat['action'] = 'delete';
|
||
break;
|
||
}
|
||
$hisDat['tstamp']=$row['tstamp'];
|
||
$hisDat['user']=$row['userid'];
|
||
$hisDat['tablename'] = $table;
|
||
$hisDat['recuid'] = $uid;
|
||
$changeLog[$row['uid']] = $hisDat;
|
||
}
|
||
}
|
||
return $changeLog;
|
||
}
|
||
return 0; // error fallback
|
||
}
|
||
/*******************************
|
||
*
|
||
* Various helper functions
|
||
*
|
||
*******************************/
|
||
/**
|
||
* creates a link for the rollback
|
||
*
|
||
* @param sting parameter which is set to rollbackFields
|
||
* @param string optional, alternative label and title tag of image
|
||
* @return string HTML output
|
||
*/
|
||
function createRollbackLink($key, $alt='') {
|
||
global $LANG;
|
||
return $this->linkPage('<img '.t3lib_iconWorks::skinImg('','gfx/history2.gif','width="13" height="12"').' title="'.$LANG->getLL('rollback',1).'" alt="'.$alt.'" title="'.$alt.'" />',array('rollbackFields'=>$key));
|
||
}
|
||
/**
|
||
* Creates a link to the same page.
|
||
*
|
||
* @param string String to wrap in <a> tags (must be htmlspecialchars()'ed prior to calling function)
|
||
* @param array Array of key/value pairs to override the default values with.
|
||
* @param string Possible anchor value.
|
||
* @return string Link.
|
||
* @access private
|
||
*/
|
||
function linkPage($str,$inparams=array(),$anchor='') {
|
||
// Setting default values based on GET parameters:
|
||
$params['element']=$this->element;
|
||
$params['returnUrl']=$this->returnUrl;
|
||
$params['diff']=$this->lastSyslogId;
|
||
// Mergin overriding values:
|
||
$params = array_merge($params,$inparams);
|
||
// Make the link:
|
||
$Ahref = 'show_rechis.php?'.t3lib_div::implodeArrayForUrl('',$params).($anchor?'#'.$anchor:'');
|
||
$link = '<a href="'.htmlspecialchars($Ahref).'">'.$str.'</a>';
|
||
// Return link:
|
||
return $link;
|
||
}
|
||
/**
|
||
* Will traverse the field names in $dataArray and look in $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")
|
||
*
|
||
* @param string Table name
|
||
* @param array The data array
|
||
* @return array The modified data array
|
||
* @access private
|
||
*/
|
||
function removeFilefields($table,$dataArray) {
|
||
global $TCA;
|
||
if ($TCA[$table]) {
|
||
t3lib_div::loadTCA($table);
|
||
foreach($TCA[$table]['columns'] as $field => $config) {
|
||
if ($config['config']['type']=='group' && $config['config']['internal_type']=='file') {
|
||
unset($dataArray[$field]);
|
||
}
|
||
}
|
||
}
|
||
return $dataArray;
|
||
}
|
||
/**
|
||
* Compares the old record with the changed fields.
|
||
*
|
||
* @param array Record with field/value pairs (what has changed)
|
||
* @param array Record with field/value pairs
|
||
* @return array Comparison result.
|
||
* @access private
|
||
*/
|
||
function cmp($newRecord,$oldRecord) {
|
||
// Initialize:
|
||
$changes=array();
|
||
// Traverse $oldRecord
|
||
foreach($oldRecord as $fN => $fV) {
|
||
if (isset($newRecord[$fN]) && strcmp($fV,$newRecord[$fN])) {
|
||
$changes['oldRecord'][$fN]=$newRecord[$fN];
|
||
$changes['newRecord'][$fN]=$fV;
|
||
}
|
||
}
|
||
return $changes;
|
||
}
|
||
/**
|
||
* Convert input element reference to workspace version if any.
|
||
*
|
||
* @param string table of input element
|
||
* @param integer UID of record
|
||
* @return integer converted UID of record
|
||
*/
|
||
function resolveElement($table,$uid) {
|
||
if (isset($GLOBALS['TCA'][$table])) {
|
||
if ($workspaceVersion = t3lib_BEfunc::getWorkspaceVersionOfRecord($GLOBALS['BE_USER']->workspace, $table, $uid, 'uid')) {
|
||
$uid = $workspaceVersion['uid'];
|
||
}
|
||
}
|
||
return $uid;
|
||
}
|
||
/**
|
||
* resolve sh_uid (used from log)
|
||
*/
|
||
function resolveShUid() {
|
||
if (t3lib_div::_GP('sh_uid')) {
|
||
$sh_uid = t3lib_div::_GP('sh_uid');
|
||
$res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*','sys_history', 'uid='.intval($sh_uid));
|
||
$record = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res);
|
||
$this->element = $record['tablename'].':'.$record['recuid'];
|
||
$this->lastSyslogId = $record['sys_log_uid']-1;
|
||
}
|
||
}
|
||
}
|
||
?>
|
TYPO3core_testing/typo3/show_rechis.php 2005-12-09 18:32:25.000000000 +0100 | ||
---|---|---|
require_once (PATH_t3lib.'class.t3lib_diff.php');
|
||
require_once (PATH_t3lib.'class.t3lib_tcemain.php');
|
||
$LANG->includeLLFile('EXT:lang/locallang_show_rechis.xml');
|
||
require_once ('class.show_rechis.inc');
|
||
require_once ('class.show_rechis_new.inc');
|
||
... | ... | |
// Start history object
|
||
$historyObj = t3lib_div::makeInstance('recordHistory');
|
||
// Return link:
|
||
if ($historyObj->returnUrl) {
|
||
$this->content .= '<a href="'.htmlspecialchars($historyObj->returnUrl).'" class="typo3-goBack"><img'.t3lib_iconWorks::skinImg('','gfx/goback.gif','width="14" height="14"').' alt="" />'.$LANG->getLL('returnLink',1).'</a>';
|
||
}
|
||
// Get content:
|
||
$this->content.= $historyObj->main();
|
||
$this->content .= $historyObj->main();
|
||
// Return link:
|
||
if ($historyObj->returnUrl) {
|
||
$link = '<a href="'.htmlspecialchars($historyObj->returnUrl).'" class="typo3-goBack"><img'.t3lib_iconWorks::skinImg('','gfx/goback.gif','width="14" height="14"').' alt="" />'.$LANG->getLL('returnLink',1).'</a>';
|
||
$this->content.= $this->doc->section($LANG->getLL('return'),$link,0,1);
|
||
$this->content .= $this->doc->section($LANG->getLL('return'),$link,0,1);
|
||
}
|
||
}
|
TYPO3core_testing/typo3/sysext/lang/locallang_show_rechis.xml 2005-12-09 19:51:24.000000000 +0100 | ||
---|---|---|
<data type="array">
|
||
<languageKey index="default" type="array">
|
||
<label index="title">Show record history</label>
|
||
<label index="tableUid">Table/Uid</label>
|
||
<label index="time">Time</label>
|
||
<label index="age">Age</label>
|
||
<label index="user">User</label>
|
||
<label index="changeCount">Changes since time</label>
|
||
<label index="changes">Changes to the record</label>
|
||
<label index="tableUid">Table:uid</label>
|
||
<label index="differences">Differences</label>
|
||
<label index="changes">Change history</label>
|
||
<label index="return">Return</label>
|
||
<label index="returnLink">Click here to go back</label>
|
||
<label index="revertAll">Revert all changes shown</label>
|
||
<label index="revertRecord">Revert complete record</label>
|
||
<label index="revertField">Restore old value of this field</label>
|
||
<label index="revertAllFields">Restore old values of all fields below</label>
|
||
<label index="externalChange">Intermediate external change!</label>
|
||
<label index="sumUpChanges">Accumulate changes up to this point</label>
|
||
<label index="historyList">Show history list</label>
|
||
<label index="users">Users</label>
|
||
<label index="similar">This state is similar to the current values of the record!</label>
|
||
<label index="prev">Previous state</label>
|
||
<label index="next">Next state</label>
|
||
<label index="saveState">Protect this state from being erased</label>
|
||
<label index="fieldName">Fieldname</label>
|
||
<label index="oldValue">Old value</label>
|
||
<label index="newValue">New value</label>
|
||
<label index="currentValue">Current value</label>
|
||
<label index="saveSnapshot">Protect this state from being erased</label>
|
||
<label index="differenceMsg">In the Difference column the <span class="diff-g">green colored text</span> are new changes and the <span class="diff-r">red colored text</span> are the old values which were removed.</label>
|
||
<label index="difference">Difference</label>
|
||
<label index="fullView">Return to full view</label>
|
||
<label index="insert">Record was inserted</label>
|
||
<label index="delete">Record was deleted</label>
|
||
<label index="rollback">Rollback changes</label>
|
||
<label index="mergedDifferences">Merged differences to the current version</label>
|
||
<label index="settings">Settings</label>
|
||
<label index="maxSteps">Show entries:</label>
|
||
<label index="maxSteps_all">ALL</label>
|
||
<label index="maxSteps_snapshots">snapshots</label>
|
||
<label index="showDiff">Show diff:</label>
|
||
<label index="showDiff_no">No</label>
|
||
<label index="showDiff_inline">Inline</label>
|
||
<label index="showDiff_popup">Popup</label>
|
||
<label index="showSubElements">Show sub elements:</label>
|
||
<label index="no">No</label>
|
||
<label index="yes">Yes</label>
|
||
<label index="showInsertDelete">Show inserts/deletes:</label>
|
||
</languageKey>
|
||
<languageKey index="dk">EXT:csh_dk/lang/dk.locallang_show_rechis.xml</languageKey>
|
||
<languageKey index="de">EXT:csh_de/lang/de.locallang_show_rechis.xml</languageKey>
|