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