Merge branch '4.6' of https://github.com/civicrm/civicrm-core
[civicrm-core.git] / CRM / Core / BAO / CMSUser.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | CiviCRM version 4.6 |
5 +--------------------------------------------------------------------+
6 | Copyright CiviCRM LLC (c) 2004-2015 |
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-2015
32 * $Id$
33 *
34 */
35
36 /**
37 * this file contains functions for synchronizing cms users with CiviCRM contacts
38 */
39
40 require_once 'DB.php';
41
42 /**
43 * Class CRM_Core_BAO_CMSUser
44 */
45 class CRM_Core_BAO_CMSUser {
46
47 /**
48 * Synchronizing cms users with CiviCRM contacts.
49 *
50 * @param bool $is_interactive
51 * Whether to show statuses & perform redirects.
52 * This behavior is misplaced in the BAO layer, but we'll preserve it to avoid
53 * contract changes in the middle of the support cycle. In the next major
54 * release, we should remove & document it.
55 *
56 * @return void
57 *
58 */
59 public static function synchronize($is_interactive = TRUE) {
60 //start of schronization code
61 $config = CRM_Core_Config::singleton();
62
63 // Build an array of rows from UF users table.
64 $rows = array();
65 if ($config->userSystem->is_drupal == '1') {
66 $id = 'uid';
67 $mail = 'mail';
68 $name = 'name';
69
70 $result = db_query("SELECT uid, mail, name FROM {users} where mail != ''");
71
72 if ($config->userFramework == 'Drupal') {
73 while ($row = $result->fetchAssoc()) {
74 $rows[] = $row;
75 }
76 }
77 elseif ($config->userFramework == 'Drupal6') {
78 while ($row = db_fetch_array($result)) {
79 $rows[] = $row;
80 }
81 }
82 }
83 elseif ($config->userFramework == 'Joomla') {
84 $id = 'id';
85 $mail = 'email';
86 $name = 'name';
87 // TODO: Insert code here to populate $rows for Joomla;
88 }
89 elseif ($config->userFramework == 'WordPress') {
90 $id = 'ID';
91 $mail = 'user_email';
92 }
93 else {
94 CRM_Core_Error::fatal('CMS user creation not supported for this framework');
95 }
96
97 if (PHP_SAPI != 'cli') {
98 set_time_limit(300);
99 }
100
101 if ($config->userSystem->is_drupal == '1') {
102 $user = new StdClass();
103 $uf = $config->userFramework;
104 $contactCount = 0;
105 $contactCreated = 0;
106 $contactMatching = 0;
107 foreach ($rows as $row) {
108 $user->$id = $row[$id];
109 $user->$mail = $row[$mail];
110 $user->$name = $row[$name];
111 $contactCount++;
112 if ($match = CRM_Core_BAO_UFMatch::synchronizeUFMatch($user, $row[$id], $row[$mail], $uf, 1, 'Individual', TRUE)) {
113 $contactCreated++;
114 }
115 else {
116 $contactMatching++;
117 }
118 if (is_object($match)) {
119 $match->free();
120 }
121 }
122 }
123 elseif ($config->userFramework == 'Joomla') {
124
125 $JUserTable = &JTable::getInstance('User', 'JTable');
126
127 $db = $JUserTable->getDbo();
128 $query = $db->getQuery(TRUE);
129 $query->select($id . ', ' . $mail . ', ' . $name);
130 $query->from($JUserTable->getTableName());
131 $query->where($mail != '');
132
133 $db->setQuery($query, 0, $limit);
134 $users = $db->loadObjectList();
135
136 $user = new StdClass();
137 $uf = $config->userFramework;
138 $contactCount = 0;
139 $contactCreated = 0;
140 $contactMatching = 0;
141 for ($i = 0; $i < count($users); $i++) {
142 $user->$id = $users[$i]->$id;
143 $user->$mail = $users[$i]->$mail;
144 $user->$name = $users[$i]->$name;
145 $contactCount++;
146 if ($match = CRM_Core_BAO_UFMatch::synchronizeUFMatch($user,
147 $users[$i]->$id,
148 $users[$i]->$mail,
149 $uf,
150 1,
151 'Individual',
152 TRUE
153 )
154 ) {
155 $contactCreated++;
156 }
157 else {
158 $contactMatching++;
159 }
160 if (is_object($match)) {
161 $match->free();
162 }
163 }
164 }
165 elseif ($config->userFramework == 'WordPress') {
166 $uf = $config->userFramework;
167 $contactCount = 0;
168 $contactCreated = 0;
169 $contactMatching = 0;
170
171 global $wpdb;
172 $wpUserIds = $wpdb->get_col("SELECT $wpdb->users.ID FROM $wpdb->users");
173
174 foreach ($wpUserIds as $wpUserId) {
175 $wpUserData = get_userdata($wpUserId);
176 $contactCount++;
177 if ($match = CRM_Core_BAO_UFMatch::synchronizeUFMatch($wpUserData,
178 $wpUserData->$id,
179 $wpUserData->$mail,
180 $uf,
181 1,
182 'Individual',
183 TRUE
184 )
185 ) {
186 $contactCreated++;
187 }
188 else {
189 $contactMatching++;
190 }
191 if (is_object($match)) {
192 $match->free();
193 }
194 }
195 }
196 //end of synchronization code
197
198 if ($is_interactive) {
199 $status = ts('Synchronize Users to Contacts completed.');
200 $status .= ' ' . ts('Checked one user record.',
201 array(
202 'count' => $contactCount,
203 'plural' => 'Checked %count user records.',
204 )
205 );
206 if ($contactMatching) {
207 $status .= ' ' . ts('Found one matching contact record.',
208 array(
209 'count' => $contactMatching,
210 'plural' => 'Found %count matching contact records.',
211 )
212 );
213 }
214
215 $status .= ' ' . ts('Created one new contact record.',
216 array(
217 'count' => $contactCreated,
218 'plural' => 'Created %count new contact records.',
219 )
220 );
221 CRM_Core_Session::setStatus($status, ts('Saved'), 'success');
222 CRM_Utils_System::redirect(CRM_Utils_System::url('civicrm/admin', 'reset=1'));
223 }
224 }
225
226 /**
227 * Create CMS user using Profile.
228 *
229 * @param array $params
230 * @param string $mail
231 * Email id for cms user.
232 *
233 * @return int
234 * contact id that has been created
235 */
236 public static function create(&$params, $mail) {
237 $config = CRM_Core_Config::singleton();
238
239 $ufID = $config->userSystem->createUser($params, $mail);
240
241 //if contact doesn't already exist create UF Match
242 if ($ufID !== FALSE &&
243 isset($params['contactID'])
244 ) {
245 // create the UF Match record
246 $ufmatch['uf_id'] = $ufID;
247 $ufmatch['contact_id'] = $params['contactID'];
248 $ufmatch['uf_name'] = $params[$mail];
249 CRM_Core_BAO_UFMatch::create($ufmatch);
250 }
251
252 return $ufID;
253 }
254
255 /**
256 * Create Form for CMS user using Profile.
257 *
258 * @param CRM_Core_Form $form
259 * @param int $gid
260 * Id of group of profile.
261 * @param bool $emailPresent
262 * True if the profile field has email(primary).
263 * @param \const|int $action
264 *
265 * @return FALSE|void
266 * WTF
267 *
268 */
269 public static function buildForm(&$form, $gid, $emailPresent, $action = CRM_Core_Action::NONE) {
270 $config = CRM_Core_Config::singleton();
271 $showCMS = FALSE;
272
273 $isDrupal = $config->userSystem->is_drupal;
274 $isJoomla = ucfirst($config->userFramework) == 'Joomla' ? TRUE : FALSE;
275 $isWordPress = $config->userFramework == 'WordPress' ? TRUE : FALSE;
276
277 //if CMS is configured for not to allow creating new CMS user,
278 //don't build the form,Fixed for CRM-4036
279 if ($isJoomla) {
280 $userParams = JComponentHelper::getParams('com_users');
281 if (!$userParams->get('allowUserRegistration')) {
282 return FALSE;
283 }
284 }
285 elseif ($isDrupal && !variable_get('user_register', TRUE)) {
286 return FALSE;
287 }
288 elseif ($isWordPress && !get_option('users_can_register')) {
289 return FALSE;
290 }
291
292 if ($gid) {
293 $isCMSUser = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_UFGroup', $gid, 'is_cms_user');
294 }
295
296 // $cms is true when there is email(primary location) is set in the profile field.
297 $session = CRM_Core_Session::singleton();
298 $userID = $session->get('userID');
299 $showUserRegistration = FALSE;
300 if ($action) {
301 $showUserRegistration = TRUE;
302 }
303 elseif (!$action && !$userID) {
304 $showUserRegistration = TRUE;
305 }
306
307 if ($isCMSUser && $emailPresent) {
308 if ($showUserRegistration) {
309 if ($isCMSUser != 2) {
310 $extra = array(
311 'onclick' => "return showHideByValue('cms_create_account','','details','block','radio',false );",
312 );
313 $form->addElement('checkbox', 'cms_create_account', ts('Create an account?'), NULL, $extra);
314 $required = FALSE;
315 }
316 else {
317 $form->add('hidden', 'cms_create_account', 1);
318 $required = TRUE;
319 }
320
321 $form->assign('isCMS', $required);
322 if (!$userID || $action & CRM_Core_Action::PREVIEW || $action & CRM_Core_Action::PROFILE) {
323 $form->add('text', 'cms_name', ts('Username'), NULL, $required);
324 if (($isDrupal && !variable_get('user_email_verification', TRUE)) OR ($isJoomla) OR ($isWordPress)) {
325 $form->add('password', 'cms_pass', ts('Password'));
326 $form->add('password', 'cms_confirm_pass', ts('Confirm Password'));
327 }
328
329 $form->addFormRule(array('CRM_Core_BAO_CMSUser', 'formRule'), $form);
330 }
331 $showCMS = TRUE;
332 }
333 }
334
335 $destination = $config->userSystem->getLoginDestination($form);
336 $loginURL = $config->userSystem->getLoginURL($destination);
337 $form->assign('loginURL', $loginURL);
338 $form->assign('showCMS', $showCMS);
339 }
340
341 /**
342 * Checks that there is a valid username & email
343 * optionally checks password is present & matches DB & gets the CMS to validate
344 *
345 * @param array $fields
346 * Posted values of form.
347 * @param array $files
348 * Uploaded files if any.
349 * @param CRM_Core_Form $form
350 *
351 * @return array|bool
352 */
353 public static function formRule($fields, $files, $form) {
354 if (empty($fields['cms_create_account'])) {
355 return TRUE;
356 }
357
358 $config = CRM_Core_Config::singleton();
359
360 $isDrupal = $config->userSystem->is_drupal;
361 $isJoomla = ucfirst($config->userFramework) == 'Joomla' ? TRUE : FALSE;
362 $isWordPress = $config->userFramework == 'WordPress' ? TRUE : FALSE;
363
364 $errors = array();
365 if ($isDrupal || $isJoomla || $isWordPress) {
366 $emailName = NULL;
367 if (!empty($form->_bltID) && array_key_exists("email-{$form->_bltID}", $fields)) {
368 // this is a transaction related page
369 $emailName = 'email-' . $form->_bltID;
370 }
371 else {
372 // find the email field in a profile page
373 foreach ($fields as $name => $dontCare) {
374 if (substr($name, 0, 5) == 'email') {
375 $emailName = $name;
376 break;
377 }
378 }
379 }
380
381 if ($emailName == NULL) {
382 $errors['_qf_default'] == ts('Could not find an email address.');
383 return $errors;
384 }
385
386 if (empty($fields['cms_name'])) {
387 $errors['cms_name'] = ts('Please specify a username.');
388 }
389
390 if (empty($fields[$emailName])) {
391 $errors[$emailName] = ts('Please specify a valid email address.');
392 }
393
394 if (($isDrupal && !variable_get('user_email_verification', TRUE)) OR ($isJoomla) OR ($isWordPress)) {
395 if (empty($fields['cms_pass']) ||
396 empty($fields['cms_confirm_pass'])
397 ) {
398 $errors['cms_pass'] = ts('Please enter a password.');
399 }
400 if ($fields['cms_pass'] != $fields['cms_confirm_pass']) {
401 $errors['cms_pass'] = ts('Password and Confirm Password values are not the same.');
402 }
403 }
404
405 if (!empty($errors)) {
406 return $errors;
407 }
408
409 // now check that the cms db does not have the user name and/or email
410 if ($isDrupal OR $isJoomla OR $isWordPress) {
411 $params = array(
412 'name' => $fields['cms_name'],
413 'mail' => $fields[$emailName],
414 );
415 }
416
417 $config->userSystem->checkUserNameEmailExists($params, $errors, $emailName);
418 }
419 return (!empty($errors)) ? $errors : TRUE;
420 }
421
422 /**
423 * @deprecated
424 * This function is not used anywhere
425 *
426 * @param array $contact
427 * Array of contact-details.
428 *
429 * @return int|bool
430 * uid if user exists, false otherwise
431 *
432 */
433 public static function userExists(&$contact) {
434 $config = CRM_Core_Config::singleton();
435
436 $isDrupal = $config->userSystem->is_drupal;
437 $isJoomla = ucfirst($config->userFramework) == 'Joomla' ? TRUE : FALSE;
438 $isWordPress = $config->userFramework == 'WordPress' ? TRUE : FALSE;
439
440 if (!$isDrupal && !$isJoomla && !$isWordPress) {
441 die('Unknown user framework');
442 }
443
444 // Use UF native framework to fetch data from UF user table
445 if ($isDrupal) {
446 $uid = db_query(
447 "SELECT uid FROM {users} where mail = :email",
448 array(':email' => $contact['email'])
449 )->fetchField();
450
451 if ($uid) {
452 $contact['user_exists'] = TRUE;
453 $result = $uid;
454 }
455 }
456 elseif ($isJoomla) {
457 $mail = $contact['email'];
458
459 $JUserTable = &JTable::getInstance('User', 'JTable');
460
461 $db = $JUserTable->getDbo();
462 $query = $db->getQuery(TRUE);
463 $query->select('username, email');
464 $query->from($JUserTable->getTableName());
465 $query->where('(LOWER(email) = LOWER(\'' . $email . '\'))');
466 $db->setQuery($query, 0, $limit);
467 $users = $db->loadAssocList();
468
469 $row = array();;
470 if (count($users)) {
471 $row = $users[0];
472 }
473
474 if (!empty($row)) {
475 $uid = CRM_Utils_Array::value('id', $row);
476 $contact['user_exists'] = TRUE;
477 $result = $uid;
478 }
479 }
480 elseif ($isWordPress) {
481 if (email_exists($params['mail'])) {
482 $contact['user_exists'] = TRUE;
483 $userObj = get_user_by('email', $params['mail']);
484 return $userObj->ID;
485 }
486 }
487
488 return $result;
489 }
490
491 /**
492 * @param $config
493 *
494 * @return object
495 */
496 public static function &dbHandle(&$config) {
497 $errorScope = CRM_Core_TemporaryErrorScope::ignoreException();
498 $db_uf = DB::connect($config->userFrameworkDSN);
499 unset($errorScope);
500 if (!$db_uf ||
501 DB::isError($db_uf)
502 ) {
503 $session = CRM_Core_Session::singleton();
504 $session->pushUserContext(CRM_Utils_System::url('civicrm/admin', 'reset=1'));
505 CRM_Core_Error::statusBounce(ts("Cannot connect to UF db via %1. Please check the CIVICRM_UF_DSN value in your civicrm.settings.php file",
506 array(1 => $db_uf->getMessage())
507 ));
508 }
509 $db_uf->query('/*!40101 SET NAMES utf8 */');
510 return $db_uf;
511 }
512
513 }