CRM-18104 Hook for hook for defining log tables.
[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 protected $db;
36 protected $log_conn_id;
37 protected $log_date;
38 protected $raw;
39 protected $tables = array();
40 protected $interval = '10 SECOND';
41
42 protected $altered_name;
43 protected $altered_by;
44 protected $altered_by_id;
45
46 // detail/summary report ids
47 protected $detail;
48 protected $summary;
49
50 /**
51 * Don't display the Add these contacts to Group button.
52 *
53 * @var bool
54 */
55 protected $_add2groupSupported = FALSE;
56
57 /**
58 * Class constructor.
59 */
60 public function __construct() {
61
62 $this->storeDB();
63
64 $this->log_conn_id = CRM_Utils_Request::retrieve('log_conn_id', 'Integer', CRM_Core_DAO::$_nullObject);
65 $this->log_date = CRM_Utils_Request::retrieve('log_date', 'String', CRM_Core_DAO::$_nullObject);
66 $this->cid = CRM_Utils_Request::retrieve('cid', 'Integer', CRM_Core_DAO::$_nullObject);
67 $this->raw = CRM_Utils_Request::retrieve('raw', 'Boolean', CRM_Core_DAO::$_nullObject);
68
69 $this->altered_name = CRM_Utils_Request::retrieve('alteredName', 'String', CRM_Core_DAO::$_nullObject);
70 $this->altered_by = CRM_Utils_Request::retrieve('alteredBy', 'String', CRM_Core_DAO::$_nullObject);
71 $this->altered_by_id = CRM_Utils_Request::retrieve('alteredById', 'Integer', CRM_Core_DAO::$_nullObject);
72
73 parent::__construct();
74
75 CRM_Utils_System::resetBreadCrumb();
76 $breadcrumb = array(
77 array(
78 'title' => ts('Home'),
79 'url' => CRM_Utils_System::url(),
80 ),
81 array(
82 'title' => ts('CiviCRM'),
83 'url' => CRM_Utils_System::url('civicrm', 'reset=1'),
84 ),
85 array(
86 'title' => ts('View Contact'),
87 'url' => CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$this->cid}"),
88 ),
89 array(
90 'title' => ts('Search Results'),
91 'url' => CRM_Utils_System::url('civicrm/contact/search', "force=1"),
92 ),
93 );
94 CRM_Utils_System::appendBreadCrumb($breadcrumb);
95
96 if (CRM_Utils_Request::retrieve('revert', 'Boolean')) {
97 $this->revert();
98 }
99
100 // make sure the report works even without the params
101 if (!$this->log_conn_id or !$this->log_date) {
102 $dao = new CRM_Core_DAO();
103 $dao->query("SELECT log_conn_id, log_date FROM `{$this->db}`.log_{$this->tables[0]} WHERE log_action = 'Update' ORDER BY log_date DESC LIMIT 1");
104 $dao->fetch();
105 $this->log_conn_id = $dao->log_conn_id;
106 $this->log_date = $dao->log_date;
107 }
108
109 $this->_columnHeaders = array(
110 'field' => array('title' => ts('Field')),
111 'from' => array('title' => ts('Changed From')),
112 'to' => array('title' => ts('Changed To')),
113 );
114 }
115
116 /**
117 * @param bool $applyLimit
118 */
119 public function buildQuery($applyLimit = TRUE) {
120 }
121
122 /**
123 * @param $sql
124 * @param $rows
125 */
126 public function buildRows($sql, &$rows) {
127 // safeguard for when there aren’t any log entries yet
128 if (!$this->log_conn_id or !$this->log_date) {
129 return;
130 }
131
132 if (empty($rows)) {
133
134 $rows = array();
135
136 }
137
138 foreach ($this->tables as $table) {
139 $rows = array_merge($rows, $this->diffsInTable($table));
140 }
141 }
142
143 /**
144 * @param $table
145 *
146 * @return array
147 */
148 protected function diffsInTable($table) {
149 $rows = array();
150
151 $differ = new CRM_Logging_Differ($this->log_conn_id, $this->log_date, $this->interval);
152 $diffs = $differ->diffsInTable($table, $this->cid);
153
154 // return early if nothing found
155 if (empty($diffs)) {
156 return $rows;
157 }
158
159 list($titles, $values) = $differ->titlesAndValuesForTable($table);
160
161 // populate $rows with only the differences between $changed and $original (skipping certain columns and NULL ↔ empty changes unless raw requested)
162 $skipped = array('contact_id', 'entity_id', 'id');
163 foreach ($diffs as $diff) {
164 $field = $diff['field'];
165 $from = $diff['from'];
166 $to = $diff['to'];
167
168 if ($this->raw) {
169 $field = "$table.$field";
170 }
171 else {
172 if (in_array($field, $skipped)) {
173 continue;
174 }
175 // $differ filters out === values; for presentation hide changes like 42 → '42'
176 if ($from == $to) {
177 continue;
178 }
179
180 // special-case for multiple values. Also works for CRM-7251: preferred_communication_method
181 if ((substr($from, 0, 1) == CRM_Core_DAO::VALUE_SEPARATOR &&
182 substr($from, -1, 1) == CRM_Core_DAO::VALUE_SEPARATOR) ||
183 (substr($to, 0, 1) == CRM_Core_DAO::VALUE_SEPARATOR &&
184 substr($to, -1, 1) == CRM_Core_DAO::VALUE_SEPARATOR)
185 ) {
186 $froms = $tos = array();
187 foreach (explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($from, CRM_Core_DAO::VALUE_SEPARATOR)) as $val) {
188 $froms[] = CRM_Utils_Array::value($val, $values[$field]);
189 }
190 foreach (explode(CRM_Core_DAO::VALUE_SEPARATOR, trim($to, CRM_Core_DAO::VALUE_SEPARATOR)) as $val) {
191 $tos[] = CRM_Utils_Array::value($val, $values[$field]);
192 }
193 $from = implode(', ', array_filter($froms));
194 $to = implode(', ', array_filter($tos));
195 }
196
197 if (isset($values[$field][$from])) {
198 $from = $values[$field][$from];
199 }
200 if (isset($values[$field][$to])) {
201 $to = $values[$field][$to];
202 }
203 if (isset($titles[$field])) {
204 $field = $titles[$field];
205 }
206 if ($diff['action'] == 'Insert') {
207 $from = '';
208 }
209 if ($diff['action'] == 'Delete') {
210 $to = '';
211 }
212 }
213
214 $rows[] = array('field' => $field . " (id: {$diff['id']})", 'from' => $from, 'to' => $to);
215 }
216
217 return $rows;
218 }
219
220 public function buildQuickForm() {
221 parent::buildQuickForm();
222
223 $this->assign('whom_url', CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$this->cid}"));
224 $this->assign('who_url', CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$this->altered_by_id}"));
225 $this->assign('whom_name', $this->altered_name);
226 $this->assign('who_name', $this->altered_by);
227
228 $this->assign('log_date', CRM_Utils_Date::mysqlToIso($this->log_date));
229
230 $q = "reset=1&log_conn_id={$this->log_conn_id}&log_date={$this->log_date}";
231 $this->assign('revertURL', CRM_Report_Utils_Report::getNextUrl($this->detail, "$q&revert=1", FALSE, TRUE));
232 $this->assign('revertConfirm', ts('Are you sure you want to revert all changes?'));
233 }
234
235 /**
236 * Store the dsn for the logging database in $this->db.
237 */
238 protected function storeDB() {
239 $dsn = defined('CIVICRM_LOGGING_DSN') ? DB::parseDSN(CIVICRM_LOGGING_DSN) : DB::parseDSN(CIVICRM_DSN);
240 $this->db = $dsn['database'];
241 }
242
243 /**
244 * Revert the changes defined by the parameters.
245 */
246 protected function revert() {
247 $reverter = new CRM_Logging_Reverter($this->log_conn_id, $this->log_date);
248 $reverter->revert($this->tables);
249 CRM_Core_Session::setStatus(ts('The changes have been reverted.'), ts('Reverted'), 'success');
250 if ($this->cid) {
251 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/view', "reset=1&selectedChild=log&cid={$this->cid}", FALSE, NULL, FALSE));
252 }
253 else {
254 CRM_Utils_System::redirect(CRM_Report_Utils_Report::getNextUrl($this->summary, 'reset=1', FALSE, TRUE));
255 }
256 }
257
258 }