‪TYPO3CMS  ‪main
RecordHistory.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
22 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
26 
32 {
38  protected ‪$maxSteps = 20;
39 
45  protected ‪$showSubElements = true;
46 
52  protected ‪$element;
53 
59  protected ‪$lastHistoryEntry = 0;
60 
65  protected ‪$pageAccessCache = [];
66 
71  protected ‪$rollbackFields = '';
72 
79  public function ‪__construct(‪$element = '', ‪$rollbackFields = '')
80  {
81  $this->element = $this->‪sanitizeElementValue((string)‪$element);
82  $this->rollbackFields = $this->‪sanitizeRollbackFieldsValue((string)‪$rollbackFields);
83  }
84 
89  {
90  $this->lastHistoryEntry = ‪$lastHistoryEntry;
91  $this->‪updateCurrentElement();
92  }
93 
94  public function ‪getLastHistoryEntryNumber(): int
95  {
97  }
98 
103  public function ‪setMaxSteps(int ‪$maxSteps): void
104  {
105  $this->maxSteps = ‪$maxSteps;
106  }
107 
112  public function ‪setShowSubElements(bool ‪$showSubElements): void
113  {
114  $this->showSubElements = ‪$showSubElements;
115  }
116 
120  public function ‪getChangeLog(): array
121  {
122  if (!empty($this->element)) {
123  [$table, $recordUid] = explode(':', $this->element);
124  return $this->‪getHistoryData($table, (int)$recordUid, $this->showSubElements, $this->lastHistoryEntry);
125  }
126  return [];
127  }
128 
132  public function ‪getElementInformation(): array
133  {
134  return !empty($this->element) ? explode(':', $this->element) : [];
135  }
136 
140  public function ‪getElementString(): string
141  {
142  return (string)‪$this->element;
143  }
144 
145  /*******************************
146  *
147  * build up history
148  *
149  *******************************/
155  public function ‪getDiff(array $changeLog): array
156  {
157  $insertsDeletes = [];
158  $newArr = [];
159  $differences = [];
160  // traverse changelog array
161  foreach ($changeLog as $value) {
162  $field = $value['tablename'] . ':' . $value['recuid'];
163  // inserts / deletes
164  if ((int)$value['actiontype'] !== ‪RecordHistoryStore::ACTION_MODIFY) {
165  if (!isset($insertsDeletes[$field])) {
166  $insertsDeletes[$field] = 0;
167  }
168  ($value['action'] ?? '') === 'insert' ? $insertsDeletes[$field]++ : $insertsDeletes[$field]--;
169  // unset not needed fields
170  if ($insertsDeletes[$field] === 0) {
171  unset($insertsDeletes[$field]);
172  }
173  } elseif (!isset($newArr[$field])) {
174  $newArr[$field] = $value['newRecord'];
175  $differences[$field] = $value['oldRecord'];
176  } else {
177  $differences[$field] = array_merge($differences[$field], $value['oldRecord']);
178  }
179  }
180  // remove entries where there were no changes effectively
181  foreach ($newArr as ‪$record => $value) {
182  foreach ($value as $key => $innerVal) {
183  if ($newArr[‪$record][$key] === $differences[‪$record][$key]) {
184  unset($newArr[‪$record][$key], $differences[‪$record][$key]);
185  }
186  }
187  if (empty($newArr[‪$record]) && empty($differences[‪$record])) {
188  unset($newArr[‪$record], $differences[‪$record]);
189  }
190  }
191  return [
192  'newData' => $newArr,
193  'oldData' => $differences,
194  'insertsDeletes' => $insertsDeletes,
195  ];
196  }
197 
203  protected function ‪getHistoryData(string $table, int ‪$uid, bool $includeSubEntries = null, int ‪$lastHistoryEntry = null): array
204  {
205  $historyDataForRecord = $this->‪getHistoryDataForRecord($table, ‪$uid, ‪$lastHistoryEntry);
206  // get history of tables of this page and merge it into changelog
207  if ($table === 'pages' && $includeSubEntries && $this->‪hasPageAccess('pages', $uid)) {
208  foreach (‪$GLOBALS['TCA'] as $tablename => $value) {
209  // check if there are records on the page
210  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($tablename);
211  $queryBuilder->getRestrictions()->removeAll();
212 
213  $result = $queryBuilder
214  ->select('uid')
215  ->from($tablename)
216  ->where(
217  $queryBuilder->expr()->eq(
218  'pid',
219  $queryBuilder->createNamedParameter(‪$uid, ‪Connection::PARAM_INT)
220  )
221  )
222  ->executeQuery();
223  while ($row = $result->fetchAssociative()) {
224  // if there is history data available, merge it into changelog
225  $newChangeLog = $this->‪getHistoryDataForRecord($tablename, $row['uid'], ‪$lastHistoryEntry);
226  if (is_array($newChangeLog) && !empty($newChangeLog)) {
227  foreach ($newChangeLog as $key => $newChangeLogEntry) {
228  $historyDataForRecord[$key] = $newChangeLogEntry;
229  }
230  }
231  }
232  }
233  }
234  usort($historyDataForRecord, static function (array $a, array $b): int {
235  if ($a['tstamp'] < $b['tstamp']) {
236  return 1;
237  }
238  if ($a['tstamp'] > $b['tstamp']) {
239  return -1;
240  }
241  return 0;
242  });
243  return $historyDataForRecord;
244  }
245 
255  public function ‪getHistoryDataForRecord(string $table, int ‪$uid, int ‪$lastHistoryEntry = null): array
256  {
257  if (empty(‪$GLOBALS['TCA'][$table]) || !$this->‪hasTableAccess($table) || !$this->‪hasPageAccess($table, ‪$uid)) {
258  return [];
259  }
260 
261  ‪$uid = $this->‪resolveElement($table, ‪$uid);
262  return $this->‪findEventsForRecord($table, ‪$uid, ($this->maxSteps ?: 0), ‪$lastHistoryEntry);
263  }
264 
268  public function ‪getUserIdFromDeleteActionForRecord(string $table, int ‪$uid): int
269  {
270  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_history');
271  $queryBuilder->select('userid')
272  ->from('sys_history')
273  ->where(
274  $queryBuilder->expr()->eq(
275  'tablename',
276  $queryBuilder->createNamedParameter($table)
277  ),
278  $queryBuilder->expr()->eq(
279  'usertype',
280  $queryBuilder->createNamedParameter('BE')
281  ),
282  $queryBuilder->expr()->eq(
283  'recuid',
284  $queryBuilder->createNamedParameter(‪$uid, ‪Connection::PARAM_INT)
285  ),
286  $queryBuilder->expr()->eq(
287  'actiontype',
288  $queryBuilder->createNamedParameter(‪RecordHistoryStore::ACTION_DELETE, ‪Connection::PARAM_INT)
289  )
290  )
291  ->setMaxResults(1);
292 
293  return (int)$queryBuilder->executeQuery()->fetchOne();
294  }
295 
296  /*******************************
297  *
298  * Various helper functions
299  *
300  *******************************/
301 
309  protected function ‪resolveElement(string $table, int ‪$uid): int
310  {
311  if (isset(‪$GLOBALS['TCA'][$table])
312  && $workspaceVersion = BackendUtility::getWorkspaceVersionOfRecord($this->‪getBackendUser()->workspace, $table, ‪$uid, 'uid')) {
313  ‪$uid = $workspaceVersion['uid'];
314  }
315  return ‪$uid;
316  }
317 
321  protected function ‪getHistoryEntry(int ‪$lastHistoryEntry): array
322  {
323  $queryBuilder = $this->‪getQueryBuilder();
324  ‪$record = $queryBuilder
325  ->select('uid', 'tablename', 'recuid')
326  ->from('sys_history')
327  ->where($queryBuilder->expr()->eq('uid', $queryBuilder->createNamedParameter(‪$lastHistoryEntry, ‪Connection::PARAM_INT)))
328  ->executeQuery()
329  ->fetchAssociative();
330 
331  if (empty(‪$record)) {
332  return [];
333  }
334 
335  return ‪$record;
336  }
337 
341  public function ‪getCreationInformationForRecord(string $table, array ‪$record): ?array
342  {
343  $queryBuilder = $this->‪getQueryBuilder();
344  $result = $queryBuilder
345  ->select('*')
346  ->from('sys_history')
347  ->where(
348  $queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter($table)),
349  $queryBuilder->expr()->eq('recuid', $queryBuilder->createNamedParameter(‪$record['uid'], ‪Connection::PARAM_INT)),
350  $queryBuilder->expr()->eq('actiontype', $queryBuilder->createNamedParameter(‪RecordHistoryStore::ACTION_ADD, ‪Connection::PARAM_INT))
351  )
352  ->setMaxResults(1)
353  ->executeQuery()
354  ->fetchAssociative();
355  return $result ?: null;
356  }
357 
362  public function ‪getCreationInformationForMultipleRecords(string $table, array $recordIds): array
363  {
364  $queryBuilder = $this->‪getQueryBuilder();
365  return $queryBuilder
366  ->select('*')
367  ->from('sys_history')
368  ->where(
369  $queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter($table)),
370  $queryBuilder->expr()->in('recuid', $queryBuilder->createNamedParameter($recordIds, ‪Connection::PARAM_INT_ARRAY)),
371  $queryBuilder->expr()->eq('actiontype', $queryBuilder->createNamedParameter(‪RecordHistoryStore::ACTION_ADD, ‪Connection::PARAM_INT))
372  )
373  ->executeQuery()
374  ->fetchAllAssociative();
375  }
376 
381  public function ‪findEventsForRecord(string $table, int ‪$uid, int $limit = 0, int $minimumUid = null): array
382  {
383  $backendUser = $this->‪getBackendUser();
384  $queryBuilder = $this->‪getQueryBuilder();
385  $queryBuilder
386  ->select('*')
387  ->from('sys_history')
388  ->where(
389  $queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter($table)),
390  $queryBuilder->expr()->eq('recuid', $queryBuilder->createNamedParameter(‪$uid, ‪Connection::PARAM_INT))
391  );
392  if ($backendUser->workspace === 0) {
393  $queryBuilder->andWhere(
394  $queryBuilder->expr()->eq('workspace', 0)
395  );
396  } else {
397  $queryBuilder->andWhere(
398  $queryBuilder->expr()->or(
399  $queryBuilder->expr()->eq('workspace', 0),
400  $queryBuilder->expr()->eq('workspace', $queryBuilder->createNamedParameter($backendUser->workspace, ‪Connection::PARAM_INT))
401  )
402  );
403  }
404  if ($limit) {
405  $queryBuilder->setMaxResults($limit);
406  }
407 
408  if ($minimumUid) {
409  $queryBuilder->andWhere($queryBuilder->expr()->gte('uid', $queryBuilder->createNamedParameter($minimumUid, ‪Connection::PARAM_INT)));
410  }
411 
412  return $this->‪prepareEventDataFromQueryBuilder($queryBuilder);
413  }
414 
415  public function ‪findEventsForCorrelation(string $correlationId): array
416  {
417  $queryBuilder = $this->‪getQueryBuilder();
418  $queryBuilder
419  ->select('*')
420  ->from('sys_history')
421  ->where($queryBuilder->expr()->eq('correlation_id', $queryBuilder->createNamedParameter($correlationId)));
422 
423  return $this->‪prepareEventDataFromQueryBuilder($queryBuilder);
424  }
425 
426  protected function ‪prepareEventDataFromQueryBuilder(QueryBuilder $queryBuilder): array
427  {
428  $events = [];
429  $result = $queryBuilder->orderBy('tstamp', 'DESC')->executeQuery();
430  while ($row = $result->fetchAssociative()) {
431  ‪$identifier = (int)$row['uid'];
432  $actionType = (int)$row['actiontype'];
433  if ($actionType === ‪RecordHistoryStore::ACTION_ADD || $actionType === ‪RecordHistoryStore::ACTION_UNDELETE) {
434  $row['action'] = 'insert';
435  }
436  if ($actionType === ‪RecordHistoryStore::ACTION_DELETE) {
437  $row['action'] = 'delete';
438  }
439  if ($row['history_data'] === null) {
440  $events[‪$identifier] = $row;
441  continue;
442  }
443  if (str_starts_with($row['history_data'], 'a')) {
444  // legacy code
445  $row['history_data'] = unserialize($row['history_data'], ['allowed_classes' => false]);
446  } else {
447  $row['history_data'] = json_decode($row['history_data'], true);
448  }
449  if (isset($row['history_data']['newRecord'])) {
450  $row['newRecord'] = $row['history_data']['newRecord'];
451  }
452  if (isset($row['history_data']['oldRecord'])) {
453  $row['oldRecord'] = $row['history_data']['oldRecord'];
454  }
455  $events[‪$identifier] = $row;
456  }
457  krsort($events);
458  return $events;
459  }
460 
467  protected function ‪hasPageAccess($table, ‪$uid): bool
468  {
469  $pageRecord = null;
470  ‪$uid = (int)‪$uid;
471 
472  if ($table === 'pages') {
473  $pageId = ‪$uid;
474  } else {
475  ‪$record = BackendUtility::getRecord($table, ‪$uid, '*', '', false);
476  $pageId = (‪$record['pid'] ?? 0);
477  }
478 
479  if ($pageId === 0 && (‪$GLOBALS['TCA'][$table]['ctrl']['security']['ignoreRootLevelRestriction'] ?? false)) {
480  return true;
481  }
482 
483  if (!isset($this->pageAccessCache[$pageId])) {
484  $isDeletedPage = false;
485  if (isset(‪$GLOBALS['TCA']['pages']['ctrl']['delete'])) {
486  $deletedField = ‪$GLOBALS['TCA']['pages']['ctrl']['delete'];
487  ‪$fields = 'pid,' . $deletedField;
488  $pageRecord = BackendUtility::getRecord('pages', $pageId, ‪$fields, '', false);
489  $isDeletedPage = (bool)($pageRecord[$deletedField] ?? false);
490  }
491  if ($isDeletedPage) {
492  // The page is deleted, so we fake its uid to be the one of the parent page.
493  // By doing so, the following API will use this id to traverse the rootline
494  // and check whether it is in the users' web mounts.
495  // We check however if the user has (or better had) access to the deleted page itself.
496  // Since the only way we got here is by requesting the history of the parent page
497  // we can be sure this parent page actually exists.
498  $pageRecord['uid'] = $pageRecord['pid'];
499  $this->pageAccessCache[$pageId] = $this->‪getBackendUser()->doesUserHaveAccess($pageRecord, ‪Permission::PAGE_SHOW);
500  } else {
501  $this->pageAccessCache[$pageId] = BackendUtility::readPageAccess(
502  $pageId,
503  $this->‪getBackendUser()->getPagePermsClause(‪Permission::PAGE_SHOW)
504  );
505  }
506  }
507 
508  return $this->pageAccessCache[$pageId] !== false;
509  }
510 
517  protected function ‪sanitizeElementValue(string $value): string
518  {
519  if ($value !== '' && !preg_match('#^[a-z\d_.]+:[\d]+$#i', $value)) {
520  return '';
521  }
522  return $value;
523  }
524 
528  protected function ‪sanitizeRollbackFieldsValue(string $value): string
529  {
530  if ($value !== '' && !preg_match('#^[a-z\d_.]+(:[\d]+(:[a-z\d_.]+)?)?$#i', $value)) {
531  return '';
532  }
533  return $value;
534  }
535 
541  protected function ‪hasTableAccess($table): bool
542  {
543  return $this->‪getBackendUser()->check('tables_select', $table);
544  }
545 
546  protected function ‪getBackendUser(): ‪BackendUserAuthentication
547  {
548  return ‪$GLOBALS['BE_USER'];
549  }
550 
551  protected function ‪getQueryBuilder(): QueryBuilder
552  {
553  return GeneralUtility::makeInstance(ConnectionPool::class)
554  ->getQueryBuilderForTable('sys_history');
555  }
556 
557  protected function ‪updateCurrentElement(): void
558  {
559  if ($this->lastHistoryEntry) {
560  $elementData = $this->‪getHistoryEntry($this->lastHistoryEntry);
561  if (!empty($elementData) && empty($this->element)) {
562  $this->element = $elementData['tablename'] . ':' . $elementData['recuid'];
563  }
564  }
565  }
566 }
‪TYPO3\CMS\Backend\History\RecordHistory\getElementInformation
‪getElementInformation()
Definition: RecordHistory.php:126
‪TYPO3\CMS\Backend\History\RecordHistory\getElementString
‪string getElementString()
Definition: RecordHistory.php:134
‪TYPO3\CMS\Backend\History\RecordHistory\getDiff
‪array getDiff(array $changeLog)
Definition: RecordHistory.php:149
‪TYPO3\CMS\Backend\History\RecordHistory\getChangeLog
‪getChangeLog()
Definition: RecordHistory.php:114
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Backend\History\RecordHistory\getUserIdFromDeleteActionForRecord
‪getUserIdFromDeleteActionForRecord(string $table, int $uid)
Definition: RecordHistory.php:262
‪TYPO3\CMS\Backend\History\RecordHistory\$rollbackFields
‪string $rollbackFields
Definition: RecordHistory.php:65
‪TYPO3\CMS\Backend\History\RecordHistory\setShowSubElements
‪setShowSubElements(bool $showSubElements)
Definition: RecordHistory.php:106
‪TYPO3\CMS\Core\DataHandling\History\RecordHistoryStore\ACTION_MODIFY
‪const ACTION_MODIFY
Definition: RecordHistoryStore.php:33
‪$fields
‪$fields
Definition: pages.php:5
‪TYPO3\CMS\Backend\History\RecordHistory\resolveElement
‪int resolveElement(string $table, int $uid)
Definition: RecordHistory.php:303
‪TYPO3\CMS\Backend\History\RecordHistory\getHistoryDataForRecord
‪array getHistoryDataForRecord(string $table, int $uid, int $lastHistoryEntry=null)
Definition: RecordHistory.php:249
‪TYPO3\CMS\Core\Type\Bitmask\Permission
Definition: Permission.php:26
‪TYPO3\CMS\Core\DataHandling\History\RecordHistoryStore
Definition: RecordHistoryStore.php:31
‪TYPO3\CMS\Backend\History
‪TYPO3\CMS\Backend\History\RecordHistory\setLastHistoryEntryNumber
‪setLastHistoryEntryNumber(int $lastHistoryEntry)
Definition: RecordHistory.php:82
‪TYPO3\CMS\Backend\History\RecordHistory
Definition: RecordHistory.php:32
‪TYPO3\CMS\Webhooks\Message\$record
‪identifier readonly int readonly array $record
Definition: PageModificationMessage.php:36
‪TYPO3\CMS\Backend\History\RecordHistory\setMaxSteps
‪setMaxSteps(int $maxSteps)
Definition: RecordHistory.php:97
‪TYPO3\CMS\Backend\History\RecordHistory\$showSubElements
‪bool $showSubElements
Definition: RecordHistory.php:43
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪TYPO3\CMS\Backend\History\RecordHistory\$pageAccessCache
‪array $pageAccessCache
Definition: RecordHistory.php:60
‪TYPO3\CMS\Core\Type\Bitmask\Permission\PAGE_SHOW
‪const PAGE_SHOW
Definition: Permission.php:35
‪TYPO3\CMS\Backend\History\RecordHistory\getQueryBuilder
‪getQueryBuilder()
Definition: RecordHistory.php:545
‪TYPO3\CMS\Backend\History\RecordHistory\hasPageAccess
‪hasPageAccess($table, $uid)
Definition: RecordHistory.php:461
‪TYPO3\CMS\Backend\History\RecordHistory\getHistoryEntry
‪getHistoryEntry(int $lastHistoryEntry)
Definition: RecordHistory.php:315
‪TYPO3\CMS\Backend\History\RecordHistory\sanitizeElementValue
‪sanitizeElementValue(string $value)
Definition: RecordHistory.php:511
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Backend\History\RecordHistory\getHistoryData
‪getHistoryData(string $table, int $uid, bool $includeSubEntries=null, int $lastHistoryEntry=null)
Definition: RecordHistory.php:197
‪TYPO3\CMS\Webhooks\Message\$uid
‪identifier readonly int $uid
Definition: PageModificationMessage.php:35
‪TYPO3\CMS\Core\DataHandling\History\RecordHistoryStore\ACTION_DELETE
‪const ACTION_DELETE
Definition: RecordHistoryStore.php:35
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Backend\History\RecordHistory\getCreationInformationForMultipleRecords
‪getCreationInformationForMultipleRecords(string $table, array $recordIds)
Definition: RecordHistory.php:356
‪TYPO3\CMS\Backend\History\RecordHistory\getBackendUser
‪getBackendUser()
Definition: RecordHistory.php:540
‪TYPO3\CMS\Backend\History\RecordHistory\getLastHistoryEntryNumber
‪getLastHistoryEntryNumber()
Definition: RecordHistory.php:88
‪TYPO3\CMS\Backend\History\RecordHistory\$element
‪string $element
Definition: RecordHistory.php:49
‪TYPO3\CMS\Core\DataHandling\History\RecordHistoryStore\ACTION_ADD
‪const ACTION_ADD
Definition: RecordHistoryStore.php:32
‪TYPO3\CMS\Backend\History\RecordHistory\findEventsForRecord
‪findEventsForRecord(string $table, int $uid, int $limit=0, int $minimumUid=null)
Definition: RecordHistory.php:375
‪TYPO3\CMS\Backend\History\RecordHistory\getCreationInformationForRecord
‪getCreationInformationForRecord(string $table, array $record)
Definition: RecordHistory.php:335
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Backend\History\RecordHistory\$lastHistoryEntry
‪int $lastHistoryEntry
Definition: RecordHistory.php:55
‪TYPO3\CMS\Core\DataHandling\History\RecordHistoryStore\ACTION_UNDELETE
‪const ACTION_UNDELETE
Definition: RecordHistoryStore.php:36
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Backend\History\RecordHistory\__construct
‪__construct($element='', $rollbackFields='')
Definition: RecordHistory.php:73
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT_ARRAY
‪const PARAM_INT_ARRAY
Definition: Connection.php:72
‪TYPO3\CMS\Backend\History\RecordHistory\$maxSteps
‪int $maxSteps
Definition: RecordHistory.php:37
‪TYPO3\CMS\Backend\History\RecordHistory\findEventsForCorrelation
‪findEventsForCorrelation(string $correlationId)
Definition: RecordHistory.php:409
‪TYPO3\CMS\Backend\History\RecordHistory\hasTableAccess
‪hasTableAccess($table)
Definition: RecordHistory.php:535
‪TYPO3\CMS\Backend\History\RecordHistory\sanitizeRollbackFieldsValue
‪sanitizeRollbackFieldsValue(string $value)
Definition: RecordHistory.php:522
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37
‪TYPO3\CMS\Backend\History\RecordHistory\prepareEventDataFromQueryBuilder
‪prepareEventDataFromQueryBuilder(QueryBuilder $queryBuilder)
Definition: RecordHistory.php:420
‪TYPO3\CMS\Backend\History\RecordHistory\updateCurrentElement
‪updateCurrentElement()
Definition: RecordHistory.php:551