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