Spike PHPCoverage Details: user.module

Line #FrequencySource Line
1 <?php
2 // $Id: user.module,v 1.898 2008/03/31 20:50:05 dries Exp $
3 
4 /**
5  * @file
6  * Enables the user registration and login system.
7  */
8 
9 define('USERNAME_MAX_LENGTH', 60);
10 define('EMAIL_MAX_LENGTH', 64);
11 
12 /**
13  * Invokes hook_user() in every module.
14  *
15  * We cannot use module_invoke() for this, because the arguments need to
16  * be passed by reference.
17  */
18 function user_module_invoke($type, &$array, &$user, $category = NULL) {
191  foreach (module_list() as $module) {
201    $function = $module .'_user';
211    if (function_exists($function)) {
221      $function($type, $array, $user, $category);
23     }
24   }
25 }
26 
27 /**
28  * Implementation of hook_theme().
29  */
30 function user_theme() {
31   return array(
32     'user_picture' => array(
33       'arguments' => array('account' => NULL),
34       'template' => 'user-picture',
35     ),
36     'user_profile' => array(
37       'arguments' => array('account' => NULL),
38       'template' => 'user-profile',
39       'file' => 'user.pages.inc',
40     ),
41     'user_profile_category' => array(
42       'arguments' => array('element' => NULL),
43       'template' => 'user-profile-category',
44       'file' => 'user.pages.inc',
45     ),
46     'user_profile_item' => array(
47       'arguments' => array('element' => NULL),
48       'template' => 'user-profile-item',
49       'file' => 'user.pages.inc',
50     ),
51     'user_list' => array(
52       'arguments' => array('users' => NULL, 'title' => NULL),
53     ),
54     'user_admin_perm' => array(
55       'arguments' => array('form' => NULL),
56       'file' => 'user.admin.inc',
57     ),
58     'user_admin_new_role' => array(
59       'arguments' => array('form' => NULL),
60       'file' => 'user.admin.inc',
61     ),
62     'user_admin_account' => array(
63       'arguments' => array('form' => NULL),
64       'file' => 'user.admin.inc',
65     ),
66     'user_filter_form' => array(
67       'arguments' => array('form' => NULL),
68       'file' => 'user.admin.inc',
69     ),
70     'user_filters' => array(
71       'arguments' => array('form' => NULL),
72       'file' => 'user.admin.inc',
73     ),
74     'user_signature' => array(
75       'arguments' => array('signature' => NULL),
76     ),
77   );
78 }
79 
80 function user_external_load($authname) {
81   $result = db_query("SELECT uid FROM {authmap} WHERE authname = '%s'", $authname);
82 
83   if ($user = db_fetch_array($result)) {
84     return user_load($user);
85   }
86   else {
87     return 0;
88   }
89 }
90 
91 /**
92  * Perform standard Drupal login operations for a user object.
93  *
94  * The user object must already be authenticated. This function verifies
95  * that the user account is not blocked/denied and then performs the login,
96  * updates the login timestamp in the database, invokes hook_user('login'),
97  * and regenerates the session.
98  *
99  * @param $account
100  *    An authenticated user object to be set as the currently logged
101  *    in user.
102  * @param $edit
103  *    The array of form values submitted by the user, if any.
104  *    This array is passed to hook_user op login.
105  * @return boolean
106  *    TRUE if the login succeeds, FALSE otherwise.
107  */
108 function user_external_login($account, $edit = array()) {
109   $form = drupal_get_form('user_login');
110 
111   $state['values'] = $edit;
112   if (empty($state['values']['name'])) {
113     $state['values']['name'] = $account->name;
114   }
115 
116   // Check if user is blocked or denied by access rules.
117   user_login_name_validate($form, $state, (array)$account);
118   if (form_get_errors()) {
119     // Invalid login.
120     return FALSE;
121   }
122 
123   // Valid login.
124   global $user;
125   $user = $account;
126   user_authenticate_finalize($state['values']);
127   return TRUE;
128 }
129 
130 /**
131  * Fetch a user object.
132  *
133  * @param $array
134  *   An associative array of attributes to search for in selecting the
135  *   user, such as user name or e-mail address.
136  *
137  * @return
138  *   A fully-loaded $user object upon successful user load or FALSE if user
139  *   cannot be loaded.
140  */
141 function user_load($array = array()) {
142   // Dynamically compose a SQL query:
143   $query = array();
144   $params = array();
145 
1461  if (is_numeric($array)) {
147     $array = array('uid' => $array);
148   }
1491  elseif (!is_array($array)) {
150     return FALSE;
151   }
152 
1531  foreach ($array as $key => $value) {
1541    if ($key == 'uid' || $key == 'status') {
1551      $query[] = "$key = %d";
1561      $params[] = $value;
157     }
158     else if ($key == 'pass') {
159       $query[] = "pass = '%s'";
160       $params[] = $value;
161     }
162     else {
163       $query[]= "LOWER($key) = LOWER('%s')";
164       $params[] = $value;
165     }
166   }
1671  $result = db_query('SELECT * FROM {users} u WHERE '. implode(' AND ', $query), $params);
168 
1691  if ($user = db_fetch_object($result)) {
1701    $user = drupal_unpack($user);
171 
1721    $user->roles = array();
1731    if ($user->uid) {
1741      $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
175     }
176     else {
177       $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
178     }
1791    $result = db_query('SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d', $user->uid);
1801    while ($role = db_fetch_object($result)) {
1811      $user->roles[$role->rid] = $role->name;
182     }
1831    user_module_invoke('load', $array, $user);
184   }
185   else {
186     $user = FALSE;
187   }
188 
1891  return $user;
190 }
191 
192 /**
193  * Save changes to a user account or add a new user.
194  *
195  * @param $account
196  *   The $user object for the user to modify or add. If $user->uid is
197  *   omitted, a new user will be added.
198  *
199  * @param $array
200  *   An array of fields and values to save. For example array('name'
201  *   => 'My name').  Keys that do not belong to columns in the user-related
202  *   tables are added to the a serialized array in the 'data' column
203  *   and will be loaded in the $user->data array by user_load().
204  *   Setting a field to NULL deletes it from the data column.
205  *
206  * @param $category
207  *   (optional) The category for storing profile information in.
208  *
209  * @return
210  *   A fully-loaded $user object upon successful save or FALSE if the save failed.
211  */
212 function user_save($account, $array = array(), $category = 'account') {
2131  $table = drupal_get_schema('users');
2141  $user_fields = $table['fields'];
215 
2161  if (!empty($array['pass'])) {
217     // Allow alternate password hashing schemes.
218     require_once variable_get('password_inc', './includes/password.inc');
2191    $array['pass'] = user_hash_password(trim($array['pass']));
220     // Abort if the hashing failed and returned FALSE.
2211    if (!$array['pass']) {
222       return FALSE;
223     }
224   }
225   else {
226     // Avoid overwriting an existing password with a blank password.
227     unset($array['pass']);
228   }
229 
2301  if (is_object($account) && $account->uid) {
231     user_module_invoke('update', $array, $account, $category);
232     $data = unserialize(db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $account->uid)));
233     // Consider users edited by an administrator as logged in, if they haven't
234     // already, so anonymous users can view the profile (if allowed).
235     if (empty($array['access']) && empty($account->access) && user_access('administer users')) {
236       $array['access'] = time();
237     }
238     foreach ($array as $key => $value) {
239       // Fields that don't pertain to the users or user_roles
240       // automatically serialized into the users.data column.
241       if ($key != 'roles' && empty($user_fields[$key])) {
242         if ($value === NULL) {
243           unset($data[$key]);
244         }
245         else {
246           $data[$key] = $value;
247         }
248       }
249     }
250 
251     $array['data'] = $data;
252     $array['uid'] = $account->uid;
253     // Save changes to the users table.
254     $success = drupal_write_record('users', $array, 'uid');
255     if (!$success) {
256       // The query failed - better to abort the save than risk further data loss.
257       return FALSE;
258     }
259 
260     // Reload user roles if provided.
261     if (isset($array['roles']) && is_array($array['roles'])) {
262       db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid);
263 
264       foreach (array_keys($array['roles']) as $rid) {
265         if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
266           db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid);
267         }
268       }
269     }
270 
271     // Delete a blocked user's sessions to kick them if they are online.
272     if (isset($array['status']) && $array['status'] == 0) {
273       sess_destroy_uid($account->uid);
274     }
275 
276     // If the password changed, delete all open sessions and recreate
277     // the current one.
278     if (!empty($array['pass'])) {
279       sess_destroy_uid($account->uid);
280       sess_regenerate();
281     }
282 
283     // Refresh user object.
284     $user = user_load(array('uid' => $account->uid));
285 
286     // Send emails after we have the new user object.
287     if (isset($array['status']) && $array['status'] != $account->status) {
288       // The user's status is changing; conditionally send notification email.
289       $op = $array['status'] == 1 ? 'status_activated' : 'status_blocked';
290       _user_mail_notify($op, $user);
291     }
292 
293     user_module_invoke('after_update', $array, $user, $category);
294   }
295   else {
296     // Allow 'created' to be set by the caller.
2971    if (!isset($array['created'])) {
2981      $array['created'] = time();
299     }
300     // Consider users created by an administrator as already logged in, so
301     // anonymous users can view the profile (if allowed).
3021    if (empty($array['access']) && user_access('administer users')) {
3031      $array['access'] = time();
304     }
305 
3061    $success = drupal_write_record('users', $array);
3071    if (!$success) {
308       // On a failed INSERT some other existing user's uid may be returned.
309       // We must abort to avoid overwriting their account.
310       return FALSE;
311     }
312 
313     // Build the initial user object.
3141    $user = user_load(array('uid' => $array['uid']));
315 
3161    user_module_invoke('insert', $array, $user, $category);
317 
318     // Note, we wait with saving the data column to prevent module-handled
319     // fields from being saved there.
320     $data = array();
3211    foreach ($array as $key => $value) {
3221      if (($key != 'roles') && (empty($user_fields[$key])) && ($value !== NULL)) {
323         $data[$key] = $value;
324       }
325     }
3261    if (!empty($data)) {
327       $data_array = array('uid' => $user->uid, 'data' => $data);
328       drupal_write_record('users', $data_array, 'uid');
329     }
330 
331     // Save user roles (delete just to be safe).
3321    if (isset($array['roles']) && is_array($array['roles'])) {
3331      db_query('DELETE FROM {users_roles} WHERE uid = %d', $array['uid']);
3341      foreach (array_keys($array['roles']) as $rid) {
3351        if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
3361          db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $array['uid'], $rid);
337         }
338       }
339     }
340 
341     // Build the finished user object.
3421    $user = user_load(array('uid' => $array['uid']));
343   }
344 
3451  return $user;
346 }
347 
348 /**
349  * Verify the syntax of the given name.
350  */
351 function user_validate_name($name) {
352   if (!strlen($name)) return t('You must enter a username.');
353   if (substr($name, 0, 1) == ' ') return t('The username cannot begin with a space.');
354   if (substr($name, -1) == ' ') return t('The username cannot end with a space.');
355   if (strpos($name, '  ') !== FALSE) return t('The username cannot contain multiple spaces in a row.');
356   if (ereg("[^\x80-\xF7 [:alnum:]@_.-]", $name)) return t('The username contains an illegal character.');
357   if (preg_match('/[\x{80}-\x{A0}'.          // Non-printable ISO-8859-1 + NBSP
358                    '\x{AD}'.                 // Soft-hyphen
359                    '\x{2000}-\x{200F}'.      // Various space characters
360                    '\x{2028}-\x{202F}'.      // Bidirectional text overrides
361                    '\x{205F}-\x{206F}'.      // Various text hinting characters
362                    '\x{FEFF}'.               // Byte order mark
363                    '\x{FF01}-\x{FF60}'.      // Full-width latin
364                    '\x{FFF9}-\x{FFFD}'.      // Replacement characters
365                    '\x{0}]/u',               // NULL byte
366                    $name)) {
367     return t('The username contains an illegal character.');
368   }
369   if (strpos($name, '@') !== FALSE && !eregi('@([0-9a-z](-?[0-9a-z])*.)+[a-z]{2}([zmuvtg]|fo|me)?$', $name)) return t('The username is not a valid authentication ID.');
370   if (strlen($name) > USERNAME_MAX_LENGTH) return t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
371 }
372 
373 function user_validate_mail($mail) {
374   if (!$mail) return t('You must enter an e-mail address.');
375   if (!valid_email_address($mail)) {
376     return t('The e-mail address %mail is not valid.', array('%mail' => $mail));
377   }
378 }
379 
380 function user_validate_picture(&$form, &$form_state) {
381   // If required, validate the uploaded picture.
382   $validators = array(
383     'file_validate_is_image' => array(),
384     'file_validate_image_resolution' => array(variable_get('user_picture_dimensions', '85x85')),
385     'file_validate_size' => array(variable_get('user_picture_file_size', '30') * 1024),
386   );
387   if ($file = file_save_upload('picture_upload', $validators)) {
388     // Remove the old picture.
389     if (isset($form_state['values']['_account']->picture) && file_exists($form_state['values']['_account']->picture)) {
390       file_delete($form_state['values']['_account']->picture);
391     }
392 
393     // The image was saved using file_save_upload() and was added to the
394     // files table as a temporary file. We'll make a copy and let the garbage
395     // collector delete the original upload.
396     $info = image_get_info($file->filepath);
397     $destination = variable_get('user_picture_path', 'pictures') .'/picture-'. $form['#uid'] .'.'. $info['extension'];
398     if (file_copy($file, $destination, FILE_EXISTS_REPLACE)) {
399       $form_state['values']['picture'] = $file->filepath;
400     }
401     else {
402       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'))));
403     }
404   }
405 }
406 
407 /**
408  * Generate a random alphanumeric password.
409  */
410 function user_password($length = 10) {
411   // This variable contains the list of allowable characters for the
412   // password. Note that the number 0 and the letter 'O' have been
413   // removed to avoid confusion between the two. The same is true
414   // of 'I', 1, and 'l'.
4151  $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
416 
417   // Zero-based count of characters in the allowable list:
4181  $len = strlen($allowable_characters) - 1;
419 
420   // Declare the password as a blank string.
4211  $pass = '';
422 
423   // Loop the number of times specified by $length.
4241  for ($i = 0; $i < $length; $i++) {
425 
426     // Each iteration, pick a random character from the
427     // allowable string and append it to the password:
4281    $pass .= $allowable_characters[mt_rand(0, $len)];
429   }
430 
4311  return $pass;
432 }
433 
434 /**
435  * Determine whether the user has a given privilege.
436  *
437  * @param $string
438  *   The permission, such as "administer nodes", being checked for.
439  * @param $account
440  *   (optional) The account to check, if not given use currently logged in user.
441  * @param $reset
442  *   (optional) Resets the user's permissions cache, which will result in a
443  *   recalculation of the user's permissions. This is necessary to support
444  *   dynamically added user roles.
445  *
446  * @return
447  *   Boolean TRUE if the current user has the requested permission.
448  *
449  * All permission checks in Drupal should go through this function. This
450  * way, we guarantee consistent behavior, and ensure that the superuser
451  * can perform all actions.
452  */
453 function user_access($string, $account = NULL, $reset = FALSE) {
4541  global $user;
455   static $perm = array();
456 
4571  if ($reset) {
458     unset($perm);
459   }
460 
4611  if (is_null($account)) {
4621    $account = $user;
463   }
464 
465   // User #1 has all privileges:
4661  if ($account->uid == 1) {
4671    return TRUE;
468   }
469 
470   // To reduce the number of SQL queries, we cache the user's permissions
471   // in a static variable.
472   if (!isset($perm[$account->uid])) {
473     $result = db_query("SELECT p.perm FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid WHERE r.rid IN (". db_placeholders($account->roles) .")", array_keys($account->roles));
474 
475     $perms = array();
476     while ($row = db_fetch_object($result)) {
477       $perms += array_flip(explode(', ', $row->perm));
478     }
479     $perm[$account->uid] = $perms;
480   }
481 
482   return isset($perm[$account->uid][$string]);
483 }
484 
485 /**
486  * Checks for usernames blocked by user administration.
487  *
488  * @return boolean TRUE for blocked users, FALSE for active.
489  */
490 function user_is_blocked($name) {
491   $deny = db_fetch_object(db_query("SELECT name FROM {users} WHERE status = 0 AND name = LOWER('%s')", $name));
492 
493   return $deny;
494 }
495 
496 /**
497  * Implementation of hook_perm().
498  */
499 function user_perm() {
500    return array(
501      'administer permissions' => t('Manage the permissions assigned to user roles. %warning', array('%warning' => t('Warning: Give to trusted roles only; this permission has security implications.'))),
502      'administer users' => t('Manage or block users, and manage their role assignments.'),
503      'access user profiles' => t('View profiles of users on the site, which may contain personal information.'),
504      'change own username' => t('Select a different username.'),
505    );
506 }
507 
508 /**
509  * Implementation of hook_file_download().
510  *
511  * Ensure that user pictures (avatars) are always downloadable.
512  */
513 function user_file_download($file) {
514   if (strpos($file, variable_get('user_picture_path', 'pictures') .'/picture-') === 0) {
515     $info = image_get_info(file_create_path($file));
516     return array('Content-type: '. $info['mime_type']);
517   }
518 }
519 
520 /**
521  * Implementation of hook_search().
522  */
523 function user_search($op = 'search', $keys = NULL, $skip_access_check = FALSE) {
524   switch ($op) {
525     case 'name':
526       if ($skip_access_check || user_access('access user profiles')) {
527         return t('Users');
528       }
529     case 'search':
530       if (user_access('access user profiles')) {
531         $find = array();
532         // Replace wildcards with MySQL/PostgreSQL wildcards.
533         $keys = preg_replace('!\*+!', '%', $keys);
534         if (user_access('administer users')) {
535           // Administrators can also search in the otherwise private email field.
536           $result = pager_query("SELECT name, uid, mail FROM {users} WHERE LOWER(name) LIKE LOWER('%%%s%%') OR LOWER(mail) LIKE LOWER('%%%s%%')", 15, 0, NULL, $keys, $keys);
537           while ($account = db_fetch_object($result)) {
538             $find[] = array('title' => $account->name .' ('. $account->mail .')', 'link' => url('user/'. $account->uid, array('absolute' => TRUE)));
539           }
540         }
541         else {
542           $result = pager_query("SELECT name, uid FROM {users} WHERE LOWER(name) LIKE LOWER('%%%s%%')", 15, 0, NULL, $keys);
543           while ($account = db_fetch_object($result)) {
544             $find[] = array('title' => $account->name, 'link' => url('user/'. $account->uid, array('absolute' => TRUE)));
545           }
546         }
547         return $find;
548       }
549   }
550 }
551 
552 /**
553  * Implementation of hook_elements().
554  */
555 function user_elements() {
556   return array(
557     'user_profile_category' => array(),
558     'user_profile_item' => array(),
559   );
560 }
561 
562 /**
563  * Implementation of hook_user().
564  */
565 function user_user($type, &$edit, &$account, $category = NULL) {
5661  if ($type == 'view') {
567     $account->content['user_picture'] = array(
568       '#value' => theme('user_picture', $account),
569       '#weight' => -10,
570     );
571     if (!isset($account->content['summary'])) {
572       $account->content['summary'] = array();
573     }
574     $account->content['summary'] += array(
575       '#type' => 'user_profile_category',
576       '#attributes' => array('class' => 'user-member'),
577       '#weight' => 5,
578       '#title' => t('History'),
579     );
580     $account->content['summary']['member_for'] =  array(
581       '#type' => 'user_profile_item',
582       '#title' => t('Member for'),
583       '#value' => format_interval(time() - $account->created),
584     );
585   }
5861  if ($type == 'form' && $category == 'account') {
587     $form_state = array();
588     return user_edit_form($form_state, arg(1), $edit);
589   }
590 
5911  if ($type == 'validate' && $category == 'account') {
592     return _user_edit_validate(arg(1), $edit);
593   }
594 
5951  if ($type == 'submit' && $category == 'account') {
596     return _user_edit_submit(arg(1), $edit);
597   }
598 
5991  if ($type == 'categories') {
6001    return array(array('name' => 'account', 'title' => t('Account settings'), 'weight' => 1));
601   }
602 }
603 
604 function user_login_block() {
605   $form = array(
606     '#action' => url($_GET['q'], array('query' => drupal_get_destination())),
607     '#id' => 'user-login-form',
608     '#validate' => user_login_default_validators(),
609     '#submit' => array('user_login_submit'),
610   );
611   $form['name'] = array('#type' => 'textfield',
612     '#title' => t('Username'),
613     '#maxlength' => USERNAME_MAX_LENGTH,
614     '#size' => 15,
615     '#required' => TRUE,
616   );
617   $form['pass'] = array('#type' => 'password',
618     '#title' => t('Password'),
619     '#maxlength' => 60,
620     '#size' => 15,
621     '#required' => TRUE,
622   );
623   $form['submit'] = array('#type' => 'submit',
624     '#value' => t('Log in'),
625   );
626   $items = array();
627   if (variable_get('user_register', 1)) {
628     $items[] = l(t('Create new account'), 'user/register', array('title' => t('Create a new user account.')));
629   }
630   $items[] = l(t('Request new password'), 'user/password', array('title' => t('Request new password via e-mail.')));
631   $form['links'] = array('#value' => theme('item_list', $items));
632   return $form;
633 }
634 
635 /**
636  * Implementation of hook_block().
637  */
638 function user_block($op = 'list', $delta = 0, $edit = array()) {
639   global $user;
640 
641   if ($op == 'list') {
642     $blocks[0]['info'] = t('User login');
643     // Not worth caching.
644     $blocks[0]['cache'] = BLOCK_NO_CACHE;
645 
646     $blocks[1]['info'] = t('Navigation');
647     // Menu blocks can't be cached because each menu item can have
648     // a custom access callback. menu.inc manages its own caching.
649     $blocks[1]['cache'] = BLOCK_NO_CACHE;
650 
651     $blocks[2]['info'] = t('Who\'s new');
652 
653     // Too dynamic to cache.
654     $blocks[3]['info'] = t('Who\'s online');
655     $blocks[3]['cache'] = BLOCK_NO_CACHE;
656     return $blocks;
657   }
658   else if ($op == 'configure' && $delta == 2) {
659     $form['user_block_whois_new_count'] = array(
660       '#type' => 'select',
661       '#title' => t('Number of users to display'),
662       '#default_value' => variable_get('user_block_whois_new_count', 5),
663       '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
664     );
665     return $form;
666   }
667   else if ($op == 'configure' && $delta == 3) {
668     $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), 'format_interval');
669     $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.'));
670     $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.'));
671 
672     return $form;
673   }
674   else if ($op == 'save' && $delta == 2) {
675     variable_set('user_block_whois_new_count', $edit['user_block_whois_new_count']);
676   }
677   else if ($op == 'save' && $delta == 3) {
678     variable_set('user_block_seconds_online', $edit['user_block_seconds_online']);
679     variable_set('user_block_max_list_count', $edit['user_block_max_list_count']);
680   }
681   else if ($op == 'view') {
682     $block = array();
683 
684     switch ($delta) {
685       case 0:
686         // For usability's sake, avoid showing two login forms on one page.
687         if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
688 
689           $block['subject'] = t('User login');
690           $block['content'] = drupal_get_form('user_login_block');
691         }
692         return $block;
693 
694       case 1:
695         if ($menu = menu_tree()) {
696           $block['subject'] = $user->uid ? check_plain($user->name) : t('Navigation');
697           $block['content'] = $menu;
698         }
699         return $block;
700 
701       case 2:
702         if (user_access('access content')) {
703           // Retrieve a list of new users who have subsequently accessed the site successfully.
704           $result = db_query_range('SELECT uid, name FROM {users} WHERE status != 0 AND access != 0 ORDER BY created DESC', 0, variable_get('user_block_whois_new_count', 5));
705           while ($account = db_fetch_object($result)) {
706             $items[] = $account;
707           }
708           $output = theme('user_list', $items);
709 
710           $block['subject'] = t('Who\'s new');
711           $block['content'] = $output;
712         }
713         return $block;
714 
715       case 3:
716         if (user_access('access content')) {
717           // Count users active within the defined period.
718           $interval = time() - variable_get('user_block_seconds_online', 900);
719 
720           // Perform database queries to gather online user lists.  We use s.timestamp
721           // rather than u.access because it is much faster.
722           $anonymous_count = sess_count($interval);
723           $authenticated_users = db_query('SELECT DISTINCT u.uid, u.name, s.timestamp FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.timestamp >= %d AND s.uid > 0 ORDER BY s.timestamp DESC', $interval);
724           $authenticated_count = 0;
725           $max_users = variable_get('user_block_max_list_count', 10);
726           $items = array();
727           while ($account = db_fetch_object($authenticated_users)) {
728             if ($max_users > 0) {
729               $items[] = $account;
730               $max_users--;
731             }
732             $authenticated_count++;
733           }
734 
735           // Format the output with proper grammar.
736           if ($anonymous_count == 1 && $authenticated_count == 1) {
737             $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')));
738           }
739           else {
740             $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')));
741           }
742 
743           // Display a list of currently online users.
744           $max_users = variable_get('user_block_max_list_count', 10);
745           if ($authenticated_count && $max_users) {
746             $output .= theme('user_list', $items, t('Online users'));
747           }
748 
749           $block['subject'] = t('Who\'s online');
750           $block['content'] = $output;
751         }
752         return $block;
753     }
754   }
755 }
756 
757 /**
758  * Process variables for user-picture.tpl.php.
759  *
760  * The $variables array contains the following arguments:
761  * - $account
762  *
763  * @see user-picture.tpl.php
764  */
765 function template_preprocess_user_picture(&$variables) {
766   $variables['picture'] = '';
767   if (variable_get('user_pictures', 0)) {
768     $account = $variables['account'];
769     if (!empty($account->picture) && file_exists($account->picture)) {
770       $picture = file_create_url($account->picture);
771     }
772     else if (variable_get('user_picture_default', '')) {
773       $picture = variable_get('user_picture_default', '');
774     }
775 
776     if (isset($picture)) {
777       $alt = t("@user's picture", array('@user' => $account->name ? $account->name : variable_get('anonymous', t('Anonymous'))));
778       $variables['picture'] = theme('image', $picture, $alt, $alt, '', FALSE);
779       if (!empty($account->uid) && user_access('access user profiles')) {
780         $attributes = array('attributes' => array('title' => t('View user profile.')), 'html' => TRUE);
781         $variables['picture'] = l($variables['picture'], $account->uid", $attributes);
782       }
783     }
784   }
785 }
786 
787 /**
788  * Make a list of users.
789  *
790  * @param $users
791  *   An array with user objects. Should contain at least the name and uid.
792  * @param $title
793  *  (optional) Title to pass on to theme_item_list().
794  *
795  * @ingroup themeable
796  */
797 function theme_user_list($users, $title = NULL) {
798   if (!empty($users)) {
799     foreach ($users as $user) {
800       $items[] = theme('username', $user);
801     }
802   }
803   return theme('item_list', $items, $title);
804 }
805 
806 function user_is_anonymous() {
807   // Menu administrators can see items for anonymous when administering.
808   return !$GLOBALS['user']->uid || !empty($GLOBALS['menu_admin']);
809 }
810 
811 function user_is_logged_in() {
812   return (bool)$GLOBALS['user']->uid;
813 }
814 
815 function user_register_access() {
816   return user_is_anonymous() && variable_get('user_register', 1);
817 }
818 
819 function user_view_access($account) {
820   return $account && $account->uid &&
821     (
822       // Always let users view their own profile.
823       ($GLOBALS['user']->uid == $account->uid) ||
824       // Administrators can view all accounts.
825       user_access('administer users') ||
826       // The user is not blocked and logged in at least once.
827       ($account->access && $account->status && user_access('access user profiles'))
828     );
829 }
830 
831 function user_edit_access($account) {
832   return (($GLOBALS['user']->uid == $account->uid) || user_access('administer users')) && $account->uid > 0;
833 }
834 
835 function user_load_self($arg) {
836   $arg[1] = user_load($GLOBALS['user']->uid);
837   return $arg;
838 }
839 
840 /**
841  * Implementation of hook_menu().
842  */
843 function user_menu() {
844   $items['user/autocomplete'] = array(
8451    'title' => 'User autocomplete',
846     'page callback' => 'user_autocomplete',
847     'access callback' => 'user_access',
848     'access arguments' => array('access user profiles'),
849     'type' => MENU_CALLBACK,
850     'file' => 'user.pages.inc',
851   );
852 
853   // Registration and login pages.
854   $items['user'] = array(
8551    'title' => 'User account',
856     'page callback' => 'user_page',
857     'access callback' => TRUE,
858     'type' => MENU_CALLBACK,
859     'file' => 'user.pages.inc',
860   );
861 
862   $items['user/login'] = array(
8631    'title' => 'Log in',
864     'access callback' => 'user_is_anonymous',
865     'type' => MENU_DEFAULT_LOCAL_TASK,
866   );
867 
868   $items['user/register'] = array(
8691    'title' => 'Create new account',
870     'page callback' => 'drupal_get_form',
871     'page arguments' => array('user_register'),
872     'access callback' => 'user_register_access',
873     'type' => MENU_LOCAL_TASK,
874     'file' => 'user.pages.inc',
875   );
876 
877   $items['user/password'] = array(
8781    'title' => 'Request new password',
879     'page callback' => 'drupal_get_form',
880     'page arguments' => array('user_pass'),
881     'access callback' => 'user_is_anonymous',
882     'type' => MENU_LOCAL_TASK,
883     'file' => 'user.pages.inc',
884   );
885   $items['user/reset/%/%/%'] = array(
8861    'title' => 'Reset password',
887     'page callback' => 'drupal_get_form',
888     'page arguments' => array('user_pass_reset', 2, 3, 4),
889     'access callback' => TRUE,
890     'type' => MENU_CALLBACK,
891     'file' => 'user.pages.inc',
892   );
893 
894   // Admin user pages.
895   $items['admin/user'] = array(
8961    'title' => 'User management',
897     'description' => "Manage your site's users, groups and access to site features.",
898     'position' => 'left',
899     'page callback' => 'system_admin_menu_block_page',
900     'access arguments' => array('access administration pages'),
901     'file' => 'system.admin.inc',
902     'file path' => drupal_get_path('module', 'system'),
903   );
904   $items['admin/user/user'] = array(
9051    'title' => 'Users',
906     'description' => 'List, add, and edit users.',
907     'page callback' => 'user_admin',
908     'page arguments' => array('list'),
909     'access arguments' => array('administer users'),
910     'file' => 'user.admin.inc',
911   );
912   $items['admin/user/user/list'] = array(
9131    'title' => 'List',
914     'type' => MENU_DEFAULT_LOCAL_TASK,
915     'weight' => -10,
916   );
917   $items['admin/user/user/create'] = array(
9181    'title' => 'Add user',
919     'page arguments' => array('create'),
920     'type' => MENU_LOCAL_TASK,
921     'file' => 'user.admin.inc',
922   );
923   $items['admin/user/settings'] = array(
9241    'title' => 'User settings',
925     'description' => 'Configure default behavior of users, including registration requirements, e-mails, and user pictures.',
926     'page callback' => 'drupal_get_form',
927     'page arguments' => array('user_admin_settings'),
928     'access arguments' => array('administer users'),
929     'file' => 'user.admin.inc',
930   );
931 
932   // Admin access pages.
933   $items['admin/user/permissions'] = array(
9341    'title' => 'Permissions',
935     'description' => 'Determine access to features by selecting permissions for roles.',
936     'page callback' => 'drupal_get_form',
937     'page arguments' => array('user_admin_perm'),
938     'access arguments' => array('administer permissions'),
939     'file' => 'user.admin.inc',
940   );
941   $items['admin/user/roles'] = array(
9421    'title' => 'Roles',
943     'description' => 'List, edit, or add user roles.',
944     'page callback' => 'drupal_get_form',
945     'page arguments' => array('user_admin_new_role'),
946     'access arguments' => array('administer permissions'),
947     'file' => 'user.admin.inc',
948   );
949   $items['admin/user/roles/edit'] = array(
9501    'title' => 'Edit role',
951     'page arguments' => array('user_admin_role'),
952     'type' => MENU_CALLBACK,
953     'file' => 'user.admin.inc',
954   );
955   $items['admin/user/rules'] = array(
9561    'title' => 'Access rules',
957     'description' => 'List and create rules to disallow usernames, e-mail addresses, and IP addresses.',
958     'page callback' => 'user_admin_access',
959     'access arguments' => array('administer permissions'),
960     'file' => 'user.admin.inc',
961   );
962   $items['admin/user/rules/list'] = array(
9631    'title' => 'List',
964     'type' => MENU_DEFAULT_LOCAL_TASK,
965     'weight' => -10,
966   );
967   $items['admin/user/rules/add'] = array(
9681    'title' => 'Add rule',
969     'page callback' => 'user_admin_access_add',
970     'type' => MENU_LOCAL_TASK,
971     'file' => 'user.admin.inc',
972   );
973   $items['admin/user/rules/check'] = array(
9741    'title' => 'Check rules',
975     'page callback' => 'user_admin_access_check',
976     'type' => MENU_LOCAL_TASK,
977     'file' => 'user.admin.inc',
978   );
979   $items['admin/user/rules/edit'] = array(
9801    'title' => 'Edit rule',
981     'page callback' => 'user_admin_access_edit',
982     'type' => MENU_CALLBACK,
983     'file' => 'user.admin.inc',
984   );
985   $items['admin/user/rules/delete'] = array(
9861    'title' => 'Delete rule',
987     'page callback' => 'drupal_get_form',
988     'page arguments' => array('user_admin_access_delete_confirm'),
989     'type' => MENU_CALLBACK,
990     'file' => 'user.admin.inc',
991   );
992 
993   $items['logout'] = array(
9941    'title' => 'Log out',
995     'access callback' => 'user_is_logged_in',
996     'page callback' => 'user_logout',
997     'weight' => 10,
998     'file' => 'user.pages.inc',
999   );
1000 
1001   $items['user/%user_current'] = array(
10021    'title' => 'My account',
1003     'title callback' => 'user_page_title',
1004     'title arguments' => array(1),
1005     'page callback' => 'user_view',
1006     'page arguments' => array(1),
1007     'access callback' => 'user_view_access',
1008     'access arguments' => array(1),
1009     'parent' => '',
1010     'file' => 'user.pages.inc',
1011   );
1012 
1013   $items['user/%user/view'] = array(
10141    'title' => 'View',
1015     'type' => MENU_DEFAULT_LOCAL_TASK,
1016     'weight' => -10,
1017   );
1018 
1019   $items['user/%user/delete'] = array(
10201    'title' => 'Delete',
1021     'page callback' => 'drupal_get_form',
1022     'page arguments' => array('user_confirm_delete', 1),
1023     'access callback' => 'user_access',
1024     'access arguments' => array('administer users'),
1025     'type' => MENU_CALLBACK,
1026     'file' => 'user.pages.inc',
1027   );
1028 
1029   $items['user/%user_category/edit'] = array(
10301    'title' => 'Edit',
1031     'page callback' => 'user_edit',
1032     'page arguments' => array(1),
1033     'access callback' => 'user_edit_access',
1034     'access arguments' => array(1),
1035     'type' => MENU_LOCAL_TASK,
1036     'load arguments' => array('%map', '%index'),
1037     'file' => 'user.pages.inc',
1038   );
1039 
1040   $items['user/%user_category/edit/account'] = array(
10411    'title' => 'Account',
1042     'type' => MENU_DEFAULT_LOCAL_TASK,
1043     'load arguments' => array('%map', '%index'),
1044   );
1045 
10461  $empty_account = new stdClass();
10471  if (($categories = _user_categories($empty_account)) && (count($categories) > 1)) {
1048     foreach ($categories as $key => $category) {
1049       // 'account' is already handled by the MENU_DEFAULT_LOCAL_TASK.
1050       if ($category['name'] != 'account') {
1051         $items['user/%user_category/edit/'. $category['name']] = array(
1052           'title callback' => 'check_plain',
1053           'title arguments' => array($category['title']),
1054           'page callback' => 'user_edit',
1055           'page arguments' => array(1, 3),
1056           'access callback' => isset($category['access callback']) ? $category['access callback'] : TRUE,
1057           'access arguments' => isset($category['access arguments']) ? $category['access arguments'] : array(),
1058           'type' => MENU_LOCAL_TASK,
1059           'weight' => $category['weight'],
1060           'load arguments' => array('%map', '%index'),
1061           'tab_parent' => 'user/%/edit',
1062           'file' => 'user.pages.inc',
1063         );
1064       }
1065     }
1066   }
10671  return $items;
1068 }
1069 
1070 function user_init() {
1071   drupal_add_css(drupal_get_path('module', 'user') .'/user.css', 'module');
1072 }
1073 
1074 function user_current_load($arg) {
1075   return user_load($arg ? $arg : $GLOBALS['user']->uid);
1076 }
1077 
1078 /**
1079  * Return a user object after checking if any profile category in the path exists.
1080  */
1081 function user_category_load($uid, &$map, $index) {
1082   static $user_categories, $accounts;
1083 
1084   // Cache $account - this load function will get called for each profile tab.
1085   if (!isset($accounts[$uid])) {
1086     $accounts[$uid] = user_load($uid);
1087   }
1088   $valid = TRUE;
1089   if ($account = $accounts[$uid]) {
1090     // Since the path is like user/%/edit/category_name, the category name will
1091     // be at a position 2 beyond the index corresponding to the % wildcard.
1092     $category_index = $index + 2;
1093     // Valid categories may contain slashes, and hence need to be imploded.
1094     $category_path = implode('/', array_slice($map, $category_index));
1095     if ($category_path) {
1096       // Check that the requested category exists.
1097       $valid = FALSE;
1098       if (!isset($user_categories)) {
1099         $empty_account = new stdClass();
1100         $user_categories = _user_categories($empty_account);
1101       }
1102       foreach ($user_categories as $category) {
1103         if ($category['name'] == $category_path) {
1104           $valid = TRUE;
1105           // Truncate the map array in case the category name had slashes.
1106           $map = array_slice($map, 0, $category_index);
1107           // Assign the imploded category name to the last map element.
1108           $map[$category_index] = $category_path;
1109           break;
1110         }
1111       }
1112     }
1113   }
1114   return $valid ? $account : FALSE;
1115 }
1116 
1117 /**
1118  * Returns the user id of the currently logged in user.
1119  */
1120 function user_current_to_arg($arg) {
1121   // Give back the current user uid when called from eg. tracker, aka.
1122   // with an empty arg. Also use the current user uid when called from
1123   // the menu with a % for the current account link.
1124   return empty($arg) || $arg == '%' ? $GLOBALS['user']->uid : $arg;
1125 }
1126 
1127 /**
1128  * Menu item title callback - use the user name if it's not the current user.
1129  */
1130 function user_page_title($account) {
1131   if ($account->uid == $GLOBALS['user']->uid) {
1132     return t('My account');
1133   }
1134   return $account->name;
1135 }
1136 
1137 /**
1138  * Discover which external authentication module(s) authenticated a username.
1139  *
1140  * @param $authname
1141  *   A username used by an external authentication module.
1142  * @return
1143  *   An associative array with module as key and username as value.
1144  */
1145 function user_get_authmaps($authname = NULL) {
1146   $result = db_query("SELECT authname, module FROM {authmap} WHERE authname = '%s'", $authname);
1147   $authmaps = array();
1148   $has_rows = FALSE;
1149   while ($authmap = db_fetch_object($result)) {
1150     $authmaps[$authmap->module] = $authmap->authname;
1151     $has_rows = TRUE;
1152   }
1153   return $has_rows ? $authmaps : 0;
1154 }
1155 
1156 /**
1157  * Save mappings of which external authentication module(s) authenticated
1158  * a user. Maps external usernames to user ids in the users table.
1159  *
1160  * @param $account
1161  *   A user object.
1162  * @param $authmaps
1163  *   An associative array with a compound key and the username as the value.
1164  *   The key is made up of 'authname_' plus the name of the external authentication
1165  *   module.
1166  * @see user_external_login_register()
1167  */
1168 function user_set_authmaps($account, $authmaps) {
1169   foreach ($authmaps as $key => $value) {
1170     $module = explode('_', $key, 2);
1171     if ($value) {
1172       db_query("UPDATE {authmap} SET authname = '%s' WHERE uid = %d AND module = '%s'", $value, $account->uid, $module[1]);
1173       if (!db_affected_rows()) {
1174         db_query("INSERT INTO {authmap} (authname, uid, module) VALUES ('%s', %d, '%s')", $value, $account->uid, $module[1]);
1175       }
1176     }
1177     else {
1178       db_query("DELETE FROM {authmap} WHERE uid = %d AND module = '%s'", $account->uid, $module[1]);
1179     }
1180   }
1181 }
1182 
1183 /**
1184  * Form builder; the main user login form.
1185  *
1186  * @ingroup forms
1187  */
1188 function user_login(&$form_state, $msg = '') {
1189   global $user;
1190 
1191   // If we are already logged on, go to the user page instead.
1192   if ($user->uid) {
1193     drupal_goto('user/'. $user->uid);
1194   }
1195 
1196   // Display login form:
1197   if ($msg) {
1198     $form['message'] = array('#value' => '<p>'. check_plain($msg) .'</p>');
1199   }
1200   $form['name'] = array('#type' => 'textfield',
1201     '#title' => t('Username'),
1202     '#size' => 60,
1203     '#maxlength' => USERNAME_MAX_LENGTH,
1204     '#required' => TRUE,
1205     '#attributes' => array('tabindex' => '1'),
1206   );
1207 
1208   $form['name']['#description'] = t('Enter your @s username.', array('@s' => variable_get('site_name', 'Drupal')));
1209   $form['pass'] = array('#type' => 'password',
1210     '#title' => t('Password'),
1211     '#description' => t('Enter the password that accompanies your username.'),
1212     '#required' => TRUE,
1213     '#attributes' => array('tabindex' => '2'),
1214   );
1215   $form['#validate'] = user_login_default_validators();
1216   $form['submit'] = array('#type' => 'submit', '#value' => t('Log in'), '#weight' => 2, '#attributes' => array('tabindex' => '3'));
1217 
1218   return $form;
1219 }
1220 
1221 /**
1222  * Set up a series for validators which check for blocked/denied users,
1223  * then authenticate against local database, then return an error if
1224  * authentication fails. Distributed authentication modules are welcome
1225  * to use hook_form_alter() to change this series in order to
1226  * authenticate against their user database instead of the local users
1227  * table.
1228  *
1229  * We use three validators instead of one since external authentication
1230  * modules usually only need to alter the second validator.
1231  *
1232  * @see user_login_name_validate()
1233  * @see user_login_authenticate_validate()
1234  * @see user_login_final_validate()
1235  * @return array
1236  *   A simple list of validate functions.
1237  */
1238 function user_login_default_validators() {
1239   return array('user_login_name_validate', 'user_login_authenticate_validate', 'user_login_final_validate');
1240 }
1241 
1242 /**
1243  * A FAPI validate handler. Sets an error if supplied username has been blocked
1244  * or denied access.
1245  */
1246 function user_login_name_validate($form, &$form_state) {
1247   if (isset($form_state['values']['name'])) {
1248     if (user_is_blocked($form_state['values']['name'])) {
1249       // blocked in user administration
1250       form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
1251     }
1252     else if (drupal_is_denied('user', $form_state['values']['name'])) {
1253       // denied by access controls
1254       form_set_error('name', t('The name %name is a reserved username.', array('%name' => $form_state['values']['name'])));
1255     }
1256   }
1257 }
1258 
1259 /**
1260  * A validate handler on the login form. Check supplied username/password
1261  * against local users table. If successful, sets the global $user object.
1262  */
1263 function user_login_authenticate_validate($form, &$form_state) {
1264   user_authenticate($form_state['values']);
1265 }
1266 
1267 /**
1268  * A validate handler on the login form. Should be the last validator. Sets an
1269  * error if user has not been authenticated yet.
1270  */
1271 function user_login_final_validate($form, &$form_state) {
1272   global $user;
1273   if (!$user->uid) {
1274     form_set_error('name', t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password'))));
1275     watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name']));
1276   }
1277 }
1278 
1279 /**
1280  * Try to log in the user locally.
1281  *
1282  * @param $form_values
1283  *   Form values with at least 'name' and 'pass' keys, as well as anything else
1284  *   which should be passed along to hook_user op 'login'.
1285  *
1286  * @return
1287  *  A $user object, if successful.
1288  */
1289 function user_authenticate($form_values = array()) {
1290   global $user;
1291 
1292   $password = trim($form_values['pass']);
1293   // Name and pass keys are required.
1294   if (!empty($form_values['name']) && !empty($password)) {
1295     $account = db_fetch_object(db_query("SELECT * FROM {users} WHERE name = '%s' AND status = 1", $form_values['name']));
1296     if ($account) {
1297       // Allow alternate password hashing schemes.
1298       require_once variable_get('password_inc', './includes/password.inc');
1299       if (user_check_password($password, $account)) {
1300         if (user_needs_new_hash($account)) {
1301            $new_hash = user_hash_password($password);
1302            if ($new_hash) {
1303              db_query("UPDATE {users} SET pass = '%s' WHERE uid = %d", $new_hash, $account->uid);
1304            }
1305         }
1306         $account = user_load(array('uid' => $account->uid, 'status' => 1));
1307         $user = $account;
1308         user_authenticate_finalize($form_values);
1309         return $user;
1310       }
1311     }
1312   }
1313 }
1314 
1315 /**
1316  * Finalize the login process. Must be called when logging in a user.
1317  *
1318  * The function records a watchdog message about the new session, saves the
1319  * login timestamp, calls hook_user op 'login' and generates a new session.
1320  *
1321  * $param $edit
1322  *   This array is passed to hook_user op login.
1323  */
1324 function user_authenticate_finalize(&$edit) {
1325   global $user;
1326   watchdog('user', 'Session opened for %name.', array('%name' => $user->name));
1327   // Update the user table timestamp noting user has logged in.
1328   // This is also used to invalidate one-time login links.
1329   $user->login = time();
1330   db_query("UPDATE {users} SET login = %d WHERE uid = %d", $user->login, $user->uid);
1331   user_module_invoke('login', $edit, $user);
1332   sess_regenerate();
1333 }
1334 
1335 /**
1336  * Submit handler for the login form. Redirects the user to a page.
1337  *
1338  * The user is redirected to the My Account page. Setting the destination in
1339  * the query string (as done by the user login block) overrides the redirect.
1340  */
1341 function user_login_submit($form, &$form_state) {
1342   global $user;
1343   if ($user->uid) {
1344     $form_state['redirect'] = 'user/'. $user->uid;
1345     return;
1346   }
1347 }
1348 
1349 /**
1350  * Helper function for authentication modules. Either login in or registers
1351  * the current user, based on username. Either way, the global $user object is
1352  * populated based on $name.
1353  */
1354 function user_external_login_register($name, $module) {
1355   global $user;
1356 
1357   $user = user_load(array('name' => $name));
1358   if (!isset($user->uid)) {
1359     // Register this new user.
1360     $userinfo = array(
1361       'name' => $name,
1362       'pass' => user_password(),
1363       'init' => $name,
1364       'status' => 1,
1365       'access' => time()
1366     );
1367     $account = user_save('', $userinfo);
1368     // Terminate if an error occured during user_save().
1369     if (!$account) {
1370       drupal_set_message(t("Error saving user account."), 'error');
1371       return;
1372     }
1373     user_set_authmaps($account, array("authname_$module" => $name));
1374     $user = $account;
1375     watchdog('user', 'New external user: %name using module %module.', array('%name' => $name, '%module' => $module), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $user->uid .'/edit'));
1376   }
1377 }
1378 
1379 function user_pass_reset_url($account) {
1380   $timestamp = time();
1381   return url(reset/$account->uid/$timestamp/". user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE));
1382 }
1383 
1384 function user_pass_rehash($password, $timestamp, $login) {
1385   return md5($timestamp . $password . $login);
1386 }
1387 
1388 function user_edit_form(&$form_state, $uid, $edit, $register = FALSE) {
1389   _user_password_dynamic_validation();
1390   $admin = user_access('administer users');
1391 
1392   // Account information:
1393   $form['account'] = array('#type' => 'fieldset',
1394     '#title' => t('Account information'),
1395     '#weight' => -10,
1396   );
1397   if (user_access('change own username') || $admin || $register) {
1398     $form['account']['name'] = array('#type' => 'textfield',
1399       '#title' => t('Username'),
1400       '#default_value' => $edit['name'],
1401       '#maxlength' => USERNAME_MAX_LENGTH,
1402       '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, and underscores.'),
1403       '#required' => TRUE,
1404     );
1405   }
1406   $form['account']['mail'] = array('#type' => 'textfield',
1407     '#title' => t('E-mail address'),
1408     '#default_value' => $edit['mail'],
1409     '#maxlength' => EMAIL_MAX_LENGTH,
1410     '#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.'),
1411     '#required' => TRUE,
1412   );
1413   if (!$register) {
1414     $form['account']['pass'] = array('#type' => 'password_confirm',
1415       '#description' => t('To change the current user password, enter the new password in both fields.'),
1416       '#size' => 25,
1417     );
1418   }
1419   elseif (!variable_get('user_email_verification', TRUE) || $admin) {
1420     $form['account']['pass'] = array(
1421       '#type' => 'password_confirm',
1422       '#description' => t('Provide a password for the new account in both fields.'),
1423       '#required' => TRUE,
1424       '#size' => 25,
1425     );
1426   }
1427   if ($admin) {
1428     $form['account']['status'] = array(
1429       '#type' => 'radios',
1430       '#title' => t('Status'),
1431       '#default_value' => isset($edit['status']) ? $edit['status'] : 1,
1432       '#options' => array(t('Blocked'), t('Active'))
1433     );
1434   }
1435   if (user_access('administer permissions')) {
1436     $roles = user_roles(TRUE);
1437 
1438     // The disabled checkbox subelement for the 'authenticated user' role
1439     // must be generated separately and added to the checkboxes element,
1440     // because of a limitation in D6 FormAPI not supporting a single disabled
1441     // checkbox within a set of checkboxes.
1442     // TODO: This should be solved more elegantly. See issue #119038.
1443     $checkbox_authenticated = array(
1444       '#type' => 'checkbox',
1445       '#title' => $roles[DRUPAL_AUTHENTICATED_RID],
1446       '#default_value' => TRUE,
1447       '#disabled' => TRUE,
1448     );
1449 
1450     unset($roles[DRUPAL_AUTHENTICATED_RID]);
1451     if ($roles) {
1452       $default = empty($edit['roles']) ? array() : array_keys($edit['roles']);
1453       $form['account']['roles'] = array(
1454         '#type' => 'checkboxes',
1455         '#title' => t('Roles'),
1456         '#default_value' => $default,
1457         '#options' => $roles,
1458         DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated,
1459       );
1460     }
1461   }
1462 
1463   // Signature:
1464   if (variable_get('user_signatures', 0) && module_exists('comment') && !$register) {
1465     $form['signature_settings'] = array(
1466       '#type' => 'fieldset',
1467       '#title' => t('Signature settings'),
1468       '#weight' => 1,
1469     );
1470     $form['signature_settings']['signature'] = array(
1471       '#type' => 'textarea',
1472       '#title' => t('Signature'),
1473       '#default_value' => $edit['signature'],
1474       '#description' => t('Your signature will be publicly displayed at the end of your comments.'),
1475     );
1476   }
1477 
1478   // Picture/avatar:
1479   if (variable_get('user_pictures', 0) && !$register) {
1480     $form['picture'] = array('#type' => 'fieldset', '#title' => t('Picture'), '#weight' => 1);
1481     $picture = theme('user_picture', (object)$edit);
1482     if ($edit['picture']) {
1483       $form['picture']['current_picture'] = array('#value' => $picture);
1484       $form['picture']['picture_delete'] = array('#type' => 'checkbox', '#title' => t('Delete picture'), '#description' => t('Check this box to delete your current picture.'));
1485     }
1486     else {
1487       $form['picture']['picture_delete'] = array('#type' => 'hidden');
1488     }
1489     $form['picture']['picture_upload'] = array('#type' => 'file', '#title' => t('Upload picture'), '#size' => 48, '#description' => t('Your virtual face or picture. Maximum dimensions are %dimensions 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', ''));
1490     $form['#validate'][] = 'user_validate_picture';
1491   }
1492   $form['#uid'] = $uid;
1493 
1494   return $form;
1495 }
1496 
1497 function _user_edit_validate($uid, &$edit) {
1498   $user = user_load(array('uid' => $uid));
1499   // Validate the username:
1500   if (user_access('change own username') || user_access('administer users') || !$user->uid) {
1501     if ($error = user_validate_name($edit['name'])) {
1502       form_set_error('name', $error);
1503     }
1504     else if (db_result(db_query("SELECT COUNT(*) FROM {users} WHERE uid != %d AND LOWER(name) = LOWER('%s')", $uid, $edit['name'])) > 0) {
1505       form_set_error('name', t('The name %name is already taken.', array('%name' => $edit['name'])));
1506     }
1507     else if (drupal_is_denied('user', $edit['name'])) {
1508       form_set_error('name', t('The name %name has been denied access.', array('%name' => $edit['name'])));
1509     }
1510   }
1511 
1512   // Validate the e-mail address:
1513   if ($error = user_validate_mail($edit['mail'])) {
1514     form_set_error('mail', $error);
1515   }
1516   else if (db_result(db_query("SELECT COUNT(*) FROM {users} WHERE uid != %d AND LOWER(mail) = LOWER('%s')", $uid, $edit['mail'])) > 0) {
1517     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'))));
1518   }
1519   else if (drupal_is_denied('mail', $edit['mail'])) {
1520     form_set_error('mail', t('The e-mail address %email has been denied access.', array('%email' => $edit['mail'])));
1521   }
1522 }
1523 
1524 function _user_edit_submit($uid, &$edit) {
1525   $user = user_load(array('uid' => $uid));
1526   // Delete picture if requested, and if no replacement picture was given.
1527   if (!empty($edit['picture_delete'])) {
1528     if ($user->picture && file_exists($user->picture)) {
1529       file_delete($user->picture);
1530     }
1531     $edit['picture'] = '';
1532   }
1533   if (isset($edit['roles'])) {
1534     $edit['roles'] = array_filter($edit['roles']);
1535   }
1536 }
1537 
1538 /**
1539  * Delete a user.
1540  *
1541  * @param $edit An array of submitted form values.
1542  * @param $uid The user ID of the user to delete.
1543  */
1544 function user_delete($edit, $uid) {
1545   $account = user_load(array('uid' => $uid));
1546   sess_destroy_uid($uid);
1547   _user_mail_notify('status_deleted', $account);
1548   db_query('DELETE FROM {users} WHERE uid = %d', $uid);
1549   db_query('DELETE FROM {users_roles} WHERE uid = %d', $uid);
1550   db_query('DELETE FROM {authmap} WHERE uid = %d', $uid);
1551   $variables = array('%name' => $account->name, '%email' => '<'. $account->mail .'>');
1552   watchdog('user', 'Deleted user: %name %email.', $variables, WATCHDOG_NOTICE);
1553   module_invoke_all('user', 'delete', $edit, $account);
1554 }
1555 
1556 /**
1557  * Builds a structured array representing the profile content.
1558  *
1559  * @param $account
1560  *   A user object.
1561  *
1562  * @return
1563  *   A structured array containing the individual elements of the profile.
1564  */
1565 function user_build_content(&$account) {
1566   $edit = NULL;
1567   user_module_invoke('view', $edit, $account);
1568   // Allow modules to modify the fully-built profile.
1569   drupal_alter('profile', $account);
1570 
1571   return $account->content;
1572 }
1573 
1574 /**
1575  * Implementation of hook_mail().
1576  */
1577 function user_mail($key, &$message, $params) {
1578   $language = $message['language'];
1579   $variables = user_mail_tokens($params['account'], $language);
1580   $message['subject'] .= _user_mail_text($key .'_subject', $language, $variables);
1581   $message['body'][] = _user_mail_text($key .'_body', $language, $variables);
1582 }
1583 
1584 /**
1585  * Returns a mail string for a variable name.
1586  *
1587  * Used by user_mail() and the settings forms to retrieve strings.
1588  */
1589 function _user_mail_text($key, $language = NULL, $variables = array()) {
1590   $langcode = isset($language) ? $language->language : NULL;
1591 
1592   if ($admin_setting = variable_get('user_mail_'. $key, FALSE)) {
1593     // An admin setting overrides the default string.
1594     return strtr($admin_setting, $variables);
1595   }
1596   else {
1597     // No override, return default string.
1598     switch ($key) {
1599       case 'register_no_approval_required_subject':
1600         return t('Account details for !username at !site', $variables, $langcode);
1601       case 'register_no_approval_required_body':
1602         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, $langcode);
1603       case 'register_admin_created_subject':
1604         return t('An administrator created an account for you at !site', $variables, $langcode);
1605       case 'register_admin_created_body':
1606         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, $langcode);
1607       case 'register_pending_approval_subject':
1608       case 'pending_approval_admin_subject':
1609         return t('Account details for !username at !site (pending admin approval)', $variables, $langcode);
1610       case 'register_pending_approval_body':
1611         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, $langcode);
1612       case 'register_pending_approval_admin_body':
1613         return t("!username has applied for an account.\n\n!edit_uri", $variables, $langcode);
1614       case 'password_reset_subject':
1615         return t('Replacement login information for !username at !site', $variables, $langcode);
1616       case 'password_reset_body':
1617         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, $langcode);
1618       case 'status_activated_subject':
1619         return t('Account details for !username at !site (approved)', $variables, $langcode);
1620       case 'status_activated_body':
1621         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, $langcode);
1622       case 'status_blocked_subject':
1623         return t('Account details for !username at !site (blocked)', $variables, $langcode);
1624       case 'status_blocked_body':
1625         return t("!username,\n\nYour account on !site has been blocked.", $variables, $langcode);
1626       case 'status_deleted_subject':
1627         return t('Account details for !username at !site (deleted)', $variables, $langcode);
1628       case 'status_deleted_body':
1629         return t("!username,\n\nYour account on !site has been deleted.", $variables, $langcode);
1630     }
1631   }
1632 }
1633 
1634 /*** Administrative features ***********************************************/
1635 
1636 /**
1637  * Retrieve an array of roles matching specified conditions.
1638  *
1639  * @param $membersonly
1640  *   Set this to TRUE to exclude the 'anonymous' role.
1641  * @param $permission
1642  *   A string containing a permission. If set, only roles containing that
1643  *   permission are returned.
1644  *
1645  * @return
1646  *   An associative array with the role id as the key and the role name as
1647  *   value.
1648  */
1649 function user_roles($membersonly = FALSE, $permission = NULL) {
1650   // System roles take the first two positions.
1651   $roles = array(
1652     DRUPAL_ANONYMOUS_RID => NULL,
1653     DRUPAL_AUTHENTICATED_RID => NULL,
1654   );
1655 
1656   if (!empty($permission)) {
1657     $result = db_query("SELECT r.* FROM {role} r INNER JOIN {permission} p ON r.rid = p.rid WHERE p.perm LIKE '%%%s%%' ORDER BY r.name", $permission);
1658   }
1659   else {
1660     $result = db_query('SELECT * FROM {role} ORDER BY name');
1661   }
1662 
1663   while ($role = db_fetch_object($result)) {
1664     switch ($role->rid) {
1665       // We only translate the built in role names
1666       case DRUPAL_ANONYMOUS_RID:
1667         if (!$membersonly) {
1668           $roles[$role->rid] = t($role->name);
1669         }
1670         break;
1671       case DRUPAL_AUTHENTICATED_RID:
1672         $roles[$role->rid] = t($role->name);
1673         break;
1674       default:
1675         $roles[$role->rid] = $role->name;
1676     }
1677   }
1678 
1679   // Filter to remove unmatched system roles.
1680   return array_filter($roles);
1681 }
1682 
1683 /**
1684  * Implementation of hook_user_operations().
1685  */
1686 function user_user_operations($form_state = array()) {
1687   $operations = array(
1688     'unblock' => array(
1689       'label' => t('Unblock the selected users'),
1690       'callback' => 'user_user_operations_unblock',
1691     ),
1692     'block' => array(
1693       'label' => t('Block the selected users'),
1694       'callback' => 'user_user_operations_block',
1695     ),
1696     'delete' => array(
1697       'label' => t('Delete the selected users'),
1698     ),
1699   );
1700 
1701   if (user_access('administer permissions')) {
1702     $roles = user_roles(TRUE);
1703     unset($roles[DRUPAL_AUTHENTICATED_RID]);  // Can't edit authenticated role.
1704 
1705     $add_roles = array();
1706     foreach ($roles as $key => $value) {
1707       $add_roles['add_role-'. $key] = $value;
1708     }
1709 
1710     $remove_roles = array();
1711     foreach ($roles as $key => $value) {
1712       $remove_roles['remove_role-'. $key] = $value;
1713     }
1714 
1715     if (count($roles)) {
1716       $role_operations = array(
1717         t('Add a role to the selected users') => array(
1718           'label' => $add_roles,
1719         ),
1720         t('Remove a role from the selected users') => array(
1721           'label' => $remove_roles,
1722         ),
1723       );
1724 
1725       $operations += $role_operations;
1726     }
1727   }
1728 
1729   // If the form has been posted, we need to insert the proper data for
1730   // role editing if necessary.
1731   if (!empty($form_state['submitted'])) {
1732     $operation_rid = explode('-', $form_state['values']['operation']);
1733     $operation = $operation_rid[0];
1734     if ($operation == 'add_role' || $operation == 'remove_role') {
1735       $rid = $operation_rid[1];
1736       if (user_access('administer permissions')) {
1737         $operations[$form_state['values']['operation']] = array(
1738           'callback' => 'user_multiple_role_edit',
1739           'callback arguments' => array($operation, $rid),
1740         );
1741       }
1742       else {
1743         watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
1744         return;
1745       }
1746     }
1747   }
1748 
1749   return $operations;
1750 }
1751 
1752 /**
1753  * Callback function for admin mass unblocking users.
1754  */
1755 function user_user_operations_unblock($accounts) {
1756   foreach ($accounts as $uid) {
1757     $account = user_load(array('uid' => (int)$uid));
1758     // Skip unblocking user if they are already unblocked.
1759     if ($account !== FALSE && $account->status == 0) {
1760       user_save($account, array('status' => 1));
1761     }
1762   }
1763 }
1764 
1765 /**
1766  * Callback function for admin mass blocking users.
1767  */
1768 function user_user_operations_block($accounts) {
1769   foreach ($accounts as $uid) {
1770     $account = user_load(array('uid' => (int)$uid));
1771     // Skip blocking user if they are already blocked.
1772     if ($account !== FALSE && $account->status == 1) {
1773       user_save($account, array('status' => 0));
1774     }
1775   }
1776 }
1777 
1778 /**
1779  * Callback function for admin mass adding/deleting a user role.
1780  */
1781 function user_multiple_role_edit($accounts, $operation, $rid) {
1782   // The role name is not necessary as user_save() will reload the user
1783   // object, but some modules' hook_user() may look at this first.
1784   $role_name = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $rid));
1785 
1786   switch ($operation) {
1787     case 'add_role':
1788       foreach ($accounts as $uid) {
1789         $account = user_load(array('uid' => (int)$uid));
1790         // Skip adding the role to the user if they already have it.
1791         if ($account !== FALSE && !isset($account->roles[$rid])) {
1792           $roles = $account->roles + array($rid => $role_name);
1793           user_save($account, array('roles' => $roles));
1794         }
1795       }
1796       break;
1797     case 'remove_role':
1798       foreach ($accounts as $uid) {
1799         $account = user_load(array('uid' => (int)$uid));
1800         // Skip removing the role from the user if they already don't have it.
1801         if ($account !== FALSE && isset($account->roles[$rid])) {
1802           $roles = array_diff($account->roles, array($rid => $role_name));
1803           user_save($account, array('roles' => $roles));
1804         }
1805       }
1806       break;
1807   }
1808 }
1809 
1810 function user_multiple_delete_confirm(&$form_state) {
1811   $edit = $form_state['post'];
1812 
1813   $form['accounts'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
1814   // array_filter() returns only elements with TRUE values.
1815   foreach (array_filter($edit['accounts']) as $uid => $value) {
1816     $user = db_result(db_query('SELECT name FROM {users} WHERE uid = %d', $uid));
1817     $form['accounts'][$uid] = array('#type' => 'hidden', '#value' => $uid, '#prefix' => '<li>', '#suffix' => check_plain($user) ."</li>\n");
1818   }
1819   $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
1820 
1821   return confirm_form($form,
1822                       t('Are you sure you want to delete these users?'),
1823                       'admin/user/user', t('This action cannot be undone.'),
1824                       t('Delete all'), t('Cancel'));
1825 }
1826 
1827 function user_multiple_delete_confirm_submit($form, &$form_state) {
1828   if ($form_state['values']['confirm']) {
1829     foreach ($form_state['values']['accounts'] as $uid => $value) {
1830       user_delete($form_state['values'], $uid);
1831     }
1832     drupal_set_message(t('The users have been deleted.'));
1833   }
1834   $form_state['redirect'] = 'admin/user/user';
1835   return;
1836 }
1837 
1838 /**
1839  * Implementation of hook_help().
1840  */
1841 function user_help($path, $arg) {
1842   global $user;
1843 
1844   switch ($path) {
1845     case 'admin/help#user':
1846       $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>';
1847       $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>';
1848       $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>';
1849       $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>';
1850       return $output;
1851     case 'admin/user/user':
1852       return '<p>'. t('Drupal allows users to register, login, log out, maintain user profiles, etc. Users of the site may not use their own names to post content until they have signed up for a user account.') .'</p>';
1853     case 'admin/user/user/create':
1854     case 'admin/user/user/account/create':
1855       return '<p>'. t("This web page allows administrators to register new users. Users' e-mail addresses and usernames must be unique.") .'</p>';
1856     case 'admin/user/rules':
1857       return '<p>'. t('Set up username and e-mail address access rules for new <em>and</em> existing accounts (currently logged in accounts will not be logged out). If a username or e-mail address for an account matches any deny rule, but not an allow rule, then the account will not be allowed to be created or to log in. A host rule is effective for every page view, not just registrations.') .'</p>';
1858     case 'admin/user/permissions':
1859       return '<p>'. t('Permissions let you control what users can do on your site. Each user role (defined on the <a href="@role">user roles page</a>) has its own set of permissions. For example, you could give users classified as "Administrators" permission to "administer nodes" but deny this power to ordinary, "authenticated" users. You can use permissions to reveal new features to privileged users (those with subscriptions, for example). Permissions also allow trusted users to share the administrative burden of running a busy site.', array('@role' => url('admin/user/roles'))) .'</p>';
1860     case 'admin/user/roles':
1861       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>
1862       <ul>
1863       <li>Anonymous user: this role is used for users that don\'t have a user account or that are not authenticated.</li>
1864       <li>Authenticated user: this role is automatically granted to all logged in users.</li>
1865       </ul>', array('@permissions' => url('admin/user/permissions')));
1866     case 'admin/user/search':
1867       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>';
1868   }
1869 }
1870 
1871 /**
1872  * Retrieve a list of all user setting/information categories and sort them by weight.
1873  */
1874 function _user_categories($account) {
1875   $categories = array();
1876 
18771  foreach (module_list() as $module) {
18781    if ($data = module_invoke($module, 'user', 'categories', NULL, $account, '')) {
18791      $categories = array_merge($data, $categories);
1880     }
1881   }
1882 
18831  usort($categories, '_user_sort');
1884 
18851  return $categories;
1886 }
1887 
1888 function _user_sort($a, $b) {
1889   $a = (array)$a + array('weight' => 0, 'title' => '');
1890   $b = (array)$b + array('weight' => 0, 'title' => '');
1891   return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1));
1892 }
1893 
1894 /**
1895  * List user administration filters that can be applied.
1896  */
1897 function user_filters() {
1898   // Regular filters
1899   $filters = array();
1900   $roles = user_roles(TRUE);
1901   unset($roles[DRUPAL_AUTHENTICATED_RID]); // Don't list authorized role.
1902   if (count($roles)) {
1903     $filters['role'] = array(
1904       'title' => t('role'),
1905       'where' => "ur.rid = %d",
1906       'options' => $roles,
1907       'join' => '',
1908     );
1909   }
1910 
1911   $options = array();
1912   foreach (module_list() as $module) {
1913     if ($permissions = module_invoke($module, 'perm')) {
1914       asort($permissions);
1915       foreach ($permissions as $permission => $description) {
1916         $options[t('@module module', array('@module' => $module))][$permission] = t($permission);
1917       }
1918     }
1919   }
1920   ksort($options);
1921   $filters['permission'] = array(
1922     'title' => t('permission'),
1923     'join' => 'LEFT JOIN {permission} p ON ur.rid = p.rid',
1924     'where' => " ((p.perm IS NOT NULL AND p.perm LIKE '%%%s%%') OR u.uid = 1) ",
1925     'options' => $options,
1926   );
1927 
1928   $filters['status'] = array(
1929     'title' => t('status'),
1930     'where' => 'u.status = %d',
1931     'join' => '',
1932     'options' => array(1 => t('active'), 0 => t('blocked')),
1933   );
1934   return $filters;
1935 }
1936 
1937 /**
1938  * Build query for user administration filters based on session.
1939  */
1940 function user_build_filter_query() {
1941   $filters = user_filters();
1942 
1943   // Build query
1944   $where = $args = $join = array();
1945   foreach ($_SESSION['user_overview_filter'] as $filter) {
1946     list($key, $value) = $filter;
1947     // This checks to see if this permission filter is an enabled permission for
1948     // the authenticated role. If so, then all users would be listed, and we can
1949     // skip adding it to the filter query.
1950     if ($key == 'permission') {
1951       $account = new stdClass();
1952       $account->uid = 'user_filter';
1953       $account->roles = array(DRUPAL_AUTHENTICATED_RID => 1);
1954       if (user_access($value, $account)) {
1955         continue;
1956       }
1957     }
1958     $where[] = $filters[$key]['where'];
1959     $args[] = $value;
1960     $join[] = $filters[$key]['join'];
1961   }
1962   $where = !empty($where) ? 'AND '. implode(' AND ', $where) : '';
1963   $join = !empty($join) ? ' '. implode(' ', array_unique($join)) : '';
1964 
1965   return array('where' => $where,
1966            'join' => $join,
1967            'args' => $args,
1968          );
1969 }
1970 
1971 /**
1972  * Implementation of hook_forms().
1973  */
1974 function user_forms() {
1975   $forms['user_admin_access_add_form']['callback'] = 'user_admin_access_form';
1976   $forms['user_admin_access_edit_form']['callback'] = 'user_admin_access_form';
1977   $forms['user_admin_new_role']['callback'] = 'user_admin_role';
1978   return $forms;
1979 }
1980 
1981 /**
1982  * Implementation of hook_comment().
1983  */
1984 function user_comment(&$comment, $op) {
1985   // Validate signature.
1986   if ($op == 'view') {
1987     if (variable_get('user_signatures', 0) && !empty($comment->signature)) {
1988       $comment->signature = check_markup($comment->signature, $comment->format);
1989     }
1990     else {
1991       $comment->signature = '';
1992     }
1993   }
1994 }
1995 
1996 /**
1997  * Theme output of user signature.
1998  *
1999  * @ingroup themeable
2000  */
2001 function theme_user_signature($signature) {
2002   $output = '';
2003   if ($signature) {
2004     $output .= '<div class="clear">';
2005     $output .= '<div>—</div>';
2006     $output .= $signature;
2007     $output .= '</div>';
2008   }
2009 
2010   return $output;
2011 }
2012 
2013 /**
2014  * Return an array of token to value mappings for user e-mail messages.
2015  *
2016  * @param $account
2017  *  The user object of the account being notified.  Must contain at
2018  *  least the fields 'uid', 'name', and 'mail'.
2019  * @param $language
2020  *  Language object to generate the tokens with.
2021  * @return
2022  *  Array of mappings from token names to values (for use with strtr()).
2023  */
2024 function user_mail_tokens($account, $language) {
2025   global $base_url;
2026   $tokens = array(
2027     '!username' => $account->name,
2028     '!site' => variable_get('site_name', 'Drupal'),
2029     '!login_url' => user_pass_reset_url($account),
2030     '!uri' => $base_url,
2031     '!uri_brief' => substr($base_url, strlen('http://')),
2032     '!mailto' => $account->mail,
2033     '!date' => format_date(time(), 'medium', '', NULL, $language->language),
2034     '!login_uri' => url('user', array('absolute' => TRUE, 'language' => $language)),
2035     '!edit_uri' => url('user/'. $account->uid .'/edit', array('absolute' => TRUE, 'language' => $language)),
2036   );
2037   if (!empty($account->password)) {
2038     $tokens['!password'] = $account->password;
2039   }
2040   return $tokens;
2041 }
2042 
2043 /**
2044  * Get the language object preferred by the user. This user preference can
2045  * be set on the user account editing page, and is only available if there
2046  * are more than one languages enabled on the site. If the user did not
2047  * choose a preferred language, or is the anonymous user, the $default
2048  * value, or if it is not set, the site default language will be returned.
2049  *
2050  * @param $account
2051  *   User account to look up language for.
2052  * @param $default
2053  *   Optional default language object to return if the account
2054  *   has no valid language.
2055  */
2056 function user_preferred_language($account, $default = NULL) {
2057   $language_list = language_list();
2058   if ($account->language && isset($language_list[$account->language])) {
2059     return $language_list[$account->language];
2060   }
2061   else {
2062     return $default ? $default : language_default();
2063   }
2064 }
2065 
2066 /**
2067  * Conditionally create and send a notification email when a certain
2068  * operation happens on the given user account.
2069  *
2070  * @see user_mail_tokens()
2071  * @see drupal_mail()
2072  *
2073  * @param $op
2074  *  The operation being performed on the account.  Possible values:
2075  *  'register_admin_created': Welcome message for user created by the admin
2076  *  'register_no_approval_required': Welcome message when user self-registers
2077  *  'register_pending_approval': Welcome message, user pending admin approval
2078  *  'password_reset': Password recovery request
2079  *  'status_activated': Account activated
2080  *  'status_blocked': Account blocked
2081  *  'status_deleted': Account deleted
2082  *
2083  * @param $account
2084  *  The user object of the account being notified.  Must contain at
2085  *  least the fields 'uid', 'name', and 'mail'.
2086  * @param $language
2087  *  Optional language to use for the notification, overriding account language.
2088  * @return
2089  *  The return value from drupal_mail_send(), if ends up being called.
2090  */
2091 function _user_mail_notify($op, $account, $language = NULL) {
2092   // By default, we always notify except for deleted and blocked.
2093   $default_notify = ($op != 'status_deleted' && $op != 'status_blocked');
2094   $notify = variable_get('user_mail_'. $op .'_notify', $default_notify);
2095   if ($notify) {
2096     $params['account'] = $account;
2097     $language = $language ? $language : user_preferred_language($account);
2098     $mail = drupal_mail('user', $op, $account->mail, $language, $params);
2099     if ($op == 'register_pending_approval') {
2100       // If a user registered requiring admin approval, notify the admin, too.
2101       // We use the site default language for this.
2102       drupal_mail('user', 'register_pending_approval_admin', variable_get('site_mail', ini_get('sendmail_from')), language_default(), $params);
2103     }
2104   }
2105   return empty($mail) ? NULL : $mail['result'];
2106 }
2107 
2108 /**
2109  * Add javascript and string translations for dynamic password validation
2110  * (strength and confirmation checking).
2111  *
2112  * This is an internal function that makes it easier to manage the translation
2113  * strings that need to be passed to the javascript code.
2114  */
2115 function _user_password_dynamic_validation() {
2116   static $complete = FALSE;
2117   global $user;
2118   // Only need to do once per page.
2119   if (!$complete) {
2120     drupal_add_js(drupal_get_path('module', 'user') .'/user.js', 'module');
2121 
2122     drupal_add_js(array(
2123       'password' => array(
2124         'strengthTitle' => t('Password strength:'),
2125         'lowStrength' => t('Low'),
2126         'mediumStrength' => t('Medium'),
2127         'highStrength' => t('High'),
2128         'tooShort' => t('It is recommended to choose a password that contains at least six characters. It should include numbers, punctuation, and both upper and lowercase letters.'),
2129         'needsMoreVariation' => t('The password does not include enough variation to be secure. Try:'),
2130         'addLetters' => t('Adding both upper and lowercase letters.'),
2131         'addNumbers' => t('Adding numbers.'),
2132         'addPunctuation' => t('Adding punctuation.'),
2133         'sameAsUsername' => t('It is recommended to choose a password different from the username.'),
2134         'confirmSuccess' => t('Yes'),
2135         'confirmFailure' => t('No'),
2136         'confirmTitle' => t('Passwords match:'),
2137         'username' => (isset($user->name) ? $user->name : ''))),
2138       'setting');
2139     $complete = TRUE;
2140   }
2141 }
2142 
2143 /**
2144  * Implementation of hook_hook_info().
2145  */
2146 function user_hook_info() {
2147   return array(
2148     'user' => array(
2149       'user' => array(
2150         'insert' => array(
2151           'runs when' => t('After a user account has been created'),
2152         ),
2153         'update' => array(
2154           'runs when' => t("After a user's profile has been updated"),
2155         ),
2156         'delete' => array(
2157           'runs when' => t('After a user has been deleted')
2158         ),
2159         'login' => array(
2160           'runs when' => t('After a user has logged in')
2161         ),
2162         'logout' => array(
2163           'runs when' => t('After a user has logged out')
2164         ),
2165         'view' => array(
2166           'runs when' => t("When a user's profile is being viewed")
2167         ),
2168       ),
2169     ),
2170   );
2171 }
2172 
2173 /**
2174  * Implementation of hook_action_info().
2175  */
2176 function user_action_info() {
2177   return array(
2178     'user_block_user_action' => array(
21791      'description' => t('Block current user'),
2180       'type' => 'user',
2181       'configurable' => FALSE,
2182       'hooks' => array(),
2183     ),
2184     'user_block_ip_action' => array(
2185       'description' => t('Ban IP address of current user'),
2186       'type' => 'user',
2187       'configurable' => FALSE,
2188       'hooks' => array(),
2189     ),
2190   );
2191 }
2192 
2193 /**
2194  * Implementation of a Drupal action.
2195  * Blocks the current user.
2196  */
2197 function user_block_user_action(&$object, $context = array()) {
2198   if (isset($object->uid)) {
2199     $uid = $object->uid;
2200   }
2201   elseif (isset($context['uid'])) {
2202     $uid = $context['uid'];
2203   }
2204   else {
2205     global $user;
2206     $uid = $user->uid;
2207   }
2208   db_query("UPDATE {users} SET status = 0 WHERE uid = %d", $uid);
2209   sess_destroy_uid($uid);
2210   watchdog('action', 'Blocked user %name.', array('%name' => check_plain($user->name)));
2211 }
2212 
2213 /**
2214  * Implementation of a Drupal action.
2215  * Adds an access rule that blocks the user's IP address.
2216  */
2217 function user_block_ip_action() {
2218   $ip = ip_address();
2219   db_query("INSERT INTO {access} (mask, type, status) VALUES ('%s', '%s', %d)", $ip, 'host', 0);
2220   watchdog('action', 'Banned IP address %ip', array('%ip' => $ip));
2221 }
2222 
2223 /**
2224  * Submit handler for the user registration form.
2225  *
2226  * This function is shared by the installation form and the normal registration form,
2227  * which is why it can't be in the user.pages.inc file.
2228  */
2229 function user_register_submit($form, &$form_state) {
2230   global $base_url;
2231   $admin = user_access('administer users');
2232 
2233   $mail = $form_state['values']['mail'];
2234   $name = $form_state['values']['name'];
2235   if (!variable_get('user_email_verification', TRUE) || $admin) {
2236     $pass = $form_state['values']['pass'];
2237   }
2238   else {
2239     $pass = user_password();
2240   };
2241   $notify = isset($form_state['values']['notify']) ? $form_state['values']['notify'] : NULL;
2242   $from = variable_get('site_mail', ini_get('sendmail_from'));
2243   if (isset($form_state['values']['roles'])) {
2244     // Remove unset roles.
2245     $roles = array_filter($form_state['values']['roles']);
2246   }
2247   else {
2248     $roles = array();
2249   }
2250 
2251   if (!$admin && array_intersect(array_keys($form_state['values']), array('uid', 'roles', 'init', 'session', 'status'))) {
2252     watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
2253     $form_state['redirect'] = 'user/register';
2254     return;
2255   }
2256   // The unset below is needed to prevent these form values from being saved as
2257   // user data.
2258   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']);
2259 
2260   $merge_data = array('pass' => $pass, 'init' => $mail, 'roles' => $roles);
2261   if (!$admin) {
2262     // Set the user's status because it was not displayed in the form.
2263     $merge_data['status'] = variable_get('user_register', 1) == 1;
2264   }
2265   $account = user_save('', array_merge($form_state['values'], $merge_data));
2266   // Terminate if an error occured during user_save().
2267   if (!$account) {
2268     drupal_set_message(t("Error saving user account."), 'error');
2269     $form_state['redirect'] = '';
2270     return;
2271   }
2272   $form_state['user'] = $account;
2273 
2274   watchdog('user', 'New user: %name (%email).', array('%name' => $name, '%email' => $mail), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit'));
2275 
2276   // The first user may login immediately, and receives a customized welcome e-mail.
2277   if ($account->uid == 1) {
2278     drupal_set_message(t('Welcome to Drupal. You are now logged in as user #1, which gives you full control over your website.'));
2279     if (variable_get('user_email_verification', TRUE)) {
2280       drupal_set_message(t('</p><p> Your password is <strong>%pass</strong>. You may change your password below.</p>', array('%pass' => $pass)));
2281     }
2282 
2283     user_authenticate(array_merge($form_state['values'], $merge_data));
2284 
2285     $form_state['redirect'] = 'user/1/edit';
2286     return;
2287   }
2288   else {
2289     // Add plain text password into user account to generate mail tokens.
2290     $account->password = $pass;
2291     if ($admin && !$notify) {
2292       drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => url($account->uid"), '%name' => $account->name)));
2293     }
2294     else if (!variable_get('user_email_verification', TRUE) && $account->status && !$admin) {
2295       // No e-mail verification is required, create new user account, and login
2296       // user immediately.
2297       _user_mail_notify('register_no_approval_required', $account);
2298       if (user_authenticate(array_merge($form_state['values'], $merge_data))) {
2299         drupal_set_message(t('Registration successful. You are now logged in.'));
2300       }
2301       $form_state['redirect'] = '';
2302       return;
2303     }
2304     else if ($account->status || $notify) {
2305       // Create new user account, no administrator approval required.
2306       $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
2307       _user_mail_notify($op, $account);
2308       if ($notify) {
2309         drupal_set_message(t('Password and further instructions have been e-mailed to the new user <a href="@url">%name</a>.', array('@url' => url($account->uid"), '%name' => $account->name)));
2310       }
2311       else {
2312         drupal_set_message(t('Your password and further instructions have been sent to your e-mail address.'));
2313         $form_state['redirect'] = '';
2314         return;
2315       }
2316     }
2317     else {
2318       // Create new user account, administrator approval required.
2319       _user_mail_notify('register_pending_approval', $account);
2320       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.'));
2321       $form_state['redirect'] = '';
2322       return;
2323 
2324     }
2325   }
2326 }
2327 
2328 /**
2329  * Form builder; The user registration form.
2330  *
2331  * @ingroup forms
2332  * @see user_register_validate()
2333  * @see user_register_submit()
2334  */
2335 function user_register() {
2336   global $user;
2337 
2338   $admin = user_access('administer users');
2339 
2340   // If we aren't admin but already logged on, go to the user page instead.
2341   if (!$admin && $user->uid) {
2342     drupal_goto('user/'. $user->uid);
2343   }
2344 
2345   $form = array();
2346 
2347   // Display the registration form.
2348   if (!$admin) {
2349     $form['user_registration_help'] = array('#value' => filter_xss_admin(variable_get('user_registration_help', '')));
2350   }
2351 
2352   // Merge in the default user edit fields.
2353   $form = array_merge($form, user_edit_form($form_state, NULL, NULL, TRUE));
2354   if ($admin) {
2355     $form['account']['notify'] = array(
2356      '#type' => 'checkbox',
2357      '#title' => t('Notify user of new account')
2358     );
2359     // Redirect back to page which initiated the create request;
2360     // usually admin/user/user/create.
2361     $form['destination'] = array('#type' => 'hidden', '#value' => $_GET['q']);
2362   }
2363 
2364   // Create a dummy variable for pass-by-reference parameters.
2365   $null = NULL;
2366   $extra = _user_forms($null, NULL, NULL, 'register');
2367 
2368   // Remove form_group around default fields if there are no other groups.
2369   if (!$extra) {
2370     foreach (array('name', 'mail', 'pass', 'status', 'roles', 'notify') as $key) {
2371       if (isset($form['account'][$key])) {
2372         $form[$key] = $form['account'][$key];
2373       }
2374     }
2375     unset($form['account']);
2376   }
2377   else {
2378     $form = array_merge($form, $extra);
2379   }
2380 
2381   if (variable_get('configurable_timezones', 1)) {
2382     // Override field ID, so we only change timezone on user registration,
2383     // and never touch it on user edit pages.
2384     $form['timezone'] = array(
2385       '#type' => 'hidden',
2386       '#default_value' => variable_get('date_default_timezone', NULL),
2387       '#id' => 'edit-user-register-timezone',
2388     );
2389 
2390     // Add the JavaScript callback to automatically set the timezone.
2391     drupal_add_js('
2392 // Global Killswitch
2393 if (Drupal.jsEnabled) {
2394   $(document).ready(function() {
2395     Drupal.setDefaultTimezone();
2396   });
2397 }', 'inline');
2398   }
2399 
2400   $form['submit'] = array('#type' => 'submit', '#value' => t('Create new account'), '#weight' => 30);
2401   $form['#validate'][] = 'user_register_validate';
2402 
2403   return $form;
2404 }
2405 
2406 function user_register_validate($form, &$form_state) {
2407   user_module_invoke('validate', $form_state['values'], $form_state['values'], 'account');
2408 }
2409 
2410 /**
2411  * Retrieve a list of all form elements for the specified category.
2412  */
2413 function _user_forms(&$edit, $account, $category, $hook = 'form') {
2414   $groups = array();
2415   foreach (module_list() as $module) {
2416     if ($data = module_invoke($module, 'user', $hook, $edit, $account, $category)) {
2417       $groups = array_merge_recursive($data, $groups);
2418     }
2419   }
2420   uasort($groups, '_user_sort');
2421 
2422   return empty($groups) ? FALSE : $groups;
2423 }