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