Imported drupal-5.5
[drupal.git] / modules / user / user.module
blob4a4983c5eae2a9315628b590aad38c4cd5c79fd5
1 <?php
2 // $Id: user.module,v 1.745.2.23 2007/12/06 19:49:14 drumm Exp $
4 /**
5  * @file
6  * Enables the user registration and login system.
7  */
9 define('USERNAME_MAX_LENGTH', 60);
10 define('EMAIL_MAX_LENGTH', 64);
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) {
19   foreach (module_list() as $module) {
20     $function = $module .'_user';
21     if (function_exists($function)) {
22       $function($type, $array, $user, $category);
23     }
24   }
27 function user_external_load($authname) {
28   $result = db_query("SELECT uid FROM {authmap} WHERE authname = '%s'", $authname);
30   if ($user = db_fetch_array($result)) {
31     return user_load($user);
32   }
33   else {
34     return 0;
35   }
38 /**
39  * Fetch a user object.
40  *
41  * @param $array
42  *   An associative array of attributes to search for in selecting the
43  *   user, such as user name or e-mail address.
44  *
45  * @return
46  *   A fully-loaded $user object upon successful user load or FALSE if user cannot be loaded.
47  */
48 function user_load($array = array()) {
49   // Dynamically compose a SQL query:
50   $query = array();
51   $params = array();
53   foreach ($array as $key => $value) {
54     if ($key == 'uid' || $key == 'status') {
55       $query[] = "$key = %d";
56       $params[] = $value;
57     }
58     else if ($key == 'pass') {
59       $query[] = "pass = '%s'";
60       $params[] = md5($value);
61     }
62     else {
63       $query[]= "LOWER($key) = LOWER('%s')";
64       $params[] = $value;
65     }
66   }
67   $result = db_query('SELECT * FROM {users} u WHERE '. implode(' AND ', $query), $params);
69   if (db_num_rows($result)) {
70     $user = db_fetch_object($result);
71     $user = drupal_unpack($user);
73     $user->roles = array();
74     if ($user->uid) {
75       $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
76     }
77     else {
78       $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
79     }
80     $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);
81     while ($role = db_fetch_object($result)) {
82       $user->roles[$role->rid] = $role->name;
83     }
84     user_module_invoke('load', $array, $user);
85   }
86   else {
87     $user = FALSE;
88   }
90   return $user;
93 /**
94  * Save changes to a user account or add a new user.
95  *
96  * @param $account
97  *   The $user object for the user to modify or add. If $user->uid is
98  *   omitted, a new user will be added.
99  *
100  * @param $array
101  *   An array of fields and values to save. For example array('name' => 'My name');
102  *   Setting a field to NULL deletes it from the data column.
104  * @param $category
105  *   (optional) The category for storing profile information in.
106  */
107 function user_save($account, $array = array(), $category = 'account') {
108   // Dynamically compose a SQL query:
109   $user_fields = user_fields();
110   if ($account->uid) {
111     user_module_invoke('update', $array, $account, $category);
113     $data = unserialize(db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $account->uid)));
114     // Consider users edited by an administrator as logged in, if they haven't
115     // already, so anonymous users can view the profile (if allowed).
116     if (empty($array['access']) && empty($account->access) && user_access('administer users')) {
117       $array['access'] = time();
118     }
119     foreach ($array as $key => $value) {
120       if ($key == 'pass' && !empty($value)) {
121         $query .= "$key = '%s', ";
122         $v[] = md5($value);
123       }
124       else if ((substr($key, 0, 4) !== 'auth') && ($key != 'pass')) {
125         if (in_array($key, $user_fields)) {
126           // Save standard fields
127           $query .= "$key = '%s', ";
128           $v[] = $value;
129         }
130         else if ($key != 'roles') {
131           // Roles is a special case: it used below.
132           if ($value === NULL) {
133             unset($data[$key]);
134           }
135           else {
136             $data[$key] = $value;
137           }
138         }
139       }
140     }
141     $query .= "data = '%s' ";
142     $v[] = serialize($data);
144     db_query("UPDATE {users} SET $query WHERE uid = %d", array_merge($v, array($account->uid)));
146     // Reload user roles if provided
147     if (is_array($array['roles'])) {
148       db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid);
150       foreach (array_keys($array['roles']) as $rid) {
151         if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
152           db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid);
153         }
154       }
155     }
157     // Delete a blocked user's sessions to kick them if they are online.
158     if (isset($array['status']) && $array['status'] == 0) {
159       sess_destroy_uid($account->uid);
160     }
162     // If the password changed, delete all open sessions and recreate
163     // the current one.
164     if (!empty($array['pass'])) {
165       sess_destroy_uid($account->uid);
166       sess_regenerate();
167     }
169     // Refresh user object
170     $user = user_load(array('uid' => $account->uid));
171     user_module_invoke('after_update', $array, $user, $category);
172   }
173   else {
174     $array['uid'] = db_next_id('{users}_uid');
176     if (!isset($array['created'])) {    // Allow 'created' to be set by hook_auth
177       $array['created'] = time();
178     }
179     // Consider users created by an administrator as already logged in, so
180     // anonymous users can view the profile (if allowed).
181     if (empty($array['access']) && user_access('administer users')) {
182       $array['access'] = time();
183     }
185     // Note, we wait with saving the data column to prevent module-handled
186     // fields from being saved there. We cannot invoke hook_user('insert') here
187     // because we don't have a fully initialized user object yet.
188     foreach ($array as $key => $value) {
189       switch ($key) {
190         case 'pass':
191           $fields[] = $key;
192           $values[] = md5($value);
193           $s[] = "'%s'";
194           break;
195         case 'uid':        case 'mode':     case 'sort':
196         case 'threshold':  case 'created':  case 'access':
197         case 'login':      case 'status':
198           $fields[] = $key;
199           $values[] = $value;
200           $s[] = "%d";
201           break;
202         default:
203           if (substr($key, 0, 4) !== 'auth' && in_array($key, $user_fields)) {
204             $fields[] = $key;
205             $values[] = $value;
206             $s[] = "'%s'";
207           }
208           break;
209       }
210     }
211     db_query('INSERT INTO {users} ('. implode(', ', $fields) .') VALUES ('. implode(', ', $s) .')', $values);
213     // Build the initial user object.
214     $user = user_load(array('uid' => $array['uid']));
216     user_module_invoke('insert', $array, $user, $category);
218     // Build and save the serialized data field now
219     $data = array();
220     foreach ($array as $key => $value) {
221       if ((substr($key, 0, 4) !== 'auth') && ($key != 'roles') && (!in_array($key, $user_fields)) && ($value !== NULL)) {
222         $data[$key] = $value;
223       }
224     }
225     db_query("UPDATE {users} SET data = '%s' WHERE uid = %d", serialize($data), $user->uid);
227     // Save user roles (delete just to be safe).
228     if (is_array($array['roles'])) {
229       db_query('DELETE FROM {users_roles} WHERE uid = %d', $array['uid']);
230       foreach (array_keys($array['roles']) as $rid) {
231         if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
232           db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $array['uid'], $rid);
233         }
234       }
235     }
237     // Build the finished user object.
238     $user = user_load(array('uid' => $array['uid']));
239   }
241   // Save distributed authentication mappings
242   $authmaps = array();
243   foreach ($array as $key => $value) {
244     if (substr($key, 0, 4) == 'auth') {
245       $authmaps[$key] = $value;
246     }
247   }
248   if (sizeof($authmaps) > 0) {
249     user_set_authmaps($user, $authmaps);
250   }
252   return $user;
256  * Verify the syntax of the given name.
257  */
258 function user_validate_name($name) {
259   if (!strlen($name)) return t('You must enter a username.');
260   if (substr($name, 0, 1) == ' ') return t('The username cannot begin with a space.');
261   if (substr($name, -1) == ' ') return t('The username cannot end with a space.');
262   if (strpos($name, '  ') !== FALSE) return t('The username cannot contain multiple spaces in a row.');
263   if (ereg("[^\x80-\xF7 [:alnum:]@_.-]", $name)) return t('The username contains an illegal character.');
264   if (preg_match('/[\x{80}-\x{A0}'.          // Non-printable ISO-8859-1 + NBSP
265                    '\x{AD}'.                 // Soft-hyphen
266                    '\x{2000}-\x{200F}'.      // Various space characters
267                    '\x{2028}-\x{202F}'.      // Bidirectional text overrides
268                    '\x{205F}-\x{206F}'.      // Various text hinting characters
269                    '\x{FEFF}'.               // Byte order mark
270                    '\x{FF01}-\x{FF60}'.      // Full-width latin
271                    '\x{FFF9}-\x{FFFD}'.      // Replacement characters
272                    '\x{0}]/u',               // NULL byte
273                    $name)) {
274     return t('The username contains an illegal character.');
275   }
276   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.');
277   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));
280 function user_validate_mail($mail) {
281   if (!$mail) return t('You must enter an e-mail address.');
282   if (!valid_email_address($mail)) {
283     return t('The e-mail address %mail is not valid.', array('%mail' => $mail));
284   }
287 function user_validate_picture($file, &$edit, $user) {
288   global $form_values;
289   // Initialize the picture:
290   $form_values['picture'] = $user->picture;
292   // Check that uploaded file is an image, with a maximum file size
293   // and maximum height/width.
294   $info = image_get_info($file->filepath);
295   list($maxwidth, $maxheight) = explode('x', variable_get('user_picture_dimensions', '85x85'));
297   if (!$info || !$info['extension']) {
298     form_set_error('picture_upload', t('The uploaded file was not an image.'));
299   }
300   else if (image_get_toolkit()) {
301     image_scale($file->filepath, $file->filepath, $maxwidth, $maxheight);
302   }
303   else if (filesize($file->filepath) > (variable_get('user_picture_file_size', '30') * 1000)) {
304     form_set_error('picture_upload', t('The uploaded image is too large; the maximum file size is %size kB.', array('%size' => variable_get('user_picture_file_size', '30'))));
305   }
306   else if ($info['width'] > $maxwidth || $info['height'] > $maxheight) {
307     form_set_error('picture_upload', t('The uploaded image is too large; the maximum dimensions are %dimensions pixels.', array('%dimensions' => variable_get('user_picture_dimensions', '85x85'))));
308   }
310   if (!form_get_errors()) {
311     if ($file = file_save_upload('picture_upload', variable_get('user_picture_path', 'pictures') .'/picture-'. $user->uid .'.'. $info['extension'], 1)) {
312       $form_values['picture'] = $file->filepath;
313     }
314     else {
315       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'))));
316     }
317   }
321  * Generate a random alphanumeric password.
322  */
323 function user_password($length = 10) {
324   // This variable contains the list of allowable characters for the
325   // password. Note that the number 0 and the letter 'O' have been
326   // removed to avoid confusion between the two. The same is true
327   // of 'I', 1, and l.
328   $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
330   // Zero-based count of characters in the allowable list:
331   $len = strlen($allowable_characters) - 1;
333   // Declare the password as a blank string.
334   $pass = '';
336   // Loop the number of times specified by $length.
337   for ($i = 0; $i < $length; $i++) {
339     // Each iteration, pick a random character from the
340     // allowable string and append it to the password:
341     $pass .= $allowable_characters[mt_rand(0, $len)];
342   }
344   return $pass;
348  * Determine whether the user has a given privilege.
350  * @param $string
351  *   The permission, such as "administer nodes", being checked for.
352  * @param $account
353  *   (optional) The account to check, if not given use currently logged in user.
355  * @return
356  *   boolean TRUE if the current user has the requested permission.
358  * All permission checks in Drupal should go through this function. This
359  * way, we guarantee consistent behavior, and ensure that the superuser
360  * can perform all actions.
361  */
362 function user_access($string, $account = NULL) {
363   global $user;
364   static $perm = array();
366   if (is_null($account)) {
367     $account = $user;
368   }
370   // User #1 has all privileges:
371   if ($account->uid == 1) {
372     return TRUE;
373   }
375   // To reduce the number of SQL queries, we cache the user's permissions
376   // in a static variable.
377   if (!isset($perm[$account->uid])) {
378     $rids = array_keys($account->roles);
379     $placeholders = implode(',', array_fill(0, count($rids), '%d'));
380     $result = db_query("SELECT DISTINCT(p.perm) FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid WHERE r.rid IN ($placeholders)", $rids);
381     $perm[$account->uid] = '';
382     while ($row = db_fetch_object($result)) {
383       $perm[$account->uid] .= "$row->perm, ";
384     }
385   }
387   if (isset($perm[$account->uid])) {
388     return strpos($perm[$account->uid], "$string, ") !== FALSE;
389   }
391   return FALSE;
395  * Checks for usernames blocked by user administration
397  * @return boolean TRUE for blocked users, FALSE for active
398  */
399 function user_is_blocked($name) {
400   $deny  = db_fetch_object(db_query("SELECT name FROM {users} WHERE status = 0 AND name = LOWER('%s')", $name));
402   return $deny;
405 function user_fields() {
406   static $fields;
408   if (!$fields) {
409     $result = db_query('SELECT * FROM {users} WHERE uid = 1');
410     if (db_num_rows($result)) {
411       $fields = array_keys(db_fetch_array($result));
412     }
413     else {
414       // Make sure we return the default fields at least
415       $fields = array('uid', 'name', 'pass', 'mail', 'picture', 'mode', 'sort', 'threshold', 'theme', 'signature', 'created', 'access', 'login', 'status', 'timezone', 'language', 'init', 'data');
416     }
417   }
419   return $fields;
423  * Implementation of hook_perm().
424  */
425 function user_perm() {
426   return array('administer access control', 'administer users', 'access user profiles', 'change own username');
430  * Implementation of hook_file_download().
432  * Ensure that user pictures (avatars) are always downloadable.
433  */
434 function user_file_download($file) {
435   if (strpos($file, variable_get('user_picture_path', 'pictures') .'/picture-') === 0) {
436     $info = image_get_info(file_create_path($file));
437     return array('Content-type: '. $info['mime_type']);
438   }
442  * Implementation of hook_search().
443  */
444 function user_search($op = 'search', $keys = NULL) {
445   switch ($op) {
446     case 'name':
447       if (user_access('access user profiles')) {
448         return t('Users');
449       }
450     case 'search':
451       if (user_access('access user profiles')) {
452         $find = array();
453         // Replace wildcards with MySQL/PostgreSQL wildcards.
454         $keys = preg_replace('!\*+!', '%', $keys);
455         $result = pager_query("SELECT name, uid FROM {users} WHERE LOWER(name) LIKE LOWER('%%%s%%')", 15, 0, NULL, $keys);
456         while ($account = db_fetch_object($result)) {
457           $find[] = array('title' => $account->name, 'link' => url('user/'. $account->uid, NULL, NULL, TRUE));
458         }
459         return $find;
460       }
461   }
465  * Implementation of hook_user().
466  */
467 function user_user($type, &$edit, &$user, $category = NULL) {
468   if ($type == 'view') {
469     $items['history'] = array('title' => t('Member for'),
470       'value' => format_interval(time() - $user->created),
471       'class' => 'member',
472     );
474     return array(t('History') => $items);
475   }
476   if ($type == 'form' && $category == 'account') {
477     return user_edit_form(arg(1), $edit);
478   }
480   if ($type == 'validate' && $category == 'account') {
481     return _user_edit_validate(arg(1), $edit);
482   }
484   if ($type == 'submit' && $category == 'account') {
485     return _user_edit_submit(arg(1), $edit);
486   }
488   if ($type == 'categories') {
489     return array(array('name' => 'account', 'title' => t('Account settings'), 'weight' => 1));
490   }
493 function user_login_block() {
494   $form = array(
495     '#action' => url($_GET['q'], drupal_get_destination()),
496     '#id' => 'user-login-form',
497     '#base' => 'user_login',
498   );
499   $form['name'] = array('#type' => 'textfield',
500     '#title' => t('Username'),
501     '#maxlength' => USERNAME_MAX_LENGTH,
502     '#size' => 15,
503     '#required' => TRUE,
504   );
505   $form['pass'] = array('#type' => 'password',
506     '#title' => t('Password'),
507     '#maxlength' => 60,
508     '#size' => 15,
509     '#required' => TRUE,
510   );
511   $form['submit'] = array('#type' => 'submit',
512     '#value' => t('Log in'),
513   );
514   $items = array();
515   if (variable_get('user_register', 1)) {
516     $items[] = l(t('Create new account'), 'user/register', array('title' => t('Create a new user account.')));
517   }
518   $items[] = l(t('Request new password'), 'user/password', array('title' => t('Request new password via e-mail.')));
519   $form['links'] = array('#value' => theme('item_list', $items));
520   return $form;
524  * Implementation of hook_block().
525  */
526 function user_block($op = 'list', $delta = 0, $edit = array()) {
527   global $user;
529   if ($op == 'list') {
530      $blocks[0]['info'] = t('User login');
531      $blocks[1]['info'] = t('Navigation');
532      $blocks[2]['info'] = t('Who\'s new');
533      $blocks[3]['info'] = t('Who\'s online');
535      return $blocks;
536   }
537   else if ($op == 'configure' && $delta == 2) {
538     $form['user_block_whois_new_count'] = array(
539       '#type' => 'select',
540       '#title' => t('Number of users to display'),
541       '#default_value' => variable_get('user_block_whois_new_count', 5),
542       '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
543     );
544     return $form;
545   }
546   else if ($op == 'configure' && $delta == 3) {
547     $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), 'format_interval');
548     $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.'));
549     $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.'));
551     return $form;
552   }
553   else if ($op == 'save' && $delta == 2) {
554     variable_set('user_block_whois_new_count', $edit['user_block_whois_new_count']);
555   }
556   else if ($op == 'save' && $delta == 3) {
557     variable_set('user_block_seconds_online', $edit['user_block_seconds_online']);
558     variable_set('user_block_max_list_count', $edit['user_block_max_list_count']);
559   }
560   else if ($op == 'view') {
561     $block = array();
563     switch ($delta) {
564       case 0:
565         // For usability's sake, avoid showing two login forms on one page.
566         if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
568           $block['subject'] = t('User login');
569           $block['content'] = drupal_get_form('user_login_block');
570         }
571         return $block;
573       case 1:
574         if ($menu = theme('menu_tree')) {
575            $block['subject'] = $user->uid ? check_plain($user->name) : t('Navigation');
576            $block['content'] = $menu;
577         }
578         return $block;
580       case 2:
581         if (user_access('access content')) {
582           // Retrieve a list of new users who have subsequently accessed the site successfully.
583           $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));
584           while ($account = db_fetch_object($result)) {
585             $items[] = $account;
586           }
587           $output = theme('user_list', $items);
589           $block['subject'] = t('Who\'s new');
590           $block['content'] = $output;
591         }
592         return $block;
594       case 3:
595         if (user_access('access content')) {
596           // Count users active within the defined period.
597           $interval = time() - variable_get('user_block_seconds_online', 900);
599           // Perform database queries to gather online user lists.  We use s.timestamp
600           // rather than u.access because it is much faster.
601           $anonymous_count = sess_count($interval);
602           $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);
603           $authenticated_count = db_num_rows($authenticated_users);
605           // Format the output with proper grammar.
606           if ($anonymous_count == 1 && $authenticated_count == 1) {
607             $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')));
608           }
609           else {
610             $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')));
611           }
613           // Display a list of currently online users.
614           $max_users = variable_get('user_block_max_list_count', 10);
615           if ($authenticated_count && $max_users) {
616             $items = array();
618             while ($max_users-- && $account = db_fetch_object($authenticated_users)) {
619               $items[] = $account;
620             }
622             $output .= theme('user_list', $items, t('Online users'));
623           }
625           $block['subject'] = t('Who\'s online');
626           $block['content'] = $output;
627         }
628         return $block;
629     }
630   }
633 function theme_user_picture($account) {
634   if (variable_get('user_pictures', 0)) {
635     if ($account->picture && file_exists($account->picture)) {
636       $picture = file_create_url($account->picture);
637     }
638     else if (variable_get('user_picture_default', '')) {
639       $picture = variable_get('user_picture_default', '');
640     }
642     if (isset($picture)) {
643       $alt = t("@user's picture", array('@user' => $account->name ? $account->name : variable_get('anonymous', t('Anonymous'))));
644       $picture = theme('image', $picture, $alt, $alt, '', FALSE);
645       if (!empty($account->uid) && user_access('access user profiles')) {
646         $picture = l($picture, "user/$account->uid", array('title' => t('View user profile.')), NULL, NULL, FALSE, TRUE);
647       }
649       return "<div class=\"picture\">$picture</div>";
650     }
651   }
655  * Theme a user page
656  * @param $account the user object
657  * @param $fields a multidimensional array for the fields, in the form of array (
658  *   'category1' => array(item_array1, item_array2), 'category2' => array(item_array3,
659  *    .. etc.). Item arrays are formatted as array(array('title' => 'item title',
660  * 'value' => 'item value', 'class' => 'class-name'), ... etc.). Module names are incorporated
661  * into the CSS class.
663  * @ingroup themeable
664  */
665 function theme_user_profile($account, $fields) {
666   $output = '<div class="profile">';
667   $output .= theme('user_picture', $account);
668   foreach ($fields as $category => $items) {
669     if (strlen($category) > 0) {
670       $output .= '<h2 class="title">'. check_plain($category) .'</h2>';
671     }
672     $output .= '<dl>';
673     foreach ($items as $item) {
674       if (isset($item['title'])) {
675         $output .= '<dt class="'. $item['class'] .'">'. $item['title'] .'</dt>';
676       }
677       $output .= '<dd class="'. $item['class'] .'">'. $item['value'] .'</dd>';
678     }
679     $output .= '</dl>';
680   }
681   $output .= '</div>';
683   return $output;
687  * Make a list of users.
688  * @param $items an array with user objects. Should contain at least the name and uid
690  * @ingroup themeable
691  */
692 function theme_user_list($users, $title = NULL) {
693   if (!empty($users)) {
694     foreach ($users as $user) {
695       $items[] = theme('username', $user);
696     }
697   }
698   return theme('item_list', $items, $title);
702  * Implementation of hook_menu().
703  */
704 function user_menu($may_cache) {
705   global $user;
707   $items = array();
709   $admin_access = user_access('administer users');
710   $access_access = user_access('administer access control');
711   $view_access = user_access('access user profiles');
713   if ($may_cache) {
714     $items[] = array('path' => 'user', 'title' => t('User account'),
715       'callback' => 'drupal_get_form', 'callback arguments' => array('user_login'),
716       'access' => !$user->uid, 'type' => MENU_CALLBACK);
718     $items[] = array('path' => 'user/autocomplete', 'title' => t('User autocomplete'),
719       'callback' => 'user_autocomplete', 'access' => $view_access, 'type' => MENU_CALLBACK);
721     // Registration and login pages.
722     $items[] = array('path' => 'user/login', 'title' => t('Log in'),
723       'callback' => 'drupal_get_form', 'callback arguments' => array('user_login'),
724       'access' => !$user->uid, 'type' => MENU_DEFAULT_LOCAL_TASK);
725     $items[] = array('path' => 'user/register', 'title' => t('Create new account'),
726       'callback' => 'drupal_get_form', 'callback arguments' => array('user_register'), 'access' => !$user->uid && variable_get('user_register', 1), 'type' => MENU_LOCAL_TASK);
727     $items[] = array('path' => 'user/password', 'title' => t('Request new password'),
728       'callback' => 'drupal_get_form', 'callback arguments' => array('user_pass'), 'access' => !$user->uid, 'type' => MENU_LOCAL_TASK);
729     $items[] = array('path' => 'user/reset', 'title' => t('Reset password'),
730       'callback' => 'drupal_get_form', 'callback arguments' => array('user_pass_reset'), 'access' => TRUE, 'type' => MENU_CALLBACK);
731     $items[] = array('path' => 'user/help', 'title' => t('Help'),
732       'callback' => 'user_help_page', 'type' => MENU_CALLBACK);
734     // Admin user pages
735     $items[] = array('path' => 'admin/user',
736       'title' => t('User management'),
737       'description' => t('Manage your site\'s users, groups and access to site features.'),
738       'position' => 'left',
739       'callback' => 'system_admin_menu_block_page',
740       'access' => user_access('administer site configuration'),
741     );
742     $items[] = array('path' => 'admin/user/user', 'title' => t('Users'),
743       'description' => t('List, add, and edit users.'),
744       'callback' => 'user_admin', 'callback arguments' => array('list'), 'access' => $admin_access);
745     $items[] = array('path' => 'admin/user/user/list', 'title' => t('List'),
746       'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
747     $items[] = array('path' => 'admin/user/user/create', 'title' => t('Add user'),
748       'callback' => 'user_admin', 'callback arguments' => array('create'), 'access' => $admin_access,
749       'type' => MENU_LOCAL_TASK);
750     $items[] = array('path' => 'admin/user/settings', 'title' => t('User settings'),
751       'description' => t('Configure default behavior of users, including registration requirements, e-mails, and user pictures.'),
752       'callback' => 'drupal_get_form', 'callback arguments' => array('user_admin_settings'));
754     // Admin access pages
755     $items[] = array('path' => 'admin/user/access', 'title' => t('Access control'),
756       'description' => t('Determine access to features by selecting permissions for roles.'),
757       'callback' => 'drupal_get_form', 'callback arguments' => array('user_admin_perm'), 'access' => $access_access);
758     $items[] = array('path' => 'admin/user/roles', 'title' => t('Roles'),
759       'description' => t('List, edit, or add user roles.'),
760       'callback' => 'drupal_get_form', 'callback arguments' => array('user_admin_new_role'), 'access' => $access_access,
761       'type' => MENU_NORMAL_ITEM);
762     $items[] = array('path' => 'admin/user/roles/edit', 'title' => t('Edit role'),
763        'callback' => 'drupal_get_form', 'callback arguments' => array('user_admin_role'), 'access' => $access_access,
764       'type' => MENU_CALLBACK);
765     $items[] = array('path' => 'admin/user/rules', 'title' => t('Access rules'),
766       'description' => t('List and create rules to disallow usernames, e-mail addresses, and IP addresses.'),
767       'callback' => 'user_admin_access', 'access' => $access_access);
768     $items[] = array('path' => 'admin/user/rules/list', 'title' => t('List'),
769       'access' => $access_access, 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
770     $items[] = array('path' => 'admin/user/rules/add', 'title' => t('Add rule'),
771       'callback' => 'user_admin_access_add', 'access' => $access_access,
772       'type' => MENU_LOCAL_TASK);
773     $items[] = array('path' => 'admin/user/rules/check', 'title' => t('Check rules'),
774       'callback' => 'user_admin_access_check', 'access' => $access_access,
775       'type' => MENU_LOCAL_TASK);
776     $items[] = array('path' => 'admin/user/rules/edit', 'title' => t('Edit rule'),
777       'callback' => 'user_admin_access_edit', 'access' => $access_access,
778       'type' => MENU_CALLBACK);
779     $items[] = array('path' => 'admin/user/rules/delete', 'title' => t('Delete rule'),
780       'callback' => 'drupal_get_form', 'callback arguments' => array('user_admin_access_delete_confirm'),
781       'access' => $access_access, 'type' => MENU_CALLBACK);
783     if (module_exists('search')) {
784       $items[] = array('path' => 'admin/user/search', 'title' => t('Search users'),
785         'description' => t('Search users by name.'),
786         'callback' => 'user_admin', 'callback arguments' => array('search'), 'access' => $admin_access,
787         'type' => MENU_NORMAL_ITEM);
788     }
790     // Your personal page
791     if ($user->uid) {
792       $items[] = array('path' => 'user/'. $user->uid, 'title' => t('My account'),
793         'callback' => 'user_view', 'callback arguments' => array(arg(1)), 'access' => TRUE,
794         'type' => MENU_DYNAMIC_ITEM);
795     }
797     $items[] = array('path' => 'logout', 'title' => t('Log out'),
798       'access' => $user->uid,
799       'callback' => 'user_logout',
800       'weight' => 10);
801   }
802   else {
803     // Add the CSS for this module. We put this in !$may_cache so it is only
804     // added once per request.
805     drupal_add_css(drupal_get_path('module', 'user') .'/user.css', 'module');
806     if ($_GET['q'] == 'user' && $user->uid) {
807       // We want to make the current user's profile accessible without knowing
808       // their uid, so just linking to /user is enough.
809       drupal_goto('user/'. $user->uid);
810     }
812     if (arg(0) == 'user' && is_numeric(arg(1)) && arg(1) > 0) {
813       $account = user_load(array('uid' => arg(1)));
815       if ($user !== FALSE) {
816         // Always let a user view their own account
817         $view_access |= $user->uid == arg(1);
818         // Only admins can view blocked accounts
819         $view_access &= $account->status || $admin_access;
821         $items[] = array('path' => 'user/'. arg(1), 'title' => t('User'),
822           'type' => MENU_CALLBACK, 'callback' => 'user_view',
823           'callback arguments' => array(arg(1)), 'access' => $view_access);
825         $items[] = array('path' => 'user/'. arg(1) .'/view', 'title' => t('View'),
826           'access' => $view_access, 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
828         $items[] = array('path' => 'user/'. arg(1) .'/edit', 'title' => t('Edit'),
829           'callback' => 'drupal_get_form', 'callback arguments' => array('user_edit'),
830           'access' => $admin_access || $user->uid == arg(1), 'type' => MENU_LOCAL_TASK);
831         $items[] = array('path' => 'user/'. arg(1) .'/delete', 'title' => t('Delete'),
832           'callback' => 'user_edit', 'access' => $admin_access,
833           'type' => MENU_CALLBACK);
835         if (arg(2) == 'edit') {
836           if (($categories = _user_categories($account)) && (count($categories) > 1)) {
837             foreach ($categories as $key => $category) {
838               $items[] = array(
839                 'path' => 'user/'. arg(1) .'/edit/'. $category['name'],
840                 'title' => $category['title'],
841                 'type' => $category['name'] == 'account' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
842                 'weight' => $category['weight'],
843                 'access' => ($admin_access || $user->uid == arg(1)));
844             }
845           }
846         }
847       }
848     }
849   }
851   return $items;
855  * Accepts an user object, $account, or a DA name and returns an associative
856  * array of modules and DA names. Called at external login.
857  */
858 function user_get_authmaps($authname = NULL) {
859   $result = db_query("SELECT authname, module FROM {authmap} WHERE authname = '%s'", $authname);
860   if (db_num_rows($result) > 0) {
861     while ($authmap = db_fetch_object($result)) {
862       $authmaps[$authmap->module] = $authmap->authname;
863     }
864     return $authmaps;
865   }
866   else {
867     return 0;
868   }
871 function user_set_authmaps($account, $authmaps) {
872   foreach ($authmaps as $key => $value) {
873     $module = explode('_', $key, 2);
874     if ($value) {
875       db_query("UPDATE {authmap} SET authname = '%s' WHERE uid = %d AND module = '%s'", $value, $account->uid, $module[1]);
876       if (!db_affected_rows()) {
877         db_query("INSERT INTO {authmap} (authname, uid, module) VALUES ('%s', %d, '%s')", $value, $account->uid, $module[1]);
878       }
879     }
880     else {
881       db_query("DELETE FROM {authmap} WHERE uid = %d AND module = '%s'", $account->uid, $module[1]);
882     }
883   }
886 function user_auth_help_links() {
887   $links = array();
888   foreach (module_list() as $module) {
889     if (module_hook($module, 'auth')) {
890       $links[] = l(module_invoke($module, 'info', 'name'), 'user/help', array(), NULL, $module);
891     }
892   }
893   return $links;
896 /*** User features *********************************************************/
900 function user_login($msg = '') {
901   global $user;
903   // If we are already logged on, go to the user page instead.
904   if ($user->uid) {
905     drupal_goto('user/'. $user->uid);
906   }
908   // Display login form:
909   if ($msg) {
910     $form['message'] = array('#value' => '<p>'. check_plain($msg) .'</p>');
911   }
912   $form['name'] = array('#type' => 'textfield',
913     '#title' => t('Username'),
914     '#size' => 60,
915     '#maxlength' => USERNAME_MAX_LENGTH,
916     '#required' => TRUE,
917     '#attributes' => array('tabindex' => '1'),
918   );
919   if (variable_get('drupal_authentication_service', FALSE) && count(user_auth_help_links()) > 0) {
920     $form['name']['#description'] = t('Enter your @s username, or an ID from one of our affiliates: !a.', array('@s' => variable_get('site_name', 'Drupal'), '!a' => implode(', ', user_auth_help_links())));
921   }
922   else {
923     $form['name']['#description'] = t('Enter your @s username.', array('@s' => variable_get('site_name', 'Drupal')));
924   }
925   $form['pass'] = array('#type' => 'password',
926     '#title' => t('Password'),
927     '#description' => t('Enter the password that accompanies your username.'),
928     '#required' => TRUE,
929     '#attributes' => array('tabindex' => '2'),
930   );
931   $form['submit'] = array('#type' => 'submit', '#value' => t('Log in'), '#weight' => 2, '#attributes' => array('tabindex' => '3'));
933   return $form;
936 function user_login_validate($form_id, $form_values) {
937   if ($form_values['name']) {
938     if (user_is_blocked($form_values['name'])) {
939       // blocked in user administration
940       form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_values['name'])));
941     }
942     else if (drupal_is_denied('user', $form_values['name'])) {
943       // denied by access controls
944       form_set_error('name', t('The name %name is a reserved username.', array('%name' => $form_values['name'])));
945     }
946     else if ($form_values['pass']) {
947       $user = user_authenticate($form_values['name'], trim($form_values['pass']));
949       if (!$user->uid) {
950         form_set_error('name', t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password'))));
951         watchdog('user', t('Login attempt failed for %user.', array('%user' => $form_values['name'])));
952       }
953     }
954   }
957 function user_login_submit($form_id, $form_values) {
958   global $user;
959   if ($user->uid) {
960     // To handle the edge case where this function is called during a
961     // bootstrap, check for the existence of t().
962     if (function_exists('t')) {
963       $message = t('Session opened for %name.', array('%name' => $user->name));
964     }
965     else {
966        $message = "Session opened for ". check_plain($user->name);
967     }
968     watchdog('user', $message);
970     // Update the user table timestamp noting user has logged in.
971     db_query("UPDATE {users} SET login = %d WHERE uid = %d", time(), $user->uid);
973     user_module_invoke('login', $form_values, $user);
975     sess_regenerate();
976     return 'user/'. $user->uid;
977   }
980 function user_authenticate($name, $pass) {
981   global $user;
983   // Try to log in the user locally. Don't set $user unless successful.
984   if ($account = user_load(array('name' => $name, 'pass' => $pass, 'status' => 1))) {
985     $user = $account;
986     return $user;
987   }
989   // Strip name and server from ID:
990   if ($server = strrchr($name, '@')) {
991     $name = substr($name, 0, strlen($name) - strlen($server));
992     $server = substr($server, 1);
993   }
995   // When possible, determine corresponding external auth source. Invoke
996   // source, and log in user if successful:
997   if ($server && ($result = user_get_authmaps("$name@$server"))) {
998     if (module_invoke(key($result), 'auth', $name, $pass, $server)) {
999       $user = user_external_load("$name@$server");
1000       watchdog('user', t('External load by %user using module %module.', array('%user' => $name .'@'. $server, '%module' => key($result))));
1001     }
1002   }
1004   // Try each external authentication source in series. Register user if
1005   // successful.
1006   else {
1007     foreach (module_implements('auth') as $module) {
1008       if (module_invoke($module, 'auth', $name, $pass, $server)) {
1009         if ($server) {
1010           $name .= '@'. $server;
1011         }
1012         $user = user_load(array('name' => $name));
1013         if (!$user->uid) { // Register this new user.
1014           $userinfo = array('name' => $name, 'pass' => user_password(), 'init' => $name, 'status' => 1);
1015           if ($server) {
1016             $userinfo["authname_$module"] = $name;
1017           }
1018           $user = user_save('', $userinfo);
1019           watchdog('user', t('New external user: %user using module %module.', array('%user' => $name, '%module' => $module)), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $user->uid .'/edit'));
1020           break;
1021         }
1022       }
1023     }
1024   }
1025   return $user;
1029  * Menu callback; logs the current user out, and redirects to the home page.
1030  */
1031 function user_logout() {
1032   global $user;
1034   watchdog('user', t('Session closed for %name.', array('%name' => $user->name)));
1036   // Destroy the current session:
1037   session_destroy();
1038   module_invoke_all('user', 'logout', NULL, $user);
1040   // Load the anonymous user
1041   $user = drupal_anonymous_user();
1043   drupal_goto();
1046 function user_pass() {
1048   // Display form:
1049   $form['name'] = array('#type' => 'textfield',
1050     '#title' => t('Username or e-mail address'),
1051     '#size' => 60,
1052     '#maxlength' => max(USERNAME_MAX_LENGTH, EMAIL_MAX_LENGTH),
1053     '#required' => TRUE,
1054   );
1055   $form['submit'] = array('#type' => 'submit',
1056     '#value' => t('E-mail new password'),
1057     '#weight' => 2,
1058   );
1059   return $form;
1062 function user_pass_validate($form_id, $form_values) {
1063   $name = $form_values['name'];
1064   $account = user_load(array('mail' => $name, 'status' => 1));
1065   if (!$account) {
1066     $account = user_load(array('name' => $name, 'status' => 1));
1067   }
1068   if ($account->uid) {
1069     form_set_value(array('#parents' => array('account')), $account);
1070   }
1071   else {
1072     form_set_error('name', t('Sorry, %name is not recognized as a user name or an email address.', array('%name' => $name)));
1073   }
1076 function user_pass_submit($form_id, $form_values) {
1077   global $base_url;
1079   $account = $form_values['account'];
1080   $from = variable_get('site_mail', ini_get('sendmail_from'));
1082   // Mail one time login URL and instructions.
1083   $variables = array('!username' => $account->name, '!site' => variable_get('site_name', 'Drupal'), '!login_url' => user_pass_reset_url($account), '!uri' => $base_url, '!uri_brief' => substr($base_url, strlen('http://')), '!mailto' => $account->mail, '!date' => format_date(time()), '!login_uri' => url('user', NULL, NULL, TRUE), '!edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE));
1084   $subject = _user_mail_text('pass_subject', $variables);
1085   $body = _user_mail_text('pass_body', $variables);
1086   $mail_success = drupal_mail('user-pass', $account->mail, $subject, $body, $from);
1088   if ($mail_success) {
1089     watchdog('user', t('Password reset instructions mailed to %name at %email.', array('%name' => $account->name, '%email' => $account->mail)));
1090     drupal_set_message(t('Further instructions have been sent to your e-mail address.'));
1091   }
1092   else {
1093     watchdog('user', t('Error mailing password reset instructions to %name at %email.', array('%name' => $account->name, '%email' => $account->mail)), WATCHDOG_ERROR);
1094     drupal_set_message(t('Unable to send mail. Please contact the site admin.'));
1095   }
1096   return 'user';
1100  * Menu callback; process one time login link and redirects to the user page on success.
1101  */
1102 function user_pass_reset($uid, $timestamp, $hashed_pass, $action = NULL) {
1103   global $user;
1105   // Check if the user is already logged in. The back button is often the culprit here.
1106   if ($user->uid) {
1107     drupal_set_message(t('You have already used this one-time login link. It is not necessary to use this link to login anymore. You are already logged in.'));
1108     drupal_goto();
1109   }
1110   else {
1111     // Time out, in seconds, until login URL expires. 24 hours = 86400 seconds.
1112     $timeout = 86400;
1113     $current = time();
1114     // Some redundant checks for extra security ?
1115     if ($timestamp < $current && $account = user_load(array('uid' => $uid, 'status' => 1)) ) {
1116       // No time out for first time login.
1117       if ($account->login && $current - $timestamp > $timeout) {
1118         drupal_set_message(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'));
1119         drupal_goto('user/password');
1120       }
1121       else if ($account->uid && $timestamp > $account->login && $timestamp < $current && $hashed_pass == user_pass_rehash($account->pass, $timestamp, $account->login)) {
1122         // First stage is a confirmation form, then login
1123         if ($action == 'login') {
1124           watchdog('user', t('User %name used one-time login link at time %timestamp.', array('%name' => $account->name, '%timestamp' => $timestamp)));
1125           // Update the user table noting user has logged in.
1126           // And this also makes this hashed password a one-time-only login.
1127           db_query("UPDATE {users} SET login = %d WHERE uid = %d", time(), $account->uid);
1128           // Now we can set the new user.
1129           $user = $account;
1130           // And proceed with normal login, going to user page.
1131           $edit = array();
1132           user_module_invoke('login', $edit, $user);
1133           drupal_set_message(t('You have just used your one-time login link. It is no longer necessary to use this link to login. Please change your password.'));
1134           drupal_goto('user/'. $user->uid .'/edit');
1135         }
1136         else {
1137           $form['message'] = array('#value' => t('<p>This is a one-time login for %user_name and will expire on %expiration_date</p><p>Click on this button to login to the site and change your password.</p>', array('%user_name' => $account->name, '%expiration_date' => format_date($timestamp + $timeout))));
1138           $form['help'] = array('#value' => '<p>'. t('This login can be used only once.') .'</p>');
1139           $form['submit'] = array('#type' => 'submit', '#value' => t('Log in'));
1140           $form['#action'] = url("user/reset/$uid/$timestamp/$hashed_pass/login");
1141           return $form;
1142         }
1143       }
1144       else {
1145         drupal_set_message(t('You have tried to use a one-time login link which has either been used or is no longer valid. Please request a new one using the form below.'));
1146         drupal_goto('user/password');
1147       }
1148     }
1149     else {
1150       // Deny access, no more clues.
1151       // Everything will be in the watchdog's URL for the administrator to check.
1152       drupal_access_denied();
1153     }
1154   }
1157 function user_pass_reset_url($account) {
1158   $timestamp = time();
1159   return url("user/reset/$account->uid/$timestamp/".user_pass_rehash($account->pass, $timestamp, $account->login), NULL, NULL, TRUE);
1162 function user_pass_rehash($password, $timestamp, $login) {
1163   return md5($timestamp . $password . $login);
1166 function user_register() {
1167   global $user;
1169   $admin = user_access('administer users');
1171   // If we aren't admin but already logged on, go to the user page instead.
1172   if (!$admin && $user->uid) {
1173     drupal_goto('user/'. $user->uid);
1174   }
1176   $form = array();
1178   // Display the registration form.
1179   if (!$admin) {
1180     $form['user_registration_help'] = array('#value' => filter_xss_admin(variable_get('user_registration_help', '')));
1181   }
1182   $affiliates = user_auth_help_links();
1183   if (!$admin && count($affiliates) > 0) {
1184     $affiliates = implode(', ', $affiliates);
1185     $form['affiliates'] = array('#value' => '<p>'. t('Note: if you have an account with one of our affiliates (!s), you may <a href="@login_uri">login now</a> instead of registering.', array('!s' => $affiliates, '@login_uri' => url('user'))) .'</p>');
1186   }
1187   // Merge in the default user edit fields.
1188   $form = array_merge($form, user_edit_form(NULL, NULL, TRUE));
1189   if ($admin) {
1190     $form['account']['notify'] = array(
1191      '#type' => 'checkbox',
1192      '#title' => t('Notify user of new account')
1193     );
1194     // Redirect back to page which initiated the create request; usually admin/user/user/create
1195     $form['destination'] = array('#type' => 'hidden', '#value' => $_GET['q']);
1196   }
1198   // Create a dummy variable for pass-by-reference parameters.
1199   $null = NULL;
1200   $extra = _user_forms($null, NULL, NULL, 'register');
1202   // Remove form_group around default fields if there are no other groups.
1203   if (!$extra) {
1204     $form['name'] = $form['account']['name'];
1205     $form['mail'] = $form['account']['mail'];
1206     $form['pass'] = $form['account']['pass'];
1207     $form['status'] = $form['account']['status'];
1208     $form['roles'] = $form['account']['roles'];
1209     $form['notify'] = $form['account']['notify'];
1210     unset($form['account']);
1211   }
1212   else {
1213     $form = array_merge($form, $extra);
1214   }
1215   $form['submit'] = array('#type' => 'submit', '#value' => t('Create new account'), '#weight' => 30);
1217   return $form;
1220 function user_register_validate($form_id, $form_values) {
1221   user_module_invoke('validate', $form_values, $form_values, 'account');
1224 function user_register_submit($form_id, $form_values) {
1225   global $base_url;
1226   $admin = user_access('administer users');
1228   $mail = $form_values['mail'];
1229   $name = $form_values['name'];
1230   if (!variable_get('user_email_verification', TRUE) || $admin) {
1231     $pass = $form_values['pass'];
1232   }
1233   else {
1234     $pass = user_password();
1235   };
1236   $notify = $form_values['notify'];
1237   $from = variable_get('site_mail', ini_get('sendmail_from'));
1238   if (isset($form_values['roles'])) {
1239     $roles = array_filter($form_values['roles']);     // Remove unset roles
1240   }
1242   if (!$admin && array_intersect(array_keys($form_values), array('uid', 'roles', 'init', 'session', 'status'))) {
1243     watchdog('security', t('Detected malicious attempt to alter protected user fields.'), WATCHDOG_WARNING);
1244     return 'user/register';
1245   }
1246   //the unset below is needed to prevent these form values from being saved as user data
1247   unset($form_values['form_token'], $form_values['submit'], $form_values['op'], $form_values['notify'], $form_values['form_id'], $form_values['affiliates'], $form_values['destination']);
1249   $merge_data = array('pass' => $pass, 'init' => $mail, 'roles' => $roles);
1250   if (!$admin) {
1251     // Set the user's status because it was not displayed in the form.
1252     $merge_data['status'] = variable_get('user_register', 1) == 1;
1253   }
1254   $account = user_save('', array_merge($form_values, $merge_data));
1255   watchdog('user', t('New user: %name %email.', array('%name' => $name, '%email' => '<'. $mail .'>')), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit'));
1257   $variables = array('!username' => $name, '!site' => variable_get('site_name', 'Drupal'), '!password' => $pass, '!uri' => $base_url, '!uri_brief' => substr($base_url, strlen('http://')), '!mailto' => $mail, '!date' => format_date(time()), '!login_uri' => url('user', NULL, NULL, TRUE), '!edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE), '!login_url' => user_pass_reset_url($account));
1259   // The first user may login immediately, and receives a customized welcome e-mail.
1260   if ($account->uid == 1) {
1261     drupal_mail('user-register-admin', $mail, t('Drupal user account details for !s', array('!s' => $name)), strtr(t("!username,\n\nYou may now login to !uri using the following username and password:\n\n  username: !username\n  password: !password\n\n!edit_uri\n\n--drupal"), $variables), $from);
1262     drupal_set_message(t('<p>Welcome to Drupal. You are user #1, which gives you full and immediate access. All future registrants will receive their passwords via e-mail, so please make sure your website e-mail address is set properly under the general settings on the <a href="@settings">site information settings page</a>.</p><p> Your password is <strong>%pass</strong>. You may change your password below.</p>', array('%pass' => $pass, '@settings' => url('admin/settings/site-information'))));
1263     user_authenticate($account->name, trim($pass));
1265     return 'user/1/edit';
1266   }
1267   else {
1268     if ($admin && !$notify) {
1269       drupal_set_message(t('Created a new user account. No e-mail has been sent.'));
1270     }
1271     else if (!variable_get('user_email_verification', TRUE) && $account->status && !$admin) {
1272       // No e-mail verification is required, create new user account, and login user immediately.
1273       $subject = _user_mail_text('welcome_subject', $variables);
1274       $body = _user_mail_text('welcome_body', $variables);
1275       drupal_mail('user-register-welcome', $mail, $subject, $body, $from);
1276       user_authenticate($account->name, trim($pass));
1277       $edit = array();
1278       user_module_invoke('login', $edit, $account);
1279       return '';
1280     }
1281     else if ($account->status || $notify) {
1282       // Create new user account, no administrator approval required.
1283       $subject = $notify ? _user_mail_text('admin_subject', $variables) : _user_mail_text('welcome_subject', $variables);
1284       $body = $notify ? _user_mail_text('admin_body', $variables) : _user_mail_text('welcome_body', $variables);
1286       drupal_mail(($notify ? 'user-register-notify' : 'user-register-welcome'), $mail, $subject, $body, $from);
1288       if ($notify) {
1289         drupal_set_message(t('Password and further instructions have been e-mailed to the new user %user.', array('%user' => $name)));
1290       }
1291       else {
1292         drupal_set_message(t('Your password and further instructions have been sent to your e-mail address.'));
1293         return '';
1294       }
1295     }
1296     else {
1297       // Create new user account, administrator approval required.
1298       $subject = _user_mail_text('approval_subject', $variables);
1299       $body = _user_mail_text('approval_body', $variables);
1301       drupal_mail('user-register-approval-user', $mail, $subject, $body, $from);
1302       drupal_mail('user-register-approval-admin', $from, $subject, t("!username has applied for an account.\n\n!edit_uri", $variables), $from);
1303       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, your password and further instructions have been sent to your e-mail address.'));
1304       return '';
1305     }
1306   }
1309 function user_edit_form($uid, $edit, $register = FALSE) {
1310   $admin = user_access('administer users');
1312   // Account information:
1313   $form['account'] = array('#type' => 'fieldset',
1314     '#title' => t('Account information'),
1315   );
1316   if (user_access('change own username') || $admin || $register) {
1317     $form['account']['name'] = array('#type' => 'textfield',
1318       '#title' => t('Username'),
1319       '#default_value' => $edit['name'],
1320       '#maxlength' => USERNAME_MAX_LENGTH,
1321       '#description' => t('Your preferred username; punctuation is not allowed except for periods, hyphens, and underscores.'),
1322       '#required' => TRUE,
1323     );
1324   }
1325   $form['account']['mail'] = array('#type' => 'textfield',
1326     '#title' => t('E-mail address'),
1327     '#default_value' => $edit['mail'],
1328     '#maxlength' => EMAIL_MAX_LENGTH,
1329     '#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.'),
1330     '#required' => TRUE,
1331   );
1332   if (!$register) {
1333     $form['account']['pass'] = array('#type' => 'password_confirm',
1334       '#description' => t('To change the current user password, enter the new password in both fields.'),
1335       '#size' => 25,
1336     );
1337   }
1338   elseif (!variable_get('user_email_verification', TRUE) || $admin) {
1339     $form['account']['pass'] = array(
1340       '#type' => 'password_confirm',
1341       '#description' => t('Provide a password for the new account in both fields.'),
1342       '#required' => TRUE,
1343       '#size' => 25,
1344     );
1345   }
1346   if ($admin) {
1347     $form['account']['status'] = array('#type' => 'radios', '#title' => t('Status'), '#default_value' => isset($edit['status']) ? $edit['status'] : 1, '#options' => array(t('Blocked'), t('Active')));
1348   }
1349   if (user_access('administer access control')) {
1350     $roles = user_roles(1);
1351     unset($roles[DRUPAL_AUTHENTICATED_RID]);
1352     if ($roles) {
1353       $form['account']['roles'] = array('#type' => 'checkboxes', '#title' => t('Roles'), '#default_value' => array_keys((array)$edit['roles']), '#options' => $roles, '#description' => t('The user receives the combined permissions of the %au role, and all roles selected here.', array('%au' => t('authenticated user'))));
1354     }
1355   }
1357   // Picture/avatar:
1358   if (variable_get('user_pictures', 0) && !$register) {
1359     $form['picture'] = array('#type' => 'fieldset', '#title' => t('Picture'), '#weight' => 1);
1360     $picture = theme('user_picture', (object)$edit);
1361     if ($picture) {
1362       $form['picture']['current_picture'] = array('#value' => $picture);
1363       $form['picture']['picture_delete'] = array('#type' => 'checkbox', '#title' => t('Delete picture'), '#description' => t('Check this box to delete your current picture.'));
1364     }
1365     else {
1366       $form['picture']['picture_delete'] = array('#type' => 'hidden');
1367     }
1368     $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', ''));
1369   }
1371   return $form;
1374 function _user_edit_validate($uid, &$edit) {
1375   $user = user_load(array('uid' => $uid));
1376   // Validate the username:
1377   if (user_access('change own username') || user_access('administer users') || arg(1) == 'register') {
1378     if ($error = user_validate_name($edit['name'])) {
1379       form_set_error('name', $error);
1380     }
1381     else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(name) = LOWER('%s')", $uid, $edit['name'])) > 0) {
1382       form_set_error('name', t('The name %name is already taken.', array('%name' => $edit['name'])));
1383     }
1384     else if (drupal_is_denied('user', $edit['name'])) {
1385       form_set_error('name', t('The name %name has been denied access.', array('%name' => $edit['name'])));
1386     }
1387   }
1389   // Validate the e-mail address:
1390   if ($error = user_validate_mail($edit['mail'])) {
1391     form_set_error('mail', $error);
1392   }
1393   else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(mail) = LOWER('%s')", $uid, $edit['mail'])) > 0) {
1394     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'))));
1395   }
1396   else if (drupal_is_denied('mail', $edit['mail'])) {
1397     form_set_error('mail', t('The e-mail address %email has been denied access.', array('%email' => $edit['mail'])));
1398   }
1400   // If required, validate the uploaded picture.
1401   if ($file = file_check_upload('picture_upload')) {
1402     user_validate_picture($file, $edit, $user);
1403   }
1406 function _user_edit_submit($uid, &$edit) {
1407   $user = user_load(array('uid' => $uid));
1408   // Delete picture if requested, and if no replacement picture was given.
1409   if ($edit['picture_delete']) {
1410     if ($user->picture && file_exists($user->picture)) {
1411       file_delete($user->picture);
1412     }
1413     $edit['picture'] = '';
1414   }
1415   if (isset($edit['roles'])) {
1416     $edit['roles'] = array_filter($edit['roles']);
1417   }
1420 function user_edit($category = 'account') {
1421   global $user;
1423   $account = user_load(array('uid' => arg(1)));
1424   if ($account === FALSE) {
1425     drupal_set_message(t('The account does not exist or has already been deleted.'));
1426     drupal_goto('admin/user/user');
1427   }
1429   $edit = $_POST['op'] ? $_POST : (array)$account;
1431   if (arg(2) == 'delete') {
1432     return drupal_get_form('user_confirm_delete', $account->name, $account->uid);
1433   }
1434   else if ($_POST['op'] == t('Delete')) {
1435     if ($_REQUEST['destination']) {
1436       $destination = drupal_get_destination();
1437       unset($_REQUEST['destination']);
1438     }
1439     // Note: we redirect from user/uid/edit to user/uid/delete to make the tabs disappear.
1440     drupal_goto("user/$account->uid/delete", $destination);
1441   }
1443   $form = _user_forms($edit, $account, $category);
1444   $form['_category'] = array('#type' => 'value', '#value' => $category);
1445   $form['_account'] = array('#type' => 'value', '#value' => $account);
1446   $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'), '#weight' => 30);
1447   if (user_access('administer users')) {
1448     $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'), '#weight' => 31);
1449   }
1450   $form['#attributes']['enctype'] = 'multipart/form-data';
1452   drupal_set_title(check_plain($account->name));
1453   return $form;
1456 function user_confirm_delete($name, $uid) {
1457   $form['uid'] = array('#type' => 'value', '#value' => $uid);
1458   return confirm_form($form,
1459     t('Are you sure you want to delete the account %name?', array('%name' => $name)),
1460     'user/'. $uid,
1461     t('All submissions made by this user will be attributed to the anonymous account. This action cannot be undone.'),
1462     t('Delete'), t('Cancel'));
1465 function user_confirm_delete_submit($form_id, $form_values) {
1466   $account = user_load(array('uid' => $form_values['uid']));
1467   user_delete((array) $account, $form_values['uid']);
1468   return 'admin/user/user';
1472  * Delete a user.
1474  * @param $edit An array of submitted form values.
1475  * @param $uid The user ID of the user to delete.
1476  */
1477 function user_delete($edit, $uid) {
1478   $account = user_load(array('uid' => $uid));
1479   sess_destroy_uid($uid);
1480   db_query('DELETE FROM {users} WHERE uid = %d', $uid);
1481   db_query('DELETE FROM {users_roles} WHERE uid = %d', $uid);
1482   db_query('DELETE FROM {authmap} WHERE uid = %d', $uid);
1483   $array = array('%name' => $account->name, '%email' => '<'. $account->mail .'>');
1484   watchdog('user', t('Deleted user: %name %email.', $array), WATCHDOG_NOTICE);
1485   drupal_set_message(t('%name has been deleted.', $array));
1486   module_invoke_all('user', 'delete', $edit, $account);
1489 function user_edit_validate($form_id, $form_values) {
1490   user_module_invoke('validate', $form_values, $form_values['_account'], $form_values['_category']);
1491   // Validate input to ensure that non-privileged users can't alter protected data.
1492   if ((!user_access('administer users') && array_intersect(array_keys($form_values), array('uid', 'init', 'session'))) || (!user_access('administer access control') && isset($form_values['roles']))) {
1493     $message = t('Detected malicious attempt to alter protected user fields.');
1494     watchdog('security', $message, WATCHDOG_WARNING);
1495     // set this to a value type field
1496     form_set_error('category', $message);
1497   }
1500 function user_edit_submit($form_id, $form_values) {
1501   $account = $form_values['_account'];
1502   $category = $form_values['_category'];
1503   unset($form_values['_account'], $form_values['op'], $form_values['submit'], $form_values['delete'], $form_values['form_token'], $form_values['form_id'], $form_values['_category']);
1504   user_module_invoke('submit', $form_values, $account, $category);
1505   user_save($account, $form_values, $category);
1507   // Delete that user's menu cache:
1508   cache_clear_all($account->uid .':', 'cache_menu', TRUE);
1510   // Clear the page cache because pages can contain usernames and/or profile information:
1511   cache_clear_all();
1513   drupal_set_message(t('The changes have been saved.'));
1514   return 'user/'. $account->uid;
1517 function user_view($uid = 0) {
1518   global $user;
1520   $account = user_load(array('uid' => $uid));
1521   if ($account === FALSE || ($account->access == 0 && !user_access('administer users'))) {
1522     return drupal_not_found();
1523   }
1524   // Retrieve and merge all profile fields:
1525   $fields = array();
1526   foreach (module_list() as $module) {
1527     if ($data = module_invoke($module, 'user', 'view', '', $account)) {
1528       foreach ($data as $category => $items) {
1529         foreach ($items as $key => $item) {
1530           $item['class'] = "$module-". $item['class'];
1531           $fields[$category][$key] = $item;
1532         }
1533       }
1534     }
1535   }
1537   // Let modules change the returned fields - useful for personal privacy
1538   // controls. Since modules communicate changes by reference, we cannot use
1539   // module_invoke_all().
1540   foreach (module_implements('profile_alter') as $module) {
1541     $function = $module .'_profile_alter';
1542     $function($account, $fields);
1543   }
1545   drupal_set_title(check_plain($account->name));
1546   return theme('user_profile', $account, $fields);
1549 /*** Administrative features ***********************************************/
1551 function _user_mail_text($messageid, $variables = array()) {
1553   // Check if an admin setting overrides the default string.
1554   if ($admin_setting = variable_get('user_mail_'. $messageid, FALSE)) {
1555     return strtr($admin_setting, $variables);
1556   }
1557   // No override, return with default strings.
1558   else {
1559     switch ($messageid) {
1560       case 'welcome_subject':
1561         return t('Account details for !username at !site', $variables);
1562       case 'welcome_body':
1563         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);
1564       case 'admin_subject':
1565         return t('An administrator created an account for you at !site', $variables);
1566       case 'admin_body':
1567         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);
1568       case 'approval_subject':
1569         return t('Account details for !username at !site (pending admin approval)', $variables);
1570       case 'approval_body':
1571         return t("!username,\n\nThank you for registering at !site. Your application for an account is currently pending approval. Once it has been granted, you may 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 may wish to change your password at !edit_uri\n\n\n--  !site team", $variables);
1572       case 'pass_subject':
1573         return t('Replacement login information for !username at !site', $variables);
1574       case 'pass_body':
1575         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 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);
1576     }
1577   }
1580 function user_admin_check_user() {
1581   $form['user'] = array('#type' => 'fieldset', '#title' => t('Username'));
1582   $form['user']['test'] = array('#type' => 'textfield', '#title' => '', '#description' => t('Enter a username to check if it will be denied or allowed.'), '#size' => 30, '#maxlength' => USERNAME_MAX_LENGTH);
1583   $form['user']['type'] = array('#type' => 'hidden', '#value' => 'user');
1584   $form['user']['submit'] = array('#type' => 'submit', '#value' => t('Check username'));
1585   $form['#base'] = 'user_admin_access_check';
1586   return $form;
1589 function user_admin_check_mail() {
1590   $form['mail'] = array('#type' => 'fieldset', '#title' => t('E-mail'));
1591   $form['mail']['test'] = array('#type' => 'textfield', '#title' => '', '#description' => t('Enter an e-mail address to check if it will be denied or allowed.'), '#size' => 30, '#maxlength' => EMAIL_MAX_LENGTH);
1592   $form['mail']['type'] = array('#type' => 'hidden', '#value' => 'mail');
1593   $form['mail']['submit'] = array('#type' => 'submit', '#value' => t('Check e-mail'));
1594   $form['#base'] = 'user_admin_access_check';
1595   return $form;
1598 function user_admin_check_host() {
1599   $form['host'] = array('#type' => 'fieldset', '#title' => t('Hostname'));
1600   $form['host']['test'] = array('#type' => 'textfield', '#title' => '', '#description' => t('Enter a hostname or IP address to check if it will be denied or allowed.'), '#size' => 30, '#maxlength' => 64);
1601   $form['host']['type'] = array('#type' => 'hidden', '#value' => 'host');
1602   $form['host']['submit'] = array('#type' => 'submit', '#value' => t('Check hostname'));
1603   $form['#base'] = 'user_admin_access_check';
1604   return $form;
1608  * Menu callback: check an access rule
1609  */
1610 function user_admin_access_check() {
1611   $output = drupal_get_form('user_admin_check_user');
1612   $output .= drupal_get_form('user_admin_check_mail');
1613   $output .= drupal_get_form('user_admin_check_host');
1614   return $output;
1617 function user_admin_access_check_validate($form_id, $form_values) {
1618   if (empty($form_values['test'])) {
1619     form_set_error($form_values['type'], t('No value entered. Please enter a test string and try again.'));
1620   }
1623 function user_admin_access_check_submit($form_id, $form_values) {
1624   switch ($form_values['type']) {
1625     case 'user':
1626       if (drupal_is_denied('user', $form_values['test'])) {
1627         drupal_set_message(t('The username %name is not allowed.', array('%name' => $form_values['test'])));
1628       }
1629       else {
1630         drupal_set_message(t('The username %name is allowed.', array('%name' => $form_values['test'])));
1631       }
1632       break;
1633     case 'mail':
1634       if (drupal_is_denied('mail', $form_values['test'])) {
1635         drupal_set_message(t('The e-mail address %mail is not allowed.', array('%mail' => $form_values['test'])));
1636       }
1637       else {
1638         drupal_set_message(t('The e-mail address %mail is allowed.', array('%mail' => $form_values['test'])));
1639       }
1640       break;
1641     case 'host':
1642       if (drupal_is_denied('host', $form_values['test'])) {
1643         drupal_set_message(t('The hostname %host is not allowed.', array('%host' => $form_values['test'])));
1644       }
1645       else {
1646         drupal_set_message(t('The hostname %host is allowed.', array('%host' => $form_values['test'])));
1647       }
1648       break;
1649     default:
1650       break;
1651   }
1655  * Menu callback: add an access rule
1656  */
1657 function user_admin_access_add($mask = NULL, $type = NULL) {
1658   if ($edit = $_POST) {
1659     if (!$edit['mask']) {
1660       form_set_error('mask', t('You must enter a mask.'));
1661     }
1662     else {
1663       $aid = db_next_id('{access}_aid');
1664       db_query("INSERT INTO {access} (aid, mask, type, status) VALUES ('%s', '%s', '%s', %d)", $aid, $edit['mask'], $edit['type'], $edit['status']);
1665       drupal_set_message(t('The access rule has been added.'));
1666       drupal_goto('admin/user/rules');
1667     }
1668   }
1669   else {
1670     $edit['mask'] = $mask;
1671     $edit['type'] = $type;
1672   }
1673   return drupal_get_form('user_admin_access_add_form', $edit, t('Add rule'));
1677  * Menu callback: delete an access rule
1678  */
1679 function user_admin_access_delete_confirm($aid = 0) {
1680   $access_types = array('user' => t('username'), 'mail' => t('e-mail'), 'host' => t('host'));
1681   $edit = db_fetch_object(db_query('SELECT aid, type, status, mask FROM {access} WHERE aid = %d', $aid));
1683   $form = array();
1684   $form['aid'] = array('#type' => 'hidden', '#value' => $aid);
1685   $output = confirm_form($form,
1686                   t('Are you sure you want to delete the @type rule for %rule?', array('@type' => $access_types[$edit->type], '%rule' => $edit->mask)),
1687                   'admin/user/rules',
1688                   t('This action cannot be undone.'),
1689                   t('Delete'),
1690                   t('Cancel'));
1691   return $output;
1694 function user_admin_access_delete_confirm_submit($form_id, $form_values) {
1695   db_query('DELETE FROM {access} WHERE aid = %d', $form_values['aid']);
1696   drupal_set_message(t('The access rule has been deleted.'));
1697   return 'admin/user/rules';
1701  * Menu callback: edit an access rule
1702  */
1703 function user_admin_access_edit($aid = 0) {
1704   if ($edit = $_POST) {
1705     if (!$edit['mask']) {
1706       form_set_error('mask', t('You must enter a mask.'));
1707     }
1708     else {
1709       db_query("UPDATE {access} SET mask = '%s', type = '%s', status = '%s' WHERE aid = %d", $edit['mask'], $edit['type'], $edit['status'], $aid);
1710       drupal_set_message(t('The access rule has been saved.'));
1711       drupal_goto('admin/user/rules');
1712     }
1713   }
1714   else {
1715     $edit = db_fetch_array(db_query('SELECT aid, type, status, mask FROM {access} WHERE aid = %d', $aid));
1716   }
1717   return drupal_get_form('user_admin_access_edit_form', $edit, t('Save rule'));
1720 function user_admin_access_form($edit, $submit) {
1721   $form['status'] = array(
1722     '#type' => 'radios',
1723     '#title' => t('Access type'),
1724     '#default_value' => $edit['status'],
1725     '#options' => array('1' => t('Allow'), '0' => t('Deny')),
1726   );
1727   $type_options = array('user' => t('Username'), 'mail' => t('E-mail'), 'host' => t('Host'));
1728   $form['type'] = array(
1729     '#type' => 'radios',
1730     '#title' => t('Rule type'),
1731     '#default_value' => (isset($type_options[$edit['type']]) ? $edit['type'] : 'user'),
1732     '#options' => $type_options,
1733   );
1734   $form['mask'] = array(
1735     '#type' => 'textfield',
1736     '#title' => t('Mask'),
1737     '#size' => 30,
1738     '#maxlength' => 64,
1739     '#default_value' => $edit['mask'],
1740     '#description' => '%: '. t('Matches any number of characters, even zero characters') .'.<br />_: '. t('Matches exactly one character.'),
1741     '#required' => TRUE,
1742   );
1743   $form['submit'] = array('#type' => 'submit', '#value' => $submit);
1745   return $form;
1749  * Menu callback: list all access rules
1750  */
1751 function user_admin_access() {
1752   $header = array(array('data' => t('Access type'), 'field' => 'status'), array('data' => t('Rule type'), 'field' => 'type'), array('data' => t('Mask'), 'field' => 'mask'), array('data' => t('Operations'), 'colspan' => 2));
1753   $result = db_query("SELECT aid, type, status, mask FROM {access}". tablesort_sql($header));
1754   $access_types = array('user' => t('username'), 'mail' => t('e-mail'), 'host' => t('host'));
1755   $rows = array();
1756   while ($rule = db_fetch_object($result)) {
1757     $rows[] = array($rule->status ? t('allow') : t('deny'), $access_types[$rule->type], $rule->mask, l(t('edit'), 'admin/user/rules/edit/'. $rule->aid), l(t('delete'), 'admin/user/rules/delete/'. $rule->aid));
1758   }
1759   if (count($rows) == 0) {
1760     $rows[] = array(array('data' => '<em>'. t('There are currently no access rules.') .'</em>', 'colspan' => 5));
1761   }
1762   $output .= theme('table', $header, $rows);
1764   return $output;
1768  * Retrieve an array of roles matching specified conditions.
1770  * @param $membersonly
1771  *   Set this to TRUE to exclude the 'anonymous' role.
1772  * @param $permission
1773  *   A string containing a permission. If set, only roles containing that permission are returned.
1775  * @return
1776  *   An associative array with the role id as the key and the role name as value.
1777  */
1778 function user_roles($membersonly = 0, $permission = 0) {
1779   $roles = array();
1781   if ($permission) {
1782     $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);
1783   }
1784   else {
1785     $result = db_query('SELECT * FROM {role} ORDER BY name');
1786   }
1787   while ($role = db_fetch_object($result)) {
1788     if (!$membersonly || ($membersonly && $role->rid != DRUPAL_ANONYMOUS_RID)) {
1789       $roles[$role->rid] = $role->name;
1790     }
1791   }
1792   return $roles;
1796  * Menu callback: administer permissions.
1797  */
1798 function user_admin_perm($rid = NULL) {
1799   if (is_numeric($rid)) {
1800     $result = db_query('SELECT r.rid, p.perm FROM {role} r LEFT JOIN {permission} p ON r.rid = p.rid WHERE r.rid = %d', $rid);
1801   }
1802   else {
1803     $result = db_query('SELECT r.rid, p.perm FROM {role} r LEFT JOIN {permission} p ON r.rid = p.rid ORDER BY name');
1804   }
1806   // Compile role array:
1807   // Add a comma at the end so when searching for a permission, we can
1808   // always search for "$perm," to make sure we do not confuse
1809   // permissions that are substrings of each other.
1810   while ($role = db_fetch_object($result)) {
1811     $role_permissions[$role->rid] = $role->perm .',';
1812   }
1814   if (is_numeric($rid)) {
1815     $result = db_query('SELECT rid, name FROM {role} r WHERE r.rid = %d ORDER BY name', $rid);
1816   }
1817   else {
1818     $result = db_query('SELECT rid, name FROM {role} ORDER BY name');
1819   }
1821   $role_names = array();
1822   while ($role = db_fetch_object($result)) {
1823     $role_names[$role->rid] = $role->name;
1824   }
1826   // Render role/permission overview:
1827   $options = array();
1828   foreach (module_list(FALSE, FALSE, TRUE) as $module) {
1829     if ($permissions = module_invoke($module, 'perm')) {
1830       $form['permission'][] = array(
1831         '#value' => $module,
1832       );
1833       asort($permissions);
1834       foreach ($permissions as $perm) {
1835         $options[$perm] = '';
1836         $form['permission'][$perm] = array('#value' => t($perm));
1837         foreach ($role_names as $rid => $name) {
1838           // Builds arrays for checked boxes for each role
1839           if (strpos($role_permissions[$rid], $perm .',') !== FALSE) {
1840             $status[$rid][] = $perm;
1841           }
1842         }
1843       }
1844     }
1845   }
1847   // Have to build checkboxes here after checkbox arrays are built
1848   foreach ($role_names as $rid => $name) {
1849     $form['checkboxes'][$rid] = array('#type' => 'checkboxes', '#options' => $options, '#default_value' => $status[$rid]);
1850     $form['role_names'][$rid] = array('#value' => $name, '#tree' => TRUE);
1851   }
1852   $form['submit'] = array('#type' => 'submit', '#value' => t('Save permissions'));
1854   return $form;
1857 function theme_user_admin_perm($form) {
1858   foreach (element_children($form['permission']) as $key) {
1859     // Don't take form control structures
1860     if (is_array($form['permission'][$key])) {
1861       $row = array();
1862       // Module name
1863       if (is_numeric($key)) {
1864         $row[] = array('data' => t('@module module', array('@module' => drupal_render($form['permission'][$key]))), 'class' => 'module', 'id' => 'module-'. $form['permission'][$key]['#value'], 'colspan' => count($form['role_names']) + 1);
1865       }
1866       else {
1867         $row[] = array('data' => drupal_render($form['permission'][$key]), 'class' => 'permission');
1868         foreach (element_children($form['checkboxes']) as $rid) {
1869           if (is_array($form['checkboxes'][$rid])) {
1870             $row[] = array('data' => drupal_render($form['checkboxes'][$rid][$key]), 'align' => 'center', 'title' => t($key));
1871           }
1872         }
1873       }
1874       $rows[] = $row;
1875     }
1876   }
1877   $header[] = (t('Permission'));
1878   foreach (element_children($form['role_names']) as $rid) {
1879     if (is_array($form['role_names'][$rid])) {
1880       $header[] = drupal_render($form['role_names'][$rid]);
1881     }
1882   }
1883   $output = theme('table', $header, $rows, array('id' => 'permissions'));
1884   $output .= drupal_render($form);
1885   return $output;
1888 function user_admin_perm_submit($form_id, $form_values) {
1889   // Save permissions:
1890   $result = db_query('SELECT * FROM {role}');
1891   while ($role = db_fetch_object($result)) {
1892     if (isset($form_values[$role->rid])) {
1893       // Delete, so if we clear every checkbox we reset that role;
1894       // otherwise permissions are active and denied everywhere.
1895       db_query('DELETE FROM {permission} WHERE rid = %d', $role->rid);
1896       $form_values[$role->rid] = array_filter($form_values[$role->rid]);
1897       if (count($form_values[$role->rid])) {
1898         db_query("INSERT INTO {permission} (rid, perm) VALUES (%d, '%s')", $role->rid, implode(', ', array_keys($form_values[$role->rid])));
1899       }
1900     }
1901   }
1903   drupal_set_message(t('The changes have been saved.'));
1905   // Clear the cached pages and menus:
1906   menu_rebuild();
1911  * Menu callback: administer roles.
1912  */
1913 function user_admin_role() {
1914   $id = arg(4);
1915   if ($id) {
1916     if (DRUPAL_ANONYMOUS_RID == $id || DRUPAL_AUTHENTICATED_RID == $id) {
1917       drupal_goto('admin/user/roles');
1918     }
1919     // Display the edit role form.
1920     $role = db_fetch_object(db_query('SELECT * FROM {role} WHERE rid = %d', $id));
1921     $form['name'] = array(
1922       '#type' => 'textfield',
1923       '#title' => t('Role name'),
1924       '#default_value' => $role->name,
1925       '#size' => 30,
1926       '#required' => TRUE,
1927       '#maxlength' => 64,
1928       '#description' => t('The name for this role. Example: "moderator", "editorial board", "site architect".'),
1929     );
1930     $form['rid'] = array(
1931       '#type' => 'value',
1932       '#value' => $id,
1933     );
1934     $form['submit'] = array(
1935       '#type' => 'submit',
1936       '#value' => t('Save role'),
1937     );
1938     $form['delete'] = array(
1939       '#type' => 'submit',
1940       '#value' => t('Delete role'),
1941     );
1942   }
1943   else {
1944     $form['name'] = array(
1945       '#type' => 'textfield',
1946       '#size' => 32,
1947       '#maxlength' => 64,
1948     );
1949     $form['submit'] = array(
1950       '#type' => 'submit',
1951       '#value' => t('Add role'),
1952     );
1953     $form['#base'] = 'user_admin_role';
1954   }
1955   return $form;
1958 function user_admin_role_validate($form_id, $form_values) {
1959   if ($form_values['name']) {
1960     if ($form_values['op'] == t('Save role')) {
1961       if (db_result(db_query("SELECT COUNT(*) FROM {role} WHERE name = '%s' AND rid != %d", $form_values['name'], $form_values['rid']))) {
1962         form_set_error('name', t('The role name %name already exists. Please choose another role name.', array('%name' => $form_values['name'])));
1963       }
1964     }
1965     else if ($form_values['op'] == t('Add role')) {
1966       if (db_result(db_query("SELECT COUNT(*) FROM {role} WHERE name = '%s'", $form_values['name']))) {
1967         form_set_error('name', t('The role name %name already exists. Please choose another role name.', array('%name' => $form_values['name'])));
1968       }
1969     }
1970   }
1971   else {
1972     form_set_error('name', t('You must specify a valid role name.'));
1973   }
1976 function user_admin_role_submit($form_id, $form_values) {
1977   if ($form_values['op'] == t('Save role')) {
1978     db_query("UPDATE {role} SET name = '%s' WHERE rid = %d", $form_values['name'], $form_values['rid']);
1979     drupal_set_message(t('The role has been renamed.'));
1980   }
1981   else if ($form_values['op'] == t('Delete role')) {
1982     db_query('DELETE FROM {role} WHERE rid = %d', $form_values['rid']);
1983     db_query('DELETE FROM {permission} WHERE rid = %d', $form_values['rid']);
1984     // Update the users who have this role set:
1985     db_query('DELETE FROM {users_roles} WHERE rid = %d', $form_values['rid']);
1987     drupal_set_message(t('The role has been deleted.'));
1988   }
1989   else if ($form_values['op'] == t('Add role')) {
1990     db_query("INSERT INTO {role} (name) VALUES ('%s')", $form_values['name']);
1991     drupal_set_message(t('The role has been added.'));
1992   }
1993   return 'admin/user/roles';
1996 function theme_user_admin_new_role($form) {
1997   $header = array(t('Name'), array('data' => t('Operations'), 'colspan' => 2));
1998   foreach (user_roles() as $rid => $name) {
1999     $edit_permissions = l(t('edit permissions'), 'admin/user/access/'. $rid);
2000     if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
2001       $rows[] = array($name, l(t('edit role'), 'admin/user/roles/edit/'. $rid), $edit_permissions);
2002     }
2003     else {
2004       $rows[] = array($name, t('locked'), $edit_permissions);
2005     }
2006   }
2007   $rows[] = array(drupal_render($form['name']), array('data' => drupal_render($form['submit']), colspan => 2));
2009   $output = drupal_render($form);
2010   $output .= theme('table', $header, $rows);
2012   return $output;
2015 function user_admin_account() {
2016   $filter = user_build_filter_query();
2018   $header = array(
2019     array(),
2020     array('data' => t('Username'), 'field' => 'u.name'),
2021     array('data' => t('Status'), 'field' => 'u.status'),
2022     t('Roles'),
2023     array('data' => t('Member for'), 'field' => 'u.created', 'sort' => 'desc'),
2024     array('data' => t('Last access'), 'field' => 'u.access'),
2025     t('Operations')
2026   );
2028   $sql = 'SELECT DISTINCT u.uid, u.name, u.status, u.created, u.access FROM {users} u LEFT JOIN {users_roles} ur ON u.uid = ur.uid '. $filter['join'] .' WHERE u.uid != 0 '. $filter['where'];
2029   $sql .= tablesort_sql($header);
2030   $query_count = 'SELECT COUNT(DISTINCT u.uid) FROM {users} u LEFT JOIN {users_roles} ur ON u.uid = ur.uid '. $filter['join'] .' WHERE u.uid != 0 '. $filter['where'];
2031   $result = pager_query($sql, 50, 0, $query_count, $filter['args']);
2033   $form['options'] = array(
2034     '#type' => 'fieldset',
2035     '#title' => t('Update options'),
2036     '#prefix' => '<div class="container-inline">',
2037     '#suffix' => '</div>',
2038   );
2039   $options = array();
2040   foreach (module_invoke_all('user_operations') as $operation => $array) {
2041     $options[$operation] = $array['label'];
2042   }
2043   $form['options']['operation'] = array(
2044     '#type' => 'select',
2045     '#options' => $options,
2046     '#default_value' => 'unblock',
2047   );
2048   $form['options']['submit'] = array(
2049     '#type' => 'submit',
2050     '#value' => t('Update'),
2051   );
2053   $destination = drupal_get_destination();
2055   $status = array(t('blocked'), t('active'));
2056   $roles = user_roles(1);
2058   while ($account = db_fetch_object($result)) {
2059     $accounts[$account->uid] = '';
2060     $form['name'][$account->uid] = array('#value' => theme('username', $account));
2061     $form['status'][$account->uid] =  array('#value' => $status[$account->status]);
2062     $users_roles = array();
2063     $roles_result = db_query('SELECT rid FROM {users_roles} WHERE uid = %d', $account->uid);
2064     while ($user_role = db_fetch_object($roles_result)) {
2065       $users_roles[] = $roles[$user_role->rid];
2066     }
2067     asort($users_roles);
2068     $form['roles'][$account->uid][0] = array('#value' => theme('item_list', $users_roles));
2069     $form['member_for'][$account->uid] = array('#value' => format_interval(time() - $account->created));
2070     $form['last_access'][$account->uid] =  array('#value' => $account->access ? t('@time ago', array('@time' => format_interval(time() - $account->access))) : t('never'));
2071     $form['operations'][$account->uid] = array('#value' => l(t('edit'), "user/$account->uid/edit", array(), $destination));
2072   }
2073   $form['accounts'] = array(
2074     '#type' => 'checkboxes',
2075     '#options' => $accounts
2076   );
2077   $form['pager'] = array('#value' => theme('pager', NULL, 50, 0));
2079   return $form;
2083  * Theme user administration overview.
2084  */
2085 function theme_user_admin_account($form) {
2086   // Overview table:
2087   $header = array(
2088     theme('table_select_header_cell'),
2089     array('data' => t('Username'), 'field' => 'u.name'),
2090     array('data' => t('Status'), 'field' => 'u.status'),
2091     t('Roles'),
2092     array('data' => t('Member for'), 'field' => 'u.created', 'sort' => 'desc'),
2093     array('data' => t('Last access'), 'field' => 'u.access'),
2094     t('Operations')
2095   );
2097   $output = drupal_render($form['options']);
2098   if (isset($form['name']) && is_array($form['name'])) {
2099     foreach (element_children($form['name']) as $key) {
2100       $rows[] = array(
2101         drupal_render($form['accounts'][$key]),
2102         drupal_render($form['name'][$key]),
2103         drupal_render($form['status'][$key]),
2104         drupal_render($form['roles'][$key]),
2105         drupal_render($form['member_for'][$key]),
2106         drupal_render($form['last_access'][$key]),
2107         drupal_render($form['operations'][$key]),
2108       );
2109     }
2110   }
2111   else  {
2112     $rows[] = array(array('data' => t('No users available.'), 'colspan' => '7'));
2113   }
2115   $output .= theme('table', $header, $rows);
2116   if ($form['pager']['#value']) {
2117     $output .= drupal_render($form['pager']);
2118   }
2120   $output .= drupal_render($form);
2122   return $output;
2126  * Submit the user administration update form.
2127  */
2128 function user_admin_account_submit($form_id, $form_values) {
2129   $operations = module_invoke_all('user_operations');
2130   $operation = $operations[$form_values['operation']];
2131   // Filter out unchecked accounts.
2132   $accounts = array_filter($form_values['accounts']);
2133   if ($function = $operation['callback']) {
2134     // Add in callback arguments if present.
2135     if (isset($operation['callback arguments'])) {
2136       $args = array_merge(array($accounts), $operation['callback arguments']);
2137     }
2138     else {
2139       $args = array($accounts);
2140     }
2141     call_user_func_array($function, $args);
2143     cache_clear_all('*', 'cache_menu', TRUE);
2144     drupal_set_message(t('The update has been performed.'));
2145   }
2148 function user_admin_account_validate($form_id, $form_values) {
2149   $form_values['accounts'] = array_filter($form_values['accounts']);
2150   if (count($form_values['accounts']) == 0) {
2151     form_set_error('', t('No users selected.'));
2152   }
2156  * Implementation of hook_user_operations().
2157  */
2158 function user_user_operations() {
2159   global $form_values;
2161   $operations = array(
2162     'unblock' => array(
2163       'label' => t('Unblock the selected users'),
2164       'callback' => 'user_user_operations_unblock',
2165     ),
2166     'block' => array(
2167       'label' => t('Block the selected users'),
2168       'callback' => 'user_user_operations_block',
2169     ),
2170     'delete' => array(
2171       'label' => t('Delete the selected users'),
2172     ),
2173   );
2175   if (user_access('administer access control')) {
2176     $roles = user_roles(1);
2177     unset($roles[DRUPAL_AUTHENTICATED_RID]);  // Can't edit authenticated role.
2179     $add_roles = array();
2180     foreach ($roles as $key => $value) {
2181       $add_roles['add_role-'. $key] = $value;
2182     }
2184     $remove_roles = array();
2185     foreach ($roles as $key => $value) {
2186       $remove_roles['remove_role-'. $key] = $value;
2187     }
2189     if (count($roles)) {
2190       $role_operations = array(
2191         t('Add a role to the selected users') => array(
2192           'label' => $add_roles,
2193         ),
2194         t('Remove a role from the selected users') => array(
2195           'label' => $remove_roles,
2196         ),
2197       );
2199       $operations += $role_operations;
2200     }
2201   }
2203   // If the form has been posted, we need to insert the proper data for role editing if necessary.
2204   if ($form_values) {
2205     $operation_rid = explode('-', $form_values['operation']);
2206     $operation = $operation_rid[0];
2207     $rid = $operation_rid[1];
2208     if ($operation == 'add_role' || $operation == 'remove_role') {
2209       if (user_access('administer access control')) {
2210         $operations[$form_values['operation']] = array(
2211           'callback' => 'user_multiple_role_edit',
2212           'callback arguments' => array($operation, $rid),
2213         );
2214       }
2215       else {
2216         watchdog('security', t('Detected malicious attempt to alter protected user fields.'), WATCHDOG_WARNING);
2217         return;
2218       }
2219     }
2220   }
2222   return $operations;
2226  * Callback function for admin mass unblocking users.
2227  */
2228 function user_user_operations_unblock($accounts) {
2229   foreach ($accounts as $uid) {
2230     $account = user_load(array('uid' => (int)$uid));
2231     // Skip unblocking user if they are already unblocked.
2232     if ($account !== FALSE && $account->status == 0) {
2233       user_save($account, array('status' => 1));
2234     }
2235   }
2239  * Callback function for admin mass blocking users.
2240  */
2241 function user_user_operations_block($accounts) {
2242   foreach ($accounts as $uid) {
2243     $account = user_load(array('uid' => (int)$uid));
2244     // Skip blocking user if they are already blocked.
2245     if ($account !== FALSE && $account->status == 1) {
2246       user_save($account, array('status' => 0));
2247     }
2248   }
2252  * Callback function for admin mass adding/deleting a user role.
2253  */
2254 function user_multiple_role_edit($accounts, $operation, $rid) {
2255   // The role name is not necessary as user_save() will reload the user
2256   // object, but some modules' hook_user() may look at this first.
2257   $role_name = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $rid));
2259   switch ($operation) {
2260     case 'add_role':
2261       foreach ($accounts as $uid) {
2262         $account = user_load(array('uid' => (int)$uid));
2263         // Skip adding the role to the user if they already have it.
2264         if ($account !== FALSE && !isset($account->roles[$rid])) {
2265           $roles = $account->roles + array($rid => $role_name);
2266           user_save($account, array('roles' => $roles));
2267         }
2268       }
2269       break;
2270     case 'remove_role':
2271       foreach ($accounts as $uid) {
2272         $account = user_load(array('uid' => (int)$uid));
2273         // Skip removing the role from the user if they already don't have it.
2274         if ($account !== FALSE && isset($account->roles[$rid])) {
2275           $roles = array_diff($account->roles, array($rid => $role_name));
2276           user_save($account, array('roles' => $roles));
2277         }
2278       }
2279       break;
2280   }
2283 function user_multiple_delete_confirm() {
2284   $edit = $_POST;
2286   $form['accounts'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
2287   // array_filter returns only elements with TRUE values
2288   foreach (array_filter($edit['accounts']) as $uid => $value) {
2289     $user = db_result(db_query('SELECT name FROM {users} WHERE uid = %d', $uid));
2290     $form['accounts'][$uid] = array('#type' => 'hidden', '#value' => $uid, '#prefix' => '<li>', '#suffix' => check_plain($user) ."</li>\n");
2291   }
2292   $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
2294   return confirm_form($form,
2295                       t('Are you sure you want to delete these users?'),
2296                       'admin/user/user', t('This action cannot be undone.'),
2297                       t('Delete all'), t('Cancel'));
2300 function user_multiple_delete_confirm_submit($form_id, $form_values) {
2301   if ($form_values['confirm']) {
2302     foreach ($form_values['accounts'] as $uid => $value) {
2303       user_delete($form_values, $uid);
2304     }
2305     drupal_set_message(t('The users have been deleted.'));
2306   }
2307   return 'admin/user/user';
2310 function user_admin_settings() {
2311   // User registration settings.
2312   $form['registration'] = array('#type' => 'fieldset', '#title' => t('User registration settings'));
2313   $form['registration']['user_register'] = array('#type' => 'radios', '#title' => t('Public registrations'), '#default_value' => variable_get('user_register', 1), '#options' => array(t('Only site administrators can create new user accounts.'), t('Visitors can create accounts and no administrator approval is required.'), t('Visitors can create accounts but administrator approval is required.')));
2314   $form['registration']['user_email_verification'] = array('#type' => 'checkbox', '#title' => t('Require e-mail verification when a visitor creates an account'), '#default_value' => variable_get('user_email_verification', TRUE), '#description' => t('If this box is checked, new users will be required to validate their e-mail address prior to logging into to the site, and will be assigned a system-generated password. With it unchecked, users will be logged in immediately upon registering, and may select their own passwords during registration.'));
2315   $form['registration']['user_registration_help'] = array('#type' => 'textarea', '#title' => t('User registration guidelines'), '#default_value' => variable_get('user_registration_help', ''), '#description' => t("This text is displayed at the top of the user registration form. It's useful for helping or instructing your users."));
2317   // User e-mail settings.
2318   $form['email'] = array('#type' => 'fieldset', '#title' => t('User e-mail settings'));
2319   $form['email']['user_mail_welcome_subject'] = array('#type' => 'textfield', '#title' => t('Subject of welcome e-mail'), '#default_value' => _user_mail_text('welcome_subject'), '#maxlength' => 180, '#description' => t('Customize the subject of your welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' !username, !site, !password, !uri, !uri_brief, !mailto, !date, !login_uri, !edit_uri, !login_url.');
2320   $form['email']['user_mail_welcome_body'] = array('#type' => 'textarea', '#title' => t('Body of welcome e-mail'), '#default_value' => _user_mail_text('welcome_body'), '#rows' => 15, '#description' => t('Customize the body of the welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' !username, !site, !password, !uri, !uri_brief, !mailto, !login_uri, !edit_uri, !login_url.');
2321   $form['email']['user_mail_admin_subject'] = array('#type' => 'textfield', '#title' => t('Subject of welcome e-mail (user created by administrator)'), '#default_value' => _user_mail_text('admin_subject'), '#maxlength' => 180, '#description' => t('Customize the subject of your welcome e-mail, which is sent to new member accounts created by an administrator.') .' '. t('Available variables are:') .' !username, !site, !password, !uri, !uri_brief, !mailto, !date, !login_uri, !edit_uri, !login_url.');
2322   $form['email']['user_mail_admin_body'] = array('#type' => 'textarea', '#title' => t('Body of welcome e-mail (user created by administrator)'), '#default_value' => _user_mail_text('admin_body'), '#rows' => 15, '#description' => t('Customize the body of the welcome e-mail, which is sent to new member accounts created by an administrator.') .' '. t('Available variables are:') .' !username, !site, !password, !uri, !uri_brief, !mailto, !login_uri, !edit_uri, !login_url.');
2323   $form['email']['user_mail_approval_subject'] = array('#type' => 'textfield', '#title' => t('Subject of welcome e-mail (awaiting admin approval)'), '#default_value' => _user_mail_text('approval_subject'), '#maxlength' => 180, '#description' => t('Customize the subject of your awaiting approval welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' !username, !site, !password, !uri, !uri_brief, !mailto, !date, !login_uri, !edit_uri, !login_url.');
2324   $form['email']['user_mail_approval_body'] = array('#type' => 'textarea', '#title' => t('Body of welcome e-mail (awaiting admin approval)'), '#default_value' => _user_mail_text('approval_body'), '#rows' => 15, '#description' => t('Customize the body of the awaiting approval welcome e-mail, which is sent to new members upon registering.') .' '. t('Available variables are:') .' !username, !site, !password, !uri, !uri_brief, !mailto, !login_uri, !edit_uri, !login_url.');
2325   $form['email']['user_mail_pass_subject'] = array('#type' => 'textfield', '#title' => t('Subject of password recovery e-mail'), '#default_value' => _user_mail_text('pass_subject'), '#maxlength' => 180, '#description' => t('Customize the subject of your forgotten password e-mail.') .' '. t('Available variables are:') .' !username, !site, !login_url, !uri, !uri_brief, !mailto, !date, !login_uri, !edit_uri.');
2326   $form['email']['user_mail_pass_body'] = array('#type' => 'textarea', '#title' => t('Body of password recovery e-mail'), '#default_value' => _user_mail_text('pass_body'), '#rows' => 15, '#description' => t('Customize the body of the forgotten password e-mail.') .' '. t('Available variables are:') .' !username, !site, !login_url, !uri, !uri_brief, !mailto, !login_uri, !edit_uri.');
2328   // If picture support is enabled, check whether the picture directory exists:
2329   if (variable_get('user_pictures', 0)) {
2330     $picture_path = file_create_path(variable_get('user_picture_path', 'pictures'));
2331     file_check_directory($picture_path, 1, 'user_picture_path');
2332   }
2334   $form['pictures'] = array('#type' => 'fieldset', '#title' => t('Pictures'));
2335   $form['pictures']['user_pictures'] = array('#type' => 'radios', '#title' => t('Picture support'), '#default_value' => variable_get('user_pictures', 0), '#options' => array(t('Disabled'), t('Enabled')), '#description' => t('Enable picture support.'));
2336   $form['pictures']['user_picture_path'] = array('#type' => 'textfield', '#title' => t('Picture image path'), '#default_value' => variable_get('user_picture_path', 'pictures'), '#size' => 30, '#maxlength' => 255, '#description' => t('Subdirectory in the directory %dir where pictures will be stored.', array('%dir' => file_directory_path() .'/')));
2337   $form['pictures']['user_picture_default'] = array('#type' => 'textfield', '#title' => t('Default picture'), '#default_value' => variable_get('user_picture_default', ''), '#size' => 30, '#maxlength' => 255, '#description' => t('URL of picture to display for users with no custom picture selected. Leave blank for none.'));
2338   $form['pictures']['user_picture_dimensions'] = array('#type' => 'textfield', '#title' => t('Picture maximum dimensions'), '#default_value' => variable_get('user_picture_dimensions', '85x85'), '#size' => 15, '#maxlength' => 10, '#description' => t('Maximum dimensions for pictures, in pixels.'));
2339   $form['pictures']['user_picture_file_size'] = array('#type' => 'textfield', '#title' => t('Picture maximum file size'), '#default_value' => variable_get('user_picture_file_size', '30'), '#size' => 15, '#maxlength' => 10, '#description' => t('Maximum file size for pictures, in kB.'));
2340   $form['pictures']['user_picture_guidelines'] = array('#type' => 'textarea', '#title' => t('Picture guidelines'), '#default_value' => variable_get('user_picture_guidelines', ''), '#description' => t("This text is displayed at the picture upload form in addition to the default guidelines. It's useful for helping or instructing your users."));
2342   return system_settings_form($form);
2345 function user_admin($callback_arg = '') {
2346   $op = isset($_POST['op']) ? $_POST['op'] : $callback_arg;
2348   switch ($op) {
2349     case 'search':
2350     case t('Search'):
2351       $output = drupal_get_form('search_form', url('admin/user/search'), $_POST['keys'], 'user') . search_data($_POST['keys'], 'user');
2352       break;
2353     case t('Create new account'):
2354     case 'create':
2355       $output = drupal_get_form('user_register');
2356       break;
2357     default:
2358       if ($_POST['accounts'] && $_POST['operation'] == 'delete') {
2359         $output = drupal_get_form('user_multiple_delete_confirm');
2360       }
2361       else {
2362         $output = drupal_get_form('user_filter_form');
2363         $output .= drupal_get_form('user_admin_account');
2364       }
2365   }
2366   return $output;
2370  * Implementation of hook_help().
2371  */
2372 function user_help($section) {
2373   global $user;
2375   switch ($section) {
2376     case 'admin/help#user':
2377       $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 can setup 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>';
2378       $output .= '<p>'. t('Users can use their own name or handle and can fine tune some personal configuration settings through their individual my account page. Registered users need to authenticate by supplying either a local username and password, or a remote username and password such as DelphiForums ID, or one from a Drupal powered website. A visitor accessing your website is assigned an unique ID, the so-called session ID, which is stored in a cookie. For security\'s sake, the cookie does not contain personal information but acts as a key to retrieve the information stored on your server.') .'</p>';
2379       $output .= '<p>'. t('For more information please read the configuration and customization handbook <a href="@user">User page</a>.', array('@user' => 'http://drupal.org/handbook/modules/user/')) .'</p>';
2380       return $output;
2381     case 'admin/user/user':
2382       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>';
2383     case 'admin/user/user/create':
2384     case 'admin/user/user/account/create':
2385       return '<p>'. t('This web page allows the administrators to register new users by hand. Note that you cannot have a user where either the e-mail address or the username match another user in the system.') .'</p>';
2386     case 'admin/user/rules':
2387       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>';
2388     case 'admin/user/access':
2389       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>';
2390     case 'admin/user/roles':
2391       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>
2392       <ul>
2393       <li>Anonymous user: this role is used for users that don\'t have a user account or that are not authenticated.</li>
2394       <li>Authenticated user: this role is automatically granted to all logged in users.</li>
2395       </ul>', array('@permissions' => url('admin/user/access')));
2396     case 'admin/user/search':
2397       return '<p>'. t('Enter a simple pattern ("*" may be used as a wildcard match) to search for a username. For example, one may search for "br" and Drupal might return "brian", "brad", and "brenda".') .'</p>';
2398     case 'user/help#user':
2399       $site = variable_get('site_name', 'Drupal');
2401       $affiliates = user_auth_help_links();
2402       if (count($affiliates)) {
2403         $affiliate_info = implode(', ', user_auth_help_links());
2404       }
2405       else {
2406         $affiliate_info = t('one of our affiliates');
2407       }
2409       $output = t('
2410       <h3>Distributed authentication<a id="da"></a></h3>
2411       <p>One of the more tedious moments in visiting a new website is filling out the registration form. Here at @site, you do not have to fill out a registration form if you are already a member of !affiliate-info. This capability is called <em>distributed authentication</em>, and <a href="@drupal">Drupal</a>, the software which powers @site, fully supports it.</p>
2412       <p>Distributed authentication enables a new user to input a username and password into the login box, and immediately be recognized, even if that user never registered at @site. This works because Drupal knows how to communicate with external registration databases. For example, lets say that new user \'Joe\' is already a registered member of <a href="@delphi-forums">Delphi Forums</a>. Drupal informs Joe on registration and login screens that he may login with his Delphi ID instead of registering with @site. Joe likes that idea, and logs in with a username of joe@remote.delphiforums.com and his usual Delphi password. Drupal then contacts the <em>remote.delphiforums.com</em> server behind the scenes (usually using <a href="@xml">XML-RPC</a>, <a href="@http-post">HTTP POST</a>, or <a href="@soap">SOAP</a>) and asks: "Is the password for user Joe correct?". If Delphi replies yes, then we create a new @site account for Joe and log him into it. Joe may keep on logging into @site in the same manner, and he will always be logged into the same account.</p>', array('!affiliate-info' => $affiliate_info, '@site' => $site, '@drupal' => 'http://drupal.org', '@delphi-forums' => 'http://www.delphiforums.com', '@xml' => 'http://www.xmlrpc.com', '@http-post' => 'http://www.w3.org/Protocols/', '@soap' => 'http://www.soapware.org'));
2414         foreach (module_list() as $module) {
2415           if (module_hook($module, 'auth')) {
2416             $output .= "<h4><a id=\"$module\"></a>". module_invoke($module, 'info', 'name') .'</h4>';
2417             $output .= module_invoke($module, 'help', "user/help#$module");
2418           }
2419         }
2421         return $output;
2422   }
2427  * Menu callback; Prints user-specific help information.
2428  */
2429 function user_help_page() {
2430   return user_help('user/help#user');
2434  * Retrieve a list of all user setting/information categories and sort them by weight.
2435  */
2436 function _user_categories($account) {
2437   $categories = array();
2439   foreach (module_list() as $module) {
2440     if ($data = module_invoke($module, 'user', 'categories', NULL, $account, '')) {
2441       $categories = array_merge($data, $categories);
2442     }
2443   }
2445   usort($categories, '_user_sort');
2447   return $categories;
2450 function _user_sort($a, $b) {
2451   return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1));
2455  * Retrieve a list of all form elements for the specified category.
2456  */
2457 function _user_forms(&$edit, $account, $category, $hook = 'form') {
2458   $groups = array();
2459   foreach (module_list() as $module) {
2460     if ($data = module_invoke($module, 'user', $hook, $edit, $account, $category)) {
2461       $groups = array_merge_recursive($data, $groups);
2462     }
2463   }
2464   uasort($groups, '_user_sort');
2466   return empty($groups) ? FALSE : $groups;
2470  * Retrieve a pipe delimited string of autocomplete suggestions for existing users
2471  */
2472 function user_autocomplete($string = '') {
2473   $matches = array();
2474   if ($string) {
2475     $result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE LOWER('%s%%')", $string, 0, 10);
2476     while ($user = db_fetch_object($result)) {
2477       $matches[$user->name] = check_plain($user->name);
2478    }
2479   }
2480   print drupal_to_js($matches);
2481   exit();
2485  * List user administration filters that can be applied.
2486  */
2487 function user_filters() {
2488   // Regular filters
2489   $filters = array();
2490   $roles = user_roles(1);
2491   unset($roles[DRUPAL_AUTHENTICATED_RID]); // Don't list authorized role.
2492   if (count($roles)) {
2493     $filters['role'] = array('title' => t('role'),
2494                          'where' => "ur.rid = %d",
2495                          'options' => $roles,
2496                        );
2497   }
2499   $options = array();
2500   $t_module = t('module');
2501   foreach (module_list() as $module) {
2502     if ($permissions = module_invoke($module, 'perm')) {
2503       asort($permissions);
2504       foreach ($permissions as $permission) {
2505         $options["$module $t_module"][$permission] = t($permission);
2506       }
2507     }
2508   }
2509   ksort($options);
2510   $filters['permission'] = array('title' => t('permission'),
2511                              'join' => 'LEFT JOIN {permission} p ON ur.rid = p.rid',
2512                              'where' => " ((p.perm IS NOT NULL AND p.perm LIKE '%%%s%%') OR u.uid = 1) ",
2513                              'options' => $options,
2514                            );
2516   $filters['status'] = array('title' => t('status'),
2517                          'where' => 'u.status = %d',
2518                          'options' => array(1 => t('active'), 0 => t('blocked')),
2519                        );
2520   return $filters;
2524  * Build query for user administration filters based on session.
2525  */
2526 function user_build_filter_query() {
2527   $filters = user_filters();
2529   // Build query
2530   $where = $args = $join = array();
2531   foreach ($_SESSION['user_overview_filter'] as $filter) {
2532     list($key, $value) = $filter;
2533     // This checks to see if this permission filter is an enabled permission for the authenticated role.
2534     // If so, then all users would be listed, and we can skip adding it to the filter query.
2535     if ($key == 'permission') {
2536       $account = new stdClass();
2537       $account->uid = 'user_filter';
2538       $account->roles = array(DRUPAL_AUTHENTICATED_RID => 1);
2539       if (user_access($value, $account)) {
2540         continue;
2541       }
2542     }
2543     $where[] = $filters[$key]['where'];
2544     $args[] = $value;
2545     $join[] = $filters[$key]['join'];
2546   }
2547   $where = count($where) ? 'AND '. implode(' AND ', $where) : '';
2548   $join = count($join) ? ' '. implode(' ', array_unique($join)) : '';
2550   return array('where' => $where,
2551            'join' => $join,
2552            'args' => $args,
2553          );
2557  * Return form for user administration filters.
2558  */
2559 function user_filter_form() {
2560   $session = &$_SESSION['user_overview_filter'];
2561   $session = is_array($session) ? $session : array();
2562   $filters = user_filters();
2564   $i = 0;
2565   $form['filters'] = array('#type' => 'fieldset',
2566                        '#title' => t('Show only users where'),
2567                        '#theme' => 'user_filters',
2568                      );
2569   foreach ($session as $filter) {
2570     list($type, $value) = $filter;
2571     // Merge an array of arrays into one if necessary.
2572     $options = $type == 'permission' ? call_user_func_array('array_merge', $filters[$type]['options']) : $filters[$type]['options'];
2573     $params = array('%property' => $filters[$type]['title'] , '%value' => $options[$value]);
2574     if ($i++ > 0) {
2575       $form['filters']['current'][] = array('#value' => t('<em>and</em> where <strong>%property</strong> is <strong>%value</strong>', $params));
2576     }
2577     else {
2578       $form['filters']['current'][] = array('#value' => t('<strong>%property</strong> is <strong>%value</strong>', $params));
2579     }
2580   }
2582   foreach ($filters as $key => $filter) {
2583     $names[$key] = $filter['title'];
2584     $form['filters']['status'][$key] = array('#type' => 'select',
2585                                          '#options' => $filter['options'],
2586                                        );
2587   }
2589   $form['filters']['filter'] = array('#type' => 'radios',
2590                                  '#options' => $names,
2591                                );
2592   $form['filters']['buttons']['submit'] = array('#type' => 'submit',
2593                                             '#value' => (count($session) ? t('Refine') : t('Filter'))
2594                                           );
2595   if (count($session)) {
2596     $form['filters']['buttons']['undo'] = array('#type' => 'submit',
2597                                             '#value' => t('Undo')
2598                                           );
2599     $form['filters']['buttons']['reset'] = array('#type' => 'submit',
2600                                              '#value' => t('Reset')
2601                                            );
2602   }
2604   return $form;
2608  * Theme user administration filter form.
2609  */
2610 function theme_user_filter_form($form) {
2611   $output = '<div id="user-admin-filter">';
2612   $output .= drupal_render($form['filters']);
2613   $output .= '</div>';
2614   $output .= drupal_render($form);
2615   return $output;
2619  * Theme user administration filter selector.
2620  */
2621 function theme_user_filters($form) {
2622   $output = '<ul class="clear-block">';
2623   if (sizeof($form['current'])) {
2624     foreach (element_children($form['current']) as $key) {
2625       $output .= '<li>'. drupal_render($form['current'][$key]) .'</li>';
2626     }
2627   }
2629   $output .= '<li><dl class="multiselect">'. (sizeof($form['current']) ? '<dt><em>'. t('and') .'</em> '. t('where') .'</dt>' : '') .'<dd class="a">';
2630   foreach (element_children($form['filter']) as $key) {
2631     $output .= drupal_render($form['filter'][$key]);
2632   }
2633   $output .= '</dd>';
2635   $output .= '<dt>'. t('is') .'</dt><dd class="b">';
2637   foreach (element_children($form['status']) as $key) {
2638     $output .= drupal_render($form['status'][$key]);
2639   }
2640   $output .= '</dd>';
2642   $output .= '</dl>';
2643   $output .= '<div class="container-inline" id="user-admin-buttons">'. drupal_render($form['buttons']) .'</div>';
2644   $output .= '</li></ul>';
2646   return $output;
2650  * Process result from user administration filter form.
2651  */
2652 function user_filter_form_submit($form_id, $form_values) {
2653   $op = $form_values['op'];
2654   $filters = user_filters();
2655   switch ($op) {
2656     case t('Filter'): case t('Refine'):
2657       if (isset($form_values['filter'])) {
2658         $filter = $form_values['filter'];
2659         // Merge an array of arrays into one if necessary.
2660         $options = $filter == 'permission' ? call_user_func_array('array_merge', $filters[$filter]['options']) : $filters[$filter]['options'];
2661         if (isset($options[$form_values[$filter]])) {
2662           $_SESSION['user_overview_filter'][] = array($filter, $form_values[$filter]);
2663         }
2664       }
2665       break;
2666     case t('Undo'):
2667       array_pop($_SESSION['user_overview_filter']);
2668       break;
2669     case t('Reset'):
2670       $_SESSION['user_overview_filter'] = array();
2671       break;
2672     case t('Update'):
2673       return;
2674   }
2676   return 'admin/user/user';
2680 function user_forms() {
2681   $forms['user_admin_access_add_form']['callback'] = 'user_admin_access_form';
2682   $forms['user_admin_access_edit_form']['callback'] = 'user_admin_access_form';
2683   $forms['user_admin_new_role']['callback'] = 'user_admin_role';
2684   return $forms;