Merge pull request #18434 from agh1/titlesingular
[civicrm-core.git] / CRM / Logging / ReportDetail.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 *
14 * @package CRM
ca5cec67 15 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
16 */
17class CRM_Logging_ReportDetail extends CRM_Report_Form {
18 protected $cid;
549cd4ca 19
20 /**
21 * Other contact ID.
22 *
23 * This would be set if we are viewing a merge of 2 contacts.
24 *
25 * @var int
26 */
27 protected $oid;
6a488035
TO
28 protected $db;
29 protected $log_conn_id;
30 protected $log_date;
31 protected $raw;
be2fb01f 32 protected $tables = [];
6a488035
TO
33 protected $interval = '10 SECOND';
34
02e02ac5
DS
35 protected $altered_name;
36 protected $altered_by;
37 protected $altered_by_id;
38
971e129b
SL
39 /**
40 * detail/summary report ids
41 * @var int
42 */
6a488035
TO
43 protected $detail;
44 protected $summary;
45
aa00132e 46 /**
47 * Instance of Differ.
48 *
49 * @var CRM_Logging_Differ
50 */
51 protected $differ;
52
53 /**
54 * Array of changes made.
55 *
56 * @var array
57 */
be2fb01f 58 protected $diffs = [];
aa00132e 59
e0ef6999 60 /**
e480ef09 61 * Don't display the Add these contacts to Group button.
62 *
63 * @var bool
64 */
65 protected $_add2groupSupported = FALSE;
66
67 /**
68 * Class constructor.
e0ef6999 69 */
00be9182 70 public function __construct() {
6a488035 71
e480ef09 72 $this->storeDB();
6a488035 73
3b45d110 74 $this->parsePropertiesFromUrl();
e0ef6999 75
6a488035
TO
76 parent::__construct();
77
78 CRM_Utils_System::resetBreadCrumb();
be2fb01f
CW
79 $breadcrumb = [
80 [
bed98343 81 'title' => ts('Home'),
82 'url' => CRM_Utils_System::url(),
be2fb01f
CW
83 ],
84 [
bed98343 85 'title' => ts('CiviCRM'),
86 'url' => CRM_Utils_System::url('civicrm', 'reset=1'),
be2fb01f
CW
87 ],
88 [
bed98343 89 'title' => ts('View Contact'),
90 'url' => CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$this->cid}"),
be2fb01f
CW
91 ],
92 [
bed98343 93 'title' => ts('Search Results'),
94 'url' => CRM_Utils_System::url('civicrm/contact/search', "force=1"),
be2fb01f
CW
95 ],
96 ];
6a488035
TO
97 CRM_Utils_System::appendBreadCrumb($breadcrumb);
98
e480ef09 99 if (CRM_Utils_Request::retrieve('revert', 'Boolean')) {
100 $this->revert();
6a488035
TO
101 }
102
be2fb01f
CW
103 $this->_columnHeaders = [
104 'field' => ['title' => ts('Field')],
105 'from' => ['title' => ts('Changed From')],
106 'to' => ['title' => ts('Changed To')],
107 ];
6a488035
TO
108 }
109
e0ef6999 110 /**
aa00132e 111 * Build query for report.
112 *
113 * We override this to be empty & calculate the rows in the buildRows function.
114 *
e0ef6999
EM
115 * @param bool $applyLimit
116 */
6ea503d4
TO
117 public function buildQuery($applyLimit = TRUE) {
118 }
6a488035 119
e0ef6999 120 /**
549cd4ca 121 * Build rows from query.
122 *
123 * @param string $sql
124 * @param array $rows
e0ef6999 125 */
00be9182 126 public function buildRows($sql, &$rows) {
6a488035 127 // safeguard for when there aren’t any log entries yet
10b32ed4 128 if (!$this->log_conn_id && !$this->log_date) {
6a488035
TO
129 return;
130 }
aa00132e 131 $this->getDiffs();
132 $rows = $this->convertDiffsToRows();
133 }
6a488035 134
aa00132e 135 /**
136 * Get the diffs for the report, calculating them if not already done.
137 *
138 * Note that contact details report now uses a more comprehensive method but
139 * the contribution logging details report still uses this.
140 *
141 * @return array
142 */
143 protected function getDiffs() {
144 if (empty($this->diffs)) {
145 foreach ($this->tables as $table) {
146 $this->diffs = array_merge($this->diffs, $this->diffsInTable($table));
147 }
6a488035 148 }
aa00132e 149 return $this->diffs;
6a488035
TO
150 }
151
e0ef6999
EM
152 /**
153 * @param $table
154 *
155 * @return array
156 */
6a488035 157 protected function diffsInTable($table) {
aa00132e 158 $this->setDiffer();
159 return $this->differ->diffsInTable($table, $this->cid);
160 }
6a488035 161
aa00132e 162 /**
163 * Convert the diffs to row format.
164 *
165 * @return array
166 */
167 protected function convertDiffsToRows() {
6a488035 168 // return early if nothing found
aa00132e 169 if (empty($this->diffs)) {
be2fb01f 170 return [];
6a488035
TO
171 }
172
6a488035 173 // populate $rows with only the differences between $changed and $original (skipping certain columns and NULL ↔ empty changes unless raw requested)
be2fb01f 174 $skipped = ['id'];
aa00132e 175 foreach ($this->diffs as $diff) {
176 $table = $diff['table'];
177 if (empty($metadata[$table])) {
10b32ed4 178 list($metadata[$table]['titles'], $metadata[$table]['values']) = $this->differ->titlesAndValuesForTable($table, $diff['log_date']);
aa00132e 179 }
be2fb01f 180 $values = CRM_Utils_Array::value('values', $metadata[$diff['table']], []);
aa00132e 181 $titles = $metadata[$diff['table']]['titles'];
6a488035 182 $field = $diff['field'];
353ffa53
TO
183 $from = $diff['from'];
184 $to = $diff['to'];
6a488035
TO
185
186 if ($this->raw) {
187 $field = "$table.$field";
188 }
189 else {
190 if (in_array($field, $skipped)) {
191 continue;
192 }
193 // $differ filters out === values; for presentation hide changes like 42 → '42'
194 if ($from == $to) {
195 continue;
196 }
197
40554aa3 198 // special-case for multiple values. Also works for CRM-7251: preferred_communication_method
e0ef6999
EM
199 if ((substr($from, 0, 1) == CRM_Core_DAO::VALUE_SEPARATOR &&
200 substr($from, -1, 1) == CRM_Core_DAO::VALUE_SEPARATOR) ||
201 (substr($to, 0, 1) == CRM_Core_DAO::VALUE_SEPARATOR &&
353ffa53
TO
202 substr($to, -1, 1) == CRM_Core_DAO::VALUE_SEPARATOR)
203 ) {
be2fb01f 204 $froms = $tos = [];
40554aa3 205 foreach (explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($from, CRM_Core_DAO::VALUE_SEPARATOR)) as $val) {
9c1bc317 206 $froms[] = $values[$field][$val] ?? NULL;
40554aa3
DS
207 }
208 foreach (explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($to, CRM_Core_DAO::VALUE_SEPARATOR)) as $val) {
9c1bc317 209 $tos[] = $values[$field][$val] ?? NULL;
40554aa3 210 }
6a488035 211 $from = implode(', ', array_filter($froms));
353ffa53 212 $to = implode(', ', array_filter($tos));
6a488035
TO
213 }
214
215 if (isset($values[$field][$from])) {
6a488035 216 $from = $values[$field][$from];
6a488035
TO
217 }
218 if (isset($values[$field][$to])) {
219 $to = $values[$field][$to];
220 }
221 if (isset($titles[$field])) {
222 $field = $titles[$field];
223 }
224 if ($diff['action'] == 'Insert') {
225 $from = '';
226 }
227 if ($diff['action'] == 'Delete') {
228 $to = '';
229 }
230 }
231
be2fb01f 232 $rows[] = ['field' => $field . " (id: {$diff['id']})", 'from' => $from, 'to' => $to];
6a488035
TO
233 }
234
235 return $rows;
236 }
237
00be9182 238 public function buildQuickForm() {
6a488035
TO
239 parent::buildQuickForm();
240
02e02ac5 241 $this->assign('whom_url', CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$this->cid}"));
353ffa53 242 $this->assign('who_url', CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$this->altered_by_id}"));
02e02ac5 243 $this->assign('whom_name', $this->altered_name);
353ffa53 244 $this->assign('who_name', $this->altered_by);
02e02ac5 245
6a488035
TO
246 $this->assign('log_date', CRM_Utils_Date::mysqlToIso($this->log_date));
247
248 $q = "reset=1&log_conn_id={$this->log_conn_id}&log_date={$this->log_date}";
549cd4ca 249 if ($this->oid) {
250 $q .= '&oid=' . $this->oid;
251 }
6a488035 252 $this->assign('revertURL', CRM_Report_Utils_Report::getNextUrl($this->detail, "$q&revert=1", FALSE, TRUE));
7266e09b 253 $this->assign('revertConfirm', ts('Are you sure you want to revert all changes?'));
6a488035 254 }
96025800 255
e480ef09 256 /**
257 * Store the dsn for the logging database in $this->db.
258 */
259 protected function storeDB() {
58d1e21e
SL
260 $dsn = defined('CIVICRM_LOGGING_DSN') ? CRM_Utils_SQL::autoSwitchDSN(CIVICRM_LOGGING_DSN) : CRM_Utils_SQL::autoSwitchDSN(CIVICRM_DSN);
261 $dsn = DB::parseDSN($dsn);
e480ef09 262 $this->db = $dsn['database'];
263 }
264
aa00132e 265 /**
266 * Calculate all the contact related diffs for the change.
aa00132e 267 */
549cd4ca 268 protected function calculateContactDiffs() {
aa00132e 269 $this->diffs = $this->getAllContactChangesForConnection();
270 }
271
aa00132e 272 /**
273 * Get an array of changes made in the mysql connection.
274 *
275 * @return mixed
276 */
277 public function getAllContactChangesForConnection() {
278 if (empty($this->log_conn_id)) {
be2fb01f 279 return [];
aa00132e 280 }
281 $this->setDiffer();
282 try {
283 return $this->differ->getAllChangesForConnection($this->tables);
284 }
285 catch (CRM_Core_Exception $e) {
286 CRM_Core_Error::statusBounce(ts($e->getMessage()));
287 }
288 }
289
290 /**
291 * Make sure the differ is defined.
292 */
293 protected function setDiffer() {
294 if (empty($this->differ)) {
295 $this->differ = new CRM_Logging_Differ($this->log_conn_id, $this->log_date, $this->interval);
296 }
297 }
298
299 /**
300 * Set this tables to reflect tables changed in a merge.
301 */
302 protected function setTablesToContactRelatedTables() {
303 $schema = new CRM_Logging_Schema();
304 $this->tables = $schema->getLogTablesForContact();
305 // allow tables to be extended by report hook query objects.
306 // This is a report specific hook. It's unclear how it interacts to / overlaps the main one.
307 // It probably precedes the main one and was never reconciled with it....
308 CRM_Report_BAO_Hook::singleton()->alterLogTables($this, $this->tables);
309 }
310
e480ef09 311 /**
312 * Revert the changes defined by the parameters.
313 */
314 protected function revert() {
315 $reverter = new CRM_Logging_Reverter($this->log_conn_id, $this->log_date);
93afbc3a 316 $reverter->calculateDiffsFromLogConnAndDate($this->tables);
317 $reverter->revert();
e480ef09 318 CRM_Core_Session::setStatus(ts('The changes have been reverted.'), ts('Reverted'), 'success');
319 if ($this->cid) {
549cd4ca 320 if ($this->oid) {
321 CRM_Utils_System::redirect(CRM_Utils_System::url(
322 'civicrm/contact/merge',
323 "reset=1&cid={$this->cid}&oid={$this->oid}",
324 FALSE,
325 NULL,
326 FALSE)
327 );
328 }
329 else {
330 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/view', "reset=1&selectedChild=log&cid={$this->cid}", FALSE, NULL, FALSE));
331 }
e480ef09 332 }
333 else {
334 CRM_Utils_System::redirect(CRM_Report_Utils_Report::getNextUrl($this->summary, 'reset=1', FALSE, TRUE));
335 }
336 }
337
3b45d110 338 /**
339 * Get the properties that might be in the URL.
340 */
341 protected function parsePropertiesFromUrl() {
a3d827a7
CW
342 $this->log_conn_id = CRM_Utils_Request::retrieve('log_conn_id', 'String');
343 $this->log_date = CRM_Utils_Request::retrieve('log_date', 'String');
344 $this->cid = CRM_Utils_Request::retrieve('cid', 'Integer');
345 $this->raw = CRM_Utils_Request::retrieve('raw', 'Boolean');
346
347 $this->altered_name = CRM_Utils_Request::retrieve('alteredName', 'String');
348 $this->altered_by = CRM_Utils_Request::retrieve('alteredBy', 'String');
349 $this->altered_by_id = CRM_Utils_Request::retrieve('alteredById', 'Integer');
3b45d110 350 }
351
6a488035 352}