Commit | Line | Data |
---|---|---|
6a488035 | 1 | <?php |
6a488035 TO |
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 | */ |
17 | class CRM_Report_Form_Contact_LoggingSummary extends CRM_Logging_ReportSummary { | |
63dc1f23 | 18 | |
19 | public $optimisedForOnlyFullGroupBy = FALSE; | |
c86d4e7c | 20 | |
74cf4551 | 21 | /** |
2fb1dd66 | 22 | * Class constructor. |
74cf4551 | 23 | */ |
00be9182 | 24 | public function __construct() { |
6a488035 TO |
25 | parent::__construct(); |
26 | ||
be2fb01f | 27 | $logTypes = []; |
481a74f4 | 28 | foreach (array_keys($this->_logTables) as $table) { |
6a488035 TO |
29 | $type = $this->getLogType($table); |
30 | $logTypes[$type] = $type; | |
31 | } | |
32 | asort($logTypes); | |
33 | ||
be2fb01f CW |
34 | $this->_columns = [ |
35 | 'log_civicrm_entity' => [ | |
6a488035 TO |
36 | 'dao' => 'CRM_Contact_DAO_Contact', |
37 | 'alias' => 'entity_log', | |
be2fb01f CW |
38 | 'fields' => [ |
39 | 'id' => [ | |
6a488035 TO |
40 | 'no_display' => TRUE, |
41 | 'required' => TRUE, | |
be2fb01f CW |
42 | ], |
43 | 'log_grouping' => [ | |
b94abe71 | 44 | 'required' => TRUE, |
45 | 'title' => ts('Extra information to control grouping'), | |
46 | 'no_display' => TRUE, | |
be2fb01f CW |
47 | ], |
48 | 'log_action' => [ | |
6a488035 TO |
49 | 'default' => TRUE, |
50 | 'title' => ts('Action'), | |
be2fb01f CW |
51 | ], |
52 | 'log_type' => [ | |
6a488035 TO |
53 | 'required' => TRUE, |
54 | 'title' => ts('Log Type'), | |
be2fb01f CW |
55 | ], |
56 | 'log_user_id' => [ | |
6a488035 TO |
57 | 'no_display' => TRUE, |
58 | 'required' => TRUE, | |
be2fb01f CW |
59 | ], |
60 | 'log_date' => [ | |
6a488035 TO |
61 | 'default' => TRUE, |
62 | 'required' => TRUE, | |
63 | 'type' => CRM_Utils_Type::T_TIME, | |
64 | 'title' => ts('When'), | |
be2fb01f CW |
65 | ], |
66 | 'altered_contact' => [ | |
6a488035 TO |
67 | 'default' => TRUE, |
68 | 'name' => 'display_name', | |
69 | 'title' => ts('Altered Contact'), | |
70 | 'alias' => 'modified_contact_civireport', | |
be2fb01f CW |
71 | ], |
72 | 'altered_contact_id' => [ | |
6a488035 TO |
73 | 'name' => 'id', |
74 | 'no_display' => TRUE, | |
75 | 'required' => TRUE, | |
353ffa53 | 76 | 'alias' => 'modified_contact_civireport', |
be2fb01f CW |
77 | ], |
78 | 'log_conn_id' => [ | |
6a488035 TO |
79 | 'no_display' => TRUE, |
80 | 'required' => TRUE, | |
be2fb01f CW |
81 | ], |
82 | 'is_deleted' => [ | |
6a488035 TO |
83 | 'no_display' => TRUE, |
84 | 'required' => TRUE, | |
85 | 'alias' => 'modified_contact_civireport', | |
be2fb01f CW |
86 | ], |
87 | ], | |
88 | 'filters' => [ | |
89 | 'log_date' => [ | |
6a488035 TO |
90 | 'title' => ts('When'), |
91 | 'operatorType' => CRM_Report_Form::OP_DATE, | |
92 | 'type' => CRM_Utils_Type::T_DATE, | |
be2fb01f CW |
93 | ], |
94 | 'altered_contact' => [ | |
6a488035 TO |
95 | 'name' => 'display_name', |
96 | 'title' => ts('Altered Contact'), | |
97 | 'type' => CRM_Utils_Type::T_STRING, | |
98 | 'alias' => 'modified_contact_civireport', | |
be2fb01f CW |
99 | ], |
100 | 'altered_contact_id' => [ | |
6a488035 TO |
101 | 'name' => 'id', |
102 | 'type' => CRM_Utils_Type::T_INT, | |
103 | 'alias' => 'modified_contact_civireport', | |
104 | 'no_display' => TRUE, | |
be2fb01f CW |
105 | ], |
106 | 'log_type' => [ | |
6a488035 TO |
107 | 'operatorType' => CRM_Report_Form::OP_MULTISELECT, |
108 | 'options' => $logTypes, | |
109 | 'title' => ts('Log Type'), | |
110 | 'type' => CRM_Utils_Type::T_STRING, | |
be2fb01f CW |
111 | ], |
112 | 'log_type_table' => [ | |
353ffa53 | 113 | 'name' => 'log_type', |
798878cc DS |
114 | 'title' => ts('Log Type Table'), |
115 | 'type' => CRM_Utils_Type::T_STRING, | |
be2fb01f CW |
116 | ], |
117 | 'log_action' => [ | |
6a488035 | 118 | 'operatorType' => CRM_Report_Form::OP_MULTISELECT, |
be2fb01f | 119 | 'options' => [ |
2fb1dd66 EM |
120 | 'Insert' => ts('Insert'), |
121 | 'Update' => ts('Update'), | |
122 | 'Delete' => ts('Delete'), | |
be2fb01f | 123 | ], |
6a488035 TO |
124 | 'title' => ts('Action'), |
125 | 'type' => CRM_Utils_Type::T_STRING, | |
be2fb01f CW |
126 | ], |
127 | 'id' => [ | |
6a488035 TO |
128 | 'no_display' => TRUE, |
129 | 'type' => CRM_Utils_Type::T_INT, | |
be2fb01f CW |
130 | ], |
131 | ], | |
132 | 'order_bys' => [ | |
133 | 'log_date' => [ | |
e85aa549 | 134 | 'title' => ts('Log Date (When)'), |
135 | 'default' => TRUE, | |
136 | 'default_weight' => '0', | |
137 | 'default_order' => 'DESC', | |
be2fb01f CW |
138 | ], |
139 | 'altered_contact' => [ | |
e85aa549 | 140 | 'name' => 'display_name', |
141 | 'title' => ts('Altered Contact'), | |
142 | 'alias' => 'modified_contact_civireport', | |
be2fb01f CW |
143 | ], |
144 | ], | |
145 | ], | |
146 | 'altered_by_contact' => [ | |
353ffa53 | 147 | 'dao' => 'CRM_Contact_DAO_Contact', |
6a488035 | 148 | 'alias' => 'altered_by_contact', |
be2fb01f CW |
149 | 'fields' => [ |
150 | 'display_name' => [ | |
6a488035 TO |
151 | 'default' => TRUE, |
152 | 'name' => 'display_name', | |
153 | 'title' => ts('Altered By'), | |
be2fb01f CW |
154 | ], |
155 | ], | |
156 | 'filters' => [ | |
157 | 'display_name' => [ | |
6a488035 TO |
158 | 'name' => 'display_name', |
159 | 'title' => ts('Altered By'), | |
160 | 'type' => CRM_Utils_Type::T_STRING, | |
be2fb01f CW |
161 | ], |
162 | ], | |
163 | 'order_bys' => [ | |
164 | 'altered_by_contact' => [ | |
e85aa549 | 165 | 'name' => 'display_name', |
166 | 'title' => ts('Altered by'), | |
be2fb01f CW |
167 | ], |
168 | ], | |
169 | ], | |
170 | ]; | |
6a488035 TO |
171 | } |
172 | ||
74cf4551 | 173 | /** |
ced9bfed EM |
174 | * Alter display of rows. |
175 | * | |
176 | * Iterate through the rows retrieved via SQL and make changes for display purposes, | |
177 | * such as rendering contacts as links. | |
2fb1dd66 EM |
178 | * |
179 | * @param array $rows | |
ced9bfed | 180 | * Rows generated by SQL, with an array for each row. |
cb22e5dc | 181 | * |
182 | * @throws \CRM_Core_Exception | |
183 | * @throws \CiviCRM_API3_Exception | |
74cf4551 | 184 | */ |
00be9182 | 185 | public function alterDisplay(&$rows) { |
6a488035 | 186 | // cache for id → is_deleted mapping |
be2fb01f CW |
187 | $isDeleted = []; |
188 | $newRows = []; | |
6a488035 TO |
189 | |
190 | foreach ($rows as $key => &$row) { | |
b94abe71 | 191 | $isMerge = 0; |
9bf49a0e | 192 | $baseQueryCriteria = "reset=1&log_conn_id={$row['log_civicrm_entity_log_conn_id']}"; |
193 | if (!CRM_Logging_Differ::checkLogCanBeUsedWithNoLogDate($row['log_civicrm_entity_log_date'])) { | |
194 | $baseQueryCriteria .= '&log_date=' . CRM_Utils_Date::isoToMysql($row['log_civicrm_entity_log_date']); | |
195 | } | |
196 | if ($this->cid) { | |
197 | $baseQueryCriteria .= '&cid=' . $this->cid; | |
198 | } | |
6a488035 | 199 | if (!isset($isDeleted[$row['log_civicrm_entity_altered_contact_id']])) { |
3bdca100 | 200 | $isDeleted[$row['log_civicrm_entity_altered_contact_id']] = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', |
201 | $row['log_civicrm_entity_altered_contact_id'], 'is_deleted') !== '0'; | |
6a488035 TO |
202 | } |
203 | ||
a7488080 | 204 | if (!empty($row['log_civicrm_entity_altered_contact']) && |
353ffa53 TO |
205 | !$isDeleted[$row['log_civicrm_entity_altered_contact_id']] |
206 | ) { | |
3bdca100 | 207 | $row['log_civicrm_entity_altered_contact_link'] = CRM_Utils_System::url('civicrm/contact/view', |
208 | 'reset=1&cid=' . $row['log_civicrm_entity_altered_contact_id']); | |
6a488035 TO |
209 | $row['log_civicrm_entity_altered_contact_hover'] = ts("Go to contact summary"); |
210 | $entity = $this->getEntityValue($row['log_civicrm_entity_id'], $row['log_civicrm_entity_log_type'], $row['log_civicrm_entity_log_date']); | |
84178120 | 211 | if ($entity) { |
cb22e5dc | 212 | $row['log_civicrm_entity_altered_contact'] .= " [{$entity}]"; |
84178120 | 213 | } |
cb22e5dc | 214 | if ($entity === 'Contact Merged') { |
221fa9c7 EE |
215 | // We're looking at a merge activity created against the surviving |
216 | // contact record. There should be a single activity created against | |
217 | // the deleted contact record, with this activity as parent. | |
549cd4ca | 218 | $deletedID = CRM_Core_DAO::singleValueQuery(' |
219 | SELECT GROUP_CONCAT(contact_id) FROM civicrm_activity_contact ac | |
220 | INNER JOIN civicrm_activity a | |
221 | ON a.id = ac.activity_id AND a.parent_id = ' . $row['log_civicrm_entity_id'] . ' AND ac.record_type_id = | |
222 | ' . CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_ActivityContact', 'record_type_id', 'Activity Targets') | |
223 | ); | |
224 | if ($deletedID && !stristr($deletedID, ',')) { | |
225 | $baseQueryCriteria .= '&oid=' . $deletedID; | |
226 | } | |
227 | $row['log_civicrm_entity_log_action'] = ts('Contact Merge'); | |
228 | $row = $this->addDetailReportLinksToRow($baseQueryCriteria, $row); | |
b94abe71 | 229 | $isMerge = 1; |
230 | } | |
231 | ||
6a488035 TO |
232 | } |
233 | $row['altered_by_contact_display_name_link'] = CRM_Utils_System::url('civicrm/contact/view', 'reset=1&cid=' . $row['log_civicrm_entity_log_user_id']); | |
cb22e5dc | 234 | $row['altered_by_contact_display_name_hover'] = ts('Go to contact summary'); |
6a488035 | 235 | |
cb22e5dc | 236 | if ($row['log_civicrm_entity_is_deleted'] && 'Update' === $row['log_civicrm_entity_log_action']) { |
6a488035 TO |
237 | $row['log_civicrm_entity_log_action'] = ts('Delete (to trash)'); |
238 | } | |
239 | ||
cb22e5dc | 240 | if ('Contact' === ($this->_logTables[$row['log_civicrm_entity_log_type']]['log_type'] ?? NULL) && |
241 | $row['log_civicrm_entity_log_action'] === ts('Insert') | |
353ffa53 | 242 | ) { |
6a488035 TO |
243 | $row['log_civicrm_entity_log_action'] = ts('Update'); |
244 | } | |
245 | ||
221fa9c7 EE |
246 | // For certain tables, we may want to look at an alternate column to |
247 | // determine which action to display, determined by the 'action_column' | |
248 | // key of the entry in $this->_logTables. | |
f813f78e | 249 | if ($newAction = $this->getEntityAction($row['log_civicrm_entity_id'], |
353ffa53 TO |
250 | $row['log_civicrm_entity_log_conn_id'], |
251 | $row['log_civicrm_entity_log_type'], | |
cb22e5dc | 252 | ($row['log_civicrm_entity_log_action'] ?? NULL)) |
353ffa53 | 253 | ) { |
6a488035 | 254 | $row['log_civicrm_entity_log_action'] = $newAction; |
84178120 | 255 | } |
6a488035 TO |
256 | |
257 | $row['log_civicrm_entity_log_type'] = $this->getLogType($row['log_civicrm_entity_log_type']); | |
258 | ||
259 | $date = CRM_Utils_Date::isoToMysql($row['log_civicrm_entity_log_date']); | |
260 | ||
cb22e5dc | 261 | if (in_array(($row['log_civicrm_entity_log_action'] ?? NULL), ['Update', 'Delete'])) { |
9bf49a0e | 262 | $row = $this->addDetailReportLinksToRow($baseQueryCriteria, $row); |
6a488035 TO |
263 | } |
264 | ||
221fa9c7 EE |
265 | // In the summary, we only want to show one row per entity type, |
266 | // connection ID, contact ID, and user ID, rolling up multiple | |
267 | // related actions against the same entity. | |
353ffa53 | 268 | $key = $date . '_' . |
f813f78e | 269 | $row['log_civicrm_entity_log_type'] . '_' . |
b94abe71 | 270 | // This ensures merge activities are not 'lost' by aggregation. |
271 | // I would prefer not to lose other entities either but it's a balancing act as | |
272 | // described in https://issues.civicrm.org/jira/browse/CRM-12867 so adding this criteria | |
273 | // while hackish saves us from figuring out if the original decision is still good. | |
221fa9c7 | 274 | $isMerge . '_' . |
f813f78e | 275 | $row['log_civicrm_entity_log_conn_id'] . '_' . |
6a488035 | 276 | $row['log_civicrm_entity_log_user_id'] . '_' . |
9a2a98cd | 277 | $row['log_civicrm_entity_altered_contact_id']; |
6a488035 TO |
278 | $newRows[$key] = $row; |
279 | ||
280 | unset($row['log_civicrm_entity_log_user_id']); | |
281 | unset($row['log_civicrm_entity_log_conn_id']); | |
282 | } | |
283 | ||
284 | krsort($newRows); | |
285 | $rows = $newRows; | |
286 | } | |
287 | ||
74cf4551 | 288 | /** |
2fb1dd66 | 289 | * Generate From Clause. |
74cf4551 | 290 | */ |
41c9c9e9 | 291 | public function from() { |
63dc1f23 | 292 | if (!$this->currentLogTable) { |
293 | // From has already been built in this case. | |
294 | return; | |
295 | } | |
41c9c9e9 | 296 | $entity = $this->currentLogTable; |
6a488035 | 297 | |
353ffa53 | 298 | $detail = $this->_logTables[$entity]; |
cb22e5dc | 299 | $tableName = $detail['table_name'] ?? $entity; |
9c1bc317 | 300 | $clause = $detail['entity_table'] ?? NULL; |
84178120 | 301 | $clause = $clause ? "AND entity_log_civireport.entity_table = 'civicrm_contact'" : NULL; |
f813f78e | 302 | |
6a488035 | 303 | $joinClause = " |
f813f78e | 304 | INNER JOIN civicrm_contact modified_contact_civireport |
6a488035 TO |
305 | ON (entity_log_civireport.{$detail['fk']} = modified_contact_civireport.id {$clause})"; |
306 | ||
a7488080 | 307 | if (!empty($detail['joins'])) { |
9c1bc317 | 308 | $clause = $detail['entity_table'] ?? NULL; |
84178120 | 309 | $clause = $clause ? "AND fk_table.entity_table = 'civicrm_contact'" : NULL; |
6a488035 TO |
310 | $joinClause = " |
311 | INNER JOIN `{$this->loggingDB}`.{$detail['joins']['table']} fk_table ON {$detail['joins']['join']} | |
f813f78e | 312 | INNER JOIN civicrm_contact modified_contact_civireport |
6a488035 TO |
313 | ON (fk_table.{$detail['fk']} = modified_contact_civireport.id {$clause})"; |
314 | } | |
315 | ||
b94abe71 | 316 | if (!empty($detail['extra_joins'])) { |
317 | $joinClause .= " | |
318 | INNER JOIN `{$this->loggingDB}`.{$detail['extra_joins']['table']} extra_table ON {$detail['extra_joins']['join']}"; | |
319 | } | |
320 | ||
6a488035 TO |
321 | $this->_from = " |
322 | FROM `{$this->loggingDB}`.$tableName entity_log_civireport | |
323 | {$joinClause} | |
f813f78e | 324 | LEFT JOIN civicrm_contact altered_by_contact_civireport |
6a488035 TO |
325 | ON (entity_log_civireport.log_user_id = altered_by_contact_civireport.id)"; |
326 | } | |
96025800 | 327 | |
9bf49a0e | 328 | /** |
329 | * Add links & hovers to the detailed report. | |
330 | * | |
331 | * @param $baseQueryCriteria | |
332 | * @param $row | |
333 | * | |
334 | * @return mixed | |
335 | */ | |
336 | protected function addDetailReportLinksToRow($baseQueryCriteria, $row) { | |
337 | $q = $baseQueryCriteria; | |
338 | $q .= (!empty($row['log_civicrm_entity_altered_contact'])) ? '&alteredName=' . $row['log_civicrm_entity_altered_contact'] : ''; | |
cb22e5dc | 339 | $q .= (!empty($row['log_civicrm_entity_altered_contact_id'])) ? '&cid=' . $row['log_civicrm_entity_altered_contact_id'] : ''; |
9bf49a0e | 340 | $q .= (!empty($row['altered_by_contact_display_name'])) ? '&alteredBy=' . $row['altered_by_contact_display_name'] : ''; |
341 | $q .= (!empty($row['log_civicrm_entity_log_user_id'])) ? '&alteredById=' . $row['log_civicrm_entity_log_user_id'] : ''; | |
342 | ||
343 | $url1 = CRM_Report_Utils_Report::getNextUrl('logging/contact/detail', "{$q}&snippet=4§ion=2&layout=overlay", FALSE, TRUE); | |
344 | $url2 = CRM_Report_Utils_Report::getNextUrl('logging/contact/detail', "{$q}§ion=2", FALSE, TRUE); | |
345 | $hoverTitle = ts('View details for this update'); | |
346 | $row['log_civicrm_entity_log_action'] = "<a href='{$url1}' class='crm-summary-link'><i class=\"crm-i fa-list-alt\"></i></a> <a title='{$hoverTitle}' href='{$url2}'>" . $row['log_civicrm_entity_log_action'] . '</a>'; | |
347 | return $row; | |
348 | } | |
349 | ||
cb75c53a | 350 | /** |
351 | * Calculate section totals. | |
352 | * | |
353 | * Override to do nothing as this does not work / make sense on this report. | |
354 | */ | |
355 | public function sectionTotals() {} | |
356 | ||
6a488035 | 357 | } |