Merge pull request #11702 from jmcclelland/pdf-receipt-filename
[civicrm-core.git] / CRM / Contact / Form / Merge.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.7 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2018 |
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-2018
32 */
33
34 /**
35 * Class CRM_Contact_Form_Merge.
36 */
37 class CRM_Contact_Form_Merge extends CRM_Core_Form {
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.
40 var $_cid = NULL;
41
42 // The id of the other contact - the duplicate one that will get deleted.
43 var $_oid = NULL;
44
45 var $_contactType = NULL;
46
47 /**
48 * Query limit to be retained in the urls.
49 *
50 * @var int
51 */
52 var $limit;
53
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 */
64 var $_qfZeroBug = 'e8cddb72-a257-11dc-b9cc-0016d3330ee9';
65
66 public function preProcess() {
67 try {
68
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);
72
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);
77
78 $urlParams = ['reset' => 1, 'rgid' => $this->_rgid, 'gid' => $this->_gid, 'limit' => $this->limit];
79
80 $this->bounceIfInvalid($this->_cid, $this->_oid);
81
82 $this->_contactType = civicrm_api3('Contact', 'getvalue', array(
83 'id' => $this->_cid,
84 'return' => 'contact_type',
85 ));
86
87 $browseUrl = CRM_Utils_System::url('civicrm/contact/dedupefind', array_merge($urlParams, ['action' => 'browse']));
88
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 }
102
103 $cacheKey = CRM_Dedupe_Merger::getMergeCacheKeyString($this->_rgid, $gid);
104
105 $join = CRM_Dedupe_Merger::getJoinOnDedupeTable();
106 $where = "de.id IS NULL";
107
108 $pos = CRM_Core_BAO_PrevNextCache::getPositions($cacheKey, $this->_cid, $this->_oid, $this->_mergeId, $join, $where, $flip);
109
110 // get user info of main contact.
111 $config = CRM_Core_Config::singleton();
112 CRM_Core_Config::setPermitCacheFlushMode(FALSE);
113
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);
123 }
124
125 $this->assign('mainUfId', $mainUfId);
126 $this->assign('mainUfName', $mainUser ? $mainUser->name : NULL);
127 }
128 $flipParams = array_merge($urlParams, ['action' => 'update', 'cid' => $this->_oid, 'oid' => $this->_cid]);
129 if (!$flip) {
130 $flipParams['flip'] = '1';
131 }
132 $flipUrl = CRM_Utils_System::url('civicrm/contact/merge',
133 $flipParams
134 );
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']) {
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);
151 $this->assign($position, $this->$position);
152 }
153 }
154 }
155
156 // get user info of other contact.
157 $otherUfId = CRM_Core_BAO_UFMatch::getUFId($this->_oid);
158 $otherUser = NULL;
159
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 }
168
169 $this->assign('otherUfId', $otherUfId);
170 $this->assign('otherUfName', $otherUser ? $otherUser->name : NULL);
171 }
172
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 );
218 }
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);
224 }
225
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()));
232 }
233 }
234
235 public function addRules() {
236 }
237
238 public function buildQuickForm() {
239 $this->unsavedChangesWarn = FALSE;
240 CRM_Utils_System::setTitle(ts('Merge %1 contacts', array(1 => $this->_contactType)));
241 $buttons = array();
242
243 $buttons[] = array(
244 'type' => 'next',
245 'name' => $this->next ? ts('Merge and go to Next Pair') : ts('Merge'),
246 'isDefault' => TRUE,
247 'icon' => $this->next ? 'circle-triangle-e' : 'check',
248 );
249
250 if ($this->next || $this->prev) {
251 $buttons[] = array(
252 'type' => 'submit',
253 'name' => ts('Merge and go to Listing'),
254 );
255 $buttons[] = array(
256 'type' => 'done',
257 'name' => ts('Merge and View Result'),
258 'icon' => 'fa-check-circle',
259 );
260 }
261
262 $buttons[] = array(
263 'type' => 'cancel',
264 'name' => ts('Cancel'),
265 );
266
267 $this->addButtons($buttons);
268 $this->addFormRule(array('CRM_Contact_Form_Merge', 'formRule'), $this);
269 }
270
271 /**
272 * @param $fields
273 * @param $files
274 * @param $self
275 *
276 * @return array
277 */
278 public static function formRule($fields, $files, $self) {
279 $errors = array();
280 $link = CRM_Utils_System::href(ts('Flip between the original and duplicate contacts.'),
281 'civicrm/contact/merge',
282 'reset=1&action=update&cid=' . $self->_oid . '&oid=' . $self->_cid . '&rgid=' . $self->_rgid . '&flip=1'
283 );
284 if (CRM_Contact_BAO_Contact::checkDomainContact($self->_oid)) {
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));
286 }
287 return $errors;
288 }
289
290 public function postProcess() {
291 $formValues = $this->exportValues();
292
293 $formValues['main_details'] = $this->_mainDetails;
294 $formValues['other_details'] = $this->_otherDetails;
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']);
298
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
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]);
305
306 if (!empty($formValues['_qf_Merge_submit'])) {
307 $urlParams['action'] = "update";
308 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/contact/dedupefind',
309 $urlParams
310 ));
311 }
312 if (!empty($formValues['_qf_Merge_done'])) {
313 CRM_Utils_System::redirect($contactViewUrl);
314 }
315
316 if ($this->next && $this->_mergeId) {
317 $cacheKey = CRM_Dedupe_Merger::getMergeCacheKeyString($this->_rgid, $this->_gid);
318
319 $join = CRM_Dedupe_Merger::getJoinOnDedupeTable();
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
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));
334 }
335 }
336
337 // Perhaps never reached.
338 CRM_Utils_System::redirect($contactViewUrl);
339 }
340
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
376 }