2 declare(strict_types
= 1);
3 namespace TYPO3\CMS\Install\Updates
;
6 * This file is part of the TYPO3 CMS project.
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
15 * The TYPO3 project - inspiring people to share!
18 use TYPO3\CMS\Core\Database\Connection
;
19 use TYPO3\CMS\Core\Database\ConnectionPool
;
20 use TYPO3\CMS\Core\History\RecordHistoryStore
;
21 use TYPO3\CMS\Core\Utility\GeneralUtility
;
24 * Merge data stored in sys_log that belongs to sys_history
26 class SeparateSysHistoryFromSysLogUpdate
extends AbstractUpdate
31 protected $title = 'Migrates existing sys_log entries into sys_history';
34 * Checks if an update is needed
36 * @param string $description The description for the update
38 * @return bool Whether an update is needed (true) or not (false)
40 public function checkForUpdate(&$description)
42 if ($this->isWizardDone()) {
46 // sys_log field has been removed, no need to do something.
47 if (!$this->checkIfFieldInTableExists('sys_history', 'sys_log_uid')) {
51 $description = 'The history of changes of a record is now solely stored within sys_history. Previous data within sys_log needs to be'
52 . ' migrated into sys_history now.</p>';
54 // Check if there is data to migrate
55 $queryBuilder = GeneralUtility
::makeInstance(ConnectionPool
::class)
56 ->getQueryBuilderForTable('sys_history');
57 $queryBuilder->getRestrictions()->removeAll();
58 $count = $queryBuilder->count('*')
60 ->where($queryBuilder->expr()->neq('sys_log_uid', 0))
68 * Moves data from sys_log into sys_history
69 * where a reference is still there: sys_history.sys_log_uid > 0
71 * @param array $databaseQueries Queries done in this update
72 * @param string $customMessage Custom messages
74 * @throws \Doctrine\DBAL\DBALException
76 public function performUpdate(array &$databaseQueries, &$customMessage)
78 // update "modify" statements (= decoupling)
79 $connection = GeneralUtility
::makeInstance(ConnectionPool
::class)->getConnectionForTable('sys_history');
80 $queryBuilder = $connection->createQueryBuilder();
82 ->select('sys_history.uid AS history_uid', 'sys_history.history_data', 'sys_log.*')
88 $queryBuilder->expr()->eq('sys_history.sys_log_uid', $queryBuilder->quoteIdentifier('sys_log.uid'))
93 foreach ($rows as $row) {
94 $logData = $row['log_data'] !== null ?
unserialize($row['log_data'], ['allowed_classes' => false]) : [];
96 'actiontype' => RecordHistoryStore
::ACTION_MODIFY
,
98 'userid' => $row['userid'],
100 'history_data' => json_encode($row['history_data'] !== null ?
unserialize($row['history_data'], ['allowed_classes' => false]) : []),
101 'originaluserid' => empty($logData['originalUser']) ?
null : $logData['originalUser']
106 ['uid' => (int)$row['history_uid']],
107 ['uid' => Connection
::PARAM_INT
]
109 // Store information about history entry in sys_log table
110 $logData['history'] = $row['history_uid'];
113 ['log_data' => serialize($logData)],
114 ['uid' => (int)$row['uid']],
115 ['uid' => Connection
::PARAM_INT
]
119 // Add insert/delete calls
120 $logQueryBuilder = GeneralUtility
::makeInstance(ConnectionPool
::class)->getQueryBuilderForTable('sys_log');
121 $result = $logQueryBuilder
122 ->select('uid', 'userid', 'action', 'tstamp', 'log_data', 'tablename', 'recuid')
125 $logQueryBuilder->expr()->eq('type', $logQueryBuilder->createNamedParameter(1, \PDO
::PARAM_INT
)),
126 $logQueryBuilder->expr()->orX(
127 $logQueryBuilder->expr()->eq('action', $logQueryBuilder->createNamedParameter(1, \PDO
::PARAM_INT
)),
128 $logQueryBuilder->expr()->eq('action', $logQueryBuilder->createNamedParameter(3, \PDO
::PARAM_INT
))
131 ->orderBy('uid', 'DESC')
134 foreach ($result as $row) {
135 $logData = unserialize($row['log_data']);
136 /** @var RecordHistoryStore $store */
137 $store = GeneralUtility
::makeInstance(
138 RecordHistoryStore
::class,
139 RecordHistoryStore
::USER_BACKEND
,
141 (empty($logData['originalUser']) ?
null : $logData['originalUser']),
144 switch ($row['action']) {
147 $store->addRecord($row['tablename'], $row['recuid'], $logData);
151 $store->deleteRecord($row['tablename'], $row['recuid']);
155 $this->markWizardAsDone();
160 * Check if given field /column in a table exists
162 * @param string $table
163 * @param string $fieldName
166 protected function checkIfFieldInTableExists($table, $fieldName)
168 $tableColumns = GeneralUtility
::makeInstance(ConnectionPool
::class)
169 ->getConnectionForTable($table)
171 ->listTableColumns($table);
173 return isset($tableColumns[$fieldName]);