PageRenderTime 82ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/typo3/class.show_rechis.inc

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