comments
[civicrm-core.git] / CRM / Contact / Page / DedupeFind.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
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-2015
32 * $Id$
33 *
34 */
35 class CRM_Contact_Page_DedupeFind extends CRM_Core_Page_Basic {
36 protected $_cid = NULL;
37 protected $_rgid;
38 protected $_mainContacts;
39 protected $_gid;
40
41 /**
42 * Get BAO Name.
43 *
44 * @return string
45 * Classname of BAO.
46 */
47 public function getBAOName() {
48 return 'CRM_Dedupe_BAO_RuleGroup';
49 }
50
51 /**
52 * Get action Links.
53 *
54 * @return void
55 * (reference) of action links
56 */
57 public function &links() {
58 }
59
60 /**
61 * Browse all rule groups.
62 *
63 * @return void
64 */
65 public function run() {
66 $gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this, FALSE, 0);
67 $action = CRM_Utils_Request::retrieve('action', 'String', $this, FALSE, 0);
68 $context = CRM_Utils_Request::retrieve('context', 'String', $this);
69
70 $session = CRM_Core_Session::singleton();
71 $contactIds = $session->get('selectedSearchContactIds');
72 if ($context == 'search' || !empty($contactIds)) {
73 $context = 'search';
74 $this->assign('backURL', $session->readUserContext());
75 }
76
77 if ($action & CRM_Core_Action::RENEW) {
78 // empty cache
79 $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE, 0);
80
81 if ($rgid) {
82 $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
83 $cacheKeyString = "merge $contactType";
84 $cacheKeyString .= $rgid ? "_{$rgid}" : '_0';
85 $cacheKeyString .= $gid ? "_{$gid}" : '_0';
86 CRM_Core_BAO_PrevNextCache::deleteItem(NULL, $cacheKeyString);
87 }
88 $urlQry = "reset=1&action=update&rgid={$rgid}";
89 if ($gid) {
90 $urlQry .= "&gid={$gid}";
91 }
92 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry));
93 }
94 elseif ($action & CRM_Core_Action::MAP) {
95 // do a batch merge if requested
96 $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE, 0);
97 $result = CRM_Dedupe_Merger::batchMerge($rgid, $gid, 'safe', TRUE, 75);
98
99 $skippedCount = CRM_Utils_Request::retrieve('skipped', 'Positive', $this, FALSE, 0);
100 $skippedCount = $skippedCount + count($result['skipped']);
101 $mergedCount = CRM_Utils_Request::retrieve('merged', 'Positive', $this, FALSE, 0);
102 $mergedCount = $mergedCount + count($result['merged']);
103
104 if (empty($result['merged']) && empty($result['skipped'])) {
105 $message = '';
106 if ($mergedCount >= 1) {
107 $message = ts("%1 pairs of duplicates were merged", array(1 => $mergedCount));
108 }
109 if ($skippedCount >= 1) {
110 $message = $message ? "{$message} and " : '';
111 $message .= ts("%1 pairs of duplicates were skipped due to conflict",
112 array(1 => $skippedCount)
113 );
114 }
115 $message .= ts(" during the batch merge process with safe mode.");
116 CRM_Core_Session::setStatus($message, ts('Merge Complete'), 'success');
117
118 $urlQry = "reset=1&action=update&rgid={$rgid}";
119 if ($gid) {
120 $urlQry .= "&gid={$gid}";
121 }
122 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry));
123 }
124 else {
125 $urlQry = "reset=1&action=map&rgid={$rgid}";
126 if ($gid) {
127 $urlQry .= "&gid={$gid}";
128 }
129 $urlQry .= "&skipped={$skippedCount}&merged={$mergedCount}";
130 CRM_Utils_System::jsRedirect(
131 CRM_Utils_System::url('civicrm/contact/dedupefind', $urlQry),
132 ts('Batch Merge Task in progress'),
133 ts('The batch merge task is still in progress. This page will be refreshed automatically.')
134 );
135 }
136 }
137
138 if ($action & CRM_Core_Action::UPDATE ||
139 $action & CRM_Core_Action::BROWSE
140 ) {
141 $cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, FALSE, 0);
142 $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE, 0);
143 $this->action = CRM_Core_Action::UPDATE;
144
145 //calculate the $contactType
146 if ($rgid) {
147 $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup',
148 $rgid,
149 'contact_type'
150 );
151 }
152
153 $sourceParams = 'snippet=4';
154 if ($gid) {
155 $sourceParams .= "&gid={$gid}";
156 }
157 if ($rgid) {
158 $sourceParams .= "&rgid={$rgid}";
159 }
160 if ($context == 'conflicts') {
161 $sourceParams .= "&selected=1";
162 }
163
164 $this->assign('sourceUrl', CRM_Utils_System::url('civicrm/ajax/dedupefind', $sourceParams, FALSE, NULL, FALSE));
165
166 //reload from cache table
167 $cacheKeyString = "merge $contactType";
168 $cacheKeyString .= $rgid ? "_{$rgid}" : '_0';
169 $cacheKeyString .= $gid ? "_{$gid}" : '_0';
170
171 $stats = CRM_Dedupe_Merger::getMergeStatsMsg($cacheKeyString);
172 if ($stats) {
173 CRM_Core_Session::setStatus($stats);
174 // reset so we not displaying same message again
175 CRM_Dedupe_Merger::resetMergeStats($cacheKeyString);
176 }
177 $join = "LEFT JOIN civicrm_dedupe_exception de ON ( pn.entity_id1 = de.contact_id1 AND
178 pn.entity_id2 = de.contact_id2 )";
179 $where = "de.id IS NULL";
180 if ($context == 'conflicts') {
181 $where .= " AND pn.is_selected = 1";
182 }
183 $this->_mainContacts = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $where);
184 if (empty($this->_mainContacts)) {
185 if ($context == 'conflicts') {
186 // if the current screen was intended to list only selected contacts, move back to full dupe list
187 $sourceParams = 'reset=1&action=update';
188 if ($gid) {
189 $sourceParams .= "&gid={$gid}";
190 }
191 if ($rgid) {
192 $sourceParams .= "&rgid={$rgid}";
193 }
194 CRM_Utils_System::redirect(CRM_Utils_System::url(CRM_Utils_System::currentPath(), $sourceParams));
195 }
196 if ($gid) {
197 $foundDupes = $this->get("dedupe_dupes_$gid");
198 if (!$foundDupes) {
199 $foundDupes = CRM_Dedupe_Finder::dupesInGroup($rgid, $gid);
200 }
201 $this->set("dedupe_dupes_$gid", $foundDupes);
202 }
203 elseif (!empty($contactIds)) {
204 $foundDupes = $this->get("search_dedupe_dupes_$gid");
205 if (!$foundDupes) {
206 $foundDupes = CRM_Dedupe_Finder::dupes($rgid, $contactIds);
207 }
208 $this->get("search_dedupe_dupes_$gid", $foundDupes);
209 }
210 else {
211 $foundDupes = $this->get('dedupe_dupes');
212 if (!$foundDupes) {
213 $foundDupes = CRM_Dedupe_Finder::dupes($rgid);
214 }
215 $this->set('dedupe_dupes', $foundDupes);
216 }
217 if (!$foundDupes) {
218 $ruleGroup = new CRM_Dedupe_BAO_RuleGroup();
219 $ruleGroup->id = $rgid;
220 $ruleGroup->find(TRUE);
221
222 $session = CRM_Core_Session::singleton();
223 $session->setStatus(ts('No possible duplicates were found using %1 rule.', array(1 => $ruleGroup->name)), ts('None Found'), 'info');
224 $url = CRM_Utils_System::url('civicrm/contact/deduperules', 'reset=1');
225 if ($context == 'search') {
226 $url = $session->readUserContext();
227 }
228 CRM_Utils_System::redirect($url);
229 }
230 else {
231 $cids = array();
232 foreach ($foundDupes as $dupe) {
233 $cids[$dupe[0]] = 1;
234 $cids[$dupe[1]] = 1;
235 }
236 $cidString = implode(', ', array_keys($cids));
237 $sql = "SELECT id, display_name FROM civicrm_contact WHERE id IN ($cidString) ORDER BY sort_name";
238 $dao = new CRM_Core_DAO();
239 $dao->query($sql);
240 $displayNames = array();
241 while ($dao->fetch()) {
242 $displayNames[$dao->id] = $dao->display_name;
243 }
244
245 // FIXME: sort the contacts; $displayName
246 // is already sort_name-sorted, so use that
247 // (also, consider sorting by dupe count first)
248 // lobo - change the sort to by threshold value
249 // so the more likely dupes are sorted first
250 $session = CRM_Core_Session::singleton();
251 $userId = $session->get('userID');
252 $mainContacts = $permission = array();
253
254 foreach ($foundDupes as $dupes) {
255 $srcID = $dupes[0];
256 $dstID = $dupes[1];
257 if ($dstID == $userId) {
258 $srcID = $dupes[1];
259 $dstID = $dupes[0];
260 }
261
262 /***
263 * Eliminate this since it introduces 3 queries PER merge row
264 * and hence is very expensive
265 * CRM-8822
266 * if ( !array_key_exists( $srcID, $permission ) ) {
267 * $permission[$srcID] = CRM_Contact_BAO_Contact_Permission::allow( $srcID, CRM_Core_Permission::EDIT );
268 * }
269 * if ( !array_key_exists( $dstID, $permission ) ) {
270 * $permission[$dstID] = CRM_Contact_BAO_Contact_Permission::allow( $dstID, CRM_Core_Permission::EDIT );
271 * }
272 *
273 * $canMerge = ( $permission[$dstID] && $permission[$srcID] );
274 *
275 */
276
277 // we'll do permission checking during the merge process
278 $canMerge = TRUE;
279
280 $mainContacts[] = $row = array(
281 'srcID' => $srcID,
282 'srcName' => $displayNames[$srcID],
283 'dstID' => $dstID,
284 'dstName' => $displayNames[$dstID],
285 'weight' => $dupes[2],
286 'canMerge' => $canMerge,
287 );
288
289 $data = CRM_Core_DAO::escapeString(serialize($row));
290 $values[] = " ( 'civicrm_contact', $srcID, $dstID, '$cacheKeyString', '$data' ) ";
291 }
292 if ($cid) {
293 $this->_cid = $cid;
294 }
295 if ($gid) {
296 $this->_gid = $gid;
297 }
298 $this->_rgid = $rgid;
299 $this->_mainContacts = $mainContacts;
300
301 CRM_Core_BAO_PrevNextCache::setItem($values);
302 $session = CRM_Core_Session::singleton();
303 if ($this->_cid) {
304 $session->pushUserContext(CRM_Utils_System::url('civicrm/contact/deduperules',
305 "action=update&rgid={$this->_rgid}&gid={$this->_gid}&cid={$this->_cid}"
306 ));
307 }
308 else {
309 $session->pushUserContext(CRM_Utils_System::url('civicrm/contact/dedupefind',
310 "reset=1&action=update&rgid={$this->_rgid}"
311 ));
312 }
313 }
314 }
315 else {
316 if ($cid) {
317 $this->_cid = $cid;
318 }
319 if ($gid) {
320 $this->_gid = $gid;
321 }
322 $this->_rgid = $rgid;
323 }
324
325 $this->assign('action', $this->action);
326 $this->browse();
327 }
328 else {
329 $this->action = CRM_Core_Action::UPDATE;
330 $this->edit($this->action);
331 $this->assign('action', $this->action);
332 }
333 $this->assign('context', $context);
334
335 // parent run
336 return parent::run();
337 }
338
339 /**
340 * Browse all rule groups.
341 *
342 * @return void
343 */
344 public function browse() {
345 $this->assign('main_contacts', $this->_mainContacts);
346
347 if ($this->_cid) {
348 $this->assign('cid', $this->_cid);
349 }
350 if (isset($this->_gid) || $this->_gid) {
351 $this->assign('gid', $this->_gid);
352 }
353 $this->assign('rgid', $this->_rgid);
354 }
355
356 /**
357 * Get name of edit form.
358 *
359 * @return string
360 * classname of edit form
361 */
362 public function editForm() {
363 return 'CRM_Contact_Form_DedupeFind';
364 }
365
366 /**
367 * Get edit form name.
368 *
369 * @return string
370 * name of this page
371 */
372 public function editName() {
373 return 'DedupeFind';
374 }
375
376 /**
377 * Get user context.
378 *
379 * @param null $mode
380 *
381 * @return string
382 * user context
383 */
384 public function userContext($mode = NULL) {
385 return 'civicrm/contact/dedupefind';
386 }
387
388 }