[REF] Update fetchAll function signature to match parent function
[civicrm-core.git] / CRM / Logging / Reverter.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
10 */
11
12 /**
13 *
14 * @package CRM
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
16 */
17 class CRM_Logging_Reverter {
18 private $db;
19 private $log_conn_id;
20 private $log_date;
21
22 /**
23 * The diffs to be reverted.
24 *
25 * @var array
26 */
27 private $diffs = [];
28
29 /**
30 * Class constructor.
31 *
32 * @param string $log_conn_id
33 * @param $log_date
34 */
35 public function __construct($log_conn_id, $log_date) {
36 $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN);
37 $this->db = $dsn['database'];
38 $this->log_conn_id = $log_conn_id;
39 $this->log_date = $log_date;
40 }
41
42 /**
43 *
44 * Calculate a set of diffs based on the connection_id and changes at a close time.
45 *
46 * @param array $tables
47 */
48 public function calculateDiffsFromLogConnAndDate($tables) {
49 $differ = new CRM_Logging_Differ($this->log_conn_id, $this->log_date);
50 $this->diffs = $differ->diffsInTables($tables);
51 }
52
53 /**
54 * Setter for diffs.
55 *
56 * @param array $diffs
57 */
58 public function setDiffs($diffs) {
59 $this->diffs = $diffs;
60 }
61
62 /**
63 * Revert changes in the array of diffs in $this->diffs.
64 */
65 public function revert() {
66
67 // get custom data tables, columns and types
68 $ctypes = [];
69 $dao = CRM_Core_DAO::executeQuery('SELECT table_name, column_name, data_type FROM civicrm_custom_group cg JOIN civicrm_custom_field cf ON (cf.custom_group_id = cg.id)');
70 while ($dao->fetch()) {
71 if (!isset($ctypes[$dao->table_name])) {
72 $ctypes[$dao->table_name] = ['entity_id' => 'Integer'];
73 }
74 $ctypes[$dao->table_name][$dao->column_name] = $dao->data_type;
75 }
76
77 $diffs = $this->diffs;
78 $deletes = [];
79 $reverts = [];
80 foreach ($diffs as $table => $changes) {
81 foreach ($changes as $change) {
82 switch ($change['action']) {
83 case 'Insert':
84 if (!isset($deletes[$table])) {
85 $deletes[$table] = [];
86 }
87 $deletes[$table][] = $change['id'];
88 break;
89
90 case 'Delete':
91 case 'Update':
92 if (!isset($reverts[$table])) {
93 $reverts[$table] = [];
94 }
95 if (!isset($reverts[$table][$change['id']])) {
96 $reverts[$table][$change['id']] = ['log_action' => $change['action']];
97 }
98 $reverts[$table][$change['id']][$change['field']] = $change['from'];
99 break;
100 }
101 }
102 }
103
104 // revert inserts by deleting
105 foreach ($deletes as $table => $ids) {
106 CRM_Core_DAO::executeQuery("DELETE FROM `$table` WHERE id IN (" . implode(', ', array_unique($ids)) . ')');
107 }
108
109 // revert updates by updating to previous values
110 foreach ($reverts as $table => $row) {
111 switch (TRUE) {
112 // DAO-based tables
113
114 case (($tableDAO = CRM_Core_DAO_AllCoreTables::getClassForTable($table)) != FALSE):
115 $dao = new $tableDAO();
116 foreach ($row as $id => $changes) {
117 $dao->id = $id;
118 foreach ($changes as $field => $value) {
119 if ($field == 'log_action') {
120 continue;
121 }
122 if (empty($value) and $value !== 0 and $value !== '0') {
123 $value = 'null';
124 }
125 // Date reaches this point in ISO format (possibly) so strip out stuff
126 // if it does have hyphens of colons demarking the date & it regexes as being a date
127 // or datetime format.
128 if (preg_match('/(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})/', $value)) {
129 $value = str_replace('-', '', $value);
130 $value = str_replace(':', '', $value);
131 }
132 $dao->$field = $value;
133 }
134 $changes['log_action'] == 'Delete' ? $dao->insert() : $dao->update();
135
136 $dao->reset();
137 }
138 break;
139
140 // custom data tables
141
142 case in_array($table, array_keys($ctypes)):
143 foreach ($row as $id => $changes) {
144 $inserts = ['id' => '%1'];
145 $updates = [];
146 $params = [1 => [$id, 'Integer']];
147 $counter = 2;
148 foreach ($changes as $field => $value) {
149 // don’t try reverting a field that’s no longer there
150 if (!isset($ctypes[$table][$field])) {
151 continue;
152 }
153 $fldVal = "%{$counter}";
154 switch ($ctypes[$table][$field]) {
155 case 'Date':
156 $value = substr(CRM_Utils_Date::isoToMysql($value), 0, 8);
157 break;
158
159 case 'Timestamp':
160 $value = CRM_Utils_Date::isoToMysql($value);
161 break;
162
163 case 'Boolean':
164 if ($value === '') {
165 $fldVal = 'DEFAULT';
166 }
167 }
168 $inserts[$field] = "%$counter";
169 $updates[] = "{$field} = {$fldVal}";
170 if ($fldVal != 'DEFAULT') {
171 $params[$counter] = [$value, $ctypes[$table][$field]];
172 }
173 $counter++;
174 }
175 if ($changes['log_action'] == 'Delete') {
176 $sql = "INSERT INTO `$table` (" . implode(', ', array_keys($inserts)) . ') VALUES (' . implode(', ', $inserts) . ')';
177 }
178 else {
179 $sql = "UPDATE `$table` SET " . implode(', ', $updates) . ' WHERE id = %1';
180 }
181 CRM_Core_DAO::executeQuery($sql, $params);
182 }
183 break;
184 }
185 }
186
187 }
188
189 }