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