Merge pull request #10532 from monishdeb/CRM-20488_soft_credit_organization
[civicrm-core.git] / CRM / Core / BAO / UFMatch.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7e9e8871 4 | CiviCRM version 4.7 |
6a488035 5 +--------------------------------------------------------------------+
0f03f337 6 | Copyright CiviCRM LLC (c) 2004-2017 |
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
0f03f337 31 * @copyright CiviCRM LLC (c) 2004-2017
6a488035
TO
32 */
33
34/**
8eedd10a 35 * The basic class that interfaces with the external user framework.
6a488035
TO
36 */
37class CRM_Core_BAO_UFMatch extends CRM_Core_DAO_UFMatch {
38
b5c2afd0 39 /**
b44e3f84 40 * Create UF Match, Note that this function is here in it's simplest form @ the moment
b5c2afd0 41 *
ced9bfed
EM
42 * @param $params
43 *
44 * @return \CRM_Core_DAO_UFMatch
b5c2afd0 45 */
00be9182 46 public static function create($params) {
6a488035
TO
47 $hook = empty($params['id']) ? 'create' : 'edit';
48 CRM_Utils_Hook::pre($hook, 'UFMatch', CRM_Utils_Array::value('id', $params), $params);
22e263ad 49 if (empty($params['domain_id'])) {
e0e29b3c 50 $params['domain_id'] = CRM_Core_Config::domainID();
51 }
6a488035
TO
52 $dao = new CRM_Core_DAO_UFMatch();
53 $dao->copyValues($params);
69301426 54 if (!$dao->find(TRUE)) {
55 $dao->save();
56 }
6a488035
TO
57 CRM_Utils_Hook::post($hook, 'UFMatch', $dao->id, $dao);
58 return $dao;
59 }
60
61
62 /**
63 * Given a UF user object, make sure there is a contact
64 * object for this user. If the user has new values, we need
65 * to update the CRM DB with the new values
66 *
6a0b768e
TO
67 * @param Object $user
68 * The drupal user object.
69 * @param bool $update
70 * Has the user object been edited.
77b97be7
EM
71 * @param $uf
72 *
73 * @param $ctype
74 * @param bool $isLogin
6a488035 75 */
00be9182 76 public static function synchronize(&$user, $update, $uf, $ctype, $isLogin = FALSE) {
2b617cb0 77 $userSystem = CRM_Core_Config::singleton()->userSystem;
6a488035
TO
78 $session = CRM_Core_Session::singleton();
79 if (!is_object($session)) {
80 CRM_Core_Error::fatal('wow, session is not an object?');
81 return;
82 }
83
2b617cb0
EM
84 $userSystemID = $userSystem->getBestUFID($user);
85 $uniqId = $userSystem->getBestUFUniqueIdentifier($user);
6a488035
TO
86
87 // if the id of the object is zero (true for anon users in drupal)
88 // have we already processed this user, if so early
89 // return.
90 $userID = $session->get('userID');
91 $ufID = $session->get('ufID');
92
32998c82 93 if (!$update && $ufID == $userSystemID) {
6a488035
TO
94 return;
95 }
96
97 //check do we have logged in user.
98 $isUserLoggedIn = CRM_Utils_System::isUserLoggedIn();
99
100 // reset the session if we are a different user
32998c82 101 if ($ufID && $ufID != $userSystemID) {
6a488035
TO
102 $session->reset();
103
104 //get logged in user ids, and set to session.
105 if ($isUserLoggedIn) {
106 $userIds = self::getUFValues();
107 $session->set('ufID', CRM_Utils_Array::value('uf_id', $userIds, ''));
108 $session->set('userID', CRM_Utils_Array::value('contact_id', $userIds, ''));
109 $session->set('ufUniqID', CRM_Utils_Array::value('uf_name', $userIds, ''));
110 }
111 }
112
113 // return early
32998c82 114 if ($userSystemID == 0) {
6a488035
TO
115 return;
116 }
117
32998c82 118 $ufmatch = self::synchronizeUFMatch($user, $userSystemID, $uniqId, $uf, NULL, $ctype, $isLogin);
6a488035
TO
119 if (!$ufmatch) {
120 return;
121 }
122
123 //make sure we have session w/ consistent ids.
353ffa53
TO
124 $ufID = $ufmatch->uf_id;
125 $userID = $ufmatch->contact_id;
6a488035
TO
126 $ufUniqID = '';
127 if ($isUserLoggedIn) {
128 $loggedInUserUfID = CRM_Utils_System::getLoggedInUfID();
129 //are we processing logged in user.
130 if ($loggedInUserUfID && $loggedInUserUfID != $ufID) {
353ffa53
TO
131 $userIds = self::getUFValues($loggedInUserUfID);
132 $ufID = CRM_Utils_Array::value('uf_id', $userIds, '');
133 $userID = CRM_Utils_Array::value('contact_id', $userIds, '');
6a488035
TO
134 $ufUniqID = CRM_Utils_Array::value('uf_name', $userIds, '');
135 }
136 }
137
138 //set user ids to session.
139 $session->set('ufID', $ufID);
140 $session->set('userID', $userID);
141 $session->set('ufUniqID', $ufUniqID);
142
143 // add current contact to recently viewed
144 if ($ufmatch->contact_id) {
79d7553f 145 list($displayName, $contactImage, $contactType, $contactSubtype, $contactImageUrl)
146 = CRM_Contact_BAO_Contact::getDisplayAndImage($ufmatch->contact_id, TRUE, TRUE);
6a488035
TO
147
148 $otherRecent = array(
149 'imageUrl' => $contactImageUrl,
150 'subtype' => $contactSubtype,
151 'editUrl' => CRM_Utils_System::url('civicrm/contact/add', "reset=1&action=update&cid={$ufmatch->contact_id}"),
152 );
153
154 CRM_Utils_Recent::add($displayName,
155 CRM_Utils_System::url('civicrm/contact/view', "reset=1&cid={$ufmatch->contact_id}"),
156 $ufmatch->contact_id,
157 $contactType,
158 $ufmatch->contact_id,
159 $displayName,
160 $otherRecent
161 );
162 }
163 }
164
165 /**
166 * Synchronize the object with the UF Match entry. Can be called stand-alone from
167 * the drupalUsers script
168 *
6a0b768e
TO
169 * @param Object $user
170 * The drupal user object.
171 * @param string $userKey
172 * The id of the user from the uf object.
173 * @param string $uniqId
174 * The OpenID of the user.
175 * @param string $uf
176 * The name of the user framework.
177 * @param int $status
178 * Returns the status if user created or already exits (used for CMS sync).
72b3a70c
CW
179 * @param string $ctype
180 * contact type
fd31fa4c 181 * @param bool $isLogin
6a488035 182 *
72b3a70c 183 * @return CRM_Core_DAO_UFMatch|bool
6a488035 184 */
00be9182 185 public static function &synchronizeUFMatch(&$user, $userKey, $uniqId, $uf, $status = NULL, $ctype = NULL, $isLogin = FALSE) {
6a488035
TO
186 $config = CRM_Core_Config::singleton();
187
188 if (!CRM_Utils_Rule::email($uniqId)) {
189 $retVal = $status ? NULL : FALSE;
190 return $retVal;
191 }
192
193 $newContact = FALSE;
194
195 // make sure that a contact id exists for this user id
353ffa53 196 $ufmatch = new CRM_Core_DAO_UFMatch();
6a488035 197 $ufmatch->domain_id = CRM_Core_Config::domainID();
353ffa53 198 $ufmatch->uf_id = $userKey;
6a488035
TO
199
200 if (!$ufmatch->find(TRUE)) {
201 $transaction = new CRM_Core_Transaction();
202
203 $dao = NULL;
204 if (!empty($_POST) && !$isLogin) {
205 $params = $_POST;
206 $params['email'] = $uniqId;
207
03b40a7b 208 $ids = CRM_Contact_BAO_Contact::getDuplicateContacts($params, 'Individual', 'Unsupervised', array(), FALSE);
6a488035 209
aaffa79f 210 if (!empty($ids) && Civi::settings()->get('uniq_email_per_site')) {
6a488035
TO
211 // restrict dupeIds to ones that belong to current domain/site.
212 $siteContacts = CRM_Core_BAO_Domain::getContactList();
213 foreach ($ids as $index => $dupeId) {
214 if (!in_array($dupeId, $siteContacts)) {
215 unset($ids[$index]);
216 }
217 }
218 // re-index the array
219 $ids = array_values($ids);
220 }
221 if (!empty($ids)) {
222 $dao = new CRM_Core_DAO();
223 $dao->contact_id = $ids[0];
224 }
225 }
226 else {
227 $dao = CRM_Contact_BAO_Contact::matchContactOnEmail($uniqId, $ctype);
228 }
229
230 $found = FALSE;
231 if ($dao) {
232 // ensure there does not exists a contact_id / uf_id pair
233 // in the DB. This might be due to multiple emails per contact
234 // CRM-9091
235 $sql = "
236SELECT id
237FROM civicrm_uf_match
238WHERE contact_id = %1
239AND domain_id = %2
240";
241 $params = array(
242 1 => array($dao->contact_id, 'Integer'),
243 2 => array(CRM_Core_Config::domainID(), 'Integer'),
244 );
245 $conflict = CRM_Core_DAO::singleValueQuery($sql, $params);
246
247 if (!$conflict) {
353ffa53 248 $found = TRUE;
6a488035 249 $ufmatch->contact_id = $dao->contact_id;
353ffa53 250 $ufmatch->uf_name = $uniqId;
6a488035
TO
251 }
252 }
253
254 if (!$found) {
3eb59ab5
AS
255 // Not sure why we're testing for this. Is there ever a case
256 // in which $user is not an object?
b7d765aa 257 if (is_object($user)) {
3eb59ab5
AS
258 if ($config->userSystem->is_drupal) {
259 $primary_email = $uniqId;
260 }
261 elseif ($uf == 'WordPress') {
262 $primary_email = $user->user_email;
263 }
264 else {
265 $primary_email = $user->email;
266 }
267 $params = array('email-Primary' => $primary_email);
6a488035
TO
268 }
269
270 if ($ctype == 'Organization') {
271 $params['organization_name'] = $uniqId;
272 }
273 elseif ($ctype == 'Household') {
274 $params['household_name'] = $uniqId;
275 }
276
277 if (!$ctype) {
278 $ctype = "Individual";
279 }
280 $params['contact_type'] = $ctype;
281
282 // extract first / middle / last name
283 // for joomla
284 if ($uf == 'Joomla' && $user->name) {
285 CRM_Utils_String::extractName($user->name, $params);
286 }
287
288 if ($uf == 'WordPress') {
289 if ($user->first_name) {
290 $params['first_name'] = $user->first_name;
291 }
292
293 if ($user->last_name) {
294 $params['last_name'] = $user->last_name;
295 }
296 }
297
353ffa53 298 $contactId = CRM_Contact_BAO_Contact::createProfileContact($params, CRM_Core_DAO::$_nullArray);
6a488035 299 $ufmatch->contact_id = $contactId;
353ffa53 300 $ufmatch->uf_name = $uniqId;
6a488035
TO
301 }
302
303 // check that there are not two CMS IDs matching the same CiviCRM contact - this happens when a civicrm
304 // user has two e-mails and there is a cms match for each of them
305 // the gets rid of the nasty fata error but still reports the error
306 $sql = "
307SELECT uf_id
308FROM civicrm_uf_match
309WHERE ( contact_id = %1
310OR uf_name = %2
311OR uf_id = %3 )
312AND domain_id = %4
313";
314 $params = array(
315 1 => array($ufmatch->contact_id, 'Integer'),
316 2 => array($ufmatch->uf_name, 'String'),
317 3 => array($ufmatch->uf_id, 'Integer'),
318 4 => array($ufmatch->domain_id, 'Integer'),
319 );
320
321 $conflict = CRM_Core_DAO::singleValueQuery($sql, $params);
322
323 if (!$conflict) {
324 $ufmatch = CRM_Core_BAO_UFMatch::create((array) $ufmatch);
325 $ufmatch->free();
326 $newContact = TRUE;
327 $transaction->commit();
328 }
329 else {
330 $msg = ts("Contact ID %1 is a match for %2 user %3 but has already been matched to %4",
331 array(
332 1 => $ufmatch->contact_id,
333 2 => $uf,
334 3 => $ufmatch->uf_id,
21dfd5f5 335 4 => $conflict,
6a488035
TO
336 )
337 );
338 unset($conflict);
339 }
340 }
341
342 if ($status) {
343 return $newContact;
344 }
345 else {
346 return $ufmatch;
347 }
348 }
349
350 /**
fe482240 351 * Update the uf_name in the user object.
6a488035 352 *
6a0b768e
TO
353 * @param int $contactId
354 * Id of the contact to update.
6a488035 355 */
00be9182 356 public static function updateUFName($contactId) {
5e7f101a 357 if (!Civi::settings()->get('syncCMSEmail') || !$contactId) {
6a488035
TO
358 return;
359 }
5e7f101a 360
6a488035
TO
361 $config = CRM_Core_Config::singleton();
362 $ufName = CRM_Contact_BAO_Contact::getPrimaryEmail($contactId);
363
364 if (!$ufName) {
365 return;
366 }
367
368 $update = FALSE;
369
370 // 1.do check for contact Id.
353ffa53 371 $ufmatch = new CRM_Core_DAO_UFMatch();
6a488035 372 $ufmatch->contact_id = $contactId;
353ffa53 373 $ufmatch->domain_id = CRM_Core_Config::domainID();
6a488035
TO
374 if (!$ufmatch->find(TRUE)) {
375 return;
376 }
377 if ($ufmatch->uf_name != $ufName) {
378 $update = TRUE;
379 }
380
381 // CRM-6928
382 // 2.do check for duplicate ufName.
383 $ufDupeName = new CRM_Core_DAO_UFMatch();
384 $ufDupeName->uf_name = $ufName;
385 $ufDupeName->domain_id = CRM_Core_Config::domainID();
386 if ($ufDupeName->find(TRUE) &&
387 $ufDupeName->contact_id != $contactId
388 ) {
389 $update = FALSE;
390 }
391
392 if (!$update) {
393 return;
394 }
395
396 // save the updated ufmatch object
397 $ufmatch->uf_name = $ufName;
398 $ufmatch->save();
399 $config->userSystem->updateCMSName($ufmatch->uf_id, $ufName);
400 }
401
402 /**
fe482240 403 * Update the email value for the contact and user profile.
6a488035 404 *
5a4f6742
CW
405 * @param int $contactId
406 * Contact ID of the user.
8eedd10a 407 * @param string $emailAddress
6a0b768e 408 * Email to be modified for the user.
6a488035 409 */
00be9182 410 public static function updateContactEmail($contactId, $emailAddress) {
6a488035
TO
411 $strtolower = function_exists('mb_strtolower') ? 'mb_strtolower' : 'strtolower';
412 $emailAddress = $strtolower($emailAddress);
413
353ffa53 414 $ufmatch = new CRM_Core_DAO_UFMatch();
6a488035 415 $ufmatch->contact_id = $contactId;
353ffa53 416 $ufmatch->domain_id = CRM_Core_Config::domainID();
6a488035
TO
417 if ($ufmatch->find(TRUE)) {
418 // Save the email in UF Match table
419 $ufmatch->uf_name = $emailAddress;
420 CRM_Core_BAO_UFMatch::create((array) $ufmatch);
421
5e7f101a 422 // If CMS integration is disabled skip Civi email update if CMS user email is changed
423 if (Civi::settings()->get('syncCMSEmail') == FALSE) {
424 return;
425 }
426
6a488035
TO
427 //check if the primary email for the contact exists
428 //$contactDetails[1] - email
429 //$contactDetails[3] - email id
430 $contactDetails = CRM_Contact_BAO_Contact_Location::getEmailDetails($contactId);
431
432 if (trim($contactDetails[1])) {
433 $emailID = $contactDetails[3];
434 //update if record is found
435 $query = "UPDATE civicrm_email
436 SET email = %1
437 WHERE id = %2";
438 $p = array(
439 1 => array($emailAddress, 'String'),
440 2 => array($emailID, 'Integer'),
441 );
442 $dao = CRM_Core_DAO::executeQuery($query, $p);
443 }
444 else {
445 //else insert a new email record
353ffa53 446 $email = new CRM_Core_DAO_Email();
6a488035
TO
447 $email->contact_id = $contactId;
448 $email->is_primary = 1;
353ffa53 449 $email->email = $emailAddress;
6a488035
TO
450 $email->save();
451 $emailID = $email->id;
452 }
453
454 CRM_Core_BAO_Log::register($contactId,
455 'civicrm_email',
456 $emailID
457 );
458 }
459 }
460
461 /**
fe482240 462 * Delete the object records that are associated with this cms user.
6a488035 463 *
6a0b768e
TO
464 * @param int $ufID
465 * Id of the user to delete.
6a488035 466 */
00be9182 467 public static function deleteUser($ufID) {
6a488035
TO
468 $ufmatch = new CRM_Core_DAO_UFMatch();
469
470 $ufmatch->uf_id = $ufID;
471 $ufmatch->domain_id = CRM_Core_Config::domainID();
472 $ufmatch->delete();
473 }
474
475 /**
fe482240 476 * Get the contact_id given a uf_id.
6a488035 477 *
6a0b768e
TO
478 * @param int $ufID
479 * Id of UF for which related contact_id is required.
6a488035 480 *
a6c01b45
CW
481 * @return int
482 * contact_id on success, null otherwise
6a488035 483 */
00be9182 484 public static function getContactId($ufID) {
6a488035
TO
485 if (!isset($ufID)) {
486 return NULL;
487 }
488
489 $ufmatch = new CRM_Core_DAO_UFMatch();
490
491 $ufmatch->uf_id = $ufID;
492 $ufmatch->domain_id = CRM_Core_Config::domainID();
493 if ($ufmatch->find(TRUE)) {
494 return (int ) $ufmatch->contact_id;
495 }
496 return NULL;
497 }
498
499 /**
fe482240 500 * Get the uf_id given a contact_id.
6a488035 501 *
6a0b768e
TO
502 * @param int $contactID
503 * ID of the contact for which related uf_id is required.
6a488035 504 *
a6c01b45
CW
505 * @return int
506 * uf_id of the given contact_id on success, null otherwise
6a488035 507 */
00be9182 508 public static function getUFId($contactID) {
6a488035
TO
509 if (!isset($contactID)) {
510 return NULL;
511 }
512 $domain = CRM_Core_BAO_Domain::getDomain();
513 $ufmatch = new CRM_Core_DAO_UFMatch();
514
515 $ufmatch->contact_id = $contactID;
516 $ufmatch->domain_id = $domain->id;
517 if ($ufmatch->find(TRUE)) {
518 return $ufmatch->uf_id;
519 }
520 return NULL;
521 }
522
b5c2afd0
EM
523 /**
524 * @return bool
525 */
00be9182 526 public static function isEmptyTable() {
6a488035
TO
527 $sql = "SELECT count(id) FROM civicrm_uf_match";
528 return CRM_Core_DAO::singleValueQuery($sql) > 0 ? FALSE : TRUE;
529 }
530
531 /**
fe482240 532 * Get the list of contact_id.
6a488035
TO
533 *
534 *
a6c01b45
CW
535 * @return int
536 * contact_id on success, null otherwise
6a488035 537 */
00be9182 538 public static function getContactIDs() {
6a488035
TO
539 $id = array();
540 $dao = new CRM_Core_DAO_UFMatch();
541 $dao->find();
542 while ($dao->fetch()) {
543 $id[] = $dao->contact_id;
544 }
545 return $id;
546 }
547
548 /**
100fef9d 549 * See if this user exists, and if so, if they're allowed to login
6a488035
TO
550 *
551 *
100fef9d 552 * @param int $openId
2a6da8d7 553 *
a6c01b45
CW
554 * @return bool
555 * true if allowed to login, false otherwise
6a488035 556 */
00be9182 557 public static function getAllowedToLogin($openId) {
6a488035
TO
558 $ufmatch = new CRM_Core_DAO_UFMatch();
559 $ufmatch->uf_name = $openId;
560 $ufmatch->allowed_to_login = 1;
561 if ($ufmatch->find(TRUE)) {
562 return TRUE;
563 }
564 return FALSE;
565 }
566
567 /**
100fef9d 568 * Get the next unused uf_id value, since the standalone UF doesn't
6a488035
TO
569 * have id's (it uses OpenIDs, which go in a different field)
570 *
571 *
a6c01b45
CW
572 * @return int
573 * next highest unused value for uf_id
6a488035 574 */
00be9182 575 public static function getNextUfIdValue() {
6a488035
TO
576 $query = "SELECT MAX(uf_id)+1 AS next_uf_id FROM civicrm_uf_match";
577 $dao = CRM_Core_DAO::executeQuery($query);
578 if ($dao->fetch()) {
579 $ufId = $dao->next_uf_id;
580 }
581
582 if (!isset($ufId)) {
583 $ufId = 1;
584 }
585 return $ufId;
586 }
587
b5c2afd0
EM
588 /**
589 * @param $email
590 *
591 * @return bool
592 */
00be9182 593 public static function isDuplicateUser($email) {
6a488035
TO
594 $session = CRM_Core_Session::singleton();
595 $contactID = $session->get('userID');
596 if (!empty($email) && isset($contactID)) {
597 $dao = new CRM_Core_DAO_UFMatch();
598 $dao->uf_name = $email;
599 if ($dao->find(TRUE) && $contactID != $dao->contact_id) {
600 return TRUE;
601 }
602 }
603 return FALSE;
604 }
605
606 /**
607 * Get uf match values for given uf id or logged in user.
608 *
6a0b768e
TO
609 * @param int $ufID
610 * Uf id.
6a488035 611 *
77b97be7 612 * @return array
76e7a76c 613 * uf values.
77b97be7 614 */
00be9182 615 public static function getUFValues($ufID = NULL) {
6a488035
TO
616 if (!$ufID) {
617 //get logged in user uf id.
618 $ufID = CRM_Utils_System::getLoggedInUfID();
619 }
620 if (!$ufID) {
621 return array();
622 }
623
624 static $ufValues;
625 if ($ufID && !isset($ufValues[$ufID])) {
353ffa53
TO
626 $ufmatch = new CRM_Core_DAO_UFMatch();
627 $ufmatch->uf_id = $ufID;
6a488035
TO
628 $ufmatch->domain_id = CRM_Core_Config::domainID();
629 if ($ufmatch->find(TRUE)) {
630 $ufValues[$ufID] = array(
631 'uf_id' => $ufmatch->uf_id,
632 'uf_name' => $ufmatch->uf_name,
633 'contact_id' => $ufmatch->contact_id,
634 'domain_id' => $ufmatch->domain_id,
635 );
636 }
637 }
638 return $ufValues[$ufID];
639 }
96025800 640
d343069c
CW
641 /**
642 * @inheritDoc
643 */
20e41014 644 public function addSelectWhereClause() {
d343069c 645 // Prevent default behavior of joining ACLs onto the contact_id field
032346cc
CW
646 $clauses = array();
647 CRM_Utils_Hook::selectWhereClause($this, $clauses);
648 return $clauses;
d343069c
CW
649 }
650
6a488035 651}