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