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