Merge pull request #6406 from eileenmcnaughton/CRM-16523
[civicrm-core.git] / CRM / Contact / Form / Merge.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
39de6fd5 4 | CiviCRM version 4.6 |
6a488035 5 +--------------------------------------------------------------------+
e7112fa7 6 | Copyright CiviCRM LLC (c) 2004-2015 |
6a488035
TO
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 +--------------------------------------------------------------------+
d25dd0ee 26 */
6a488035
TO
27
28/**
29 *
30 * @package CRM
e7112fa7 31 * @copyright CiviCRM LLC (c) 2004-2015
6a488035
TO
32 * $Id$
33 *
34 */
35
36require_once 'api/api.php';
86538308
EM
37
38/**
39 * Class CRM_Contact_Form_Merge
40 */
6a488035
TO
41class CRM_Contact_Form_Merge extends CRM_Core_Form {
42 // the id of the contact that tere's a duplicate for; this one will
43 // possibly inherit some of $_oid's properties and remain in the system
44 var $_cid = NULL;
45
46 // the id of the other contact - the duplicate one that will get deleted
47 var $_oid = NULL;
48
49 var $_contactType = NULL;
50
51 // variable to keep all location block ids.
52 protected $_locBlockIds = array();
53
54 // FIXME: QuickForm can't create advcheckboxes with value set to 0 or '0' :(
55 // see HTML_QuickForm_advcheckbox::setValues() - but patching that doesn't
56 // help, as QF doesn't put the 0-value elements in exportValues() anyway...
57 // to side-step this, we use the below UUID as a (re)placeholder
8ef12e64 58 var $_qfZeroBug = 'e8cddb72-a257-11dc-b9cc-0016d3330ee9';
59
00be9182 60 public function preProcess() {
6a488035
TO
61 if (!CRM_Core_Permission::check('merge duplicate contacts')) {
62 CRM_Core_Error::fatal(ts('You do not have access to this page'));
63 }
64
65 $rows = array();
353ffa53
TO
66 $cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE);
67 $oid = CRM_Utils_Request::retrieve('oid', 'Positive', $this, TRUE);
6a488035
TO
68 $flip = CRM_Utils_Request::retrieve('flip', 'Positive', $this, FALSE);
69
353ffa53
TO
70 $this->_rgid = $rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE);
71 $this->_gid = $gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this, FALSE);
6a488035
TO
72 $this->_mergeId = CRM_Utils_Request::retrieve('mergeId', 'Positive', $this, FALSE);
73
6d5a3f21
CW
74 // Sanity check
75 if ($cid == $oid) {
76 CRM_Core_Error::statusBounce(ts('Cannot merge a contact with itself.'));
77 }
78
6a488035 79 if (!CRM_Dedupe_BAO_Rule::validateContacts($cid, $oid)) {
cce70162 80 CRM_Core_Error::statusBounce(ts('The selected pair of contacts are marked as non duplicates. If these records should be merged, you can remove this exception on the <a href="%1">Dedupe Exceptions</a> page.', array(1 => CRM_Utils_System::url('civicrm/dedupe/exception', 'reset=1'))));
6a488035
TO
81 }
82
83 //load cache mechanism
84 $contactType = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $cid, 'contact_type');
85 $cacheKey = "merge $contactType";
86 $cacheKey .= $rgid ? "_{$rgid}" : '_0';
87 $cacheKey .= $gid ? "_{$gid}" : '_0';
88
8ef12e64 89 $join = "LEFT JOIN civicrm_dedupe_exception de ON ( pn.entity_id1 = de.contact_id1 AND
6a488035
TO
90 pn.entity_id2 = de.contact_id2 )";
91 $where = "de.id IS NULL";
92
93 $pos = CRM_Core_BAO_PrevNextCache::getPositions($cacheKey, $cid, $oid, $this->_mergeId, $join, $where, $flip);
94
95 // Block access if user does not have EDIT permissions for both contacts.
96 if (!(CRM_Contact_BAO_Contact_Permission::allow($cid, CRM_Core_Permission::EDIT) &&
353ffa53
TO
97 CRM_Contact_BAO_Contact_Permission::allow($oid, CRM_Core_Permission::EDIT)
98 )
99 ) {
6a488035
TO
100 CRM_Utils_System::permissionDenied();
101 }
102
103 // get user info of main contact.
104 $config = CRM_Core_Config::singleton();
105 $config->doNotResetCache = 1;
106
107 $viewUser = CRM_Core_Permission::check('access user profiles');
108 $mainUfId = CRM_Core_BAO_UFMatch::getUFId($cid);
109 $mainUser = NULL;
110 if ($mainUfId) {
111 // d6 compatible
112 if ($config->userSystem->is_drupal == '1') {
113 $mainUser = user_load($mainUfId);
114 }
115 elseif ($config->userFramework == 'Joomla') {
116 $mainUser = JFactory::getUser($mainUfId);
117 }
118
119 $this->assign('mainUfId', $mainUfId);
120 $this->assign('mainUfName', $mainUser ? $mainUser->name : NULL);
121 }
122
395d8dc6 123 $flipUrl = CRM_Utils_System::url('civicrm/contact/merge',
6a488035
TO
124 "reset=1&action=update&cid={$oid}&oid={$cid}&rgid={$rgid}&gid={$gid}"
125 );
126 if (!$flip) {
127 $flipUrl .= '&flip=1';
128 }
129 $this->assign('flip', $flipUrl);
130
131 $this->prev = $this->next = NULL;
132 foreach (array(
353ffa53 133 'prev',
389bcebf 134 'next',
353ffa53 135 ) as $position) {
6a488035
TO
136 if (!empty($pos[$position])) {
137 if ($pos[$position]['id1'] && $pos[$position]['id2']) {
138 $urlParam = "reset=1&cid={$pos[$position]['id1']}&oid={$pos[$position]['id2']}&mergeId={$pos[$position]['mergeId']}&action=update";
139
140 if ($rgid) {
141 $urlParam .= "&rgid={$rgid}";
142 }
143 if ($gid) {
144 $urlParam .= "&gid={$gid}";
145 }
146
395d8dc6 147 $this->$position = CRM_Utils_System::url('civicrm/contact/merge', $urlParam);
6a488035
TO
148 $this->assign($position, $this->$position);
149 }
150 }
151 }
152
153 // get user info of other contact.
154 $otherUfId = CRM_Core_BAO_UFMatch::getUFId($oid);
155 $otherUser = NULL;
156
157 if ($otherUfId) {
158 // d6 compatible
159 if ($config->userSystem->is_drupal == '1') {
160 $otherUser = user_load($otherUfId);
161 }
162 elseif ($config->userFramework == 'Joomla') {
163 $otherUser = JFactory::getUser($otherUfId);
164 }
165
166 $this->assign('otherUfId', $otherUfId);
167 $this->assign('otherUfName', $otherUser ? $otherUser->name : NULL);
168 }
169
170 $cmsUser = ($mainUfId && $otherUfId) ? TRUE : FALSE;
171 $this->assign('user', $cmsUser);
172
173 $session = CRM_Core_Session::singleton();
174
175 // context fixed.
176 if ($rgid) {
177 $urlParam = "reset=1&action=browse&rgid={$rgid}";
178 if ($gid) {
179 $urlParam .= "&gid={$gid}";
180 }
395d8dc6 181 $session->pushUserContext(CRM_Utils_System::url('civicrm/contact/dedupefind', $urlParam));
6a488035
TO
182 }
183
184 // ensure that oid is not the current user, if so refuse to do the merge
185 if ($session->get('userID') == $oid) {
186 $display_name = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $oid, 'display_name');
187 $message = ts('The contact record which is linked to the currently logged in user account - \'%1\' - cannot be deleted.',
188 array(1 => $display_name)
189 );
190 CRM_Core_Error::statusBounce($message);
191 }
192
193 $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($cid, $oid);
471ec338
BS
194 $main = $this->_mainDetails = &$rowsElementsAndInfo['main_details'];
195 $other = $this->_otherDetails = &$rowsElementsAndInfo['other_details'];
6a488035
TO
196
197 if ($main['contact_id'] != $cid) {
198 CRM_Core_Error::fatal(ts('The main contact record does not exist'));
199 }
200
201 if ($other['contact_id'] != $oid) {
202 CRM_Core_Error::fatal(ts('The other contact record does not exist'));
203 }
204
6a488035 205 $this->assign('contact_type', $main['contact_type']);
6a488035
TO
206 $this->assign('main_name', $main['display_name']);
207 $this->assign('other_name', $other['display_name']);
208 $this->assign('main_cid', $main['contact_id']);
209 $this->assign('other_cid', $other['contact_id']);
00c461ec 210 $this->assign('rgid', $rgid);
6a488035 211
353ffa53
TO
212 $this->_cid = $cid;
213 $this->_oid = $oid;
214 $this->_rgid = $rgid;
6a488035 215 $this->_contactType = $main['contact_type'];
d664f648 216 $this->addElement('checkbox', 'toggleSelect', NULL, NULL, array('class' => 'select-rows'));
6a488035
TO
217
218 $this->assign('mainLocBlock', json_encode($rowsElementsAndInfo['main_loc_block']));
219 $this->assign('rows', $rowsElementsAndInfo['rows']);
220
221 $this->_locBlockIds = array(
222 'main' => $rowsElementsAndInfo['main_details']['loc_block_ids'],
21dfd5f5 223 'other' => $rowsElementsAndInfo['other_details']['loc_block_ids'],
6a488035
TO
224 );
225
226 // add elements
227 foreach ($rowsElementsAndInfo['elements'] as $element) {
228 $this->addElement($element[0],
229 $element[1],
230 array_key_exists('2', $element) ? $element[2] : NULL,
231 array_key_exists('3', $element) ? $element[3] : NULL,
232 array_key_exists('4', $element) ? $element[4] : NULL,
233 array_key_exists('5', $element) ? $element[5] : NULL
234 );
235 }
236
237 // add related table elements
238 foreach ($rowsElementsAndInfo['rel_table_elements'] as $relTableElement) {
239 $element = $this->addElement($relTableElement[0], $relTableElement[1]);
240 $element->setChecked(TRUE);
241 }
242
243 $this->assign('rel_tables', $rowsElementsAndInfo['rel_tables']);
244 $this->assign('userContextURL', $session->readUserContext());
245 }
246
86538308
EM
247 /**
248 * This virtual function is used to set the default values of
249 * various form elements
250 *
251 * access public
252 *
a6c01b45
CW
253 * @return array
254 * reference to the array of default values
86538308
EM
255 */
256 /**
257 * @return array
258 */
00be9182 259 public function setDefaultValues() {
6a488035
TO
260 return array('deleteOther' => 1);
261 }
262
6ea503d4
TO
263 public function addRules() {
264 }
6a488035
TO
265
266 public function buildQuickForm() {
d5be719d 267 CRM_Utils_System::setTitle(ts('Merge %1 contacts', array(1 => $this->_contactType)));
fd66df85
CW
268 $buttons = array();
269
270 $buttons[] = array(
271 'type' => 'next',
272 'name' => $this->next ? ts('Merge and Goto Next Pair') : ts('Merge'),
273 'isDefault' => TRUE,
274 'icon' => $this->next ? 'circle-triangle-e' : 'check',
275 );
6a488035
TO
276
277 if ($this->next || $this->prev) {
fd66df85
CW
278 $buttons[] = array(
279 'type' => 'submit',
280 'name' => ts('Merge and Goto Listing'),
6a488035 281 );
fd66df85
CW
282 $buttons[] = array(
283 'type' => 'done',
284 'name' => ts('Merge and View Result'),
389bcebf 285 'icon' => 'circle-check',
6a488035
TO
286 );
287 }
288
fd66df85
CW
289 $buttons[] = array(
290 'type' => 'cancel',
291 'name' => ts('Cancel'),
292 );
293
294 $this->addButtons($buttons);
4b87bd02 295 $this->addFormRule(array('CRM_Contact_Form_Merge', 'formRule'), $this);
296 }
297
86538308
EM
298 /**
299 * @param $fields
300 * @param $files
301 * @param $self
302 *
303 * @return array
304 */
00be9182 305 public static function formRule($fields, $files, $self) {
4b87bd02 306 $errors = array();
0653585f 307 $link = CRM_Utils_System::href(ts('Flip between the original and duplicate contacts.'),
b74201e4 308 'civicrm/contact/merge',
309 'reset=1&action=update&cid=' . $self->_oid . '&oid=' . $self->_cid . '&rgid=' . $self->_rgid . '&flip=1'
310 );
4b87bd02 311 if (CRM_Contact_BAO_Contact::checkDomainContact($self->_oid)) {
0653585f 312 $errors['_qf_default'] = ts("The Default Organization contact cannot be merged into another contact record. It is associated with the CiviCRM installation for this domain and contains information used for system functions. If you want to merge these records, you can: %1", array(1 => $link));
4b87bd02 313 }
314 return $errors;
6a488035
TO
315 }
316
317 public function postProcess() {
318 $formValues = $this->exportValues();
8ef12e64 319
6a488035
TO
320 // reset all selected contact ids from session
321 // when we came from search context, CRM-3526
322 $session = CRM_Core_Session::singleton();
323 if ($session->get('selectedSearchContactIds')) {
324 $session->resetScope('selectedSearchContactIds');
325 }
326
471ec338
BS
327 $formValues['main_details'] = $this->_mainDetails;
328 $formValues['other_details'] = $this->_otherDetails;
6a488035
TO
329
330 CRM_Dedupe_Merger::moveAllBelongings($this->_cid, $this->_oid, $formValues);
331
7b99ead3
CW
332 $name = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_cid, 'display_name');
333 $message = '<ul><li>' . ts('%1 has been updated.', array(1 => $name)) . '</li><li>' . ts('Contact ID %1 has been deleted.', array(1 => $this->_oid)) . '</li></ul>';
334 CRM_Core_Session::setStatus($message, ts('Contacts Merged'), 'success');
335
d9235b45
DG
336 //create activity for merge
337 //To do: this should be refactored into BAO layer at some point.
338 $messageActivity = ts('Contact ID %1 has been merged and deleted.', array(1 => $this->_oid));
339 $activityParams = array(
340 'subject' => $messageActivity,
341 'source_contact_id' => $session->get('userID'),
342 'target_contact_id' => $this->_cid,
343 'activity_type_id' => CRM_Core_PseudoConstant::getKey('CRM_Activity_BAO_Activity', 'activity_type_id', 'Contact Merged'),
344 'status_id' => 'Completed',
345 'priority_id' => 'Normal',
346 'activity_date_time' => date('YmdHis'),
347 );
348 civicrm_api3('activity', 'create', $activityParams);
349
6a488035 350 $url = CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$this->_cid}");
a7488080 351 if (!empty($formValues['_qf_Merge_submit'])) {
6a488035
TO
352 $listParamsURL = "reset=1&action=update&rgid={$this->_rgid}";
353 if ($this->_gid) {
354 $listParamsURL .= "&gid={$this->_gid}";
355 }
356 $lisitingURL = CRM_Utils_System::url('civicrm/contact/dedupefind',
357 $listParamsURL
358 );
359 CRM_Utils_System::redirect($lisitingURL);
360 }
353ffa53 361 if (!empty($formValues['_qf_Merge_done'])) {
6a488035
TO
362 CRM_Utils_System::redirect($url);
363 }
364
365 if ($this->next && $this->_mergeId) {
366 $cacheKey = "merge {$this->_contactType}";
367 $cacheKey .= $this->_rgid ? "_{$this->_rgid}" : '_0';
368 $cacheKey .= $this->_gid ? "_{$this->_gid}" : '_0';
369
8ef12e64 370 $join = "LEFT JOIN civicrm_dedupe_exception de ON ( pn.entity_id1 = de.contact_id1 AND
6a488035
TO
371 pn.entity_id2 = de.contact_id2 )";
372 $where = "de.id IS NULL";
373
374 $pos = CRM_Core_BAO_PrevNextCache::getPositions($cacheKey, NULL, NULL, $this->_mergeId, $join, $where);
375
376 if (!empty($pos) &&
377 $pos['next']['id1'] &&
378 $pos['next']['id2']
379 ) {
380
381 $urlParam = "reset=1&cid={$pos['next']['id1']}&oid={$pos['next']['id2']}&mergeId={$pos['next']['mergeId']}&action=update";
382 if ($this->_rgid) {
383 $urlParam .= "&rgid={$this->_rgid}";
384 }
385 if ($this->_gid) {
386 $urlParam .= "&gid={$this->_gid}";
387 }
388
395d8dc6 389 $url = CRM_Utils_System::url('civicrm/contact/merge', $urlParam);
6a488035
TO
390 }
391 }
392
393 CRM_Utils_System::redirect($url);
394 }
96025800 395
6a488035 396}