Simpletest Coverage - modules/user/user.module

1 <?php
2 // $Id: user.module,v 1.1021 2009/08/12 12:36:05 dries Exp $
3
4 /**
5 * @file
6 * Enables the user registration and login system.
7 */
8
9 /**
10 * Maximum length of username text field.
11 */
12 define('USERNAME_MAX_LENGTH', 60);
13
14 /**
15 * Maximum length of user e-mail text field.
16 */
17 define('EMAIL_MAX_LENGTH', 64);
18
19
20 /**
21 * Invokes hook_user() in every module.
22 *
23 * We cannot use module_invoke() for this, because the arguments need to
24 * be passed by reference.
25 */
26 function user_module_invoke($type, &$edit, $account, $category = NULL) {
27 foreach (module_implements('user_' . $type) as $module) {
28 $function = $module . '_user_' . $type;
29 $function($edit, $account, $category);
30 }
31 }
32
33 /**
34 * Implement hook_theme().
35 */
36 function user_theme() {
37 return array(
38 'user_picture' => array(
39 'arguments' => array('account' => NULL),
40 'template' => 'user-picture',
41 ),
42 'user_profile' => array(
43 'arguments' => array('elements' => NULL),
44 'template' => 'user-profile',
45 'file' => 'user.pages.inc',
46 ),
47 'user_profile_category' => array(
48 'arguments' => array('element' => NULL),
49 'template' => 'user-profile-category',
50 'file' => 'user.pages.inc',
51 ),
52 'user_profile_item' => array(
53 'arguments' => array('element' => NULL),
54 'template' => 'user-profile-item',
55 'file' => 'user.pages.inc',
56 ),
57 'user_list' => array(
58 'arguments' => array('users' => NULL, 'title' => NULL),
59 ),
60 'user_admin_permissions' => array(
61 'arguments' => array('form' => NULL),
62 'file' => 'user.admin.inc',
63 ),
64 'user_admin_new_role' => array(
65 'arguments' => array('form' => NULL),
66 'file' => 'user.admin.inc',
67 ),
68 'user_admin_account' => array(
69 'arguments' => array('form' => NULL),
70 'file' => 'user.admin.inc',
71 ),
72 'user_filter_form' => array(
73 'arguments' => array('form' => NULL),
74 'file' => 'user.admin.inc',
75 ),
76 'user_filters' => array(
77 'arguments' => array('form' => NULL),
78 'file' => 'user.admin.inc',
79 ),
80 'user_signature' => array(
81 'arguments' => array('signature' => NULL),
82 ),
83 );
84 }
85
86 /**
87 * Implement hook_fieldable_info().
88 */
89 function user_fieldable_info() {
90 $return = array(
91 'user' => array(
92 'label' => t('User'),
93 'object keys' => array(
94 'id' => 'uid',
95 ),
96 'bundles' => array(
97 'user' => array(
98 'label' => t('User'),
99 'admin' => array(
100 'path' => 'admin/settings/user',
101 'access arguments' => array('administer users'),
102 ),
103 ),
104 ),
105 ),
106 );
107 return $return;
108 }
109
110 /**
111 * Implement hook_field_build_modes().
112 */
113 function user_field_build_modes($obj_type) {
114 $modes = array();
115 if ($obj_type == 'user') {
116 $modes = array(
117 'full' => t('User account'),
118 );
119 }
120 return $modes;
121 }
122
123 function user_external_load($authname) {
124 $uid = db_query("SELECT uid FROM {authmap} WHERE authname = :authname", array(':authname' => $authname))->fetchField();
125
126 if ($uid) {
127 return user_load($uid);
128 }
129 else {
130 return FALSE;
131 }
132 }
133
134 /**
135 * Load multiple users based on certain conditions.
136 *
137 * This function should be used whenever you need to load more than one user
138 * from the database. Users are loaded into memory and will not require
139 * database access if loaded again during the same page request.
140 *
141 * @param $uids
142 * An array of user IDs.
143 * @param $conditions
144 * An array of conditions to match against the {users} table. These
145 * should be supplied in the form array('field_name' => 'field_value').
146 * @param $reset
147 * A boolean indicating that the internal cache should be reset. Use this if
148 * loading a user object which has been altered during the page request.
149 * @return
150 * An array of user objects, indexed by uid.
151 *
152 * @see user_load()
153 * @see user_load_by_mail()
154 * @see user_load_by_name()
155 */
156 function user_load_multiple($uids = array(), $conditions = array(), $reset = FALSE) {
157 static $user_cache = array();
158 if ($reset) {
159 $user_cache = array();
160 }
161
162 $users = array();
163
164 // Create a new variable which is either a prepared version of the $uids
165 // array for later comparison with the user cache, or FALSE if no $uids were
166 // passed. The $uids array is reduced as items are loaded from cache, and we
167 // need to know if it's empty for this reason to avoid querying the database
168 // when all requested users are loaded from cache.
169 $passed_uids = !empty($uids) ? array_flip($uids) : FALSE;
170
171 // Load any available users from the internal cache.
172 if ($user_cache) {
173 if ($uids && !$conditions) {
174 $users += array_intersect_key($user_cache, $passed_uids);
175 // If any users were loaded, remove them from the $uids still to load.
176 $uids = array_keys(array_diff_key($passed_uids, $users));
177 }
178 }
179
180 // Load any remaining users from the database, this is necessary if we have
181 // $uids still to load, or if $conditions was passed without $uids.
182 if ($uids || ($conditions && !$passed_uids)) {
183 $query = db_select('users', 'u')->fields('u');
184
185 // If the $uids array is populated, add those to the query.
186 if ($uids) {
187 $query->condition('u.uid', $uids, 'IN');
188 }
189 // If the conditions array is populated, add those to the query.
190 if ($conditions) {
191 // TODO D7: Using LIKE() to get a case insensitive comparison because Crell
192 // and chx promise that dbtng will map it to ILIKE in postgres.
193 if (isset($conditions['name'])) {
194 $query->condition('u.name', $conditions['name'], 'LIKE');
195 unset($conditions['name']);
196 }
197 if (isset($conditions['mail'])) {
198 $query->condition('u.mail', $conditions['mail'], 'LIKE');
199 unset($conditions['mail']);
200 }
201 foreach ($conditions as $field => $value) {
202 $query->condition('u.' . $field, $value);
203 }
204 }
205 $result = $query->execute();
206
207 $queried_users = array();
208 // Build an array of user picture IDs so that these can be fetched later.
209 $picture_fids = array();
210 foreach ($result as $record) {
211 $picture_fids[] = $record->picture;
212 $queried_users[$record->uid] = drupal_unpack($record);
213 $queried_users[$record->uid]->roles = array();
214 if ($record->uid) {
215 $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
216 }
217 else {
218 $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
219 }
220 }
221
222 if (!empty($queried_users)) {
223 // Add any additional roles from the database.
224 $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
225 foreach ($result as $record) {
226 $queried_users[$record->uid]->roles[$record->rid] = $record->name;
227 }
228
229 // Add the full file objects for user pictures if enabled.
230 if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
231 $pictures = file_load_multiple($picture_fids);
232 foreach ($queried_users as $account) {
233 if (!empty($account->picture) && isset($pictures[$account->picture])) {
234 $account->picture = $pictures[$account->picture];
235 }
236 else {
237 $account->picture = NULL;
238 }
239 }
240 }
241
242 field_attach_load('user', $queried_users);
243
244 // Invoke hook_user_load() on the users loaded from the database
245 // and add them to the static cache.
246 foreach (module_implements('user_load') as $module) {
247 $function = $module . '_user_load';
248 $function($queried_users);
249 }
250
251
252
253 $users = $users + $queried_users;
254 $user_cache = $user_cache + $queried_users;
255 }
256 }
257
258 // Ensure that the returned array is ordered the same as the original $uids
259 // array if this was passed in and remove any invalid uids.
260 if ($passed_uids) {
261 // Remove any invalid uids from the array.
262 $passed_uids = array_intersect_key($passed_uids, $users);
263 foreach ($users as $user) {
264 $passed_uids[$user->uid] = $user;
265 }
266 $users = $passed_uids;
267 }
268
269 return $users;
270 }
271
272
273 /**
274 * Fetch a user object.
275 *
276 * @param $uid
277 * Integer specifying the user id.
278 * @param $reset
279 * A boolean indicating that the internal cache should be reset.
280 * @return
281 * A fully-loaded $user object upon successful user load or FALSE if user
282 * cannot be loaded.
283 *
284 * @see user_load_multiple()
285 */
286 function user_load($uid, $reset = FALSE) {
287 $users = user_load_multiple(array($uid), array(), $reset);
288 return reset($users);
289 }
290
291 /**
292 * Fetch a user object by email address.
293 *
294 * @param $mail
295 * String with the account's e-mail address.
296 * @return
297 * A fully-loaded $user object upon successful user load or FALSE if user
298 * cannot be loaded.
299 *
300 * @see user_load_multiple()
301 */
302 function user_load_by_mail($mail) {
303 $users = user_load_multiple(array(), array('mail' => $mail));
304 return reset($users);
305 }
306
307 /**
308 * Fetch a user object by account name.
309 *
310 * @param $name
311 * String with the account's user name.
312 * @return
313 * A fully-loaded $user object upon successful user load or FALSE if user
314 * cannot be loaded.
315 *
316 * @see user_load_multiple()
317 */
318 function user_load_by_name($name) {
319 $users = user_load_multiple(array(), array('name' => $name));
320 return reset($users);
321 }
322
323 /**
324 * Save changes to a user account or add a new user.
325 *
326 * @param $account
327 * (optional) The user object to modify or add. If you want to modify
328 * an existing user account, you will need to ensure that (a) $account
329 * is an object, and (b) you have set $account->uid to the numeric
330 * user ID of the user account you wish to modify. If you
331 * want to create a new user account, you can set $account->is_new to
332 * TRUE or omit the $account->uid field.
333 * @param $edit
334 * An array of fields and values to save. For example array('name'
335 * => 'My name'). Keys that do not belong to columns in the user-related
336 * tables are added to the a serialized array in the 'data' column
337 * and will be loaded in the $user->data array by user_load().
338 * Setting a field to NULL deletes it from the data column, if you are
339 * modifying an existing user account.
340 * @param $category
341 * (optional) The category for storing profile information in.
342 *
343 * @return
344 * A fully-loaded $user object upon successful save or FALSE if the save failed.
345 */
346 function user_save($account, $edit = array(), $category = 'account') {
347 $table = drupal_get_schema('users');
348 $user_fields = $table['fields'];
349
350 if (!empty($edit['pass'])) {
351 // Allow alternate password hashing schemes.
352 require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
353 $edit['pass'] = user_hash_password(trim($edit['pass']));
354 // Abort if the hashing failed and returned FALSE.
355 if (!$edit['pass']) {
356 return FALSE;
357 }
358 }
359 else {
360 // Avoid overwriting an existing password with a blank password.
361 unset($edit['pass']);
362 }
363
364 // Get the fields form so we can recognize the fields in the $edit
365 // form that should not go into the serialized data array.
366 $field_form = array();
367 $field_form_state = array();
368 $edit = (object) $edit;
369 field_attach_form('user', $edit, $field_form, $field_form_state);
370
371 // Presave fields.
372 field_attach_presave('user', $edit);
373
374 $edit = (array) $edit;
375
376 if (!isset($account->is_new)) {
377 $account->is_new = empty($account->uid);
378 }
379 if (is_object($account) && !$account->is_new) {
380 user_module_invoke('update', $edit, $account, $category);
381 $data = unserialize(db_query('SELECT data FROM {users} WHERE uid = :uid', array(':uid' => $account->uid))->fetchField());
382 // Consider users edited by an administrator as logged in, if they haven't
383 // already, so anonymous users can view the profile (if allowed).
384 if (empty($edit['access']) && empty($account->access) && user_access('administer users')) {
385 $edit['access'] = REQUEST_TIME;
386 }
387 foreach ($edit as $key => $value) {
388 // Form fields that don't pertain to the users, user_roles, or
389 // Field API are automatically serialized into the users.data
390 // column.
391 if (!in_array($key, array('roles', 'is_new')) && empty($user_fields[$key]) && empty($field_form[$key])) {
392 if ($value === NULL) {
393 unset($data[$key]);
394 }
395 else {
396 $data[$key] = $value;
397 }
398 }
399 }
400
401 // Process picture uploads.
402 if (!empty($edit['picture']->fid)) {
403 $picture = $edit['picture'];
404 // If the picture is a temporary file move it to its final location and
405 // make it permanent.
406 if (($picture->status & FILE_STATUS_PERMANENT) == 0) {
407 $info = image_get_info($picture->filepath);
408 $destination = file_create_path(variable_get('user_picture_path', 'pictures') . '/picture-' . $account->uid . '.' . $info['extension']);
409 if ($picture = file_move($picture, $destination, FILE_EXISTS_REPLACE)) {
410 $picture->status |= FILE_STATUS_PERMANENT;
411 $edit['picture'] = file_save($picture);
412 }
413 }
414 }
415 $edit['picture'] = empty($edit['picture']->fid) ? 0 : $edit['picture']->fid;
416
417 $edit['data'] = $data;
418 // Do not allow 'uid' to be changed.
419 $edit['uid'] = $account->uid;
420 // Save changes to the user table.
421 $success = drupal_write_record('users', $edit, 'uid');
422 if (!$success) {
423 // The query failed - better to abort the save than risk further
424 // data loss.
425
426 // TODO: Fields change: I think this is a bug. If no columns in
427 // the user table are changed, drupal_write_record returns
428 // FALSE because rowCount() (rows changed) is 0. However,
429 // non-users data may have been changed, e.g. fields.
430 // return FALSE;
431 }
432
433 // If the picture changed or was unset, remove the old one. This step needs
434 // to occur after updating the {users} record so that user_file_references()
435 // doesn't report it in use and block the deletion.
436 if (!empty($account->picture->fid) && ($edit['picture'] != $account->picture->fid)) {
437 file_delete($account->picture);
438 }
439
440 // Reload user roles if provided.
441 if (isset($edit['roles']) && is_array($edit['roles'])) {
442 db_delete('users_roles')
443 ->condition('uid', $account->uid)
444 ->execute();
445
446 $query = db_insert('users_roles')->fields(array('uid', 'rid'));
447 foreach (array_keys($edit['roles']) as $rid) {
448 if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
449 $query->values(array(
450 'uid' => $account->uid,
451 'rid' => $rid,
452 ));
453 }
454 }
455 $query->execute();
456 }
457
458 // Delete a blocked user's sessions to kick them if they are online.
459 if (isset($edit['status']) && $edit['status'] == 0) {
460 drupal_session_destroy_uid($account->uid);
461 }
462
463 // If the password changed, delete all open sessions and recreate
464 // the current one.
465 if (!empty($edit['pass'])) {
466 drupal_session_destroy_uid($account->uid);
467 if ($account->uid == $GLOBALS['user']->uid) {
468 drupal_session_regenerate();
469 }
470 }
471
472 // Save Field data.
473 $object = (object) $edit;
474 field_attach_update('user', $object);
475
476 // Refresh user object.
477 $user = user_load($account->uid, TRUE);
478
479 // Send emails after we have the new user object.
480 if (isset($edit['status']) && $edit['status'] != $account->status) {
481 // The user's status is changing; conditionally send notification email.
482 $op = $edit['status'] == 1 ? 'status_activated' : 'status_blocked';
483 _user_mail_notify($op, $user);
484 }
485
486 user_module_invoke('after_update', $edit, $user, $category);
487 }
488 else {
489 // Allow 'created' to be set by the caller.
490 if (!isset($edit['created'])) {
491 $edit['created'] = REQUEST_TIME;
492 }
493 // Consider users created by an administrator as already logged in, so
494 // anonymous users can view the profile (if allowed).
495 if (empty($edit['access']) && user_access('administer users')) {
496 $edit['access'] = REQUEST_TIME;
497 }
498
499 $edit['mail'] = trim($edit['mail']);
500 $success = drupal_write_record('users', $edit);
501 if (!$success) {
502 // On a failed INSERT some other existing user's uid may be returned.
503 // We must abort to avoid overwriting their account.
504 return FALSE;
505 }
506
507 // Build the initial user object.
508 $user = user_load($edit['uid'], TRUE);
509
510 $object = (object) $edit;
511 field_attach_insert('user', $object);
512
513 user_module_invoke('insert', $edit, $user, $category);
514
515 // Note, we wait with saving the data column to prevent module-handled
516 // fields from being saved there.
517 $data = array();
518 foreach ($edit as $key => $value) {
519 // Form fields that don't pertain to the users, user_roles, or
520 // Field API are automatically serialized into the user.data
521 // column.
522 if ((!in_array($key, array('roles', 'is_new'))) && (empty($user_fields[$key]) && empty($field_form[$key])) && ($value !== NULL)) {
523 $data[$key] = $value;
524 }
525 }
526 if (!empty($data)) {
527 $data_array = array('uid' => $user->uid, 'data' => $data);
528 drupal_write_record('users', $data_array, 'uid');
529 }
530
531 // Save user roles (delete just to be safe).
532 if (isset($edit['roles']) && is_array($edit['roles'])) {
533 db_delete('users_roles')
534 ->condition('uid', $edit['uid'])
535 ->execute();
536 $query = db_insert('users_roles')->fields(array('uid', 'rid'));
537 foreach (array_keys($edit['roles']) as $rid) {
538 if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
539 $query->values(array(
540 'uid' => $edit['uid'],
541 'rid' => $rid,
542 ));
543 }
544 }
545 $query->execute();
546 }
547
548 // Build the finished user object.
549 $user = user_load($edit['uid'], TRUE);
550 }
551
552 return $user;
553 }
554
555 /**
556 * Verify the syntax of the given name.
557 */
558 function user_validate_name($name) {
559 if (!$name) {
560 return t('You must enter a username.');
561 }
562 if (substr($name, 0, 1) == ' ') {
563 return t('The username cannot begin with a space.');
564 }
565 if (substr($name, -1) == ' ') {
566 return t('The username cannot end with a space.');
567 }
568 if (strpos($name, ' ') !== FALSE) {
569 return t('The username cannot contain multiple spaces in a row.');
570 }
571 if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)) {
572 return t('The username contains an illegal character.');
573 }
574 if (preg_match('/[\x{80}-\x{A0}' . // Non-printable ISO-8859-1 + NBSP
575 '\x{AD}' . // Soft-hyphen
576 '\x{2000}-\x{200F}' . // Various space characters
577 '\x{2028}-\x{202F}' . // Bidirectional text overrides
578 '\x{205F}-\x{206F}' . // Various text hinting characters
579 '\x{FEFF}' . // Byte order mark
580 '\x{FF01}-\x{FF60}' . // Full-width latin
581 '\x{FFF9}-\x{FFFD}' . // Replacement characters
582 '\x{0}-\x{1F}]/u', // NULL byte and control characters
583 $name)) {
584 return t('The username contains an illegal character.');
585 }
586 if (drupal_strlen($name) > USERNAME_MAX_LENGTH) {
587 return t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
588 }
589 }
590
591 function user_validate_mail($mail) {
592 $mail = trim($mail);
593 if (!$mail) {
594 return t('You must enter an e-mail address.');
595 }
596 if (!valid_email_address($mail)) {
597 return t('The e-mail address %mail is not valid.', array('%mail' => $mail));
598 }
599 }
600
601 function user_validate_picture(&$form, &$form_state) {
602 // If required, validate the uploaded picture.
603 $validators = array(
604 'file_validate_is_image' => array(),
605 'file_validate_image_resolution' => array(variable_get('user_picture_dimensions', '85x85')),
606 'file_validate_size' => array(variable_get('user_picture_file_size', '30') * 1024),
607 );
608
609 // Save the file as a temporary file.
610 $file = file_save_upload('picture_upload', $validators);
611 if ($file === FALSE) {
612 form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist or is not writable.", array('%directory' => variable_get('user_picture_path', 'pictures'))));
613 }
614 elseif ($file !== NULL) {
615 $form_state['values']['picture_upload'] = $file;
616 }
617 }
618
619 /**
620 * Generate a random alphanumeric password.
621 */
622 function user_password($length = 10) {
623 // This variable contains the list of allowable characters for the
624 // password. Note that the number 0 and the letter 'O' have been
625 // removed to avoid confusion between the two. The same is true
626 // of 'I', 1, and 'l'.
627 $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
628
629 // Zero-based count of characters in the allowable list:
630 $len = strlen($allowable_characters) - 1;
631
632 // Declare the password as a blank string.
633 $pass = '';
634
635 // Loop the number of times specified by $length.
636 for ($i = 0; $i < $length; $i++) {
637
638 // Each iteration, pick a random character from the
639 // allowable string and append it to the password:
640 $pass .= $allowable_characters[mt_rand(0, $len)];
641 }
642
643 return $pass;
644 }
645
646 /**
647 * Determine the permissions for one or more roles.
648 *
649 * @param $roles
650 * An array whose keys are the role IDs of interest, such as $user->roles.
651 * @param $reset
652 * Optional parameter - if TRUE data in the static variable is rebuilt.
653 *
654 * @return
655 * An array indexed by role ID. Each value is an array whose keys are the
656 * permission strings for the given role ID.
657 */
658 function user_role_permissions($roles = array(), $reset = FALSE) {
659 static $stored_permissions = array();
660
661 if ($reset) {
662 // Clear the data cached in the static variable.
663 $stored_permissions = array();
664 }
665
666 $role_permissions = $fetch = array();
667
668 if ($roles) {
669 foreach ($roles as $rid => $name) {
670 if (isset($stored_permissions[$rid])) {
671 $role_permissions[$rid] = $stored_permissions[$rid];
672 }
673 else {
674 // Add this rid to the list of those needing to be fetched.
675 $fetch[] = $rid;
676 // Prepare in case no permissions are returned.
677 $stored_permissions[$rid] = array();
678 }
679 }
680
681 if ($fetch) {
682 // Get from the database permissions that were not in the static variable.
683 // Only role IDs with at least one permission assigned will return rows.
684 $result = db_query("SELECT rid, permission FROM {role_permission} WHERE rid IN (:fetch)", array(':fetch' => $fetch));
685
686 foreach ($result as $row) {
687 $stored_permissions[$row->rid][$row->permission] = TRUE;
688 }
689 foreach ($fetch as $rid) {
690 // For every rid, we know we at least assigned an empty array.
691 $role_permissions[$rid] = $stored_permissions[$rid];
692 }
693 }
694 }
695
696 return $role_permissions;
697 }
698
699 /**
700 * Determine whether the user has a given privilege.
701 *
702 * @param $string
703 * The permission, such as "administer nodes", being checked for.
704 * @param $account
705 * (optional) The account to check, if not given use currently logged in user.
706 * @param $reset
707 * (optional) Resets the user's permissions cache, which will result in a
708 * recalculation of the user's permissions. This is necessary to support
709 * dynamically added user roles.
710 *
711 * @return
712 * Boolean TRUE if the current user has the requested permission.
713 *
714 * All permission checks in Drupal should go through this function. This
715 * way, we guarantee consistent behavior, and ensure that the superuser
716 * can perform all actions.
717 */
718 function user_access($string, $account = NULL, $reset = FALSE) {
719 global $user;
720 static $perm = array();
721
722 if ($reset) {
723 $perm = array();
724 }
725
726 if (!isset($account)) {
727 $account = $user;
728 }
729
730 // User #1 has all privileges:
731 if ($account->uid == 1) {
732 return TRUE;
733 }
734
735 // To reduce the number of SQL queries, we cache the user's permissions
736 // in a static variable.
737 if (!isset($perm[$account->uid])) {
738 $role_permissions = user_role_permissions($account->roles, $reset);
739
740 $perms = array();
741 foreach ($role_permissions as $one_role) {
742 $perms += $one_role;
743 }
744 $perm[$account->uid] = $perms;
745 }
746
747 return isset($perm[$account->uid][$string]);
748 }
749
750 /**
751 * Checks for usernames blocked by user administration.
752 *
753 * @return boolean TRUE for blocked users, FALSE for active.
754 */
755 function user_is_blocked($name) {
756 $deny = db_query("SELECT name FROM {users} WHERE status = 0 AND name = LOWER(:name)", array(':name' => $name))->fetchObject();
757
758 return $deny;
759 }
760
761 /**
762 * Implement hook_permission().
763 */
764 function user_permission() {
765 return array(
766 'administer permissions' => array(
767 'title' => t('Administer permissions'),
768 'description' => t('Manage the permissions assigned to user roles. %warning', array('%warning' => t('Warning: Give to trusted roles only; this permission has security implications.'))),
769 ),
770 'administer users' => array(
771 'title' => t('Administer users'),
772 'description' => t('Manage or block users, and manage their role assignments.'),
773 ),
774 'access user profiles' => array(
775 'title' => t('Access user profiles'),
776 'description' => t('View profiles of users on the site, which may contain personal information.'),
777 ),
778 'change own username' => array(
779 'title' => t('Change own username'),
780 'description' => t('Select a different username.'),
781 ),
782 'cancel account' => array(
783 'title' => t('Cancel account'),
784 'description' => t('Remove or disable own user account and unpublish, anonymize, or remove own submissions depending on the configured <a href="@user-settings-url">user settings</a>.', array('@user-settings-url' => url('admin/settings/user'))),
785 ),
786 'select account cancellation method' => array(
787 'title' => t('Select method for cancelling own account'),
788 'description' => t('Select the method for cancelling own user account. %warning', array('%warning' => t('Warning: Give to trusted roles only; this permission has security implications.'))),
789 ),
790 );
791 }
792
793 /**
794 * Implement hook_file_download().
795 *
796 * Ensure that user pictures (avatars) are always downloadable.
797 */
798 function user_file_download($filepath) {
799 if (strpos($filepath, variable_get('user_picture_path', 'pictures') . '/picture-') === 0) {
800 $info = image_get_info(file_create_path($filepath));
801 return array('Content-Type' => $info['mime_type']);
802 }
803 }
804
805 /**
806 * Implement hook_file_references().
807 */
808 function user_file_references($file) {
809 // Determine if the file is used by this module.
810 $file_used = (bool) db_query_range('SELECT 1 FROM {users} WHERE picture = :fid', array(':fid' => $file->fid), 0, 1)->fetchField();
811 if ($file_used) {
812 // Return the name of the module and how many references it has to the file.
813 return array('user' => $count);
814 }
815 }
816
817 /**
818 * Implement hook_file_delete().
819 */
820 function user_file_delete($file) {
821 // Remove any references to the file.
822 db_update('users')
823 ->fields(array('picture' => 0))
824 ->condition('picture', $file->fid)
825 ->execute();
826 }
827
828 /**
829 * Implement hook_search().
830 */
831 function user_search($op = 'search', $keys = NULL, $skip_access_check = FALSE) {
832 switch ($op) {
833 case 'name':
834 if ($skip_access_check || user_access('access user profiles')) {
835 return t('Users');
836 }
837 case 'search':
838 if (user_access('access user profiles')) {
839 $find = array();
840 // Replace wildcards with MySQL/PostgreSQL wildcards.
841 $keys = preg_replace('!\*+!', '%', $keys);
842 $query = db_select('users')->extend('PagerDefault');
843 $query->fields('users', array('name', 'uid', 'mail'));
844 if (user_access('administer users')) {
845 // Administrators can also search in the otherwise private email field.
846 $query->condition(db_or()->
847 where('LOWER(name) LIKE LOWER(:name)', array(':name' => "%$keys%"))->
848 where('LOWER(mail) LIKE LOWER(:mail)', array(':mail' => "%$keys%")));
849 }
850 else {
851 $query->where('LOWER(name) LIKE LOWER(:name)', array(':name' => "%$keys%"));
852 }
853 $result = $query
854 ->limit(15)
855 ->execute();
856 foreach ($result as $account) {
857 $find[] = array('title' => $account->name . ' (' . $account->mail . ')', 'link' => url('user/' . $account->uid, array('absolute' => TRUE)));
858 }
859 return $find;
860 }
861 }
862 }
863
864 /**
865 * Implement hook_elements().
866 */
867 function user_elements() {
868 return array(
869 'user_profile_category' => array(
870 '#theme_wrappers' => array('user_profile_category')
871 ),
872 'user_profile_item' => array(
873 '#theme' => 'user_profile_item'
874 ),
875 );
876 }
877
878 /**
879 * Implement hook_user_view().
880 */
881 function user_user_view($account) {
882 $account->content['user_picture'] = array(
883 '#markup' => theme('user_picture', $account),
884 '#weight' => -10,
885 );
886 if (!isset($account->content['summary'])) {
887 $account->content['summary'] = array();
888 }
889 $account->content['summary'] += array(
890 '#type' => 'user_profile_category',
891 '#attributes' => array('class' => 'user-member'),
892 '#weight' => 5,
893 '#title' => t('History'),
894 );
895 $account->content['summary']['member_for'] = array(
896 '#type' => 'user_profile_item',
897 '#title' => t('Member for'),
898 '#markup' => format_interval(REQUEST_TIME - $account->created),
899 );
900 }
901
902 /**
903 * Implement hook_user_form.
904 */
905 function user_user_form(&$edit, $account, $category) {
906 if ($category == 'account') {
907 $form_state = array();
908 return user_edit_form($form_state, (isset($account->uid) ? $account->uid : FALSE), $edit);
909 }
910 }
911
912 /**
913 * Implement hook_user_validate().
914 */
915 function user_user_validate(&$edit, $account, $category) {
916 if ($category == 'account') {
917 $uid = isset($account->uid) ? $account->uid : FALSE;
918 // Validate the username when: new user account; or user is editing own account and can change username; or an admin user.
919 if (!$uid || ($GLOBALS['user']->uid == $uid && user_access('change own username')) || user_access('administer users')) {
920 if ($error = user_validate_name($edit['name'])) {
921 form_set_error('name', $error);
922 }
923 elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(name) = LOWER(:name)", array(':uid' => $uid, ':name' => $edit['name']), 0, 1)->fetchField()) {
924 form_set_error('name', t('The name %name is already taken.', array('%name' => $edit['name'])));
925 }
926 }
927
928 // Validate the e-mail address, and check if it is taken by an existing user.
929 if ($error = user_validate_mail($edit['mail'])) {
930 form_set_error('mail', $error);
931 }
932 elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(mail) = LOWER(:mail)", array(':uid' => $uid, ':mail' => $edit['mail']), 0, 1)->fetchField()) {
933 // Format error message dependent on whether the user is logged in or not.
934 if ($GLOBALS['user']->uid) {
935 form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $edit['mail'])));
936 }
937 else {
938 form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $edit['mail'], '@password' => url('user/password'))));
939 }
940 }
941
942 // Make sure the signature isn't longer than the size of the database field.
943 // Signatures are disabled by default, so make sure it exists first.
944 if (isset($edit['signature'])) {
945 $user_schema = drupal_get_schema('users');
946 if (strlen($edit['signature']) > $user_schema['fields']['signature']['length']) {
947 form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
948 }
949 }
950 }
951 }
952
953 /**
954 * Implement hook_user_submit().
955 */
956 function user_user_submit(&$edit, $account, $category) {
957 if ($category == 'account') {
958 if (!empty($edit['picture_upload'])) {
959 $edit['picture'] = $edit['picture_upload'];
960 }
961 // Delete picture if requested, and if no replacement picture was given.
962 elseif (!empty($edit['picture_delete'])) {
963 $edit['picture'] = NULL;
964 }
965 // Remove these values so they don't end up serialized in the data field.
966 $edit['picture_upload'] = NULL;
967 $edit['picture_delete'] = NULL;
968
969 if (isset($edit['roles'])) {
970 $edit['roles'] = array_filter($edit['roles']);
971 }
972 }
973 }
974
975 /**
976 * Implement hook_user_categories().
977 */
978 function user_user_categories() {
979 return array(array(
980 'name' => 'account',
981 'title' => t('Account settings'),
982 'weight' => 1,
983 ));
984 }
985
986 function user_login_block() {
987 $form = array(
988 '#action' => url($_GET['q'], array('query' => drupal_get_destination())),
989 '#id' => 'user-login-form',
990 '#validate' => user_login_default_validators(),
991 '#submit' => array('user_login_submit'),
992 );
993 $form['name'] = array('#type' => 'textfield',
994 '#title' => t('Username'),
995 '#maxlength' => USERNAME_MAX_LENGTH,
996 '#size' => 15,
997 '#required' => TRUE,
998 );
999 $form['pass'] = array('#type' => 'password',
1000 '#title' => t('Password'),
1001 '#maxlength' => 60,
1002 '#size' => 15,
1003 '#required' => TRUE,
1004 );
1005 $form['submit'] = array('#type' => 'submit',
1006 '#value' => t('Log in'),
1007 );
1008 $items = array();
1009 if (variable_get('user_register', 1)) {
1010 $items[] = l(t('Create new account'), 'user/register', array('attributes' => array('title' => t('Create a new user account.'))));
1011 }
1012 $items[] = l(t('Request new password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
1013 $form['links'] = array('#markup' => theme('item_list', $items));
1014 return $form;
1015 }
1016
1017 /**
1018 * Implement hook_block_list().
1019 */
1020 function user_block_list() {
1021 global $user;
1022
1023 $blocks['login']['info'] = t('User login');
1024 // Not worth caching.
1025 $blocks['login']['cache'] = BLOCK_NO_CACHE;
1026
1027 $blocks['new']['info'] = t('Who\'s new');
1028
1029 // Too dynamic to cache.
1030 $blocks['online']['info'] = t('Who\'s online');
1031 $blocks['online']['cache'] = BLOCK_NO_CACHE;
1032 return $blocks;
1033 }
1034
1035 /**
1036 * Implement hook_block_configure().
1037 */
1038 function user_block_configure($delta = '') {
1039 global $user;
1040
1041 switch ($delta) {
1042 case 'new':
1043 $form['user_block_whois_new_count'] = array(
1044 '#type' => 'select',
1045 '#title' => t('Number of users to display'),
1046 '#default_value' => variable_get('user_block_whois_new_count', 5),
1047 '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
1048 );
1049 return $form;
1050
1051 case 'online':
1052 $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), 'format_interval');
1053 $form['user_block_seconds_online'] = array('#type' => 'select', '#title' => t('User activity'), '#default_value' => variable_get('user_block_seconds_online', 900), '#options' => $period, '#description' => t('A user is considered online for this long after they have last viewed a page.'));
1054 $form['user_block_max_list_count'] = array('#type' => 'select', '#title' => t('User list length'), '#default_value' => variable_get('user_block_max_list_count', 10), '#options' => drupal_map_assoc(array(0, 5, 10, 15, 20, 25, 30, 40, 50, 75, 100)), '#description' => t('Maximum number of currently online users to display.'));
1055 $form['user_block_cache'] = array('#markup' => '<p>If page caching is disabled, the block shows the number of anonymous and authenticated users, respectively. If page caching is enabled, only the number of authenticated users is displayed.</p>');
1056 return $form;
1057 }
1058 }
1059
1060 /**
1061 * Implement hook_block_save().
1062 */
1063 function user_block_save($delta = '', $edit = array()) {
1064 global $user;
1065
1066 switch ($delta) {
1067 case 'new':
1068 variable_set('user_block_whois_new_count', $edit['user_block_whois_new_count']);
1069 break;
1070
1071 case 'online':
1072 variable_set('user_block_seconds_online', $edit['user_block_seconds_online']);
1073 variable_set('user_block_max_list_count', $edit['user_block_max_list_count']);
1074 break;
1075 }
1076 }
1077
1078 /**
1079 * Implement hook_block_view().
1080 */
1081 function user_block_view($delta = '') {
1082 global $user;
1083
1084 $block = array();
1085
1086 switch ($delta) {
1087 case 'login':
1088 // For usability's sake, avoid showing two login forms on one page.
1089 if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
1090
1091 $block['subject'] = t('User login');
1092 $block['content'] = drupal_get_form('user_login_block');
1093 }
1094 return $block;
1095
1096 case 'new':
1097 if (user_access('access content')) {
1098 // Retrieve a list of new users who have subsequently accessed the site successfully.
1099 $items = db_query_range('SELECT uid, name FROM {users} WHERE status <> 0 AND access <> 0 ORDER BY created DESC', array(), 0, variable_get('user_block_whois_new_count', 5))->fetchAll();
1100 $output = theme('user_list', $items);
1101
1102 $block['subject'] = t('Who\'s new');
1103 $block['content'] = $output;
1104 }
1105 return $block;
1106
1107 case 'online':
1108 if (user_access('access content')) {
1109 // Count users active within the defined period.
1110 $interval = REQUEST_TIME - variable_get('user_block_seconds_online', 900);
1111
1112 // Perform database queries to gather online user lists. We use s.timestamp
1113 // rather than u.access because it is much faster.
1114 $authenticated_count = db_query("SELECT COUNT(DISTINCT s.uid) FROM {sessions} s WHERE s.timestamp >= :timestamp AND s.uid > 0", array(':timestamp' => $interval))->fetchField();
1115
1116 // When page caching is enabled, sessions are only created for
1117 // anonymous users when needed.
1118 if (variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED) {
1119 $anonymous_count = drupal_session_count($interval);
1120 // Format the output with proper grammar.
1121 if ($anonymous_count == 1 && $authenticated_count == 1) {
1122 $output = t('There is currently %members and %visitors online.', array('%members' => format_plural($authenticated_count, '1 user', '@count users'), '%visitors' => format_plural($anonymous_count, '1 guest', '@count guests')));
1123 }
1124 else {
1125 $output = t('There are currently %members and %visitors online.', array('%members' => format_plural($authenticated_count, '1 user', '@count users'), '%visitors' => format_plural($anonymous_count, '1 guest', '@count guests')));
1126 }
1127 }
1128 else {
1129 $output = format_plural($authenticated_count, 'There is currently 1 user online.', 'There are currently @count users online.');
1130 }
1131
1132 // Display a list of currently online users.
1133 $max_users = variable_get('user_block_max_list_count', 10);
1134 if ($authenticated_count && $max_users) {
1135 $items = db_query_range('SELECT u.uid, u.name, MAX(s.timestamp) AS max_timestamp FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.timestamp >= :interval AND s.uid > 0 GROUP BY u.uid, u.name ORDER BY max_timestamp DESC', array(':interval' => $interval), 0, $max_users)->fetchAll();
1136 $output .= theme('user_list', $items, t('Online users'));
1137 }
1138
1139 $block['subject'] = t('Who\'s online');
1140 $block['content'] = $output;
1141 }
1142 return $block;
1143 }
1144 }
1145
1146 /**
1147 * Process variables for user-picture.tpl.php.
1148 *
1149 * The $variables array contains the following arguments:
1150 * - $account: A user, node or comment object with 'name', 'uid' and 'picture'
1151 * fields.
1152 *
1153 * @see user-picture.tpl.php
1154 */
1155 function template_preprocess_user_picture(&$variables) {
1156 $variables['user_picture'] = '';
1157 if (variable_get('user_pictures', 0)) {
1158 $account = $variables['account'];
1159 if (!empty($account->picture)) {
1160 // @TODO: Ideally this function would only be passed file objects, but
1161 // since there's a lot of legacy code that JOINs the {users} table to
1162 // {node} or {comments} and passes the results into this function if we
1163 // a numeric value in the picture field we'll assume it's a file id
1164 // and load it for them. Once we've got user_load_multiple() and
1165 // comment_load_multiple() functions the user module will be able to load
1166 // the picture files in mass during the object's load process.
1167 if (is_numeric($account->picture)) {
1168 $account->picture = file_load($account->picture);
1169 }
1170 if (!empty($account->picture->filepath)) {
1171 $filepath = $account->picture->filepath;
1172 }
1173 }
1174 elseif (variable_get('user_picture_default', '')) {
1175 $filepath = variable_get('user_picture_default', '');
1176 }
1177 if (isset($filepath)) {
1178 $alt = t("@user's picture", array('@user' => $account->name ? $account->name : variable_get('anonymous', t('Anonymous'))));
1179 if (module_exists('image') && $style = variable_get('user_picture_style', '')) {
1180 $variables['user_picture'] = theme('image_style', $style, $filepath, $alt, $alt, array(), FALSE);
1181 }
1182 else {
1183 $variables['user_picture'] = theme('image', $filepath, $alt, $alt, array(), FALSE);
1184 }
1185 if (!empty($account->uid) && user_access('access user profiles')) {
1186 $attributes = array('attributes' => array('title' => t('View user profile.')), 'html' => TRUE);
1187 $variables['user_picture'] = l($variables['user_picture'], "user/$account->uid", $attributes);
1188 }
1189 }
1190 }
1191 }
1192
1193 /**
1194 * Make a list of users.
1195 *
1196 * @param $users
1197 * An array with user objects. Should contain at least the name and uid.
1198 * @param $title
1199 * (optional) Title to pass on to theme_item_list().
1200 *
1201 * @ingroup themeable
1202 */
1203 function theme_user_list($users, $title = NULL) {
1204 if (!empty($users)) {
1205 foreach ($users as $user) {
1206 $items[] = theme('username', $user);
1207 }
1208 }
1209 return theme('item_list', $items, $title);
1210 }
1211
1212 function user_is_anonymous() {
1213 // Menu administrators can see items for anonymous when administering.
1214 return !$GLOBALS['user']->uid || !empty($GLOBALS['menu_admin']);
1215 }
1216
1217 function user_is_logged_in() {
1218 return (bool)$GLOBALS['user']->uid;
1219 }
1220
1221 function user_register_access() {
1222 return user_is_anonymous() && variable_get('user_register', 1);
1223 }
1224
1225 function user_view_access($account) {
1226 return $account && $account->uid &&
1227 (
1228 // Always let users view their own profile.
1229 ($GLOBALS['user']->uid == $account->uid) ||
1230 // Administrators can view all accounts.
1231 user_access('administer users') ||
1232 // The user is not blocked and logged in at least once.
1233 ($account->access && $account->status && user_access('access user profiles'))
1234 );
1235 }
1236
1237 /**
1238 * Access callback for user account editing.
1239 */
1240 function user_edit_access($account) {
1241 return (($GLOBALS['user']->uid == $account->uid) || user_access('administer users')) && $account->uid > 0;
1242 }
1243
1244 /**
1245 * Menu access callback; limit access to account cancellation pages.
1246 *
1247 * Limit access to users with the 'cancel account' permission or administrative
1248 * users, and prevent the anonymous user from cancelling the account.
1249 */
1250 function user_cancel_access($account) {
1251 return ((($GLOBALS['user']->uid == $account->uid) && user_access('cancel account')) || user_access('administer users')) && $account->uid > 0;
1252 }
1253
1254 function user_load_self($arg) {
1255 $arg[1] = user_load($GLOBALS['user']->uid);
1256 return $arg;
1257 }
1258
1259 /**
1260 * Implement hook_menu().
1261 */
1262 function user_menu() {
1263 $items['user/autocomplete'] = array(
1264 'title' => 'User autocomplete',
1265 'page callback' => 'user_autocomplete',
1266 'access callback' => 'user_access',
1267 'access arguments' => array('access user profiles'),
1268 'type' => MENU_CALLBACK,
1269 );
1270
1271 // Registration and login pages.
1272 $items['user'] = array(
1273 'title' => 'User account',
1274 'page callback' => 'user_page',
1275 'access callback' => TRUE,
1276 'type' => MENU_CALLBACK,
1277 );
1278
1279 $items['user/login'] = array(
1280 'title' => 'Log in',
1281 'access callback' => 'user_is_anonymous',
1282 'type' => MENU_DEFAULT_LOCAL_TASK,
1283 );
1284
1285 $items['user/register'] = array(
1286 'title' => 'Create new account',
1287 'page callback' => 'drupal_get_form',
1288 'page arguments' => array('user_register'),
1289 'access callback' => 'user_register_access',
1290 'type' => MENU_LOCAL_TASK,
1291 );
1292
1293 $items['user/password'] = array(
1294 'title' => 'Request new password',
1295 'page callback' => 'drupal_get_form',
1296 'page arguments' => array('user_pass'),
1297 'access callback' => 'user_is_anonymous',
1298 'type' => MENU_LOCAL_TASK,
1299 );
1300 $items['user/reset/%/%/%'] = array(
1301 'title' => 'Reset password',
1302 'page callback' => 'drupal_get_form',
1303 'page arguments' => array('user_pass_reset', 2, 3, 4),
1304 'access callback' => TRUE,
1305 'type' => MENU_CALLBACK,
1306 );
1307
1308 $items['user/logout'] = array(
1309 'title' => 'Log out',
1310 'access callback' => 'user_is_logged_in',
1311 'page callback' => 'user_logout',
1312 'weight' => 10,
1313 'menu_name' => 'user-menu',
1314 );
1315
1316 // User administration pages.
1317 $items['admin/people'] = array(
1318 'title' => 'People',
1319 'page callback' => 'user_admin',
1320 'page arguments' => array('list'),
1321 'access arguments' => array('administer users'),
1322 'weight' => -4,
1323 );
1324 $items['admin/people/list'] = array(
1325 'title' => 'List',
1326 'type' => MENU_DEFAULT_LOCAL_TASK,
1327 'weight' => -10,
1328 );
1329 $items['admin/people/create'] = array(
1330 'title' => 'Add user',
1331 'page arguments' => array('create'),
1332 'access arguments' => array('administer users'),
1333 'type' => MENU_LOCAL_TASK,
1334 );
1335 $items['admin/settings/user'] = array(
1336 'title' => 'Users',
1337 'description' => 'Configure default behavior of users, including registration requirements, e-mails, and user pictures.',
1338 'page callback' => 'drupal_get_form',
1339 'page arguments' => array('user_admin_settings'),
1340 'access arguments' => array('administer users'),
1341 );
1342
1343 // Permission administration pages.
1344 $items['admin/settings/permissions'] = array(
1345 'title' => 'Permissions',
1346 'description' => 'Determine access to features by selecting permissions for roles.',
1347 'page callback' => 'drupal_get_form',
1348 'page arguments' => array('user_admin_permissions'),
1349 'access arguments' => array('administer permissions'),
1350 );
1351 $items['admin/settings/roles'] = array(
1352 'title' => 'Roles',
1353 'description' => 'List, edit, or add user roles.',
1354 'page callback' => 'drupal_get_form',
1355 'page arguments' => array('user_admin_new_role'),
1356 'access arguments' => array('administer permissions'),
1357 );
1358 $items['admin/settings/roles/edit'] = array(
1359 'title' => 'Edit role',
1360 'page arguments' => array('user_admin_role'),
1361 'access arguments' => array('administer permissions'),
1362 'type' => MENU_CALLBACK,
1363 );
1364
1365 $items['user/%user_uid_optional'] = array(
1366 'title' => 'My account',
1367 'title callback' => 'user_page_title',
1368 'title arguments' => array(1),
1369 'page callback' => 'user_view',
1370 'page arguments' => array(1),
1371 'access callback' => 'user_view_access',
1372 'access arguments' => array(1),
1373 'weight' => -10,
1374 'menu_name' => 'user-menu',
1375 );
1376
1377 $items['user/%user/view'] = array(
1378 'title' => 'View',
1379 'type' => MENU_DEFAULT_LOCAL_TASK,
1380 'weight' => -10,
1381 );
1382
1383 $items['user/%user/cancel'] = array(
1384 'title' => 'Cancel account',
1385 'page callback' => 'drupal_get_form',
1386 'page arguments' => array('user_cancel_confirm_form', 1),
1387 'access callback' => 'user_cancel_access',
1388 'access arguments' => array(1),
1389 'type' => MENU_CALLBACK,
1390 );
1391
1392 $items['user/%user/cancel/confirm/%/%'] = array(
1393 'title' => 'Confirm account cancellation',
1394 'page callback' => 'user_cancel_confirm',
1395 'page arguments' => array(1, 4, 5),
1396 'access callback' => 'user_cancel_access',
1397 'access arguments' => array(1),
1398 'type' => MENU_CALLBACK,
1399 );
1400
1401 $items['user/%user/edit'] = array(
1402 'title' => 'Edit',
1403 'page callback' => 'user_edit',
1404 'page arguments' => array(1),
1405 'access callback' => 'user_edit_access',
1406 'access arguments' => array(1),
1407 'type' => MENU_LOCAL_TASK,
1408 );
1409
1410 $items['user/%user_category/edit/account'] = array(
1411 'title' => 'Account',
1412 'type' => MENU_DEFAULT_LOCAL_TASK,
1413 'load arguments' => array('%map', '%index'),
1414 );
1415
1416 if (($categories = _user_categories()) && (count($categories) > 1)) {
1417 foreach ($categories as $key => $category) {
1418 // 'account' is already handled by the MENU_DEFAULT_LOCAL_TASK.
1419 if ($category['name'] != 'account') {
1420 $items['user/%user_category/edit/' . $category['name']] = array(
1421 'title callback' => 'check_plain',
1422 'title arguments' => array($category['title']),
1423 'page callback' => 'user_edit',
1424 'page arguments' => array(1, 3),
1425 'access callback' => isset($category['access callback']) ? $category['access callback'] : 'user_edit_access',
1426 'access arguments' => isset($category['access arguments']) ? $category['access arguments'] : array(1),
1427 'type' => MENU_LOCAL_TASK,
1428 'weight' => $category['weight'],
1429 'load arguments' => array('%map', '%index'),
1430 'tab_parent' => 'user/%/edit',
1431 );
1432 }
1433 }
1434 }
1435 return $items;
1436 }
1437
1438 function user_init() {
1439 drupal_add_css(drupal_get_path('module', 'user') . '/user.css');
1440 }
1441
1442 function user_uid_optional_load($arg) {
1443 return user_load(isset($arg) ? $arg : $GLOBALS['user']->uid);
1444 }
1445
1446 /**
1447 * Return a user object after checking if any profile category in the path exists.
1448 */
1449 function user_category_load($uid, &$map, $index) {
1450 static $user_categories, $accounts;
1451
1452 // Cache $account - this load function will get called for each profile tab.
1453 if (!isset($accounts[$uid])) {
1454 $accounts[$uid] = user_load($uid);
1455 }
1456 $valid = TRUE;
1457 if ($account = $accounts[$uid]) {
1458 // Since the path is like user/%/edit/category_name, the category name will
1459 // be at a position 2 beyond the index corresponding to the % wildcard.
1460 $category_index = $index + 2;
1461 // Valid categories may contain slashes, and hence need to be imploded.
1462 $category_path = implode('/', array_slice($map, $category_index));
1463 if ($category_path) {
1464 // Check that the requested category exists.
1465 $valid = FALSE;
1466 if (!isset($user_categories)) {
1467 $user_categories = _user_categories();
1468 }
1469 foreach ($user_categories as $category) {
1470 if ($category['name'] == $category_path) {
1471 $valid = TRUE;
1472 // Truncate the map array in case the category name had slashes.
1473 $map = array_slice($map, 0, $category_index);
1474 // Assign the imploded category name to the last map element.
1475 $map[$category_index] = $category_path;
1476 break;
1477 }
1478 }
1479 }
1480 }
1481 return $valid ? $account : FALSE;
1482 }
1483
1484 /**
1485 * Returns the user id of the currently logged in user.
1486 */
1487 function user_uid_optional_to_arg($arg) {
1488 // Give back the current user uid when called from eg. tracker, aka.
1489 // with an empty arg. Also use the current user uid when called from
1490 // the menu with a % for the current account link.
1491 return empty($arg) || $arg == '%' ? $GLOBALS['user']->uid : $arg;
1492 }
1493
1494 /**
1495 * Menu item title callback - use the user name if it's not the current user.
1496 */
1497 function user_page_title($account) {
1498 if ($account->uid == $GLOBALS['user']->uid) {
1499 return t('My account');
1500 }
1501 return $account->name;
1502 }
1503
1504 /**
1505 * Discover which external authentication module(s) authenticated a username.
1506 *
1507 * @param $authname
1508 * A username used by an external authentication module.
1509 * @return
1510 * An associative array with module as key and username as value.
1511 */
1512 function user_get_authmaps($authname = NULL) {
1513 $authmaps = db_query("SELECT authname, module FROM {authmap} WHERE authname = :authname", array(':authname' => $authname))->fetchAllKeyed();
1514 return count($authmaps) ? $authmaps : 0;
1515 }
1516
1517 /**
1518 * Save mappings of which external authentication module(s) authenticated
1519 * a user. Maps external usernames to user ids in the users table.
1520 *
1521 * @param $account
1522 * A user object.
1523 * @param $authmaps
1524 * An associative array with a compound key and the username as the value.
1525 * The key is made up of 'authname_' plus the name of the external authentication
1526 * module.
1527 * @see user_external_login_register()
1528 */
1529 function user_set_authmaps($account, $authmaps) {
1530 foreach ($authmaps as $key => $value) {
1531 $module = explode('_', $key, 2);
1532 if ($value) {
1533 db_merge('authmap')
1534 ->key(array(
1535 'uid' => $account->uid,
1536 'module' => $module[1],
1537 ))
1538 ->fields(array('authname' => $value))
1539 ->execute();
1540 }
1541 else {
1542 db_delete('authmap')
1543 ->condition('uid', $account->uid)
1544 ->condition('module', $module[1])
1545 ->execute();
1546 }
1547 }
1548 }
1549
1550 /**
1551 * Form builder; the main user login form.
1552 *
1553 * @ingroup forms
1554 */
1555 function user_login(&$form_state) {
1556 global $user;
1557
1558 // If we are already logged on, go to the user page instead.
1559 if ($user->uid) {
1560 drupal_goto('user/' . $user->uid);
1561 }
1562
1563 // Display login form:
1564 $form['name'] = array('#type' => 'textfield',
1565 '#title' => t('Username'),
1566 '#size' => 60,
1567 '#maxlength' => USERNAME_MAX_LENGTH,
1568 '#required' => TRUE,
1569 );
1570
1571 $form['name']['#description'] = t('Enter your @s username.', array('@s' => variable_get('site_name', 'Drupal')));
1572 $form['pass'] = array('#type' => 'password',
1573 '#title' => t('Password'),
1574 '#description' => t('Enter the password that accompanies your username.'),
1575 '#required' => TRUE,
1576 );
1577 $form['#validate'] = user_login_default_validators();
1578 $form['submit'] = array('#type' => 'submit', '#value' => t('Log in'), '#weight' => 2);
1579
1580 return $form;
1581 }
1582
1583 /**
1584 * Set up a series for validators which check for blocked users,
1585 * then authenticate against local database, then return an error if
1586 * authentication fails. Distributed authentication modules are welcome
1587 * to use hook_form_alter() to change this series in order to
1588 * authenticate against their user database instead of the local users
1589 * table. If a distributed authentication module is successful, it
1590 * should set $form_state['uid'] to a user ID.
1591 *
1592 * We use three validators instead of one since external authentication
1593 * modules usually only need to alter the second validator.
1594 *
1595 * @see user_login_name_validate()
1596 * @see user_login_authenticate_validate()
1597 * @see user_login_final_validate()
1598 * @return array
1599 * A simple list of validate functions.
1600 */
1601 function user_login_default_validators() {
1602 return array('user_login_name_validate', 'user_login_authenticate_validate', 'user_login_final_validate');
1603 }
1604
1605 /**
1606 * A FAPI validate handler. Sets an error if supplied username has been blocked.
1607 */
1608 function user_login_name_validate($form, &$form_state) {
1609 if (isset($form_state['values']['name']) && user_is_blocked($form_state['values']['name'])) {
1610 // Blocked in user administration.
1611 form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
1612 }
1613 }
1614
1615 /**
1616 * A validate handler on the login form. Check supplied username/password
1617 * against local users table. If successful, $form_state['uid']
1618 * is set to the matching user ID.
1619 */
1620 function user_login_authenticate_validate($form, &$form_state) {
1621 $password = trim($form_state['values']['pass']);
1622 if (!empty($form_state['values']['name']) && !empty($password)) {
1623 // Do not allow any login from the current user's IP if the limit has been
1624 // reached. Default is 50 failed attempts allowed in one hour. This is
1625 // independent of the per-user limit to catch attempts from one IP to log
1626 // in to many different user accounts. We have a reasonably high limit
1627 // since there may be only one apparent IP for all users at an institution.
1628 if (!flood_is_allowed('failed_login_attempt_ip', variable_get('user_failed_login_ip_limit', 50), variable_get('user_failed_login_ip_window', 3600))) {
1629 $form_state['flood_control_triggered'] = 'ip';
1630 return;
1631 }
1632 $account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $form_state['values']['name']))->fetchObject();
1633 if ($account) {
1634 if (variable_get('user_failed_login_identifier_uid_only', FALSE)) {
1635 // Register flood events based on the uid only, so they apply for any
1636 // IP address. This is the most secure option.
1637 $identifier = $account->uid;
1638 }
1639 else {
1640 // The default identifier is a combination of uid and IP address. This
1641 // is less secure but more resistant to denial-of-service attacks that
1642 // could lock out all users with public user names.
1643 $identifier = $account->uid . '-' . ip_address();
1644 }
1645 $form_state['flood_control_user_identifier'] = $identifier;
1646
1647 // Don't allow login if the limit for this user has been reached.
1648 // Default is to allow 5 failed attempts every 6 hours.
1649 if (!flood_is_allowed('failed_login_attempt_user', variable_get('user_failed_login_user_limit', 5), variable_get('user_failed_login_user_window', 21600), $identifier)) {
1650 $form_state['flood_control_triggered'] = 'user';
1651 return;
1652 }
1653 }
1654 // We are not limited by flood control, so try to authenticate.
1655 // Set $form_state['uid'] as a flag for user_login_final_validate().
1656 $form_state['uid'] = user_authenticate($form_state['values']['name'], $password);
1657 }
1658 }
1659
1660 /**
1661 * The final validation handler on the login form.
1662 *
1663 * Sets a form error if user has not been authenticated, or if too many
1664 * logins have been attempted. This validation function should always
1665 * be the last one.
1666 */
1667 function user_login_final_validate($form, &$form_state) {
1668 if (empty($form_state['uid'])) {
1669 // Always register an IP-based failed login event.
1670 flood_register_event('failed_login_attempt_ip');
1671 // Register a per-user failed login event.
1672 if (isset($form_state['flood_control_user_identifier'])) {
1673 flood_register_event('failed_login_attempt_user', $form_state['flood_control_user_identifier']);
1674 }
1675
1676 if (isset($form_state['flood_control_triggered'])) {
1677 if ($form_state['flood_control_triggered'] == 'user') {
1678 form_set_error('name', format_plural(variable_get('user_failed_login_user_limit', 5), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Please try again later, or <a href="@url">request a new password</a>.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Please try again later, or <a href="@url">request a new password</a>.', array('@url' => url('user/password'))));
1679 }
1680 else {
1681 // We did not find a uid, so the limit is IP-based.
1682 form_set_error('name', t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Please try again later, or <a href="@url">request a new password</a>.', array('@url' => url('user/password'))));
1683 }
1684 }
1685 else {
1686 form_set_error('name', t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password'))));
1687 watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name']));
1688 }
1689 }
1690 elseif (isset($form_state['flood_control_user_identifier'])) {
1691 // Clear past failures for this user so as not to block a user who might
1692 // log in and out more than once in an hour.
1693 flood_clear_event('failed_login_attempt_user', $form_state['flood_control_user_identifier']);
1694 }
1695 }
1696
1697 /**
1698 * Try to validate the user's login credentials locally.
1699 *
1700 * @param $name
1701 * User name to authenticate.
1702 * @param $password
1703 * A plain-text password, such as trimmed text from form values.
1704 * @return
1705 * The user's uid on success, or FALSE on failure to authenticate.
1706 */
1707 function user_authenticate($name, $password) {
1708 $uid = FALSE;
1709 if (!empty($name) && !empty($password)) {
1710 $account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $name))->fetchObject();
1711 if ($account) {
1712 // Allow alternate password hashing schemes.
1713 require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
1714 if (user_check_password($password, $account)) {
1715 // Successful authentication.
1716 $uid = $account->uid;
1717
1718 // Update user to new password scheme if needed.
1719 if (user_needs_new_hash($account)) {
1720 $new_hash = user_hash_password($password);
1721 if ($new_hash) {
1722 db_update('users')
1723 ->fields(array('pass' => $new_hash))
1724 ->condition('uid', $account->uid)
1725 ->execute();
1726 }
1727 }
1728 }
1729 }
1730 }
1731 return $uid;
1732 }
1733
1734 /**
1735 * Finalize the login process. Must be called when logging in a user.
1736 *
1737 * The function records a watchdog message about the new session, saves the
1738 * login timestamp, calls hook_user op 'login' and generates a new session. *
1739 */
1740 function user_login_finalize(&$edit = array()) {
1741 global $user;
1742 watchdog('user', 'Session opened for %name.', array('%name' => $user->name));
1743 // Update the user table timestamp noting user has logged in.
1744 // This is also used to invalidate one-time login links.
1745 $user->login = REQUEST_TIME;
1746 db_update('users')
1747 ->fields(array('login' => $user->login))
1748 ->condition('uid', $user->uid)
1749 ->execute();
1750
1751 // Regenerate the session ID to prevent against session fixation attacks.
1752 // This is called before hook_user in case one of those functions fails
1753 // or incorrectly does a redirect which would leave the old session in place.
1754 drupal_session_regenerate();
1755
1756 user_module_invoke('login', $edit, $user);
1757 }
1758
1759 /**
1760 * Submit handler for the login form. Load $user object and perform standard login
1761 * tasks. The user is then redirected to the My Account page. Setting the
1762 * destination in the query string overrides the redirect.
1763 */
1764 function user_login_submit($form, &$form_state) {
1765 global $user;
1766 $user = user_load($form_state['uid']);
1767 user_login_finalize();
1768
1769 $form_state['redirect'] = 'user/' . $user->uid;
1770 }
1771
1772 /**
1773 * Helper function for authentication modules. Either logs in or registers
1774 * the current user, based on username. Either way, the global $user object is
1775 * populated and login tasks are performed.
1776 */
1777 function user_external_login_register($name, $module) {
1778 $account = user_load_by_name($name);
1779 if (!$account->uid) {
1780 // Register this new user.
1781 $userinfo = array(
1782 'name' => $name,
1783 'pass' => user_password(),
1784 'init' => $name,
1785 'status' => 1,
1786 'access' => REQUEST_TIME
1787 );
1788 $account = user_save('', $userinfo);
1789 // Terminate if an error occurred during user_save().
1790 if (!$account) {
1791 drupal_set_message(t("Error saving user account."), 'error');
1792 return;
1793 }
1794 user_set_authmaps($account, array("authname_$module" => $name));
1795 }
1796
1797 // Log user in.
1798 $form_state['uid'] = $account->uid;
1799 user_login_submit(array(), $form_state);
1800 }
1801
1802 function user_pass_reset_url($account) {
1803 $timestamp = REQUEST_TIME;
1804 return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE));
1805 }
1806
1807 /**
1808 * Generate a URL to confirm an account cancellation request.
1809 *
1810 * @see user_mail_tokens()
1811 * @see user_cancel_confirm()
1812 */
1813 function user_cancel_url($account) {
1814 $timestamp = REQUEST_TIME;
1815 return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE));
1816 }
1817
1818 function user_pass_rehash($password, $timestamp, $login) {
1819 return md5($timestamp . $password . $login);
1820 }
1821
1822 function user_edit_form(&$form_state, $uid, $edit, $register = FALSE) {
1823 _user_password_dynamic_validation();
1824 $admin = user_access('administer users');
1825
1826 $form = array();
1827
1828 // Account information:
1829 $form['account'] = array('#type' => 'fieldset',
1830 '#title' => t('Account information'),
1831 '#weight' => -10,
1832 );
1833 // Only show name field when: registration page; or user is editing own account and can change username; or an admin user.
1834 if ($register || ($GLOBALS['user']->uid == $uid && user_access('change own username')) || $admin) {
1835 $form['account']['name'] = array('#type' => 'textfield',
1836 '#title' => t('Username'),
1837 '#default_value' => $edit['name'],
1838 '#maxlength' => USERNAME_MAX_LENGTH,
1839 '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, apostrophes, and underscores.'),
1840 '#required' => TRUE,
1841 '#attributes' => array('class' => 'username'),
1842 );
1843 }
1844 $form['account']['mail'] = array('#type' => 'textfield',
1845 '#title' => t('E-mail address'),
1846 '#default_value' => $edit['mail'],
1847 '#maxlength' => EMAIL_MAX_LENGTH,
1848 '#description' => t('A valid e-mail address. All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'),
1849 '#required' => TRUE,
1850 );
1851 if (!$register) {
1852 $form['account']['pass'] = array('#type' => 'password_confirm',
1853 '#description' => t('To change the current user password, enter the new password in both fields.'),
1854 '#size' => 25,
1855 );
1856 }
1857 elseif (!variable_get('user_email_verification', TRUE) || $admin) {
1858 $form['account']['pass'] = array(
1859 '#type' => 'password_confirm',
1860 '#description' => t('Provide a password for the new account in both fields.'),
1861 '#required' => TRUE,
1862 '#size' => 25,
1863 );
1864 }
1865 if ($admin) {
1866 $form['account']['status'] = array(
1867 '#type' => 'radios',
1868 '#title' => t('Status'),
1869 '#default_value' => isset($edit['status']) ? $edit['status'] : 1,
1870 '#options' => array(t('Blocked'), t('Active'))
1871 );
1872 }
1873 if (user_access('administer permissions')) {
1874 $roles = user_roles(TRUE);
1875
1876 // The disabled checkbox subelement for the 'authenticated user' role
1877 // must be generated separately and added to the checkboxes element,
1878 // because of a limitation in D6 FormAPI not supporting a single disabled
1879 // checkbox within a set of checkboxes.
1880 // TODO: This should be solved more elegantly. See issue #119038.
1881 $checkbox_authenticated = array(
1882 '#type' => 'checkbox',
1883 '#title' => $roles[DRUPAL_AUTHENTICATED_RID],
1884 '#default_value' => TRUE,
1885 '#disabled' => TRUE,
1886 );
1887
1888 unset($roles[DRUPAL_AUTHENTICATED_RID]);
1889 if ($roles) {
1890 $default = empty($edit['roles']) ? array() : array_keys($edit['roles']);
1891 $form['account']['roles'] = array(
1892 '#type' => 'checkboxes',
1893 '#title' => t('Roles'),
1894 '#default_value' => $default,
1895 '#options' => $roles,
1896 DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated,
1897 );
1898 }
1899 }
1900
1901 // Signature:
1902 if (variable_get('user_signatures', 0) && module_exists('comment') && !$register) {
1903 $form['signature_settings'] = array(
1904 '#type' => 'fieldset',
1905 '#title' => t('Signature settings'),
1906 '#weight' => 1,
1907 );
1908 $form['signature_settings']['signature'] = array(
1909 '#type' => 'textarea',
1910 '#title' => t('Signature'),
1911 '#default_value' => $edit['signature'],
1912 '#description' => t('Your signature will be publicly displayed at the end of your comments.'),
1913 );
1914 }
1915
1916 // Picture/avatar:
1917 if (variable_get('user_pictures', 0) && !$register) {
1918 $form['picture'] = array(
1919 '#type' => 'fieldset',
1920 '#title' => t('Picture'),
1921 '#weight' => 1,
1922 );
1923 $form['picture']['picture'] = array(
1924 '#type' => 'value',
1925 '#value' => $edit['picture'],
1926 );
1927 $form['picture']['picture_current'] = array(
1928 '#markup' => theme('user_picture', (object)$edit),
1929 );
1930 $form['picture']['picture_delete'] = array(
1931 '#type' => 'checkbox',
1932 '#title' => t('Delete picture'),
1933 '#access' => !empty($edit['picture']->fid),
1934 '#description' => t('Check this box to delete your current picture.'),
1935 );
1936 $form['picture']['picture_upload'] = array(
1937 '#type' => 'file',
1938 '#title' => t('Upload picture'),
1939 '#size' => 48,
1940 '#description' => t('Your virtual face or picture. Maximum dimensions are %dimensions pixels and the maximum size is %size kB.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'), '%size' => variable_get('user_picture_file_size', '30'))) . ' ' . variable_get('user_picture_guidelines', ''),
1941 );
1942 $form['#validate'][] = 'user_validate_picture';
1943 }
1944 $form['#uid'] = $uid;
1945
1946 return $form;
1947 }
1948
1949 /**
1950 * Cancel a user account.
1951 *
1952 * Since the user cancellation process needs to be run in a batch, either
1953 * Form API will invoke it, or batch_process() needs to be invoked after calling
1954 * this function and should define the path to redirect to.
1955 *
1956 * @param $edit
1957 * An array of submitted form values.
1958 * @param $uid
1959 * The user ID of the user account to cancel.
1960 * @param $method
1961 * The account cancellation method to use.
1962 *
1963 * @see _user_cancel()
1964 */
1965 function user_cancel($edit, $uid, $method) {
1966 global $user;
1967
1968 $account = user_load($uid);
1969
1970 if (!$account) {
1971 drupal_set_message(t('The user account %id does not exist.', array('%id' => $uid)), 'error');
1972 watchdog('user', 'Attempted to cancel non-existing user account: %id.', array('%id' => $uid), WATCHDOG_ERROR);
1973 return;
1974 }
1975
1976 // Initialize batch (to set title).
1977 $batch = array(
1978 'title' => t('Cancelling account'),
1979 'operations' => array(),
1980 );
1981 batch_set($batch);
1982
1983 // Allow modules to add further sets to this batch.
1984 module_invoke_all('user_cancel', $edit, $account, $method);
1985
1986 // Finish the batch and actually cancel the account.
1987 $batch = array(
1988 'title' => t('Cancelling user account'),
1989 'operations' => array(
1990 array('_user_cancel', array($edit, $account, $method)),
1991 ),
1992 );
1993 batch_set($batch);
1994
1995 // Batch processing is either handled via Form API or has to be invoked
1996 // manually.
1997 }
1998
1999 /**
2000 * Last batch processing step for cancelling a user account.
2001 *
2002 * Since batch and session API require a valid user account, the actual
2003 * cancellation of a user account needs to happen last.
2004 *
2005 * @see user_cancel()
2006 */
2007 function _user_cancel($edit, $account, $method) {
2008 global $user;
2009
2010 switch ($method) {
2011 case 'user_cancel_block':
2012 case 'user_cancel_block_unpublish':
2013 default:
2014 // Send account blocked notification if option was checked.
2015 if (!empty($edit['user_cancel_notify'])) {
2016 _user_mail_notify('status_blocked', $account);
2017 }
2018 db_update('users')
2019 ->fields(array('status' => 0))
2020 ->condition('uid', $account->uid)
2021 ->execute();
2022 drupal_set_message(t('%name has been disabled.', array('%name' => $account->name)));
2023 watchdog('user', 'Blocked user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
2024 break;
2025
2026 case 'user_cancel_reassign':
2027 case 'user_cancel_delete':
2028 // Send account canceled notification if option was checked.
2029 if (!empty($edit['user_cancel_notify'])) {
2030 _user_mail_notify('status_canceled', $account);
2031 }
2032 db_delete('users')
2033 ->condition('uid', $account->uid)
2034 ->execute();
2035 db_delete('users_roles')
2036 ->condition('uid', $account->uid)
2037 ->execute();
2038 db_delete('authmap')
2039 ->condition('uid', $account->uid)
2040 ->execute();
2041 drupal_set_message(t('%name has been deleted.', array('%name' => $account->name)));
2042 watchdog('user', 'Deleted user: %name %email.', array('%name' => $account->name, '%email' => '<' . $account->mail . '>'), WATCHDOG_NOTICE);
2043 break;
2044 }
2045
2046 // After cancelling account, ensure that user is logged out.
2047 if ($account->uid == $user->uid) {
2048 // Destroy the current session, and reset $user to the anonymous user.
2049 session_destroy();
2050 }
2051 else {
2052 drupal_session_destroy_uid($account->uid);
2053 }
2054
2055 // Clear the cache for anonymous users.
2056 cache_clear_all();
2057 }
2058
2059 /**
2060 * Builds a structured array representing the profile content.
2061 *
2062 * @param $account
2063 * A user object.
2064 *
2065 * @return
2066 * A structured array containing the individual elements of the profile.
2067 */
2068 function user_build_content($account) {
2069 $account->content = array();
2070
2071 // Build fields content.
2072 // TODO D7 : figure out where exactly this needs to go
2073 $account->content += field_attach_view('user', $account);
2074
2075 module_invoke_all('user_view', $account);
2076 return $account->content;
2077 }
2078
2079 /**
2080 * Implement hook_mail().
2081 */
2082 function user_mail($key, &$message, $params) {
2083 $language = $message['language'];
2084 $variables = user_mail_tokens($params['account'], $language);
2085 $message['subject'] .= _user_mail_text($key . '_subject', $language, $variables);
2086 $message['body'][] = _user_mail_text($key . '_body', $language, $variables);
2087 }
2088
2089 /**
2090 * Returns a mail string for a variable name.
2091 *
2092 * Used by user_mail() and the settings forms to retrieve strings.
2093 */
2094 function _user_mail_text($key, $language = NULL, $variables = array()) {
2095 $langcode = isset($language) ? $language->language : NULL;
2096
2097 if ($admin_setting = variable_get('user_mail_' . $key, FALSE)) {
2098 // An admin setting overrides the default string.
2099 return strtr($admin_setting, $variables);
2100 }
2101 else {
2102 // No override, return default string.
2103 switch ($key) {
2104 case 'register_no_approval_required_subject':
2105 return t('Account details for !username at !site', $variables, array('langcode' => $langcode));
2106 case 'register_no_approval_required_body':
2107 return t("!username,\n\nThank you for registering at !site. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n-- !site team", $variables, array('langcode' => $langcode));
2108 case 'register_admin_created_subject':
2109 return t('An administrator created an account for you at !site', $variables, array('langcode' => $langcode));
2110 case 'register_admin_created_body':
2111 return t("!username,\n\nA site administrator at !site has created an account for you. You may now log in to !login_uri using the following username and password:\n\nusername: !username\npassword: !password\n\nYou may also log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\n\n-- !site team", $variables, array('langcode' => $langcode));
2112 case 'register_pending_approval_subject':
2113 case 'register_pending_approval_admin_subject':
2114 return t('Account details for !username at !site (pending admin approval)', $variables, array('langcode' => $langcode));
2115 case 'register_pending_approval_body':
2116 return t("!username,\n\nThank you for registering at !site. Your application for an account is currently pending approval. Once it has been approved, you will receive another e-mail containing information about how to log in, set your password, and other details.\n\n\n-- !site team", $variables, array('langcode' => $langcode));
2117 case 'register_pending_approval_admin_body':
2118 return t("!username has applied for an account.\n\n!edit_uri", $variables, array('langcode' => $langcode));
2119 case 'password_reset_subject':
2120 return t('Replacement login information for !username at !site', $variables, array('langcode' => $langcode));
2121 case 'password_reset_body':
2122 return t("!username,\n\nA request to reset the password for your account has been made at !site.\n\nYou may now log in to !uri_brief by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once. It expires after one day and nothing will happen if it's not used.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.", $variables, array('langcode' => $langcode));
2123 case 'status_activated_subject':
2124 return t('Account details for !username at !site (approved)', $variables, array('langcode' => $langcode));
2125 case 'status_activated_body':
2126 return t("!username,\n\nYour account at !site has been activated.\n\nYou may now log in by clicking on this link or copying and pasting it in your browser:\n\n!login_url\n\nThis is a one-time login, so it can be used only once.\n\nAfter logging in, you will be redirected to !edit_uri so you can change your password.\n\nOnce you have set your own password, you will be able to log in to !login_uri in the future using:\n\nusername: !username\n", $variables, array('langcode' => $langcode));
2127 case 'status_blocked_subject':
2128 return t('Account details for !username at !site (blocked)', $variables, array('langcode' => $langcode));
2129 case 'status_blocked_body':
2130 return t("!username,\n\nYour account on !site has been blocked.", $variables, array('langcode' => $langcode));
2131
2132 case 'cancel_confirm_subject':
2133 return t('Account cancellation request for !username at !site', $variables, array('langcode' => $langcode));
2134 case 'cancel_confirm_body':
2135 return t("!username,
2136
2137 A request to cancel your account has been made at !site.
2138
2139 You may now cancel your account on !uri_brief by clicking this link or copying and pasting it into your browser:
2140
2141 !cancel_url
2142
2143 NOTE: The cancellation of your account is not reversible.
2144
2145 This link expires in one day and nothing will happen if it is not used.", $variables, array('langcode' => $langcode));
2146
2147 case 'status_canceled_subject':
2148 return t('Account details for !username at !site (canceled)', $variables, array('langcode' => $langcode));
2149 case 'status_canceled_body':
2150 return t("!username,
2151
2152 Your account on !site has been canceled.", $variables, array('langcode' => $langcode));
2153 }
2154 }
2155 }
2156
2157 /*** Administrative features ***********************************************/
2158
2159 /**
2160 * Retrieve an array of roles matching specified conditions.
2161 *
2162 * @param $membersonly
2163 * Set this to TRUE to exclude the 'anonymous' role.
2164 * @param $permission
2165 * A string containing a permission. If set, only roles containing that
2166 * permission are returned.
2167 *
2168 * @return
2169 * An associative array with the role id as the key and the role name as
2170 * value.
2171 */
2172 function user_roles($membersonly = FALSE, $permission = NULL) {
2173 // System roles take the first two positions.
2174 $roles = array(
2175 DRUPAL_ANONYMOUS_RID => NULL,
2176 DRUPAL_AUTHENTICATED_RID => NULL,
2177 );
2178
2179 if (!empty($permission)) {
2180 $result = db_query("SELECT r.* FROM {role} r INNER JOIN {role_permission} p ON r.rid = p.rid WHERE p.permission = :permission ORDER BY r.name", array(':permission' => $permission));
2181 }
2182 else {
2183 $result = db_query('SELECT * FROM {role} ORDER BY name');
2184 }
2185
2186 foreach ($result as $role) {
2187 switch ($role->rid) {
2188 // We only translate the built in role names
2189 case DRUPAL_ANONYMOUS_RID:
2190 if (!$membersonly) {
2191 $roles[$role->rid] = t($role->name);
2192 }
2193 break;
2194 case DRUPAL_AUTHENTICATED_RID:
2195 $roles[$role->rid] = t($role->name);
2196 break;
2197 default:
2198 $roles[$role->rid] = $role->name;
2199 }
2200 }
2201
2202 // Filter to remove unmatched system roles.
2203 return array_filter($roles);
2204 }
2205
2206 /**
2207 * Implement hook_user_operations().
2208 */
2209 function user_user_operations($form_state = array()) {
2210 $operations = array(
2211 'unblock' => array(
2212 'label' => t('Unblock the selected users'),
2213 'callback' => 'user_user_operations_unblock',
2214 ),
2215 'block' => array(
2216 'label' => t('Block the selected users'),
2217 'callback' => 'user_user_operations_block',
2218 ),
2219 'cancel' => array(
2220 'label' => t('Cancel the selected user accounts'),
2221 ),
2222 );
2223
2224 if (user_access('administer permissions')) {
2225 $roles = user_roles(TRUE);
2226 unset($roles[DRUPAL_AUTHENTICATED_RID]); // Can't edit authenticated role.
2227
2228 $add_roles = array();
2229 foreach ($roles as $key => $value) {
2230 $add_roles['add_role-' . $key] = $value;
2231 }
2232
2233 $remove_roles = array();
2234 foreach ($roles as $key => $value) {
2235 $remove_roles['remove_role-' . $key] = $value;
2236 }
2237
2238 if (count($roles)) {
2239 $role_operations = array(
2240 t('Add a role to the selected users') => array(
2241 'label' => $add_roles,
2242 ),
2243 t('Remove a role from the selected users') => array(
2244 'label' => $remove_roles,
2245 ),
2246 );
2247
2248 $operations += $role_operations;
2249 }
2250 }
2251
2252 // If the form has been posted, we need to insert the proper data for
2253 // role editing if necessary.
2254 if (!empty($form_state['submitted'])) {
2255 $operation_rid = explode('-', $form_state['values']['operation']);
2256 $operation = $operation_rid[0];
2257 if ($operation == 'add_role' || $operation == 'remove_role') {
2258 $rid = $operation_rid[1];
2259 if (user_access('administer permissions')) {
2260 $operations[$form_state['values']['operation']] = array(
2261 'callback' => 'user_multiple_role_edit',
2262 'callback arguments' => array($operation, $rid),
2263 );
2264 }
2265 else {
2266 watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
2267 return;
2268 }
2269 }
2270 }
2271
2272 return $operations;
2273 }
2274
2275 /**
2276 * Callback function for admin mass unblocking users.
2277 */
2278 function user_user_operations_unblock($accounts) {
2279 foreach ($accounts as $uid) {
2280 $account = user_load($uid);
2281 // Skip unblocking user if they are already unblocked.
2282 if ($account !== FALSE && $account->status == 0) {
2283 user_save($account, array('status' => 1));
2284 }
2285 }
2286 }
2287
2288 /**
2289 * Callback function for admin mass blocking users.
2290 */
2291 function user_user_operations_block($accounts) {
2292 foreach ($accounts as $uid) {
2293 $account = user_load($uid);
2294 // Skip blocking user if they are already blocked.
2295 if ($account !== FALSE && $account->status == 1) {
2296 user_save($account, array('status' => 0));
2297 }
2298 }
2299 }
2300
2301 /**
2302 * Callback function for admin mass adding/deleting a user role.
2303 */
2304 function user_multiple_role_edit($accounts, $operation, $rid) {
2305 // The role name is not necessary as user_save() will reload the user
2306 // object, but some modules' hook_user() may look at this first.
2307 $role_name = db_query('SELECT name FROM {role} WHERE rid = :rid', array(':rid' => $rid))->fetchField();
2308
2309 switch ($operation) {
2310 case 'add_role':
2311 foreach ($accounts as $uid) {
2312 $account = user_load($uid);
2313 // Skip adding the role to the user if they already have it.
2314 if ($account !== FALSE && !isset($account->roles[$rid])) {
2315 $roles = $account->roles + array($rid => $role_name);
2316 user_save($account, array('roles' => $roles));
2317 }
2318 }
2319 break;
2320 case 'remove_role':
2321 foreach ($accounts as $uid) {
2322 $account = user_load($uid);
2323 // Skip removing the role from the user if they already don't have it.
2324 if ($account !== FALSE && isset($account->roles[$rid])) {
2325 $roles = array_diff($account->roles, array($rid => $role_name));
2326 user_save($account, array('roles' => $roles));
2327 }
2328 }
2329 break;
2330 }
2331 }
2332
2333 function user_multiple_cancel_confirm(&$form_state) {
2334 $edit = $form_state['input'];
2335
2336 $form['accounts'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
2337 // array_filter() returns only elements with TRUE values.
2338 foreach (array_filter($edit['accounts']) as $uid => $value) {
2339 $user = db_query('SELECT name FROM {users} WHERE uid = :uid', array(':uid' => $uid))->fetchField();
2340 $form['accounts'][$uid] = array('#type' => 'hidden', '#value' => $uid, '#prefix' => '<li>', '#suffix' => check_plain($user) . "</li>\n");
2341 }
2342
2343 $form['operation'] = array('#type' => 'hidden', '#value' => 'cancel');
2344
2345 module_load_include('inc', 'user', 'user.pages');
2346 $form['user_cancel_method'] = array(
2347 '#type' => 'item',
2348 '#title' => t('When cancelling these accounts'),
2349 );
2350 $form['user_cancel_method'] += user_cancel_methods();
2351 // Remove method descriptions.
2352 foreach (element_children($form['user_cancel_method']) as $element) {
2353 unset($form['user_cancel_method'][$element]['#description']);
2354 }
2355
2356 // Allow to send the account cancellation confirmation mail.
2357 $form['user_cancel_confirm'] = array(
2358 '#type' => 'checkbox',
2359 '#title' => t('Require e-mail confirmation to cancel account.'),
2360 '#default_value' => FALSE,
2361 '#description' => t('When enabled, the user must confirm the account cancellation via e-mail.'),
2362 );
2363 // Also allow to send account canceled notification mail, if enabled.
2364 $form['user_cancel_notify'] = array(
2365 '#type' => 'checkbox',
2366 '#title' => t('Notify user when account is canceled.'),
2367 '#default_value' => FALSE,
2368 '#access' => variable_get('user_mail_status_canceled_notify', FALSE),
2369 '#description' => t('When enabled, the user will receive an e-mail notification after the account has been cancelled.'),
2370 );
2371
2372 return confirm_form($form,
2373 t('Are you sure you want to cancel these user accounts?'),
2374 'admin/people', t('This action cannot be undone.'),
2375 t('Cancel accounts'), t('Cancel'));
2376 }
2377
2378 /**
2379 * Submit handler for mass-account cancellation form.
2380 *
2381 * @see user_multiple_cancel_confirm()
2382 * @see user_cancel_confirm_form_submit()
2383 */
2384 function user_multiple_cancel_confirm_submit($form, &$form_state) {
2385 global $user;
2386
2387 if ($form_state['values']['confirm']) {
2388 foreach ($form_state['values']['accounts'] as $uid => $value) {
2389 // Prevent user administrators from deleting themselves without confirmation.
2390 if ($uid == $user->uid) {
2391 $admin_form_state = $form_state;
2392 unset($admin_form_state['values']['user_cancel_confirm']);
2393 $admin_form_state['values']['_account'] = $user;
2394 user_cancel_confirm_form_submit(array(), $admin_form_state);
2395 }
2396 else {
2397 user_cancel($form_state['values'], $uid, $form_state['values']['user_cancel_method']);
2398 }
2399 }
2400 }
2401 $form_state['redirect'] = 'admin/people';
2402 return;
2403 }
2404
2405 /**
2406 * Implement hook_help().
2407 */
2408 function user_help($path, $arg) {
2409 global $user;
2410
2411 switch ($path) {
2412 case 'admin/help#user':
2413 $output = '<p>' . t('The user module allows users to register, login, and log out. Users benefit from being able to sign on because it associates content they create with their account and allows various permissions to be set for their roles. The user module supports user roles which establish fine grained permissions allowing each role to do only what the administrator wants them to. Each user is assigned to one or more roles. By default there are two roles <em>anonymous</em> - a user who has not logged in, and <em>authenticated</em> a user who has signed up and who has been authorized.') . '</p>';
2414 $output .= '<p>' . t("Users can use their own name or handle and can specify personal configuration settings through their individual <em>My account</em> page. Users must authenticate by supplying a local username and password or through their OpenID, an optional and secure method for logging into many websites with a single username and password. In some configurations, users may authenticate using a username and password from another Drupal site, or through some other site-specific mechanism.") . '</p>';
2415 $output .= '<p>' . t('A visitor accessing your website is assigned a unique ID, or session ID, which is stored in a cookie. The cookie does not contain personal information, but acts as a key to retrieve information from your site. Users should have cookies enabled in their web browser when using your site.') . '</p>';
2416 $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@user">User module</a>.', array('@user' => 'http://drupal.org/handbook/modules/user/')) . '</p>';
2417 return $output;
2418 case 'admin/people/create':
2419 return '<p>' . t("This web page allows administrators to register new users. Users' e-mail addresses and usernames must be unique.") . '</p>';
2420 case 'admin/settings/permissions':
2421 return '<p>' . t('Permissions let you control what users can do and see on your site. You can define a specific set of permissions for each role. (See the <a href="@role">Roles</a> page to create a role). Two important roles to consider are Authenticated Users and Administrators. Any permissions granted to the Authenticated Users role will be given to any user who can log into your site. You can make any role the Administrator role for the site, meaning this will be granted all new permissions automatically. You can do this on the <a href="@settings">User Settings</a> page. You should be careful to ensure that only trusted users are given this access and level of control of your site.', array('@role' => url('admin/settings/roles'), '@settings' => url('admin/settings/user'))) . '</p>';
2422 case 'admin/settings/roles':
2423 return t('<p>Roles allow you to fine tune the security and administration of Drupal. A role defines a group of users that have certain privileges as defined in <a href="@permissions">user permissions</a>. Examples of roles include: anonymous user, authenticated user, moderator, administrator and so on. In this area you will define the <em>role names</em> of the various roles. To delete a role choose "edit".</p><p>By default, Drupal comes with two user roles:</p>
2424 <ul>
2425 <li>Anonymous user: this role is used for users that don\'t have a user account or that are not authenticated.</li>
2426 <li>Authenticated user: this role is automatically granted to all logged in users.</li>
2427 </ul>', array('@permissions' => url('admin/settings/permissions')));
2428 case 'admin/people/search':
2429 return '<p>' . t('Enter a simple pattern ("*" may be used as a wildcard match) to search for a username or e-mail address. For example, one may search for "br" and Drupal might return "brian", "brad", and "brenda@example.com".') . '</p>';
2430 }
2431 }
2432
2433 /**
2434 * Retrieve a list of all user setting/information categories and sort them by weight.
2435 */
2436 function _user_categories() {
2437 $categories = module_invoke_all('user_categories');
2438 usort($categories, '_user_sort');
2439
2440 return $categories;
2441 }
2442
2443 function _user_sort($a, $b) {
2444 $a = (array)$a + array('weight' => 0, 'title' => '');
2445 $b = (array)$b + array('weight' => 0, 'title' => '');
2446 return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1));
2447 }
2448
2449 /**
2450 * List user administration filters that can be applied.
2451 */
2452 function user_filters() {
2453 // Regular filters
2454 $filters = array();
2455 $roles = user_roles(TRUE);
2456 unset($roles[DRUPAL_AUTHENTICATED_RID]); // Don't list authorized role.
2457 if (count($roles)) {
2458 $filters['role'] = array(
2459 'title' => t('role'),
2460 'field' => 'ur.rid',
2461 'options' => $roles,
2462 );
2463 }
2464
2465 $options = array();
2466 foreach (module_implements('permission') as $module) {
2467 $function = $module . '_permission';
2468 if ($permissions = $function('permission')) {
2469 asort($permissions);
2470 foreach ($permissions as $permission => $description) {
2471 $options[t('@module module', array('@module' => $module))][$permission] = t($permission);
2472 }
2473 }
2474 }
2475 ksort($options);
2476 $filters['permission'] = array(
2477 'title' => t('permission'),
2478 'options' => $options,
2479 );
2480
2481 $filters['status'] = array(
2482 'title' => t('status'),
2483 'field' => 'u.status',
2484 'options' => array(1 => t('active'), 0 => t('blocked')),
2485 );
2486 return $filters;
2487 }
2488
2489 /**
2490 * Extends a query object for user administration filters based on session.
2491 *
2492 * @param $query
2493 * Query object that should be filtered.
2494 */
2495 function user_build_filter_query(SelectQuery $query) {
2496 $filters = user_filters();
2497
2498 // Extend Query with filter conditions.
2499 foreach (isset($_SESSION['user_overview_filter']) ? $_SESSION['user_overview_filter'] : array() as $filter) {
2500 list($key, $value) = $filter;
2501 // This checks to see if this permission filter is an enabled permission for
2502 // the authenticated role. If so, then all users would be listed, and we can
2503 // skip adding it to the filter query.
2504 if ($key == 'permission') {
2505 $account = new stdClass();
2506 $account->uid = 'user_filter';
2507 $account->roles = array(DRUPAL_AUTHENTICATED_RID => 1);
2508 if (user_access($value, $account)) {
2509 continue;
2510 }
2511 $query->leftJoin('role_permission', 'p', 'ur.rid = p.rid');
2512 $query->condition(db_or()->condition('u.uid', 1)->condition('p.permission', $value));
2513 }
2514 else {
2515 $query->condition($filters[$key]['field'], $value);
2516 }
2517 }
2518 }
2519
2520 /**
2521 * Implement hook_forms().
2522 */
2523 function user_forms() {
2524 $forms['user_admin_access_add_form']['callback'] = 'user_admin_access_form';
2525 $forms['user_admin_access_edit_form']['callback'] = 'user_admin_access_form';
2526 $forms['user_admin_new_role']['callback'] = 'user_admin_role';
2527 return $forms;
2528 }
2529
2530 /**
2531 * Implement hook_comment_view().
2532 */
2533 function user_comment_view($comment) {
2534 if (variable_get('user_signatures', 0) && !empty($comment->signature)) {
2535 $comment->signature = check_markup($comment->signature, $comment->format);
2536 }
2537 else {
2538 $comment->signature = '';
2539 }
2540 }
2541
2542 /**
2543 * Theme output of user signature.
2544 *
2545 * @ingroup themeable
2546 */
2547 function theme_user_signature($signature) {
2548 $output = '';
2549 if ($signature) {
2550 $output .= '<div class="clear">';
2551 $output .= '<div>—</div>';
2552 $output .= $signature;
2553 $output .= '</div>';
2554 }
2555
2556 return $output;
2557 }
2558
2559 /**
2560 * Return an array of token to value mappings for user e-mail messages.
2561 *
2562 * @param $account
2563 * The user object of the account being notified. Must contain at
2564 * least the fields 'uid', 'name', and 'mail'.
2565 * @param $language
2566 * Language object to generate the tokens with.
2567 * @return
2568 * Array of mappings from token names to values (for use with strtr()).
2569 */
2570 function user_mail_tokens($account, $language) {
2571 global $base_url;
2572 $tokens = array(
2573 '!username' => $account->name,
2574 '!site' => variable_get('site_name', 'Drupal'),
2575 '!login_url' => user_pass_reset_url($account),
2576 '!cancel_url' => user_cancel_url($account),
2577 '!uri' => $base_url,
2578 '!uri_brief' => preg_replace('!^https?://!', '', $base_url),
2579 '!mailto' => $account->mail,
2580 '!date' => format_date(REQUEST_TIME, 'medium', '', NULL, $language->language),
2581 '!login_uri' => url('user', array('absolute' => TRUE, 'language' => $language)),
2582 '!edit_uri' => url('user/' . $account->uid . '/edit', array('absolute' => TRUE, 'language' => $language)),
2583 );
2584 if (!empty($account->password)) {
2585 $tokens['!password'] = $account->password;
2586 }
2587 return $tokens;
2588 }
2589
2590 /**
2591 * Get the language object preferred by the user. This user preference can
2592 * be set on the user account editing page, and is only available if there
2593 * are more than one languages enabled on the site. If the user did not
2594 * choose a preferred language, or is the anonymous user, the $default
2595 * value, or if it is not set, the site default language will be returned.
2596 *
2597 * @param $account
2598 * User account to look up language for.
2599 * @param $default
2600 * Optional default language object to return if the account
2601 * has no valid language.
2602 */
2603 function user_preferred_language($account, $default = NULL) {
2604 $language_list = language_list();
2605 if ($account->language && isset($language_list[$account->language])) {
2606 return $language_list[$account->language];
2607 }
2608 else {
2609 return $default ? $default : language_default();
2610 }
2611 }
2612
2613 /**
2614 * Conditionally create and send a notification email when a certain
2615 * operation happens on the given user account.
2616 *
2617 * @see user_mail_tokens()
2618 * @see drupal_mail()
2619 *
2620 * @param $op
2621 * The operation being performed on the account. Possible values:
2622 * 'register_admin_created': Welcome message for user created by the admin
2623 * 'register_no_approval_required': Welcome message when user self-registers
2624 * 'register_pending_approval': Welcome message, user pending admin approval
2625 * 'password_reset': Password recovery request
2626 * 'status_activated': Account activated
2627 * 'status_blocked': Account blocked
2628 * 'cancel_confirm': Account cancellation request
2629 * 'status_canceled': Account canceled
2630 *
2631 * @param $account
2632 * The user object of the account being notified. Must contain at
2633 * least the fields 'uid', 'name', and 'mail'.
2634 * @param $language
2635 * Optional language to use for the notification, overriding account language.
2636 * @return
2637 * The return value from drupal_mail_send(), if ends up being called.
2638 */
2639 function _user_mail_notify($op, $account, $language = NULL) {
2640 // By default, we always notify except for canceled and blocked.
2641 $default_notify = ($op != 'status_canceled' && $op != 'status_blocked');
2642 $notify = variable_get('user_mail_' . $op . '_notify', $default_notify);
2643 if ($notify) {
2644 $params['account'] = $account;
2645 $language = $language ? $language : user_preferred_language($account);
2646 $mail = drupal_mail('user', $op, $account->mail, $language, $params);
2647 if ($op == 'register_pending_approval') {
2648 // If a user registered requiring admin approval, notify the admin, too.
2649 // We use the site default language for this.
2650 drupal_mail('user', 'register_pending_approval_admin', variable_get('site_mail', ini_get('sendmail_from')), language_default(), $params);
2651 }
2652 }
2653 return empty($mail) ? NULL : $mail['result'];
2654 }
2655
2656 /**
2657 * Add javascript and string translations for dynamic password validation
2658 * (strength and confirmation checking).
2659 *
2660 * This is an internal function that makes it easier to manage the translation
2661 * strings that need to be passed to the javascript code.
2662 */
2663 function _user_password_dynamic_validation() {
2664 static $complete = FALSE;
2665 global $user;
2666 // Only need to do once per page.
2667 if (!$complete) {
2668 drupal_add_js(drupal_get_path('module', 'user') . '/user.js');
2669
2670 drupal_add_js(array(
2671 'password' => array(
2672 'strengthTitle' => t('Password strength:'),
2673 'hasWeaknesses' => t('To make your password stronger:'),
2674 'tooShort' => t('Make it at least 6 characters'),
2675 'addLowerCase' => t('Add lowercase letters'),
2676 'addUpperCase' => t('Add uppercase letters'),
2677 'addNumbers' => t('Add numbers'),
2678 'addPunctuation' => t('Add punctuation'),
2679 'sameAsUsername' => t('Make it different from your username'),
2680 'confirmSuccess' => t('yes'),
2681 'confirmFailure' => t('no'),
2682 'confirmTitle' => t('Passwords match:'),
2683 'username' => (isset($user->name) ? $user->name : ''))),
2684 'setting');
2685 $complete = TRUE;
2686 }
2687 }
2688
2689 /**
2690 * Implementation of hook_node_load().
2691 */
2692 function user_node_load($nodes, $types) {
2693 // Build an array of all uids for node authors, keyed by nid.
2694 $uids = array();
2695 foreach ($nodes as $nid => $node) {
2696 $uids[$nid] = $node->uid;
2697 }
2698
2699 // Fetch name, picture, and data for these users.
2700 $user_fields = db_query("SELECT uid, name, picture, data FROM {users} WHERE uid IN (:uids)", array(':uids' => $uids))->fetchAllAssoc('uid');
2701
2702 // Add these values back into the node objects.
2703 foreach ($uids as $nid => $uid) {
2704 $nodes[$nid]->name = $user_fields[$uid]->name;
2705 $nodes[$nid]->picture = $user_fields[$uid]->picture;
2706 $nodes[$nid]->data = $user_fields[$uid]->data;
2707 }
2708 }
2709
2710 /**
2711 * Implement hook_image_style_delete().
2712 */
2713 function user_image_style_delete($style) {
2714 // If a style is deleted, update the variables.
2715 // Administrators choose a replacement style when deleting.
2716 user_image_style_save($style);
2717 }
2718
2719 /**
2720 * Implement hook_image_style_save().
2721 */
2722 function user_image_style_save($style) {
2723 // If a style is renamed, update the variables that use it.
2724 if (isset($style['old_name']) && $style['old_name'] == variable_get('user_picture_style', '')) {
2725 variable_set('user_picture_style', $style['name']);
2726 }
2727 }
2728
2729 /**
2730 * Implement hook_hook_info().
2731 */
2732 function user_hook_info() {
2733 return array(
2734 'user' => array(
2735 'user' => array(
2736 'insert' => array(
2737 'runs when' => t('After a user account has been created'),
2738 ),
2739 'update' => array(
2740 'runs when' => t("After a user's profile has been updated"),
2741 ),
2742 'delete' => array(
2743 'runs when' => t('After a user has been deleted')
2744 ),
2745 'login' => array(
2746 'runs when' => t('After a user has logged in')
2747 ),
2748 'logout' => array(
2749 'runs when' => t('After a user has logged out')
2750 ),
2751 'view' => array(
2752 'runs when' => t("When a user's profile is being viewed")
2753 ),
2754 ),
2755 ),
2756 );
2757 }
2758
2759 /**
2760 * Implement hook_action_info().
2761 */
2762 function user_action_info() {
2763 return array(
2764 'user_block_user_action' => array(
2765 'description' => t('Block current user'),
2766 'type' => 'user',
2767 'configurable' => FALSE,
2768 'hooks' => array(),
2769 ),
2770 );
2771 }
2772
2773 /**
2774 * Implement a Drupal action.
2775 * Blocks the current user.
2776 */
2777 function user_block_user_action(&$object, $context = array()) {
2778 if (isset($object->uid)) {
2779 $uid = $object->uid;
2780 }
2781 elseif (isset($context['uid'])) {
2782 $uid = $context['uid'];
2783 }
2784 else {
2785 global $user;
2786 $uid = $user->uid;
2787 }
2788 db_update('users')
2789 ->fields(array('status' => 0))
2790 ->condition('uid', $uid)
2791 ->execute();
2792 drupal_session_destroy_uid($uid);
2793 watchdog('action', 'Blocked user %name.', array('%name' => $user->name));
2794 }
2795
2796 /**
2797 * Submit handler for the user registration form.
2798 *
2799 * This function is shared by the installation form and the normal registration form,
2800 * which is why it can't be in the user.pages.inc file.
2801 */
2802 function user_register_submit($form, &$form_state) {
2803 global $base_url;
2804 $admin = user_access('administer users');
2805
2806 $mail = $form_state['values']['mail'];
2807 $name = $form_state['values']['name'];
2808 if (!variable_get('user_email_verification', TRUE) || $admin) {
2809 $pass = $form_state['values']['pass'];
2810 }
2811 else {
2812 $pass = user_password();
2813 };
2814 $notify = isset($form_state['values']['notify']) ? $form_state['values']['notify'] : NULL;
2815 $from = variable_get('site_mail', ini_get('sendmail_from'));
2816 if (isset($form_state['values']['roles'])) {
2817 // Remove unset roles.
2818 $roles = array_filter($form_state['values']['roles']);
2819 }
2820 else {
2821 $roles = array();
2822 }
2823
2824 if (!$admin && array_intersect(array_keys($form_state['values']), array('uid', 'roles', 'init', 'session', 'status'))) {
2825 watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
2826 $form_state['redirect'] = 'user/register';
2827 return;
2828 }
2829 // The unset below is needed to prevent these form values from being saved as
2830 // user data.
2831 unset($form_state['values']['form_token'], $form_state['values']['submit'], $form_state['values']['op'], $form_state['values']['notify'], $form_state['values']['form_id'], $form_state['values']['affiliates'], $form_state['values']['destination'], $form_state['values']['form_build_id']);
2832
2833 $merge_data = array('pass' => $pass, 'init' => $mail, 'roles' => $roles);
2834 if (!$admin) {
2835 // Set the user's status because it was not displayed in the form.
2836 $merge_data['status'] = variable_get('user_register', 1) == 1;
2837 }
2838 $account = user_save('', array_merge($form_state['values'], $merge_data));
2839 // Terminate if an error occurred during user_save().
2840 if (!$account) {
2841 drupal_set_message(t("Error saving user account."), 'error');
2842 $form_state['redirect'] = '';
2843 return;
2844 }
2845 $form_state['user'] = $account;
2846
2847 watchdog('user', 'New user: %name (%email).', array('%name' => $name, '%email' => $mail), WATCHDOG_NOTICE, l(t('edit'), 'user/' . $account->uid . '/edit'));
2848
2849 // The first user may login immediately, and receives a customized welcome e-mail.
2850 if ($account->uid == 1) {
2851 drupal_set_message(t('Welcome to Drupal. You are now logged in as user #1, which gives you full control over your website.'));
2852 if (variable_get('user_email_verification', TRUE)) {
2853 drupal_set_message(t('</p><p> Your password is <strong>%pass</strong>. You may change your password below.</p>', array('%pass' => $pass)));
2854 }
2855
2856 $form_state['values'] += $merge_data;
2857 user_authenticate(array_merge($form_state));
2858
2859 $form_state['redirect'] = 'user/1/edit';
2860 return;
2861 }
2862 else {
2863 // Add plain text password into user account to generate mail tokens.
2864 $account->password = $pass;
2865 if ($admin && !$notify) {
2866 drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => url("user/$account->uid"), '%name' => $account->name)));
2867 }
2868 elseif (!variable_get('user_email_verification', TRUE) && $account->status && !$admin) {
2869 // No e-mail verification is required, create new user account, and login
2870 // user immediately.
2871 _user_mail_notify('register_no_approval_required', $account);
2872 if (user_authenticate(array_merge($form_state['values'], $merge_data))) {
2873 drupal_set_message(t('Registration successful. You are now logged in.'));
2874 }
2875 $form_state['redirect'] = '';
2876 return;
2877 }
2878 elseif ($account->status || $notify) {
2879 // Create new user account, no administrator approval required.
2880 $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
2881 _user_mail_notify($op, $account);
2882 if ($notify) {
2883 drupal_set_message(t('Password and further instructions have been e-mailed to the new user <a href="@url">%name</a>.', array('@url' => url("user/$account->uid"), '%name' => $account->name)));
2884 }
2885 else {
2886 drupal_set_message(t('Your password and further instructions have been sent to your e-mail address.'));
2887 $form_state['redirect'] = '';
2888 return;
2889 }
2890 }
2891 else {
2892 // Create new user account, administrator approval required.
2893 _user_mail_notify('register_pending_approval', $account);
2894 drupal_set_message(t('Thank you for applying for an account. Your account is currently pending approval by the site administrator.<br />In the meantime, a welcome message with further instructions has been sent to your e-mail address.'));
2895 $form_state['redirect'] = '';
2896 return;
2897
2898 }
2899 }
2900 }
2901
2902 /**
2903 * Form builder; The user registration form.
2904 *
2905 * @ingroup forms
2906 * @see user_register_validate()
2907 * @see user_register_submit()
2908 */
2909 function user_register() {
2910 global $user;
2911
2912 $admin = user_access('administer users');
2913
2914 // If we aren't admin but already logged on, go to the user page instead.
2915 if (!$admin && $user->uid) {
2916 drupal_goto('user/' . $user->uid);
2917 }
2918
2919 // Start with the default user edit fields.
2920 $form = user_edit_form($form_state, NULL, NULL, TRUE);
2921 if ($admin) {
2922 $form['account']['notify'] = array(
2923 '#type' => 'checkbox',
2924 '#title' => t('Notify user of new account')
2925 );
2926 // Redirect back to page which initiated the create request;
2927 // usually admin/people/create.
2928 $form['destination'] = array('#type' => 'hidden', '#value' => $_GET['q']);
2929 }
2930
2931 // Create a dummy variable for pass-by-reference parameters.
2932 $null = NULL;
2933 $extra = _user_forms($null, NULL, NULL, 'register');
2934 if ($extra) {
2935 $form = array_merge_recursive($form, $extra);
2936 }
2937
2938 // If the "account" fieldset is the only element at the top level, its
2939 // borders are hidden for aesthetic reasons. We do not remove the fieldset but
2940 // preserve the form structure so that modules implementing
2941 // hook_form_FORM_ID_alter() know where to find the basic elements.
2942 if (count(element_children($form)) == 1) {
2943 $form['account']['#type'] = 'markup';
2944 }
2945
2946 $form['submit'] = array('#type' => 'submit', '#value' => t('Create new account'), '#weight' => 30);
2947 $form['#validate'][] = 'user_register_validate';
2948
2949 return $form;
2950 }
2951
2952 function user_register_validate($form, &$form_state) {
2953 user_module_invoke('validate', $form_state['values'], $form_state['values'], 'account');
2954 }
2955
2956 /**
2957 * Retrieve a list of all form elements for the specified category.
2958 */
2959 function _user_forms(&$edit, $account, $category, $hook = 'form') {
2960 $groups = array();
2961 foreach (module_implements('user_' . $hook) as $module) {
2962 $function = $module . '_user_' . $hook;
2963 if ($data = $function($edit, $account, $category)) {
2964 $groups = array_merge_recursive($data, $groups);
2965 }
2966 }
2967 uasort($groups, '_user_sort');
2968
2969 return empty($groups) ? FALSE : $groups;
2970 }
2971
2972

Legend

Missed
lines code that were not excersized during program execution.
Covered
lines code were excersized during program execution.
Comment/non executable
Comment or non-executable line of code.
Dead
lines of code that according to xdebug could not be executed. This is counted as coverage code because in almost all cases it is code that runnable.