3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
19 * The basic class that interfaces with the external user framework.
21 class CRM_Core_BAO_UFMatch
extends CRM_Core_DAO_UFMatch
{
24 * Create UF Match, Note that this function is here in it's simplest form @ the moment
28 * @return \CRM_Core_DAO_UFMatch
30 public static function create($params) {
31 $hook = empty($params['id']) ?
'create' : 'edit';
32 CRM_Utils_Hook
::pre($hook, 'UFMatch', CRM_Utils_Array
::value('id', $params), $params);
33 if (empty($params['domain_id'])) {
34 $params['domain_id'] = CRM_Core_Config
::domainID();
36 $dao = new CRM_Core_DAO_UFMatch();
37 $dao->copyValues($params);
38 // Fixme: this function cannot update records
39 if (!$dao->find(TRUE)) {
41 Civi
::$statics[__CLASS__
][$params['domain_id']][(int) $dao->contact_id
] = (int) $dao->uf_id
;
42 CRM_Utils_Hook
::post($hook, 'UFMatch', $dao->id
, $dao);
48 * Given a UF user object, make sure there is a contact
49 * object for this user. If the user has new values, we need
50 * to update the CRM DB with the new values
53 * The drupal user object.
55 * Has the user object been edited.
59 * @param bool $isLogin
61 * @throws CRM_Core_Exception
63 public static function synchronize(&$user, $update, $uf, $ctype, $isLogin = FALSE) {
64 $userSystem = CRM_Core_Config
::singleton()->userSystem
;
65 $session = CRM_Core_Session
::singleton();
66 if (!is_object($session)) {
67 throw new CRM_Core_Exception('wow, session is not an object?');
71 $userSystemID = $userSystem->getBestUFID($user);
72 $uniqId = $userSystem->getBestUFUniqueIdentifier($user);
74 // if the id of the object is zero (true for anon users in drupal)
75 // have we already processed this user, if so early
77 $userID = $session->get('userID');
78 $ufID = $session->get('ufID');
80 if (!$update && $ufID == $userSystemID) {
84 //check do we have logged in user.
85 $isUserLoggedIn = CRM_Utils_System
::isUserLoggedIn();
87 // reset the session if we are a different user
88 if ($ufID && $ufID != $userSystemID) {
91 //get logged in user ids, and set to session.
92 if ($isUserLoggedIn) {
93 $userIds = self
::getUFValues();
94 $session->set('ufID', CRM_Utils_Array
::value('uf_id', $userIds, ''));
95 $session->set('userID', CRM_Utils_Array
::value('contact_id', $userIds, ''));
100 if ($userSystemID == 0) {
104 $ufmatch = self
::synchronizeUFMatch($user, $userSystemID, $uniqId, $uf, NULL, $ctype, $isLogin);
109 //make sure we have session w/ consistent ids.
110 $ufID = $ufmatch->uf_id
;
111 $userID = $ufmatch->contact_id
;
112 if ($isUserLoggedIn) {
113 $loggedInUserUfID = CRM_Utils_System
::getLoggedInUfID();
114 //are we processing logged in user.
115 if ($loggedInUserUfID && $loggedInUserUfID != $ufID) {
116 $userIds = self
::getUFValues($loggedInUserUfID);
117 $ufID = CRM_Utils_Array
::value('uf_id', $userIds, '');
118 $userID = CRM_Utils_Array
::value('contact_id', $userIds, '');
122 //set user ids to session.
123 $session->set('ufID', $ufID);
124 $session->set('userID', $userID);
126 // add current contact to recently viewed
127 if ($ufmatch->contact_id
) {
128 list($displayName, $contactImage, $contactType, $contactSubtype, $contactImageUrl)
129 = CRM_Contact_BAO_Contact
::getDisplayAndImage($ufmatch->contact_id
, TRUE, TRUE);
132 'imageUrl' => $contactImageUrl,
133 'subtype' => $contactSubtype,
134 'editUrl' => CRM_Utils_System
::url('civicrm/contact/add', "reset=1&action=update&cid={$ufmatch->contact_id}"),
137 CRM_Utils_Recent
::add($displayName,
138 CRM_Utils_System
::url('civicrm/contact/view', "reset=1&cid={$ufmatch->contact_id}"),
139 $ufmatch->contact_id
,
141 $ufmatch->contact_id
,
149 * Synchronize the object with the UF Match entry. Can be called stand-alone from
150 * the drupalUsers script
152 * @param Object $user
153 * The drupal user object.
154 * @param string $userKey
155 * The id of the user from the uf object.
156 * @param string $uniqId
157 * The OpenID of the user.
159 * The name of the user framework.
161 * Returns the status if user created or already exits (used for CMS sync).
162 * @param string $ctype
164 * @param bool $isLogin
166 * @return CRM_Core_DAO_UFMatch|bool
168 public static function &synchronizeUFMatch(&$user, $userKey, $uniqId, $uf, $status = NULL, $ctype = NULL, $isLogin = FALSE) {
169 $config = CRM_Core_Config
::singleton();
172 // make sure that a contact id exists for this user id
173 $ufmatch = new CRM_Core_DAO_UFMatch();
174 $ufmatch->domain_id
= CRM_Core_Config
::domainID();
175 $ufmatch->uf_id
= $userKey;
177 if (!$ufmatch->find(TRUE)) {
178 $transaction = new CRM_Core_Transaction();
181 if (!empty($_POST) && !$isLogin) {
183 $params['email'] = $uniqId;
184 // dev/core#1858 Ensure that if we have a contactID parameter which is set in the Create user Record contact task form
185 // That this contacID value is passed through as the contact_id to the get duplicate contacts function. This is necessary because for Drupal 8 this function gets invoked
186 // Before the civicrm_uf_match record is added where as in D7 it isn't called until the user tries to actually login.
187 if (!empty($params['contactID'])) {
188 $params['contact_id'] = $params['contactID'];
191 $ids = CRM_Contact_BAO_Contact
::getDuplicateContacts($params, 'Individual', 'Unsupervised', [], FALSE);
193 if (!empty($ids) && Civi
::settings()->get('uniq_email_per_site')) {
194 // restrict dupeIds to ones that belong to current domain/site.
195 $siteContacts = CRM_Core_BAO_Domain
::getContactList();
196 foreach ($ids as $index => $dupeId) {
197 if (!in_array($dupeId, $siteContacts)) {
201 // re-index the array
202 $ids = array_values($ids);
205 $dao = new CRM_Core_DAO();
206 $dao->contact_id
= $ids[0];
210 $dao = CRM_Contact_BAO_Contact
::matchContactOnEmail($uniqId, $ctype);
215 // ensure there does not exists a contact_id / uf_id pair
216 // in the DB. This might be due to multiple emails per contact
220 FROM civicrm_uf_match
221 WHERE contact_id = %1
225 1 => [$dao->contact_id
, 'Integer'],
226 2 => [CRM_Core_Config
::domainID(), 'Integer'],
228 $conflict = CRM_Core_DAO
::singleValueQuery($sql, $params);
232 $ufmatch->contact_id
= $dao->contact_id
;
233 $ufmatch->uf_name
= $uniqId;
238 // Not sure why we're testing for this. Is there ever a case
239 // in which $user is not an object?
240 if (is_object($user)) {
241 if ($config->userSystem
->is_drupal
) {
242 $primary_email = $uniqId;
244 elseif ($uf == 'WordPress') {
245 $primary_email = $user->user_email
;
248 $primary_email = $user->email
;
250 $params = ['email-Primary' => $primary_email];
253 if ($ctype == 'Organization') {
254 $params['organization_name'] = $uniqId;
256 elseif ($ctype == 'Household') {
257 $params['household_name'] = $uniqId;
261 $ctype = "Individual";
263 $params['contact_type'] = $ctype;
265 // extract first / middle / last name
267 if ($uf == 'Joomla' && $user->name
) {
268 CRM_Utils_String
::extractName($user->name
, $params);
271 if ($uf == 'WordPress') {
272 if ($user->first_name
) {
273 $params['first_name'] = $user->first_name
;
276 if ($user->last_name
) {
277 $params['last_name'] = $user->last_name
;
281 $contactId = CRM_Contact_BAO_Contact
::createProfileContact($params);
282 $ufmatch->contact_id
= $contactId;
283 $ufmatch->uf_name
= $uniqId;
286 // check that there are not two CMS IDs matching the same CiviCRM contact - this happens when a civicrm
287 // user has two e-mails and there is a cms match for each of them
288 // the gets rid of the nasty fata error but still reports the error
291 FROM civicrm_uf_match
292 WHERE ( contact_id = %1
298 1 => [$ufmatch->contact_id
, 'Integer'],
299 2 => [$ufmatch->uf_name
, 'String'],
300 3 => [$ufmatch->uf_id
, 'Integer'],
301 4 => [$ufmatch->domain_id
, 'Integer'],
304 $conflict = CRM_Core_DAO
::singleValueQuery($sql, $params);
307 $ufmatch = CRM_Core_BAO_UFMatch
::create((array) $ufmatch);
309 $transaction->commit();
312 $msg = ts("Contact ID %1 is a match for %2 user %3 but has already been matched to %4",
314 1 => $ufmatch->contact_id
,
316 3 => $ufmatch->uf_id
,
333 * Update the uf_name in the user object.
335 * @param int $contactId
336 * Id of the contact to update.
338 public static function updateUFName($contactId) {
339 if (!Civi
::settings()->get('syncCMSEmail') ||
!$contactId) {
343 // 1.do check for contact Id.
344 $ufmatch = new CRM_Core_DAO_UFMatch();
345 $ufmatch->contact_id
= $contactId;
346 $ufmatch->domain_id
= CRM_Core_Config
::domainID();
347 if (!$ufmatch->find(TRUE)) {
351 $config = CRM_Core_Config
::singleton();
352 $ufName = CRM_Contact_BAO_Contact
::getPrimaryEmail($contactId);
360 if ($ufmatch->uf_name
!= $ufName) {
365 // 2.do check for duplicate ufName.
366 $ufDupeName = new CRM_Core_DAO_UFMatch();
367 $ufDupeName->uf_name
= $ufName;
368 $ufDupeName->domain_id
= CRM_Core_Config
::domainID();
369 if ($ufDupeName->find(TRUE) &&
370 $ufDupeName->contact_id
!= $contactId
379 // save the updated ufmatch object
380 $ufmatch->uf_name
= $ufName;
382 $config->userSystem
->updateCMSName($ufmatch->uf_id
, $ufName);
386 * Update the email value for the contact and user profile.
388 * @param int $contactId
389 * Contact ID of the user.
390 * @param string $emailAddress
391 * Email to be modified for the user.
393 public static function updateContactEmail($contactId, $emailAddress) {
394 $strtolower = function_exists('mb_strtolower') ?
'mb_strtolower' : 'strtolower';
395 $emailAddress = $strtolower($emailAddress);
397 $ufmatch = new CRM_Core_DAO_UFMatch();
398 $ufmatch->contact_id
= $contactId;
399 $ufmatch->domain_id
= CRM_Core_Config
::domainID();
400 if ($ufmatch->find(TRUE)) {
401 // Save the email in UF Match table
402 $ufmatch->uf_name
= $emailAddress;
403 CRM_Core_BAO_UFMatch
::create((array) $ufmatch);
405 // If CMS integration is disabled skip Civi email update if CMS user email is changed
406 if (Civi
::settings()->get('syncCMSEmail') == FALSE) {
410 //check if the primary email for the contact exists
411 //$contactDetails[1] - email
412 //$contactDetails[3] - email id
413 $contactDetails = CRM_Contact_BAO_Contact_Location
::getEmailDetails($contactId);
415 if (trim($contactDetails[1])) {
416 //update if record is found but different
417 $emailID = $contactDetails[3];
418 if (trim($contactDetails[1]) != $emailAddress) {
419 civicrm_api3('Email', 'create', [
421 'email' => $emailAddress,
426 //else insert a new email record
427 $result = civicrm_api3('Email', 'create', [
428 'contact_id' => $contactId,
429 'email' => $emailAddress,
432 $emailID = $result['id'];
435 CRM_Core_BAO_Log
::register($contactId,
443 * Delete the object records that are associated with this cms user.
446 * Id of the user to delete.
448 public static function deleteUser($ufID) {
449 $ufmatch = new CRM_Core_DAO_UFMatch();
451 $ufmatch->uf_id
= $ufID;
452 $ufmatch->domain_id
= $domainId = CRM_Core_Config
::domainID();
456 Civi
::$statics[__CLASS__
][$domainId] = [];
460 * Get the contact_id given a uf_id.
463 * Id of UF for which related contact_id is required.
466 * contact_id on success, null otherwise
468 public static function getContactId($ufID) {
472 $domainId = CRM_Core_Config
::domainID();
474 if (!isset(Civi
::$statics[__CLASS__
][$domainId])) {
475 Civi
::$statics[__CLASS__
][$domainId] = [];
477 $contactId = array_search($ufID, Civi
::$statics[__CLASS__
][$domainId]);
481 $ufmatch = new CRM_Core_DAO_UFMatch();
482 $ufmatch->uf_id
= $ufID;
483 $ufmatch->domain_id
= $domainId;
484 if ($ufmatch->find(TRUE)) {
485 $contactId = (int) $ufmatch->contact_id
;
486 Civi
::$statics[__CLASS__
][$domainId][$contactId] = (int) $ufID;
493 * Get the uf_id given a contact_id.
495 * @param int $contactID
496 * ID of the contact for which related uf_id is required.
499 * uf_id of the given contact_id on success, null otherwise
501 public static function getUFId($contactID) {
505 $domainId = CRM_Core_Config
::domainID();
506 $contactID = (int) $contactID;
508 if (empty(Civi
::$statics[__CLASS__
][$domainId]) ||
!array_key_exists($contactID, Civi
::$statics[__CLASS__
][$domainId])) {
509 Civi
::$statics[__CLASS__
][$domainId][$contactID] = NULL;
510 $ufmatch = new CRM_Core_DAO_UFMatch();
511 $ufmatch->contact_id
= $contactID;
512 $ufmatch->domain_id
= $domainId;
513 if ($ufmatch->find(TRUE)) {
514 Civi
::$statics[__CLASS__
][$domainId][$contactID] = (int) $ufmatch->uf_id
;
517 return Civi
::$statics[__CLASS__
][$domainId][$contactID];
524 public static function isEmptyTable() {
525 CRM_Core_Error
::deprecatedFunctionWarning('unused function to be removed');
526 $sql = "SELECT count(id) FROM civicrm_uf_match";
527 return CRM_Core_DAO
::singleValueQuery($sql) > 0 ?
FALSE : TRUE;
531 * Get the list of contact_id.
535 * contact_id on success, null otherwise
537 public static function getContactIDs() {
538 CRM_Core_Error
::deprecatedFunctionWarning('unused function to be removed');
540 $dao = new CRM_Core_DAO_UFMatch();
542 while ($dao->fetch()) {
543 $id[] = $dao->contact_id
;
549 * See if this user exists, and if so, if they're allowed to login
555 * true if allowed to login, false otherwise
557 public static function getAllowedToLogin($openId) {
558 CRM_Core_Error
::deprecatedFunctionWarning('unused function to be removed');
559 $ufmatch = new CRM_Core_DAO_UFMatch();
560 $ufmatch->uf_name
= $openId;
561 $ufmatch->allowed_to_login
= 1;
562 if ($ufmatch->find(TRUE)) {
569 * Get the next unused uf_id value, since the standalone UF doesn't
570 * have id's (it uses OpenIDs, which go in a different field)
574 * next highest unused value for uf_id
576 public static function getNextUfIdValue() {
577 CRM_Core_Error
::deprecatedFunctionWarning('unused function to be removed');
578 $query = "SELECT MAX(uf_id)+1 AS next_uf_id FROM civicrm_uf_match";
579 $dao = CRM_Core_DAO
::executeQuery($query);
581 $ufId = $dao->next_uf_id
;
595 public static function isDuplicateUser($email) {
596 CRM_Core_Error
::deprecatedFunctionWarning('unused function to be removed');
597 $session = CRM_Core_Session
::singleton();
598 $contactID = $session->get('userID');
599 if (!empty($email) && isset($contactID)) {
600 $dao = new CRM_Core_DAO_UFMatch();
601 $dao->uf_name
= $email;
602 if ($dao->find(TRUE) && $contactID != $dao->contact_id
) {
610 * Get uf match values for given uf id or logged in user.
618 public static function getUFValues($ufID = NULL) {
620 //get logged in user uf id.
621 $ufID = CRM_Utils_System
::getLoggedInUfID();
627 if (!isset(Civi
::$statics[__CLASS__
][__FUNCTION__
][$ufID])) {
628 $ufmatch = new CRM_Core_DAO_UFMatch();
629 $ufmatch->uf_id
= $ufID;
630 $ufmatch->domain_id
= CRM_Core_Config
::domainID();
631 if ($ufmatch->find(TRUE)) {
632 Civi
::$statics[__CLASS__
][__FUNCTION__
][$ufID] = [
633 'uf_id' => $ufmatch->uf_id
,
634 'uf_name' => $ufmatch->uf_name
,
635 'contact_id' => $ufmatch->contact_id
,
636 'domain_id' => $ufmatch->domain_id
,
640 return Civi
::$statics[__CLASS__
][__FUNCTION__
][$ufID] ??
NULL;
646 public function addSelectWhereClause() {
647 // Prevent default behavior of joining ACLs onto the contact_id field
649 CRM_Utils_Hook
::selectWhereClause($this, $clauses);