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