Merge pull request #11702 from jmcclelland/pdf-receipt-filename
[civicrm-core.git] / CRM / Contact / Form / Merge.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
8c9251b3 6 | Copyright CiviCRM LLC (c) 2004-2018 |
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
8c9251b3 31 * @copyright CiviCRM LLC (c) 2004-2018
6a488035
TO
32 */
33
86538308 34/**
5a409b50 35 * Class CRM_Contact_Form_Merge.
86538308 36 */
6a488035 37class CRM_Contact_Form_Merge extends CRM_Core_Form {
5af77f03 38 // The id of the contact that there's a duplicate for; this one will
39 // possibly inherit some of $_oid's properties and remain in the system.
6a488035
TO
40 var $_cid = NULL;
41
5af77f03 42 // The id of the other contact - the duplicate one that will get deleted.
6a488035
TO
43 var $_oid = NULL;
44
45 var $_contactType = NULL;
46
dc6285d5 47 /**
48 * Query limit to be retained in the urls.
49 *
50 * @var int
51 */
52 var $limit;
53
5af77f03 54 /**
55 * String for quickform bug handling.
56 *
57 * FIXME: QuickForm can't create advcheckboxes with value set to 0 or '0' :(
58 * see HTML_QuickForm_advcheckbox::setValues() - but patching that doesn't
59 * help, as QF doesn't put the 0-value elements in exportValues() anyway...
60 * to side-step this, we use the below UUID as a (re)placeholder
61 *
62 * @var string
63 */
8ef12e64 64 var $_qfZeroBug = 'e8cddb72-a257-11dc-b9cc-0016d3330ee9';
65
00be9182 66 public function preProcess() {
fcc3d8ee 67 try {
6a488035 68
fcc3d8ee 69 $this->_cid = CRM_Utils_Request::retrieve('cid', 'Positive', $this, TRUE);
70 $this->_oid = CRM_Utils_Request::retrieve('oid', 'Positive', $this, TRUE);
71 $flip = CRM_Utils_Request::retrieve('flip', 'Positive', $this, FALSE);
6a488035 72
fcc3d8ee 73 $this->_rgid = CRM_Utils_Request::retrieve('rgid', 'Positive', $this, FALSE);
74 $this->_gid = $gid = CRM_Utils_Request::retrieve('gid', 'Positive', $this, FALSE);
75 $this->_mergeId = CRM_Utils_Request::retrieve('mergeId', 'Positive', $this, FALSE);
76 $this->limit = CRM_Utils_Request::retrieve('limit', 'Positive', $this, FALSE);
c0cc2ad4 77
78 $urlParams = ['reset' => 1, 'rgid' => $this->_rgid, 'gid' => $this->_gid, 'limit' => $this->limit];
6d5a3f21 79
fcc3d8ee 80 $this->bounceIfInvalid($this->_cid, $this->_oid);
186e5c44 81
fcc3d8ee 82 $this->_contactType = civicrm_api3('Contact', 'getvalue', array(
83 'id' => $this->_cid,
84 'return' => 'contact_type',
e9299e88 85 ));
6a488035 86
c0cc2ad4 87 $browseUrl = CRM_Utils_System::url('civicrm/contact/dedupefind', array_merge($urlParams, ['action' => 'browse']));
2ae26001 88
fcc3d8ee 89 if (!$this->_rgid) {
90 // Unset browse URL as we have come from the search screen.
91 $browseUrl = '';
92 $this->_rgid = civicrm_api3('RuleGroup', 'getvalue', array(
93 'contact_type' => $this->_contactType,
94 'used' => 'Supervised',
95 'return' => 'id',
96 ));
97 }
98 $this->assign('browseUrl', $browseUrl);
99 if ($browseUrl) {
100 CRM_Core_Session::singleton()->pushUserContext($browseUrl);
101 }
6a488035 102
fcc3d8ee 103 $cacheKey = CRM_Dedupe_Merger::getMergeCacheKeyString($this->_rgid, $gid);
6a488035 104
fcc3d8ee 105 $join = CRM_Dedupe_Merger::getJoinOnDedupeTable();
106 $where = "de.id IS NULL";
6a488035 107
fcc3d8ee 108 $pos = CRM_Core_BAO_PrevNextCache::getPositions($cacheKey, $this->_cid, $this->_oid, $this->_mergeId, $join, $where, $flip);
6a488035 109
fcc3d8ee 110 // get user info of main contact.
111 $config = CRM_Core_Config::singleton();
0626851e 112 CRM_Core_Config::setPermitCacheFlushMode(FALSE);
6a488035 113
fcc3d8ee 114 $mainUfId = CRM_Core_BAO_UFMatch::getUFId($this->_cid);
115 $mainUser = NULL;
116 if ($mainUfId) {
117 // d6 compatible
118 if ($config->userSystem->is_drupal == '1') {
119 $mainUser = user_load($mainUfId);
120 }
121 elseif ($config->userFramework == 'Joomla') {
122 $mainUser = JFactory::getUser($mainUfId);
6a488035 123 }
6a488035 124
fcc3d8ee 125 $this->assign('mainUfId', $mainUfId);
126 $this->assign('mainUfName', $mainUser ? $mainUser->name : NULL);
127 }
c0cc2ad4 128 $flipParams = array_merge($urlParams, ['action' => 'update', 'cid' => $this->_oid, 'oid' => $this->_cid]);
fcc3d8ee 129 if (!$flip) {
c0cc2ad4 130 $flipParams['flip'] = '1';
6a488035 131 }
c0cc2ad4 132 $flipUrl = CRM_Utils_System::url('civicrm/contact/merge',
133 $flipParams
134 );
fcc3d8ee 135 $this->assign('flip', $flipUrl);
136
137 $this->prev = $this->next = NULL;
138 foreach (array(
139 'prev',
140 'next',
141 ) as $position) {
142 if (!empty($pos[$position])) {
143 if ($pos[$position]['id1'] && $pos[$position]['id2']) {
c0cc2ad4 144 $rowParams = array_merge($urlParams, [
145 'action' => 'update',
146 'cid' => $pos[$position]['id1'],
147 'oid' => $pos[$position]['id2'],
148 'mergeId' => $pos[$position]['mergeId'],
149 ]);
150 $this->$position = CRM_Utils_System::url('civicrm/contact/merge', $rowParams);
fcc3d8ee 151 $this->assign($position, $this->$position);
152 }
153 }
6a488035
TO
154 }
155
fcc3d8ee 156 // get user info of other contact.
157 $otherUfId = CRM_Core_BAO_UFMatch::getUFId($this->_oid);
158 $otherUser = NULL;
6a488035 159
fcc3d8ee 160 if ($otherUfId) {
161 // d6 compatible
162 if ($config->userSystem->is_drupal == '1') {
163 $otherUser = user_load($otherUfId);
164 }
165 elseif ($config->userFramework == 'Joomla') {
166 $otherUser = JFactory::getUser($otherUfId);
167 }
6a488035 168
fcc3d8ee 169 $this->assign('otherUfId', $otherUfId);
170 $this->assign('otherUfName', $otherUser ? $otherUser->name : NULL);
171 }
6a488035 172
fcc3d8ee 173 $cmsUser = ($mainUfId && $otherUfId) ? TRUE : FALSE;
174 $this->assign('user', $cmsUser);
175
176 $rowsElementsAndInfo = CRM_Dedupe_Merger::getRowsElementsAndInfo($this->_cid, $this->_oid);
177 $main = $this->_mainDetails = $rowsElementsAndInfo['main_details'];
178 $other = $this->_otherDetails = $rowsElementsAndInfo['other_details'];
179
180 $this->assign('contact_type', $main['contact_type']);
181 $this->assign('main_name', $main['display_name']);
182 $this->assign('other_name', $other['display_name']);
183 $this->assign('main_cid', $main['contact_id']);
184 $this->assign('other_cid', $other['contact_id']);
185 $this->assign('rgid', $this->_rgid);
186
187 $this->addElement('checkbox', 'toggleSelect', NULL, NULL, array('class' => 'select-rows'));
188
189 $this->assign('mainLocBlock', json_encode($rowsElementsAndInfo['main_details']['location_blocks']));
190 $this->assign('locationBlockInfo', json_encode(CRM_Dedupe_Merger::getLocationBlockInfo()));
191 $this->assign('rows', $rowsElementsAndInfo['rows']);
192
193 // add elements
194 foreach ($rowsElementsAndInfo['elements'] as $element) {
195 // We could push this down to the getRowsElementsAndInfo function but it's
196 // already so overloaded - let's start moving towards doing form-things
197 // on the form.
198 if (substr($element[1], 0, 13) === 'move_location') {
199 $element[4] = array_merge(
200 (array) CRM_Utils_Array::value(4, $element, array()),
201 array(
202 'data-location' => substr($element[1], 14),
203 'data-is_location' => TRUE,
204 ));
205 }
206 if (substr($element[1], 0, 15) === 'location_blocks') {
207 // @todo We could add some data elements here to make jquery manipulation more straight-forward
208 // @todo consider enabling if it is an add & defaulting to true.
209 $element[4] = array_merge((array) CRM_Utils_Array::value(4, $element, array()), array('disabled' => TRUE));
210 }
211 $this->addElement($element[0],
212 $element[1],
213 array_key_exists('2', $element) ? $element[2] : NULL,
214 array_key_exists('3', $element) ? $element[3] : NULL,
215 array_key_exists('4', $element) ? $element[4] : NULL,
216 array_key_exists('5', $element) ? $element[5] : NULL
217 );
972e23db 218 }
fcc3d8ee 219
220 // add related table elements
221 foreach ($rowsElementsAndInfo['rel_table_elements'] as $relTableElement) {
222 $element = $this->addElement($relTableElement[0], $relTableElement[1]);
223 $element->setChecked(TRUE);
972e23db 224 }
6a488035 225
fcc3d8ee 226 $this->assign('rel_tables', $rowsElementsAndInfo['rel_tables']);
227 $this->assign('userContextURL', CRM_Core_Session::singleton()
228 ->readUserContext());
229 }
230 catch (CRM_Core_Exception $e) {
231 CRM_Core_Error::statusBounce(ts($e->getMessage()));
6a488035 232 }
6a488035
TO
233 }
234
6ea503d4
TO
235 public function addRules() {
236 }
6a488035
TO
237
238 public function buildQuickForm() {
d45c349e 239 $this->unsavedChangesWarn = FALSE;
d5be719d 240 CRM_Utils_System::setTitle(ts('Merge %1 contacts', array(1 => $this->_contactType)));
fd66df85
CW
241 $buttons = array();
242
243 $buttons[] = array(
244 'type' => 'next',
3709190a 245 'name' => $this->next ? ts('Merge and go to Next Pair') : ts('Merge'),
fd66df85
CW
246 'isDefault' => TRUE,
247 'icon' => $this->next ? 'circle-triangle-e' : 'check',
248 );
6a488035
TO
249
250 if ($this->next || $this->prev) {
fd66df85
CW
251 $buttons[] = array(
252 'type' => 'submit',
3709190a 253 'name' => ts('Merge and go to Listing'),
6a488035 254 );
fd66df85
CW
255 $buttons[] = array(
256 'type' => 'done',
257 'name' => ts('Merge and View Result'),
0291a521 258 'icon' => 'fa-check-circle',
6a488035
TO
259 );
260 }
261
fd66df85
CW
262 $buttons[] = array(
263 'type' => 'cancel',
264 'name' => ts('Cancel'),
265 );
266
267 $this->addButtons($buttons);
4b87bd02 268 $this->addFormRule(array('CRM_Contact_Form_Merge', 'formRule'), $this);
269 }
270
86538308
EM
271 /**
272 * @param $fields
273 * @param $files
274 * @param $self
275 *
276 * @return array
277 */
00be9182 278 public static function formRule($fields, $files, $self) {
4b87bd02 279 $errors = array();
0653585f 280 $link = CRM_Utils_System::href(ts('Flip between the original and duplicate contacts.'),
b74201e4 281 'civicrm/contact/merge',
282 'reset=1&action=update&cid=' . $self->_oid . '&oid=' . $self->_cid . '&rgid=' . $self->_rgid . '&flip=1'
283 );
4b87bd02 284 if (CRM_Contact_BAO_Contact::checkDomainContact($self->_oid)) {
0653585f 285 $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 286 }
287 return $errors;
6a488035
TO
288 }
289
290 public function postProcess() {
291 $formValues = $this->exportValues();
8ef12e64 292
471ec338
BS
293 $formValues['main_details'] = $this->_mainDetails;
294 $formValues['other_details'] = $this->_otherDetails;
b54f692d 295 $migrationData = array('migration_info' => $formValues);
296 CRM_Utils_Hook::merge('form', $migrationData, $this->_cid, $this->_oid);
297 CRM_Dedupe_Merger::moveAllBelongings($this->_cid, $this->_oid, $migrationData['migration_info']);
6a488035 298
7b99ead3
CW
299 $name = CRM_Core_DAO::getFieldValue('CRM_Contact_DAO_Contact', $this->_cid, 'display_name');
300 $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>';
301 CRM_Core_Session::setStatus($message, ts('Contacts Merged'), 'success');
302
c0cc2ad4 303 $urlParams = ['reset' => 1, 'cid' => $this->_cid, 'rgid' => $this->_rgid, 'gid' => $this->_gid, 'limit' => $this->limit];
304 $contactViewUrl = CRM_Utils_System::url('civicrm/contact/view', ['reset' => 1, 'cid' => $this->_cid]);
dc6285d5 305
a7488080 306 if (!empty($formValues['_qf_Merge_submit'])) {
c0cc2ad4 307 $urlParams['action'] = "update";
308 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind',
dc6285d5 309 $urlParams
c0cc2ad4 310 ));
6a488035 311 }
353ffa53 312 if (!empty($formValues['_qf_Merge_done'])) {
c0cc2ad4 313 CRM_Utils_System::redirect($contactViewUrl);
6a488035
TO
314 }
315
316 if ($this->next && $this->_mergeId) {
2ae26001 317 $cacheKey = CRM_Dedupe_Merger::getMergeCacheKeyString($this->_rgid, $this->_gid);
6a488035 318
2ae26001 319 $join = CRM_Dedupe_Merger::getJoinOnDedupeTable();
6a488035
TO
320 $where = "de.id IS NULL";
321
322 $pos = CRM_Core_BAO_PrevNextCache::getPositions($cacheKey, NULL, NULL, $this->_mergeId, $join, $where);
323
324 if (!empty($pos) &&
325 $pos['next']['id1'] &&
326 $pos['next']['id2']
327 ) {
328
c0cc2ad4 329 $urlParams['cid'] = $pos['next']['id1'];
330 $urlParams['oid'] = $pos['next']['id2'];
331 $urlParams['mergeId'] = $pos['next']['mergeId'];
332 $urlParams['action'] = 'update';
333 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/merge', $urlParams));
6a488035
TO
334 }
335 }
336
c0cc2ad4 337 // Perhaps never reached.
338 CRM_Utils_System::redirect($contactViewUrl);
6a488035 339 }
96025800 340
fa74cac4 341 /**
342 * Bounce if the merge action is invalid.
343 *
344 * We don't allow the merge if it is nonsensical, marked as a duplicate
345 * or outside the user's permission.
346 *
347 * @param int $cid
348 * Contact ID to retain
349 * @param int $oid
350 * Contact ID to delete.
351 */
352 public function bounceIfInvalid($cid, $oid) {
353 if ($cid == $oid) {
354 CRM_Core_Error::statusBounce(ts('Cannot merge a contact with itself.'));
355 }
356
357 if (!CRM_Dedupe_BAO_Rule::validateContacts($cid, $oid)) {
358 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'))));
359 }
360
361 if (!(CRM_Contact_BAO_Contact_Permission::allow($cid, CRM_Core_Permission::EDIT) &&
362 CRM_Contact_BAO_Contact_Permission::allow($oid, CRM_Core_Permission::EDIT)
363 )
364 ) {
365 CRM_Utils_System::permissionDenied();
366 }
367 // ensure that oid is not the current user, if so refuse to do the merge
368 if (CRM_Core_Session::singleton()->getLoggedInContactID() == $oid) {
369 $message = ts('The contact record which is linked to the currently logged in user account - \'%1\' - cannot be deleted.',
370 array(1 => CRM_Core_Session::singleton()->getLoggedInContactDisplayName())
371 );
372 CRM_Core_Error::statusBounce($message);
373 }
374 }
375
6a488035 376}