3 +--------------------------------------------------------------------+
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2019 |
7 +--------------------------------------------------------------------+
8 | This file is a part of CiviCRM. |
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. |
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. |
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 +--------------------------------------------------------------------+
31 * @copyright CiviCRM LLC (c) 2004-2019
35 * The basic class that interfaces with the external user framework.
37 class CRM_Core_BAO_UFMatch
extends CRM_Core_DAO_UFMatch
{
40 * Create UF Match, Note that this function is here in it's simplest form @ the moment
44 * @return \CRM_Core_DAO_UFMatch
46 public static function create($params) {
47 $hook = empty($params['id']) ?
'create' : 'edit';
48 CRM_Utils_Hook
::pre($hook, 'UFMatch', CRM_Utils_Array
::value('id', $params), $params);
49 if (empty($params['domain_id'])) {
50 $params['domain_id'] = CRM_Core_Config
::domainID();
52 $dao = new CRM_Core_DAO_UFMatch();
53 $dao->copyValues($params);
54 // Fixme: this function cannot update records
55 if (!$dao->find(TRUE)) {
57 Civi
::$statics[__CLASS__
][$params['domain_id']][(int) $dao->contact_id
] = (int) $dao->uf_id
;
58 CRM_Utils_Hook
::post($hook, 'UFMatch', $dao->id
, $dao);
64 * Given a UF user object, make sure there is a contact
65 * object for this user. If the user has new values, we need
66 * to update the CRM DB with the new values
69 * The drupal user object.
71 * Has the user object been edited.
75 * @param bool $isLogin
77 public static function synchronize(&$user, $update, $uf, $ctype, $isLogin = FALSE) {
78 $userSystem = CRM_Core_Config
::singleton()->userSystem
;
79 $session = CRM_Core_Session
::singleton();
80 if (!is_object($session)) {
81 CRM_Core_Error
::fatal('wow, session is not an object?');
85 $userSystemID = $userSystem->getBestUFID($user);
86 $uniqId = $userSystem->getBestUFUniqueIdentifier($user);
88 // if the id of the object is zero (true for anon users in drupal)
89 // have we already processed this user, if so early
91 $userID = $session->get('userID');
92 $ufID = $session->get('ufID');
94 if (!$update && $ufID == $userSystemID) {
98 //check do we have logged in user.
99 $isUserLoggedIn = CRM_Utils_System
::isUserLoggedIn();
101 // reset the session if we are a different user
102 if ($ufID && $ufID != $userSystemID) {
105 //get logged in user ids, and set to session.
106 if ($isUserLoggedIn) {
107 $userIds = self
::getUFValues();
108 $session->set('ufID', CRM_Utils_Array
::value('uf_id', $userIds, ''));
109 $session->set('userID', CRM_Utils_Array
::value('contact_id', $userIds, ''));
110 $session->set('ufUniqID', CRM_Utils_Array
::value('uf_name', $userIds, ''));
115 if ($userSystemID == 0) {
119 $ufmatch = self
::synchronizeUFMatch($user, $userSystemID, $uniqId, $uf, NULL, $ctype, $isLogin);
124 //make sure we have session w/ consistent ids.
125 $ufID = $ufmatch->uf_id
;
126 $userID = $ufmatch->contact_id
;
128 if ($isUserLoggedIn) {
129 $loggedInUserUfID = CRM_Utils_System
::getLoggedInUfID();
130 //are we processing logged in user.
131 if ($loggedInUserUfID && $loggedInUserUfID != $ufID) {
132 $userIds = self
::getUFValues($loggedInUserUfID);
133 $ufID = CRM_Utils_Array
::value('uf_id', $userIds, '');
134 $userID = CRM_Utils_Array
::value('contact_id', $userIds, '');
135 $ufUniqID = CRM_Utils_Array
::value('uf_name', $userIds, '');
139 //set user ids to session.
140 $session->set('ufID', $ufID);
141 $session->set('userID', $userID);
142 $session->set('ufUniqID', $ufUniqID);
144 // add current contact to recently viewed
145 if ($ufmatch->contact_id
) {
146 list($displayName, $contactImage, $contactType, $contactSubtype, $contactImageUrl)
147 = CRM_Contact_BAO_Contact
::getDisplayAndImage($ufmatch->contact_id
, TRUE, TRUE);
150 'imageUrl' => $contactImageUrl,
151 'subtype' => $contactSubtype,
152 'editUrl' => CRM_Utils_System
::url('civicrm/contact/add', "reset=1&action=update&cid={$ufmatch->contact_id}"),
155 CRM_Utils_Recent
::add($displayName,
156 CRM_Utils_System
::url('civicrm/contact/view', "reset=1&cid={$ufmatch->contact_id}"),
157 $ufmatch->contact_id
,
159 $ufmatch->contact_id
,
167 * Synchronize the object with the UF Match entry. Can be called stand-alone from
168 * the drupalUsers script
170 * @param Object $user
171 * The drupal user object.
172 * @param string $userKey
173 * The id of the user from the uf object.
174 * @param string $uniqId
175 * The OpenID of the user.
177 * The name of the user framework.
179 * Returns the status if user created or already exits (used for CMS sync).
180 * @param string $ctype
182 * @param bool $isLogin
184 * @return CRM_Core_DAO_UFMatch|bool
186 public static function &synchronizeUFMatch(&$user, $userKey, $uniqId, $uf, $status = NULL, $ctype = NULL, $isLogin = FALSE) {
187 $config = CRM_Core_Config
::singleton();
189 if (!CRM_Utils_Rule
::email($uniqId)) {
190 $retVal = $status ?
NULL : FALSE;
196 // make sure that a contact id exists for this user id
197 $ufmatch = new CRM_Core_DAO_UFMatch();
198 $ufmatch->domain_id
= CRM_Core_Config
::domainID();
199 $ufmatch->uf_id
= $userKey;
201 if (!$ufmatch->find(TRUE)) {
202 $transaction = new CRM_Core_Transaction();
205 if (!empty($_POST) && !$isLogin) {
207 $params['email'] = $uniqId;
209 $ids = CRM_Contact_BAO_Contact
::getDuplicateContacts($params, 'Individual', 'Unsupervised', [], FALSE);
211 if (!empty($ids) && Civi
::settings()->get('uniq_email_per_site')) {
212 // restrict dupeIds to ones that belong to current domain/site.
213 $siteContacts = CRM_Core_BAO_Domain
::getContactList();
214 foreach ($ids as $index => $dupeId) {
215 if (!in_array($dupeId, $siteContacts)) {
219 // re-index the array
220 $ids = array_values($ids);
223 $dao = new CRM_Core_DAO();
224 $dao->contact_id
= $ids[0];
228 $dao = CRM_Contact_BAO_Contact
::matchContactOnEmail($uniqId, $ctype);
233 // ensure there does not exists a contact_id / uf_id pair
234 // in the DB. This might be due to multiple emails per contact
238 FROM civicrm_uf_match
239 WHERE contact_id = %1
243 1 => [$dao->contact_id
, 'Integer'],
244 2 => [CRM_Core_Config
::domainID(), 'Integer'],
246 $conflict = CRM_Core_DAO
::singleValueQuery($sql, $params);
250 $ufmatch->contact_id
= $dao->contact_id
;
251 $ufmatch->uf_name
= $uniqId;
256 // Not sure why we're testing for this. Is there ever a case
257 // in which $user is not an object?
258 if (is_object($user)) {
259 if ($config->userSystem
->is_drupal
) {
260 $primary_email = $uniqId;
262 elseif ($uf == 'WordPress') {
263 $primary_email = $user->user_email
;
266 $primary_email = $user->email
;
268 $params = ['email-Primary' => $primary_email];
271 if ($ctype == 'Organization') {
272 $params['organization_name'] = $uniqId;
274 elseif ($ctype == 'Household') {
275 $params['household_name'] = $uniqId;
279 $ctype = "Individual";
281 $params['contact_type'] = $ctype;
283 // extract first / middle / last name
285 if ($uf == 'Joomla' && $user->name
) {
286 CRM_Utils_String
::extractName($user->name
, $params);
289 if ($uf == 'WordPress') {
290 if ($user->first_name
) {
291 $params['first_name'] = $user->first_name
;
294 if ($user->last_name
) {
295 $params['last_name'] = $user->last_name
;
299 $contactId = CRM_Contact_BAO_Contact
::createProfileContact($params);
300 $ufmatch->contact_id
= $contactId;
301 $ufmatch->uf_name
= $uniqId;
304 // check that there are not two CMS IDs matching the same CiviCRM contact - this happens when a civicrm
305 // user has two e-mails and there is a cms match for each of them
306 // the gets rid of the nasty fata error but still reports the error
309 FROM civicrm_uf_match
310 WHERE ( contact_id = %1
316 1 => [$ufmatch->contact_id
, 'Integer'],
317 2 => [$ufmatch->uf_name
, 'String'],
318 3 => [$ufmatch->uf_id
, 'Integer'],
319 4 => [$ufmatch->domain_id
, 'Integer'],
322 $conflict = CRM_Core_DAO
::singleValueQuery($sql, $params);
325 $ufmatch = CRM_Core_BAO_UFMatch
::create((array) $ufmatch);
327 $transaction->commit();
330 $msg = ts("Contact ID %1 is a match for %2 user %3 but has already been matched to %4",
332 1 => $ufmatch->contact_id
,
334 3 => $ufmatch->uf_id
,
351 * Update the uf_name in the user object.
353 * @param int $contactId
354 * Id of the contact to update.
356 public static function updateUFName($contactId) {
357 if (!Civi
::settings()->get('syncCMSEmail') ||
!$contactId) {
361 $config = CRM_Core_Config
::singleton();
362 $ufName = CRM_Contact_BAO_Contact
::getPrimaryEmail($contactId);
370 // 1.do check for contact Id.
371 $ufmatch = new CRM_Core_DAO_UFMatch();
372 $ufmatch->contact_id
= $contactId;
373 $ufmatch->domain_id
= CRM_Core_Config
::domainID();
374 if (!$ufmatch->find(TRUE)) {
377 if ($ufmatch->uf_name
!= $ufName) {
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
396 // save the updated ufmatch object
397 $ufmatch->uf_name
= $ufName;
399 $config->userSystem
->updateCMSName($ufmatch->uf_id
, $ufName);
403 * Update the email value for the contact and user profile.
405 * @param int $contactId
406 * Contact ID of the user.
407 * @param string $emailAddress
408 * Email to be modified for the user.
410 public static function updateContactEmail($contactId, $emailAddress) {
411 $strtolower = function_exists('mb_strtolower') ?
'mb_strtolower' : 'strtolower';
412 $emailAddress = $strtolower($emailAddress);
414 $ufmatch = new CRM_Core_DAO_UFMatch();
415 $ufmatch->contact_id
= $contactId;
416 $ufmatch->domain_id
= CRM_Core_Config
::domainID();
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);
422 // If CMS integration is disabled skip Civi email update if CMS user email is changed
423 if (Civi
::settings()->get('syncCMSEmail') == FALSE) {
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);
432 if (trim($contactDetails[1])) {
433 //update if record is found but different
434 $emailID = $contactDetails[3];
435 if (trim($contactDetails[1]) != $emailAddress) {
436 civicrm_api3('Email', 'create', [
438 'email' => $emailAddress,
443 //else insert a new email record
444 $result = civicrm_api3('Email', 'create', [
445 'contact_id' => $contactId,
446 'email' => $emailAddress,
449 $emailID = $result->id
;
452 CRM_Core_BAO_Log
::register($contactId,
460 * Delete the object records that are associated with this cms user.
463 * Id of the user to delete.
465 public static function deleteUser($ufID) {
466 $ufmatch = new CRM_Core_DAO_UFMatch();
468 $ufmatch->uf_id
= $ufID;
469 $ufmatch->domain_id
= $domainId = CRM_Core_Config
::domainID();
473 Civi
::$statics[__CLASS__
][$domainId] = [];
477 * Get the contact_id given a uf_id.
480 * Id of UF for which related contact_id is required.
483 * contact_id on success, null otherwise
485 public static function getContactId($ufID) {
489 $domainId = CRM_Core_Config
::domainID();
491 if (!isset(Civi
::$statics[__CLASS__
][$domainId])) {
492 Civi
::$statics[__CLASS__
][$domainId] = [];
494 $contactId = array_search($ufID, Civi
::$statics[__CLASS__
][$domainId]);
498 $ufmatch = new CRM_Core_DAO_UFMatch();
499 $ufmatch->uf_id
= $ufID;
500 $ufmatch->domain_id
= $domainId;
501 if ($ufmatch->find(TRUE)) {
502 $contactId = (int) $ufmatch->contact_id
;
503 Civi
::$statics[__CLASS__
][$domainId][$contactId] = (int) $ufID;
510 * Get the uf_id given a contact_id.
512 * @param int $contactID
513 * ID of the contact for which related uf_id is required.
516 * uf_id of the given contact_id on success, null otherwise
518 public static function getUFId($contactID) {
522 $domainId = CRM_Core_Config
::domainID();
523 $contactID = (int) $contactID;
525 if (empty(Civi
::$statics[__CLASS__
][$domainId]) ||
!array_key_exists($contactID, Civi
::$statics[__CLASS__
][$domainId])) {
526 Civi
::$statics[__CLASS__
][$domainId][$contactID] = NULL;
527 $ufmatch = new CRM_Core_DAO_UFMatch();
528 $ufmatch->contact_id
= $contactID;
529 $ufmatch->domain_id
= $domainId;
530 if ($ufmatch->find(TRUE)) {
531 Civi
::$statics[__CLASS__
][$domainId][$contactID] = (int) $ufmatch->uf_id
;
534 return Civi
::$statics[__CLASS__
][$domainId][$contactID];
540 public static function isEmptyTable() {
541 $sql = "SELECT count(id) FROM civicrm_uf_match";
542 return CRM_Core_DAO
::singleValueQuery($sql) > 0 ?
FALSE : TRUE;
546 * Get the list of contact_id.
550 * contact_id on success, null otherwise
552 public static function getContactIDs() {
554 $dao = new CRM_Core_DAO_UFMatch();
556 while ($dao->fetch()) {
557 $id[] = $dao->contact_id
;
563 * See if this user exists, and if so, if they're allowed to login
569 * true if allowed to login, false otherwise
571 public static function getAllowedToLogin($openId) {
572 $ufmatch = new CRM_Core_DAO_UFMatch();
573 $ufmatch->uf_name
= $openId;
574 $ufmatch->allowed_to_login
= 1;
575 if ($ufmatch->find(TRUE)) {
582 * Get the next unused uf_id value, since the standalone UF doesn't
583 * have id's (it uses OpenIDs, which go in a different field)
587 * next highest unused value for uf_id
589 public static function getNextUfIdValue() {
590 $query = "SELECT MAX(uf_id)+1 AS next_uf_id FROM civicrm_uf_match";
591 $dao = CRM_Core_DAO
::executeQuery($query);
593 $ufId = $dao->next_uf_id
;
607 public static function isDuplicateUser($email) {
608 $session = CRM_Core_Session
::singleton();
609 $contactID = $session->get('userID');
610 if (!empty($email) && isset($contactID)) {
611 $dao = new CRM_Core_DAO_UFMatch();
612 $dao->uf_name
= $email;
613 if ($dao->find(TRUE) && $contactID != $dao->contact_id
) {
621 * Get uf match values for given uf id or logged in user.
629 public static function getUFValues($ufID = NULL) {
631 //get logged in user uf id.
632 $ufID = CRM_Utils_System
::getLoggedInUfID();
639 if ($ufID && !isset($ufValues[$ufID])) {
640 $ufmatch = new CRM_Core_DAO_UFMatch();
641 $ufmatch->uf_id
= $ufID;
642 $ufmatch->domain_id
= CRM_Core_Config
::domainID();
643 if ($ufmatch->find(TRUE)) {
645 'uf_id' => $ufmatch->uf_id
,
646 'uf_name' => $ufmatch->uf_name
,
647 'contact_id' => $ufmatch->contact_id
,
648 'domain_id' => $ufmatch->domain_id
,
652 return $ufValues[$ufID];
658 public function addSelectWhereClause() {
659 // Prevent default behavior of joining ACLs onto the contact_id field
661 CRM_Utils_Hook
::selectWhereClause($this, $clauses);