specfile
[plewww.git] / modules / user.module
1 <?php
2 // $Id: user.module 144 2007-03-28 07:52:20Z thierry $
3
4 /**
5  * @file
6  * Enables the user registration and login system.
7  */
8
9 /**
10  * Invokes hook_user() in every module.
11  *
12  * We cannot use module_invoke() for this, because the arguments need to
13  * be passed by reference.
14  */
15 function user_module_invoke($type, &$array, &$user, $category = NULL) {
16   foreach (module_list() as $module) {
17     $function = $module .'_user';
18     if (function_exists($function)) {
19       $function($type, $array, $user, $category);
20     }
21   }
22 }
23
24 function user_external_load($authname) {
25   $result = db_query("SELECT uid FROM {authmap} WHERE authname = '%s'", $authname);
26
27   if ($user = db_fetch_object($result)) {
28     return user_load($user);
29   }
30   else {
31     return 0;
32   }
33 }
34
35 /**
36  * Fetch a user object.
37  *
38  * @param $array
39  *   An associative array of attributes to search for in selecting the
40  *   user, such as user name or e-mail address.
41  *
42  * @return
43  *   A fully-loaded $user object upon successful user load or FALSE if user cannot be loaded.
44  */
45 function user_load($array = array()) {
46   // Dynamically compose a SQL query:
47   $query = array();
48   $params = array();
49
50   foreach ($array as $key => $value) {
51     if ($key == 'uid' || $key == 'status') {
52       $query[] = "$key = %d";
53       $params[] = $value;
54     }
55     else if ($key == 'pass') {
56       $query[] = "pass = '%s'";
57       $params[] = md5($value);
58     }
59     else {
60       $query[]= "LOWER($key) = LOWER('%s')";
61       $params[] = $value;
62     }
63   }
64   $result = db_query('SELECT * FROM {users} u WHERE ' . implode(' AND ', $query), $params);
65
66   if (db_num_rows($result)) {
67     $user = db_fetch_object($result);
68     $user = drupal_unpack($user);
69
70     $user->roles = array();
71     if ($user->uid) {
72       $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
73     }
74     else {
75       $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
76     }
77     $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);
78     while ($role = db_fetch_object($result)) {
79       $user->roles[$role->rid] = $role->name;
80     }
81     user_module_invoke('load', $array, $user);
82   }
83   else {
84     $user = FALSE;
85   }
86
87   return $user;
88 }
89
90 /**
91  * Save changes to a user account or add a new user.
92  *
93  * @param $account
94  *   The $user object for the user to modify or add. If $user->uid is
95  *   omitted, a new user will be added.
96  *
97  * @param $array
98  *   An array of fields and values to save. For example array('name' => 'My name');
99  *   Setting a field to null deletes it from the data column.
100  *
101  * @param $category
102  *   (optional) The category for storing profile information in.
103  */
104 function user_save($account, $array = array(), $category = 'account') {
105   // Dynamically compose a SQL query:
106   $user_fields = user_fields();
107   if ($account->uid) {
108     user_module_invoke('update', $array, $account, $category);
109
110     $data = unserialize(db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $account->uid)));
111     foreach ($array as $key => $value) {
112       if ($key == 'pass' && !empty($value)) {
113         $query .= "$key = '%s', ";
114         $v[] = md5($value);
115       }
116       else if ((substr($key, 0, 4) !== 'auth') && ($key != 'pass')) {
117         if (in_array($key, $user_fields)) {
118           // Save standard fields
119           $query .= "$key = '%s', ";
120           $v[] = $value;
121         }
122         else if ($key != 'roles') {
123           // Roles is a special case: it used below.
124           if ($value === null) {
125             unset($data[$key]);
126           }
127           else {
128             $data[$key] = $value;
129           }
130         }
131       }
132     }
133     $query .= "data = '%s' ";
134     $v[] = serialize($data);
135
136     db_query("UPDATE {users} SET $query WHERE uid = %d", array_merge($v, array($account->uid)));
137
138     // Reload user roles if provided
139     if (is_array($array['roles'])) {
140       db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid);
141
142       foreach (array_keys($array['roles']) as $rid) {
143         if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
144           db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid);
145         }
146       }
147     }
148
149     // Delete a blocked user's sessions to kick them if they are online.
150     if (isset($array['status']) && $array['status'] == 0) {
151       db_query('DELETE FROM {sessions} WHERE uid = %d', $account->uid);
152     }
153
154     // Refresh user object
155     $user = user_load(array('uid' => $account->uid));
156   }
157   else {
158     $array['created'] = time();
159     $array['uid'] = db_next_id('{users}_uid');
160
161     // Note, we wait with saving the data column to prevent module-handled
162     // fields from being saved there. We cannot invoke hook_user('insert') here
163     // because we don't have a fully initialized user object yet.
164     foreach ($array as $key => $value) {
165       switch($key) {
166         case 'pass':
167           $fields[] = $key;
168           $values[] = md5($value);
169           $s[] = "'%s'";
170           break;
171         case 'uid':        case 'mode':     case 'sort':
172         case 'threshold':  case 'created':  case 'access':
173         case 'login':      case 'status':
174           $fields[] = $key;
175           $values[] = $value;
176           $s[] = "%d";
177           break;
178         default:
179           if (substr($key, 0, 4) !== 'auth' && in_array($key, $user_fields)) {
180             $fields[] = $key;
181             $values[] = $value;
182             $s[] = "'%s'";
183           }
184           break;
185       }
186     }
187     db_query('INSERT INTO {users} ('. implode(', ', $fields) .') VALUES ('. implode(', ', $s) .')', $values);
188
189     // Reload user roles (delete just to be safe).
190     db_query('DELETE FROM {users_roles} WHERE uid = %d', $array['uid']);
191     foreach ((array)$array['roles'] as $rid) {
192       if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
193         db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $array['uid'], $rid);
194       }
195     }
196
197     // Build the initial user object.
198     $user = user_load(array('uid' => $array['uid']));
199
200     user_module_invoke('insert', $array, $user, $category);
201
202     // Build and save the serialized data field now
203     $data = array();
204     foreach ($array as $key => $value) {
205       if ((substr($key, 0, 4) !== 'auth') && ($key != 'roles') && (!in_array($key, $user_fields)) && ($value !== null)) {
206         $data[$key] = $value;
207       }
208     }
209     db_query("UPDATE {users} SET data = '%s' WHERE uid = %d", serialize($data), $user->uid);
210
211     // Build the finished user object.
212     $user = user_load(array('uid' => $array['uid']));
213   }
214
215   // Save distributed authentication mappings
216   $authmaps = array();
217   foreach ($array as $key => $value) {
218     if (substr($key, 0, 4) == 'auth') {
219       $authmaps[$key] = $value;
220     }
221   }
222   if (sizeof($authmaps) > 0) {
223     user_set_authmaps($user, $authmaps);
224   }
225
226   return $user;
227 }
228
229 /**
230  * Verify the syntax of the given name.
231  */
232 function user_validate_name($name) {
233   if (!strlen($name)) return t('You must enter a username.');
234   if (substr($name, 0, 1) == ' ') return t('The username cannot begin with a space.');
235   if (substr($name, -1) == ' ') return t('The username cannot end with a space.');
236   if (ereg('  ', $name)) return t('The username cannot contain multiple spaces in a row.');
237   if (ereg("[^\x80-\xF7 [:alnum:]@_.-]", $name)) return t('The username contains an illegal character.');
238   if (preg_match('/[\x{80}-\x{A0}'.          // Non-printable ISO-8859-1 + NBSP
239                    '\x{AD}'.                 // Soft-hyphen
240                    '\x{2000}-\x{200F}'.      // Various space characters
241                    '\x{2028}-\x{202F}'.      // Bidirectional text overrides
242                    '\x{205F}-\x{206F}'.      // Various text hinting characters
243                    '\x{FEFF}'.               // Byte order mark
244                    '\x{FF01}-\x{FF60}'.      // Full-width latin
245                    '\x{FFF9}-\x{FFFD}]/u',   // Replacement characters
246                    $name)) {
247     return t('The username contains an illegal character.');
248   }
249   if (ereg('@', $name) && !eregi('@([0-9a-z](-?[0-9a-z])*.)+[a-z]{2}([zmuvtg]|fo|me)?$', $name)) return t('The username is not a valid authentication ID.');
250   if (strlen($name) > 56) return t('The username %name is too long: it must be less than 56 characters.', array('%name' => theme('placeholder', $name)));
251 }
252
253 function user_validate_mail($mail) {
254   if (!$mail) return t('You must enter an e-mail address.');
255   if (!valid_email_address($mail)) {
256     return t('The e-mail address %mail is not valid.', array('%mail' => theme('placeholder', $mail)));
257   }
258 }
259
260 function user_validate_picture($file, &$edit, $user) {
261   global $form_values;
262   // Initialize the picture:
263   $form_values['picture'] = $user->picture;
264
265   // Check that uploaded file is an image, with a maximum file size
266   // and maximum height/width.
267   $info = image_get_info($file->filepath);
268   list($maxwidth, $maxheight) = explode('x', variable_get('user_picture_dimensions', '85x85'));
269
270   if (!$info || !$info['extension']) {
271     form_set_error('picture_upload', t('The uploaded file was not an image.'));
272   }
273   else if (image_get_toolkit()) {
274     image_scale($file->filepath, $file->filepath, $maxwidth, $maxheight);
275   }
276   else if (filesize($file->filepath) > (variable_get('user_picture_file_size', '30') * 1000)) {
277     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'))));
278   }
279   else if ($info['width'] > $maxwidth || $info['height'] > $maxheight) {
280     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'))));
281   }
282
283   if (!form_get_errors()) {
284     if ($file = file_save_upload('picture_upload', variable_get('user_picture_path', 'pictures') .'/picture-'. $user->uid . '.' . $info['extension'], 1)) {
285       $form_values['picture'] = $file->filepath;
286     }
287     else {
288       form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist.", array('%directory' => '<em>'. variable_get('user_picture_path', 'pictures') .'</em>')));
289     }
290   }
291 }
292
293 /**
294  * Generate a random alphanumeric password.
295  */
296 function user_password($length = 10) {
297   // This variable contains the list of allowable characters for the
298   // password.  Note that the number 0 and the letter 'O' have been
299   // removed to avoid confusion between the two.  The same is true
300   // of 'I', 1, and l.
301   $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
302
303   // Zero-based count of characters in the allowable list:
304   $len = strlen($allowable_characters) - 1;
305
306   // Declare the password as a blank string.
307   $pass = '';
308
309   // Loop the number of times specified by $length.
310   for ($i = 0; $i < $length; $i++) {
311
312     // Each iteration, pick a random character from the
313     // allowable string and append it to the password:
314     $pass .= $allowable_characters[mt_rand(0, $len)];
315   }
316
317   return $pass;
318 }
319
320 /**
321  * Determine whether the user has a given privilege.
322  *
323  * @param $string
324  *   The permission, such as "administer nodes", being checked for.
325  * @param $account
326  *   (optional) The account to check, if not given use currently logged in user.
327  *
328  * @return
329  *   boolean TRUE if the current user has the requested permission.
330  *
331  * All permission checks in Drupal should go through this function. This
332  * way, we guarantee consistent behavior, and ensure that the superuser
333  * can perform all actions.
334  */
335 function user_access($string, $account = NULL) {
336   global $user;
337   static $perm = array();
338
339   if (is_null($account)) {
340     $account = $user;
341   }
342
343   // User #1 has all privileges:
344   if ($account->uid == 1) {
345     return TRUE;
346   }
347
348   // To reduce the number of SQL queries, we cache the user's permissions
349   // in a static variable.
350   if (!isset($perm[$account->uid])) {
351     $result = db_query("SELECT DISTINCT(p.perm) FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid WHERE r.rid IN (%s)", implode(',', array_keys($account->roles)));
352
353     $perm[$account->uid] = '';
354     while ($row = db_fetch_object($result)) {
355       $perm[$account->uid] .= "$row->perm, ";
356     }
357   }
358
359   if (isset($perm[$account->uid])) {
360     return strpos($perm[$account->uid], "$string, ") !== FALSE;
361   }
362
363   return FALSE;
364 }
365
366 /**
367  * Checks for usernames blocked by user administration
368  *
369  * @return boolean true for blocked users, false for active
370  */
371 function user_is_blocked($name) {
372   $allow = db_fetch_object(db_query("SELECT * FROM {users} WHERE status = 1 AND name = LOWER('%s')", $name));
373   $deny  = db_fetch_object(db_query("SELECT * FROM {users} WHERE status = 0 AND name = LOWER('%s')", $name));
374
375   return $deny && !$allow;
376 }
377
378 /**
379  * Send an e-mail message, using Drupal variables and default settings.
380  * More information in the <a href="http://php.net/manual/en/function.mail.php">PHP function reference for mail()</a>
381  * @param $mail
382  *  The mail adres or addresses where the message will be send to. The
383  *  formatting of this string must comply with RFC 2822. Some examples are:
384  *    user@example.com
385  *    user@example.com, anotheruser@example.com
386  *    User <user@example.com>
387  *    User <user@example.com>, Another User <anotheruser@example.com>
388  *  @param $subject
389  *    Subject of the email to be sent. This must not contain any newline characters, or the mail may not be sent properly.
390  *  @param $message
391  *    Message to be sent. Drupal will format the correct line endings for you.
392  *  @param $header
393  *    String to be inserted at the end of the email header. This is typically
394  *    used to add extra headers (From, Cc, and Bcc). Multiple extra headers
395  *    should be separated with a CRLF (\r\n).
396  *    <em>When sending mail, the mail must contain a From header.</em>
397  * @return Returns TRUE if the mail was successfully accepted for delivery, FALSE otherwise.
398  */
399 function user_mail($mail, $subject, $message, $header) {
400   if (variable_get('smtp_library', '') && file_exists(variable_get('smtp_library', ''))) {
401    include_once './' . variable_get('smtp_library', '');
402     return user_mail_wrapper($mail, $subject, $message, $header);
403   }
404   else {
405     /*
406     ** Note: if you are having problems with sending mail, or mails look wrong
407     ** when they are received you may have to modify the str_replace to suit
408     ** your systems.
409     **  - \r\n will work under dos and windows.
410     **  - \n will work for linux, unix and BSDs.
411     **  - \r will work for macs.
412     **
413     ** According to RFC 2646, it's quite rude to not wrap your e-mails:
414     **
415     ** "The Text/Plain media type is the lowest common denominator of
416     ** Internet e-mail, with lines of no more than 997 characters (by
417     ** convention usually no more than 80), and where the CRLF sequence
418     ** represents a line break [MIME-IMT]."
419     **
420     ** CRLF === \r\n
421     **
422     ** http://www.rfc-editor.org/rfc/rfc2646.txt
423     **
424     */
425     return mail(
426       $mail,
427       mime_header_encode($subject),
428       str_replace("\r", '', $message),
429       "MIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8; format=flowed\nContent-transfer-encoding: 8Bit\n" . $header
430     );
431   }
432 }
433
434 function user_fields() {
435   static $fields;
436
437   if (!$fields) {
438     $result = db_query('SELECT * FROM {users} WHERE uid = 1');
439     if (db_num_rows($result)) {
440       $fields = array_keys(db_fetch_array($result));
441     }
442     else {
443       // Make sure we return the default fields at least
444       $fields = array('uid', 'name', 'pass', 'mail', 'picture', 'mode', 'sort', 'threshold', 'theme', 'signature', 'created', 'access', 'login', 'status', 'timezone', 'language', 'init', 'data');
445     }
446   }
447
448   return $fields;
449 }
450
451 /**
452  * Implementation of hook_perm().
453  */
454 function user_perm() {
455   return array('administer access control', 'administer users', 'access user profiles', 'change own username');
456 }
457
458 /**
459  * Implementation of hook_file_download().
460  *
461  * Ensure that user pictures (avatars) are always downloadable.
462  */
463 function user_file_download($file) {
464   if (strpos($file, variable_get('user_picture_path', 'pictures') .'/picture-') === 0) {
465     $info = image_get_info(file_create_path($file));
466     return array('Content-type: '. $info['mime_type']);
467   }
468 }
469
470 /**
471  * Implementation of hook_search().
472  */
473 function user_search($op = 'search', $keys = null) {
474   switch ($op) {
475     case 'name':
476       if (user_access('access user profiles')) {
477         return t('users');
478       }
479     case 'search':
480       if (user_access('access user profiles')) {
481         $find = array();
482         // Replace wildcards with MySQL/PostgreSQL wildcards.
483         $keys = preg_replace('!\*+!', '%', $keys);
484         $result = pager_query("SELECT * FROM {users} WHERE LOWER(name) LIKE LOWER('%%%s%%')", 15, 0, NULL, $keys);
485         while ($account = db_fetch_object($result)) {
486           $find[] = array('title' => $account->name, 'link' => url('user/'. $account->uid));
487         }
488         return $find;
489       }
490   }
491 }
492
493 /**
494  * Implementation of hook_user().
495  */
496 function user_user($type, &$edit, &$user, $category = NULL) {
497   if ($type == 'view') {
498     $items[] = array('title' => t('Member for'),
499       'value' => format_interval(time() - $user->created),
500       'class' => 'member',
501     );
502
503     return array(t('History') => $items);
504   }
505   if ($type == 'form' && $category == 'account') {
506     return user_edit_form(arg(1), $edit);
507   }
508
509   if ($type == 'validate' && $category == 'account') {
510     return _user_edit_validate(arg(1), $edit);
511   }
512
513   if ($type == 'submit' && $category == 'account') {
514     return _user_edit_submit(arg(1), $edit);
515   }
516
517   if ($type == 'categories') {
518     return array(array('name' => 'account', 'title' => t('account settings'), 'weight' => 1));
519   }
520 }
521
522 /**
523  * Implementation of hook_block().
524  */
525 function user_block($op = 'list', $delta = 0, $edit = array()) {
526   global $user;
527
528   if ($op == 'list') {
529      $blocks[0]['info'] = t('User login');
530      $blocks[1]['info'] = t('Navigation');
531      $blocks[2]['info'] = t('Who\'s new');
532      $blocks[3]['info'] = t('Who\'s online');
533
534      return $blocks;
535   }
536   else if ($op == 'configure' && $delta == 3) {
537     $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), 'format_interval');
538     $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.'));
539     $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.'));
540
541     return $form;
542   }
543   else if ($op == 'save' && $delta == 3) {
544     variable_set('user_block_seconds_online', $edit['user_block_seconds_online']);
545     variable_set('user_block_max_list_count', $edit['user_block_max_list_count']);
546   }
547   else if ($op == 'view') {
548     $block = array();
549
550     switch ($delta) {
551       case 0:
552         // For usability's sake, avoid showing two login forms on one page.
553         if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
554           // Unset the time, if any. This prevents a 404 if a user relogs in
555           // on the page they were sent to when they logged out. See
556           // user_login_submit() for explanation of the time parameter.
557           unset($_GET['time']);
558           $form['#action'] = url($_GET['q'], drupal_get_destination());
559           $form['#id'] = 'user-login-form';
560           $form['name'] = array('#type' => 'textfield',
561             '#title' => t('Username'),
562             '#maxlength' => 60,
563             '#size' => 15,
564             '#required' => TRUE,
565           );
566           $form['pass'] = array('#type' => 'password',
567             '#title' => t('Password'),
568             '#size' => 15,
569             '#required' => TRUE,
570           );
571           $form['submit'] = array('#type' => 'submit',
572             '#value' => t('Log in'),
573           );
574
575           if (variable_get('user_register', 1)) {
576             $items[] = l(t('Create new account'), 'user/register', array('title' => t('Create a new user account.')));
577           }
578           $items[] = l(t('Request new password'), 'user/password', array('title' => t('Request new password via e-mail.')));
579           $form['links'] = array('#value' => theme('item_list', $items));
580
581           $block['subject'] = t('User login');
582           $block['content'] = drupal_get_form('user_login_block', $form, 'user_login');
583         }
584         return $block;
585
586       case 1:
587         if ($menu = theme('menu_tree')) {
588            $block['subject'] = $user->uid ? $user->name : t('Navigation');
589            $block['content'] = $menu;
590         }
591         return $block;
592
593       case 2:
594         if (user_access('access content')) {
595           // Retrieve a list of new users who have subsequently accessed the site successfully.
596           $result = db_query_range('SELECT uid, name FROM {users} WHERE status != 0 AND access != 0 ORDER BY created DESC', 0, 5);
597           while ($account = db_fetch_object($result)) {
598             $items[] = $account;
599           }
600           $output = theme('user_list', $items);
601
602           $block['subject'] = t('Who\'s new');
603           $block['content'] = $output;
604         }
605         return $block;
606
607       case 3:
608         if (user_access('access content')) {
609           // Count users with activity in the past defined period.
610           $time_period = variable_get('user_block_seconds_online', 900);
611
612           // Perform database queries to gather online user lists.
613           $guests = db_fetch_object(db_query('SELECT COUNT(sid) AS count FROM {sessions} WHERE timestamp >= %d AND uid = 0', time() - $time_period));
614           $users = db_query('SELECT uid, name, access FROM {users} WHERE access >= %d AND uid != 0 ORDER BY access DESC', time() - $time_period);
615           $total_users = db_num_rows($users);
616
617           // Format the output with proper grammar.
618           if ($total_users == 1 && $guests->count == 1) {
619             $output = t('There is currently %members and %visitors online.', array('%members' => format_plural($total_users, '1 user', '%count users'), '%visitors' => format_plural($guests->count, '1 guest', '%count guests')));
620           }
621           else {
622             $output = t('There are currently %members and %visitors online.', array('%members' => format_plural($total_users, '1 user', '%count users'), '%visitors' => format_plural($guests->count, '1 guest', '%count guests')));
623           }
624
625           // Display a list of currently online users.
626           $max_users = variable_get('user_block_max_list_count', 10);
627           if ($total_users && $max_users) {
628             $items = array();
629
630             while ($max_users-- && $account = db_fetch_object($users)) {
631               $items[] = $account;
632             }
633
634             $output .= theme('user_list', $items, t('Online users'));
635           }
636
637           $block['subject'] = t('Who\'s online');
638           $block['content'] = $output;
639         }
640         return $block;
641     }
642   }
643 }
644
645 function theme_user_picture($account) {
646   if (variable_get('user_pictures', 0)) {
647     if ($account->picture && file_exists($account->picture)) {
648       $picture = file_create_url($account->picture);
649     }
650     else if (variable_get('user_picture_default', '')) {
651       $picture = variable_get('user_picture_default', '');
652     }
653
654     if (isset($picture)) {
655       $alt = t('%user\'s picture', array('%user' => $account->name ? $account->name : variable_get('anonymous', 'Anonymous')));
656       $picture = theme('image', $picture, $alt, $alt, '', false);
657       if (!empty($account->uid) && user_access('access user profiles')) {
658         $picture = l($picture, "user/$account->uid", array('title' => t('View user profile.')), NULL, NULL, FALSE, TRUE);
659       }
660
661       return "<div class=\"picture\">$picture</div>";
662     }
663   }
664 }
665
666 /**
667  * Theme a user page
668  * @param $account the user object
669  * @param $fields a multidimensional array for the fields, in the form of array (
670  *   'category1' => array(item_array1, item_array2), 'category2' => array(item_array3,
671  *    .. etc.). Item arrays are formatted as array(array('title' => 'item title',
672  * 'value' => 'item value', 'class' => 'class-name'), ... etc.). Module names are incorporated
673  * into the CSS class.
674  *
675  * @ingroup themeable
676  */
677 function theme_user_profile($account, $fields) {
678   $output = '<div class="profile">';
679   $output .= theme('user_picture', $account);
680   foreach ($fields as $category => $items) {
681     if (strlen($category) > 0) {
682       $output .= '<h2 class="title">'. $category .'</h2>';
683     }
684     $output .= '<dl>';
685     foreach ($items as $item) {
686       if (isset($item['title'])) {
687         $output .= '<dt class="'. $item['class'] .'">'. $item['title'] .'</dt>';
688       }
689       $output .= '<dd class="'. $item['class'] .'">'. $item['value'] .'</dd>';
690     }
691     $output .= '</dl>';
692   }
693   $output .= '</div>';
694
695   return $output;
696 }
697
698 /**
699  * Make a list of users.
700  * @param $items an array with user objects. Should contain at least the name and uid
701  *
702  * @ingroup themeable
703  */
704 function theme_user_list($users, $title = NULL) {
705   if (!empty($users)) {
706     foreach ($users as $user) {
707       $items[] = theme('username', $user);
708     }
709   }
710   return theme('item_list', $items, $title);
711 }
712
713 /**
714  * Implementation of hook_menu().
715  */
716 function user_menu($may_cache) {
717   global $user;
718
719   $items = array();
720
721   $admin_access = user_access('administer users');
722   $access_access = user_access('administer access control');
723   $view_access = user_access('access user profiles');
724
725   if ($may_cache) {
726     $items[] = array('path' => 'user', 'title' => t('user account'),
727       'callback' => 'user_login', 'access' => TRUE, 'type' => MENU_CALLBACK);
728
729     $items[] = array('path' => 'user/autocomplete', 'title' => t('user autocomplete'),
730       'callback' => 'user_autocomplete', 'access' => $view_access, 'type' => MENU_CALLBACK);
731
732     // Registration and login pages.
733     $items[] = array('path' => 'user/login', 'title' => t('log in'),
734       'callback' => 'user_login', 'type' => MENU_DEFAULT_LOCAL_TASK);
735     $items[] = array('path' => 'user/register', 'title' => t('register'),
736       'callback' => 'user_register', 'access' => $user->uid == 0 && variable_get('user_register', 1), 'type' => MENU_LOCAL_TASK);
737     $items[] = array('path' => 'user/password', 'title' => t('request new password'),
738       'callback' => 'user_pass', 'access' => $user->uid == 0, 'type' => MENU_LOCAL_TASK);
739     $items[] = array('path' => 'user/reset', 'title' => t('reset password'),
740       'callback' => 'user_pass_reset', 'access' => TRUE, 'type' => MENU_CALLBACK);
741     $items[] = array('path' => 'user/help', 'title' => t('help'),
742       'callback' => 'user_help_page', 'type' => MENU_CALLBACK);
743
744     // Admin user pages
745     $items[] = array('path' => 'admin/user', 'title' => t('users'),
746       'callback' => 'user_admin', 'access' => $admin_access);
747     $items[] = array('path' => 'admin/user/list', 'title' => t('list'),
748       'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
749     $items[] = array('path' => 'admin/user/create', 'title' => t('add user'),
750       'callback' => 'user_admin', 'access' => $admin_access,
751       'type' => MENU_LOCAL_TASK);
752     $items[] = array('path' => 'admin/settings/user', 'title' => t('users'),
753       'callback' => 'user_configure');
754
755     // Admin access pages
756     $items[] = array('path' => 'admin/access', 'title' => t('access control'),
757       'callback' => 'user_admin_perm', 'access' => $access_access);
758     $items[] = array('path' => 'admin/access/permissions', 'title' => t('permissions'),
759       'callback' => 'user_admin_perm', 'access' => $access_access,
760       'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
761     $items[] = array('path' => 'admin/access/roles', 'title' => t('roles'),
762       'callback' => 'user_admin_role', 'access' => $access_access,
763       'type' => MENU_LOCAL_TASK);
764     $items[] = array('path' => 'admin/access/roles/edit', 'title' => t('edit role'),
765       'callback' => 'user_admin_role', 'access' => $access_access,
766       'type' => MENU_CALLBACK);
767     $items[] = array('path' => 'admin/access/rules', 'title' => t('access rules'),
768       'callback' => 'user_admin_access', 'access' => $access_access,
769       'type' => MENU_LOCAL_TASK, 'weight' => 10);
770     $items[] = array('path' => 'admin/access/rules/list', 'title' => t('list'),
771       'access' => $access_access, 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
772     $items[] = array('path' => 'admin/access/rules/add', 'title' => t('add rule'),
773       'callback' => 'user_admin_access_add', 'access' => $access_access,
774       'type' => MENU_LOCAL_TASK);
775     $items[] = array('path' => 'admin/access/rules/check', 'title' => t('check rules'),
776       'callback' => 'user_admin_access_check', 'access' => $access_access,
777       'type' => MENU_LOCAL_TASK);
778     $items[] = array('path' => 'admin/access/rules/edit', 'title' => t('edit rule'),
779       'callback' => 'user_admin_access_edit', 'access' => $access_access,
780       'type' => MENU_CALLBACK);
781     $items[] = array('path' => 'admin/access/rules/delete', 'title' => t('delete rule'),
782       'callback' => 'user_admin_access_delete', 'access' => $access_access,
783       'type' => MENU_CALLBACK);
784
785     if (module_exist('search')) {
786       $items[] = array('path' => 'admin/user/search', 'title' => t('search'),
787         'callback' => 'user_admin', 'access' => $admin_access,
788         'type' => MENU_LOCAL_TASK);
789     }
790
791     // Your personal page
792     if ($user->uid) {
793       $items[] = array('path' => 'user/'. $user->uid, 'title' => t('my account'),
794         'callback' => 'user_view', 'callback arguments' => array(arg(1)), 'access' => TRUE,
795         'type' => MENU_DYNAMIC_ITEM);
796     }
797
798     $items[] = array('path' => 'logout', 'title' => t('log out'),
799       'access' => $user->uid != 0,
800       'callback' => 'user_logout',
801       'weight' => 10);
802   }
803   else {
804     if (arg(0) == 'user' && is_numeric(arg(1)) && arg(1) > 0) {
805       $account = user_load(array('uid' => arg(1)));
806
807       if ($user !== FALSE) {
808         // Always let a user view their own account
809         $view_access |= $user->uid == arg(1);
810         // Only admins can view blocked accounts
811         $view_access &= $account->status || $admin_access;
812
813         $items[] = array('path' => 'user/'. arg(1), 'title' => t('user'),
814           'type' => MENU_CALLBACK, 'callback' => 'user_view',
815           'callback arguments' => array(arg(1)), 'access' => $view_access);
816
817         $items[] = array('path' => 'user/'. arg(1) .'/view', 'title' => t('view'),
818           'access' => $view_access, 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
819
820         $items[] = array('path' => 'user/'. arg(1) .'/edit', 'title' => t('edit'),
821           'callback' => 'user_edit', 'access' => $admin_access || $user->uid == arg(1),
822           'type' => MENU_LOCAL_TASK);
823         $items[] = array('path' => 'user/'. arg(1) .'/delete', 'title' => t('delete'),
824           'callback' => 'user_edit', 'access' => $admin_access,
825           'type' => MENU_CALLBACK);
826
827         if (arg(2) == 'edit') {
828           if (($categories = _user_categories($account)) && (count($categories) > 1)) {
829             foreach ($categories as $key => $category) {
830               $items[] = array(
831                 'path' => 'user/'. arg(1) .'/edit/'. $category['name'],
832                 'title' => $category['title'],
833                 'type' => $category['name'] == 'account' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
834                 'weight' => $category['weight'],
835                 'access' => ($admin_access || $user->uid == arg(1)));
836             }
837           }
838         }
839       }
840     }
841   }
842
843   return $items;
844 }
845
846 /**
847  * Accepts an user object, $account, or a DA name and returns an associative
848  * array of modules and DA names. Called at external login.
849  */
850 function user_get_authmaps($authname = NULL) {
851   $result = db_query("SELECT authname, module FROM {authmap} WHERE authname = '%s'", $authname);
852   if (db_num_rows($result) > 0) {
853     while ($authmap = db_fetch_object($result)) {
854       $authmaps[$authmap->module] = $authmap->authname;
855     }
856     return $authmaps;
857   }
858   else {
859     return 0;
860   }
861 }
862
863 function user_set_authmaps($account, $authmaps) {
864   foreach ($authmaps as $key => $value) {
865     $module = explode('_', $key, 2);
866     if ($value) {
867       db_query("UPDATE {authmap} SET authname = '%s' WHERE uid = %d AND module = '%s'", $value, $account->uid, $module['1']);
868       if (!db_affected_rows()) {
869         db_query("INSERT INTO {authmap} (authname, uid, module) VALUES ('%s', %d, '%s')", $value, $account->uid, $module[1]);
870       }
871     }
872     else {
873       db_query("DELETE FROM {authmap} WHERE uid = %d AND module = '%s'", $account->uid, $module['1']);
874     }
875   }
876 }
877
878 function user_auth_help_links() {
879   $links = array();
880   foreach (module_list() as $module) {
881     if (module_hook($module, 'auth')) {
882       $links[] = l(module_invoke($module, 'info', 'name'), "user/help#$module");
883     }
884   }
885   return $links;
886 }
887
888 /*** User features *********************************************************/
889
890
891
892 function user_login($msg = '') {
893   global $user, $base_url;
894
895   // If we are already logged on, go to the user
896   // page instead. The added time prevents caching.
897   if ($user->uid) {
898     drupal_goto('user/'. $user->uid, 'time='. time());
899   }
900
901   // Display login form:
902   if ($msg) {
903     $form['message'] = array('#value' => '<p>'. check_plain($msg) .'</p>');
904   }
905   unset($_GET['time']);
906   $form['#action'] = url($_GET['q'], drupal_get_destination());
907   $form['name'] = array('#type' => 'textfield',
908     '#title' => t('Username'),
909     '#size' => 30,
910     '#maxlength' => 60,
911     '#required' => TRUE,
912     '#attributes' => array('tabindex' => '1'),
913   );
914   if (variable_get('drupal_authentication_service', FALSE) && count(user_auth_help_links()) > 0) {
915     $form['name']['#description'] = t('Enter your %s username, or an ID from one of our affiliates: %a.', array('%s' => variable_get('site_name', 'local'), '%a' => implode(', ', user_auth_help_links())));
916   }
917   else {
918     $form['name']['#description'] = t('Enter your %s username.', array('%s' => variable_get('site_name', 'local')));
919   }
920   $form['pass'] = array('#type' => 'password',
921     '#title' => t('Password'),
922     '#description' => t('Enter the password that accompanies your username.'),
923     '#required' => TRUE,
924     '#attributes' => array('tabindex' => '2'),
925   );
926   $form['submit'] = array('#type' => 'submit', '#value' => t('Log in'), '#weight' => 2, '#attributes' => array('tabindex' => '3'));
927   return drupal_get_form('user_login', $form);
928 }
929
930 function user_login_validate($form_id, $form_values) {
931   if ($form_values['name']) {
932     if (user_is_blocked($form_values['name'])) {
933       // blocked in user administration
934       form_set_error('login', t('The username %name has not been activated or is blocked.', array('%name' => theme('placeholder', $form_values['name']))));
935     }
936     else if (drupal_is_denied('user', $form_values['name'])) {
937       // denied by access controls
938       form_set_error('login', t('The name %name is a reserved username.', array('%name' => theme('placeholder', $form_values['name']))));
939     }
940     else if ($form_values['pass']) {
941       $user = user_authenticate($form_values['name'], trim($form_values['pass']));
942
943       if (!$user->uid) {
944         form_set_error('login', t('Sorry. Unrecognized username or password.') .' '. l(t('Have you forgotten your password?'), 'user/password'));
945         watchdog('user', t('Login attempt failed for %user.', array('%user' => theme('placeholder', $form_values['name']))));
946       }
947     }
948   }
949 }
950
951 function user_login_submit($form_id, $form_values) {
952   global $user;
953   if ($user->uid) {
954     // To handle the edge case where this function is called during a
955     // bootstrap, check for the existence of t().
956     if (function_exists('t')) {
957       $message = t('Session opened for %name.', array('%name' => theme('placeholder', $user->name)));
958     }
959     else {
960        $message = "Session opened for ". check_plain($user->name);
961     }
962     watchdog('user', $message);
963
964     // Update the user table timestamp noting user has logged in.
965     db_query("UPDATE {users} SET login = %d WHERE uid = %d", time(), $user->uid);
966
967     user_module_invoke('login', $form_values, $user);
968
969     $old_session_id = session_id();
970     session_regenerate_id();
971     db_query("UPDATE {sessions} SET sid = '%s' WHERE sid = '%s'", session_id(), $old_session_id);
972
973     // Create a timestamped final URL so that browsers don't return the user to
974     // a cached page (where it would appear as if they never logged in or out).
975     return array($_REQUEST['destination'], 'time='. time());
976   }
977 }
978
979 function user_authenticate($name, $pass) {
980   global $user;
981
982   // Try to log in the user locally. Don't set $user unless successful.
983   if ($account = user_load(array('name' => $name, 'pass' => $pass, 'status' => 1))) {
984     $user = $account;
985   }
986
987   // Strip name and server from ID:
988   if ($server = strrchr($name, '@')) {
989     $name = substr($name, 0, strlen($name) - strlen($server));
990     $server = substr($server, 1);
991   }
992
993   // When possible, determine corresponding external auth source. Invoke
994   // source, and log in user if successful:
995   if (!$user->uid && $server && $result = user_get_authmaps("$name@$server")) {
996     if (module_invoke(key($result), 'auth', $name, $pass, $server)) {
997       $user = user_external_load("$name@$server");
998       watchdog('user', t('External load by %user using module %module.', array('%user' => theme('placeholder', $name .'@'. $server), '%module' => theme('placeholder', key($result)))));
999     }
1000     else {
1001       $error = t('Invalid password for %s.', array('%s' => theme('placeholder', $name .'@'. $server)));
1002     }
1003   }
1004
1005   // Try each external authentication source in series. Register user if
1006   // successful.
1007   else if (!$user->uid && $server) {
1008     foreach (module_list() as $module) {
1009       if (module_hook($module, 'auth')) {
1010         if (module_invoke($module, 'auth', $name, $pass, $server)) {
1011           if (variable_get('user_register', 1) == 1) {
1012             $account = user_load(array('name' => "$name@$server"));
1013             if (!$account->uid) { // Register this new user.
1014               $user = user_save('', array('name' => "$name@$server", 'pass' => user_password(), 'init' => "$name@$server", 'status' => 1, "authname_$module" => "$name@$server"));
1015               watchdog('user', t('New external user: %user using module %module.', array('%user' => theme('placeholder', $name .'@'. $server), '%module' => theme('placeholder', $module))), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $user->uid .'/edit'));
1016               break;
1017             }
1018           }
1019         }
1020       }
1021     }
1022   }
1023   return $user;
1024 }
1025
1026 /**
1027  * Menu callback; logs the current user out, and redirects to the home page.
1028  */
1029 function user_logout() {
1030   global $user;
1031
1032   watchdog('user', t('Session closed for %name.', array('%name' => theme('placeholder', $user->name))));
1033
1034   // Destroy the current session:
1035   session_destroy();
1036   module_invoke_all('user', 'logout', NULL, $user);
1037
1038   // We have to use $GLOBALS to unset a global variable:
1039   $user = user_load(array('uid' => 0));
1040
1041   // The time prevents caching.
1042   drupal_goto(NULL, 'time='. time());
1043 }
1044
1045 function user_pass() {
1046
1047   // Display form:
1048   $form['name'] = array('#type' => 'textfield',
1049     '#title' => t('Username'),
1050     '#size' => 30,
1051     '#maxlength' => 60,
1052   );
1053   $form['mail'] = array('#type' => 'textfield',
1054     '#title' => t('E-mail address'),
1055     '#size' => 30,
1056     '#maxlength' => 64,
1057   );
1058   $form['submit'] = array('#type' => 'submit',
1059     '#value' => t('E-mail new password'),
1060     '#weight' => 2,
1061   );
1062   return drupal_get_form('user_pass', $form);
1063 }
1064
1065 function user_pass_validate() {
1066   global $form_values;
1067
1068   $name = $form_values['name'];
1069   $mail = $form_values['mail'];
1070   if ($name && !($form_values['account'] = user_load(array('name' => $name, 'status' => 1)))) {
1071     form_set_error('name', t('Sorry. The username %name is not recognized.', array('%name' => theme('placeholder', $name))));
1072   }
1073   else if ($mail && !($form_values['account'] = user_load(array('mail' => $mail, 'status' => 1)))) {
1074     form_set_error('mail', t('Sorry. The e-mail address %email is not recognized.', array('%email' => theme('placeholder', $mail))));
1075   }
1076   else if (!$mail && !$name) {
1077     form_set_error('password', t('You must provide either a username or e-mail address.'));
1078   }
1079 }
1080
1081 function user_pass_submit($form_id, $form_values) {
1082   global $base_url;
1083
1084   $account = $form_values['account'];
1085   $from = variable_get('site_mail', ini_get('sendmail_from'));
1086
1087   // Mail one time login URL and instructions.
1088   $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));
1089   $subject = _user_mail_text('pass_subject', $variables);
1090   $body = _user_mail_text('pass_body', $variables);
1091   $headers = "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from";
1092   $mail_success = user_mail($account->mail, $subject, $body, $headers);
1093
1094   if ($mail_success) {
1095     watchdog('user', t('Password reset instructions mailed to %name at %email.', array('%name' => '<em>'. $account->name .'</em>', '%email' => '<em>'. $account->mail .'</em>')));
1096     drupal_set_message(t('Further instructions have been sent to your e-mail address.'));
1097   }
1098   else {
1099     watchdog('user', t('Error mailing password reset instructions to %name at %email.', array('%name' => theme('placeholder', $account->name), '%email' => theme('placeholder', $account->mail))), WATCHDOG_ERROR);
1100     drupal_set_message(t('Unable to send mail. Please contact the site admin.'));
1101   }
1102   return 'user';
1103 }
1104
1105 function theme_user_pass($form) {
1106   $output = '<p>'. t('Enter your username <strong><em>or</em></strong> your e-mail address.') .'</p>';
1107   $output .= form_render($form);
1108   return $output;
1109 }
1110
1111 /**
1112  * Menu callback; process one time login link and redirects to the user page on success.
1113  */
1114 function user_pass_reset($uid, $timestamp, $hashed_pass, $action = NULL) {
1115   global $user;
1116
1117   // Check if the user is already logged in. The back button is often the culprit here.
1118   if ($user->uid) {
1119     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.'));
1120     drupal_goto();
1121   }
1122   else {
1123     // Time out, in seconds, until login URL expires. 24 hours = 86400 seconds.
1124     $timeout = 86400;
1125     $current = time();
1126     // Some redundant checks for extra security ?
1127     if ($timestamp < $current && $account = user_load(array('uid' => $uid, 'status' => 1)) ) {
1128       // No time out for first time login.
1129       if ($account->login && $current - $timestamp > $timeout) {
1130         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.'));
1131         drupal_goto('user/password');
1132       }
1133       else if ($account->uid && $timestamp > $account->login && $timestamp < $current && $hashed_pass == user_pass_rehash($account->pass, $timestamp, $account->login)) {
1134         // First stage is a confirmation form, then login
1135         if ($action == 'login') {
1136           watchdog('user', t('User %name used one-time login link at time %timestamp.', array('%name' => "<em>$account->name</em>", '%timestamp' => $timestamp)));
1137           // Update the user table noting user has logged in.
1138           // And this also makes this hashed password a one-time-only login.
1139           db_query("UPDATE {users} SET login = %d WHERE uid = %d", time(), $account->uid);
1140           // Now we can set the new user.
1141           $user = $account;
1142           // And proceed with normal login, going to user page.
1143           user_module_invoke('login', $edit, $user);
1144           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.'));
1145           drupal_goto('user/'. $user->uid .'/edit');
1146         }
1147         else {
1148           $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' => theme('placeholder',$account->name), '%expiration_date' => format_date($timestamp + $timeout))));
1149           $form['help'] = array('#value' => t('<p>This login can be used only once.</p>'));
1150           $form['submit'] = array('#type' => 'submit', '#value' => t('Log in'));
1151           $form['#action'] = url("user/reset/$uid/$timestamp/$hashed_pass/login");
1152           return drupal_get_form('user_pass_reset', $form);
1153         }
1154       }
1155       else {
1156         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.'));
1157         drupal_goto('user/password');
1158       }
1159     }
1160     else {
1161       // Deny access, no more clues.
1162       // Everything will be in the watchdog's URL for the administrator to check.
1163       drupal_access_denied();
1164     }
1165   }
1166 }
1167
1168 function user_pass_reset_url($account) {
1169   $timestamp = time();
1170   return url("user/reset/$account->uid/$timestamp/".user_pass_rehash($account->pass, $timestamp, $account->login), NULL, NULL, TRUE);
1171 }
1172
1173 function user_pass_rehash($password, $timestamp, $login) {
1174   return md5($timestamp . $password . $login);
1175 }
1176
1177 function user_register() {
1178   global $user;
1179
1180   $admin = user_access('administer users');
1181
1182   // If we aren't admin but already logged on, go to the
1183   // user page instead. The added time prevents caching.
1184   if (!$admin && $user->uid) {
1185     drupal_goto('user/'. $user->uid, 'time='. time());
1186   }
1187
1188   // Display the registration form.
1189   if (!$admin) {
1190     $form['user_registration_help'] = array('#type' => 'markup', '#value' => filter_xss_admin(variable_get('user_registration_help', '')));
1191   }
1192   $affiliates = user_auth_help_links();
1193   if (!$admin && count($affiliates) > 0) {
1194     $affiliates = implode(', ', $affiliates);
1195     $form['affiliates'] = array('#type' => 'markup', '#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>');
1196   }
1197   $form['name'] = array('#type' => 'textfield',
1198     '#title' => t('Username'),
1199     '#size' => 30,
1200     '#maxlength' => 60,
1201     '#description' => t('Your full name or your preferred username; only letters, numbers and spaces are allowed.'),
1202     '#required' => TRUE);
1203   $form['mail'] = array('#type' => 'textfield',
1204     '#title' => t('E-mail address'),
1205     '#size' => 30,
1206     '#maxlength' => 64,
1207     '#description' => t('A password and instructions will be sent to this e-mail address, so make sure it is accurate.'),
1208     '#required' => TRUE,
1209   );
1210   if ($admin) {
1211     $form['pass'] = array('#type' => 'password',
1212       '#title' => t('Password'),
1213       '#size' => 30,
1214       '#description' => t('Provide a password for the new account.'),
1215       '#required' => TRUE,
1216     );
1217     $form['notify'] = array(
1218      '#type' => 'checkbox',
1219      '#title' => t('Notify user of new account')
1220     );
1221   }
1222   $extra = _user_forms($null, $null, $null, 'register');
1223
1224   // Only display form_group around default fields if there are other groups.
1225   if ($extra) {
1226     $form['account'] = array('#type' => 'fieldset', '#title' => t('Account information'));
1227     $form['account']['name'] = $form['name'];
1228     $form['account']['mail'] = $form['mail'];
1229     $form['account']['pass'] = $form['pass'];
1230     $form['account']['notify'] = $form['notify'];
1231     unset($form['name'], $form['mail'], $form['pass'], $form['notify']);
1232     $form = array_merge($form, $extra);
1233   }
1234   $form['submit'] = array('#type' => 'submit', '#value' => t('Create new account'), '#weight' => 30);
1235
1236   return drupal_get_form('user_register', $form);
1237 }
1238
1239 function user_register_validate($form_id, $form_values) {
1240   user_module_invoke('validate', $form_values, $form_values, 'account');
1241 }
1242
1243 function user_register_submit($form_id, $form_values) {
1244   global $base_url;
1245
1246   $admin = user_access('administer users');
1247
1248   $mail = $form_values['mail'];
1249   $name = $form_values['name'];
1250   $pass = $admin ? $form_values['pass'] : user_password();
1251   $notify = $form_values['notify'];
1252   $from = variable_get('site_mail', ini_get('sendmail_from'));
1253
1254   if (!$admin && array_intersect(array_keys($form_values), array('uid', 'roles', 'init', 'session', 'status'))) {
1255     watchdog('security', t('Detected malicious attempt to alter protected user fields.'), WATCHDOG_WARNING);
1256     return 'user/register';
1257   }
1258
1259   $account = user_save('', array_merge($form_values, array('pass' => $pass, 'init' => $mail, 'status' => ($admin || variable_get('user_register', 1) == 1))));
1260   watchdog('user', t('New user: %name %email.', array('%name' => theme('placeholder', $name), '%email' => theme('placeholder', '<'. $mail .'>'))), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit'));
1261
1262   $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));
1263
1264   // The first user may login immediately, and receives a customized welcome e-mail.
1265   if ($account->uid == 1) {
1266     user_mail($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: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from");
1267     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">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'))));
1268     user_authenticate($account->name, trim($pass));
1269
1270     // Set the installed schema version of the system module to the most recent version.
1271     include_once './includes/install.inc';
1272     drupal_set_installed_schema_version('system', max(drupal_get_schema_versions('system')));
1273
1274     return 'user/1/edit';
1275   }
1276   else {
1277     if ($admin && !$notify) {
1278       drupal_set_message(t('Created a new user account. No e-mail has been sent.'));
1279
1280       return 'admin/user';
1281     }
1282     else if ($account->status || $notify) {
1283       // Create new user account, no administrator approval required.
1284       $subject = $notify ? _user_mail_text('admin_subject', $variables) : _user_mail_text('welcome_subject', $variables);
1285       $body = $notify ? _user_mail_text('admin_body', $variables) : _user_mail_text('welcome_body', $variables);
1286
1287       user_mail($mail, $subject, $body, "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from");
1288
1289       if ($notify) {
1290         drupal_set_message(t('Password and further instructions have been e-mailed to the new user %user.', array('%user' => theme('placeholder', $name))));
1291         return 'admin/user';
1292       }
1293       else {
1294         drupal_set_message(t('Your password and further instructions have been sent to your e-mail address.'));
1295         return '';
1296       }
1297     }
1298     else {
1299       // Create new user account, administrator approval required.
1300       $subject = _user_mail_text('approval_subject', $variables);
1301       $body = _user_mail_text('approval_body', $variables);
1302
1303       user_mail($mail, $subject, $body, "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from");
1304       user_mail(variable_get('site_mail', ini_get('sendmail_from')), $subject, t("%u has applied for an account.\n\n%uri", array('%u' => $account->name, '%uri' => url("user/$account->uid/edit", NULL, NULL, TRUE))), "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from");
1305       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.'));
1306
1307     }
1308   }
1309 }
1310
1311 function user_edit_form($uid, $edit) {
1312   // Account information:
1313   $form['account'] = array('#type' => 'fieldset',
1314     '#title' => t('Account information'),
1315   );
1316   if (user_access('change own username') || user_access('administer users')) {
1317     $form['account']['name'] = array('#type' => 'textfield',
1318       '#title' => t('Username'),
1319       '#default_value' => $edit['name'],
1320       '#maxlength' => 60,
1321       '#description' => t('Your full name or your preferred username: only letters, numbers and spaces are allowed.'),
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' => 64,
1329     '#description' => t('Insert 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   $form['account']['pass'] = array('#type' => 'password_confirm',
1333     '#title' => t('Password'),
1334     '#description' => t('To change the current user password, enter the new password in both fields.'),
1335   );
1336   if (user_access('administer users')) {
1337     $form['account']['status'] = array('#type' => 'radios', '#title' => t('Status'), '#default_value' => $edit['status'], '#options' => array(t('Blocked'), t('Active')));
1338   }
1339   if (user_access('administer access control')) {
1340     $roles = user_roles(1);
1341     unset($roles[DRUPAL_AUTHENTICATED_RID]);
1342     if ($roles) {
1343       $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' => theme('placeholder', t('authenticated user')))));
1344     }
1345   }
1346
1347   // Picture/avatar:
1348   if (variable_get('user_pictures', 0)) {
1349     $form['picture'] = array('#type' => 'fieldset', '#title' => t('Picture'), '#weight' => 1);
1350     $picture = theme('user_picture', (object)$edit);
1351     if ($picture) {
1352       $form['picture']['current_picture'] = array('#type' => 'markup', '#value' => $picture);
1353       $form['picture']['picture_delete'] = array('#type' => 'checkbox', '#title' => t('Delete picture'), '#description' => t('Check this box to delete your current picture.'));
1354     } else {
1355       $form['picture']['picture_delete'] = array('#type' => 'hidden');
1356     }
1357     $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', ''));
1358   }
1359
1360   return $form;
1361 }
1362
1363 function _user_edit_validate($uid, &$edit) {
1364   $user = user_load(array('uid' => $uid));
1365   // Validate the username:
1366   if (user_access('change own username') || user_access('administer users') || arg(1) == 'register') {
1367     if ($error = user_validate_name($edit['name'])) {
1368       form_set_error('name', $error);
1369     }
1370     else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(name) = LOWER('%s')", $uid, $edit['name'])) > 0) {
1371       form_set_error('name', t('The name %name is already taken.', array('%name' => theme('placeholder', $edit['name']))));
1372     }
1373     else if (drupal_is_denied('user', $edit['name'])) {
1374       form_set_error('name', t('The name %name has been denied access.', array('%name' => theme('placeholder', $edit['name']))));
1375     }
1376   }
1377
1378   // Validate the e-mail address:
1379   if ($error = user_validate_mail($edit['mail'])) {
1380     form_set_error('mail', $error);
1381   }
1382   else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(mail) = LOWER('%s')", $uid, $edit['mail'])) > 0) {
1383     form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => theme('placeholder', $edit['mail']))));
1384   }
1385   else if (drupal_is_denied('mail', $edit['mail'])) {
1386     form_set_error('mail', t('The e-mail address %email has been denied access.', array('%email' => theme('placeholder', $edit['mail']))));
1387   }
1388
1389   // If required, validate the uploaded picture.
1390   if ($file = file_check_upload('picture_upload')) {
1391     user_validate_picture($file, $edit, $user);
1392   }
1393 }
1394
1395 function _user_edit_submit($uid, &$edit) {
1396   $user = user_load(array('uid' => $uid));
1397   // Delete picture if requested, and if no replacement picture was given.
1398   if ($edit['picture_delete']) {
1399     if ($user->picture && file_exists($user->picture)) {
1400       file_delete($user->picture);
1401     }
1402     $edit['picture'] = '';
1403   }
1404   if (isset($edit['roles'])) {
1405     $edit['roles'] = array_filter($edit['roles']);
1406   }
1407 }
1408
1409 function user_edit($category = 'account') {
1410   global $user;
1411
1412   $account = user_load(array('uid' => arg(1)));
1413   if ($account === FALSE) {
1414     drupal_set_message(t('The account does not exist or has already been deleted.'));
1415     drupal_goto('admin/user');
1416   }
1417   $edit = $_POST['op'] ? $_POST['edit'] : (array)$account;
1418
1419   if (arg(2) == 'delete') {
1420     if ($edit['confirm']) {
1421       db_query('DELETE FROM {users} WHERE uid = %d', $account->uid);
1422       db_query('DELETE FROM {sessions} WHERE uid = %d', $account->uid);
1423       db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid);
1424       db_query('DELETE FROM {authmap} WHERE uid = %d', $account->uid);
1425       watchdog('user', t('Deleted user: %name %email.', array('%name' => theme('placeholder', $account->name), '%email' => theme('placeholder', '<'. $account->mail .'>'))), WATCHDOG_NOTICE);
1426       drupal_set_message(t('The account has been deleted.'));
1427       module_invoke_all('user', 'delete', $edit, $account);
1428       drupal_goto('admin/user');
1429     }
1430     else {
1431       return confirm_form('user_confirm_delete', array(), t('Are you sure you want to delete the account %name?', array('%name' => theme('placeholder', $account->name))), 'user/'. $account->uid, t('All submissions made by this user will be attributed to the anonymous account. This action cannot be undone.'), t('Delete'), t('Cancel'));
1432     }
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   }
1442
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';
1451
1452   drupal_set_title($account->name);
1453   return drupal_get_form('user_edit', $form);
1454 }
1455
1456 function user_edit_validate($form_id, $form_values) {
1457   user_module_invoke('validate', $form_values, $form_values['_account'], $form_values['_category']);
1458   // Validate input to ensure that non-privileged users can't alter protected data.
1459   if ((!user_access('administer users') && array_intersect(array_keys($form_values), array('uid', 'init', 'session'))) || (!user_access('administer access control') && isset($form_values['roles']))) {
1460     $message = t('Detected malicious attempt to alter protected user fields.');
1461     watchdog('security', $message, WATCHDOG_WARNING);
1462     // set this to a value type field
1463     form_set_error('category', $message);
1464   }
1465 }
1466
1467 function user_edit_submit($form_id, $form_values) {
1468   $account = $form_values['_account'];
1469   $category = $form_values['_category'];
1470   unset($form_values['_account'], $form_values['submit'], $form_values['delete'], $form_values['form_id'], $form_values['_category']);
1471   user_module_invoke('submit', $form_values, $account, $category);
1472   user_save($account, $form_values, $category);
1473   // Delete that user's menu cache.
1474   cache_clear_all('menu:'. $account->uid, TRUE);
1475   drupal_set_message(t('The changes have been saved.'));
1476   return 'user/'. $account->uid;
1477 }
1478
1479 function user_view($uid = 0) {
1480   global $user;
1481
1482   $account = user_load(array('uid' => $uid));
1483   if ($account === FALSE || ($account->access == 0 && !user_access('administer users'))) {
1484     return drupal_not_found();
1485   }
1486   // Retrieve and merge all profile fields:
1487   $fields = array();
1488   foreach (module_list() as $module) {
1489     if ($data = module_invoke($module, 'user', 'view', '', $account)) {
1490       foreach ($data as $category => $items) {
1491         foreach ($items as $item) {
1492           $item['class'] = "$module-". $item['class'];
1493           $fields[$category][] = $item;
1494         }
1495       }
1496     }
1497   }
1498   drupal_set_title($account->name);
1499   return theme('user_profile', $account, $fields);
1500 }
1501
1502 /*** Administrative features ***********************************************/
1503
1504 function _user_mail_text($messageid, $variables = array()) {
1505
1506   // Check if an admin setting overrides the default string.
1507   if ($admin_setting = variable_get('user_mail_' . $messageid, FALSE)) {
1508     return strtr($admin_setting, $variables);
1509   }
1510   // No override, return with default strings.
1511   else {
1512     switch ($messageid) {
1513       case 'welcome_subject':
1514         return t('Account details for %username at %site', $variables);
1515       case 'welcome_body':
1516         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\nYour new %site membership also enables to you to login to other Drupal powered websites (e.g. http://drupal.org/) without registering. Just use the following Drupal ID and password:\n\nDrupal ID: %username@%uri_brief\npassword: %password\n\n\n--  %site team", $variables);
1517       case 'admin_subject':
1518         return t('An administrator created an account for you at %site', $variables);
1519       case 'admin_body':
1520         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\nYour new %site membership also enables to you to login to other Drupal powered websites (e.g. http://www.drupal.org/) without registering. Just use the following Drupal ID and password:\n\nDrupal ID: %username@%uri_brief\npassword: %password\n\n\n--  %site team", $variables);
1521       case 'approval_subject':
1522         return t('Account details for %username at %site (pending admin approval)', $variables);
1523       case 'approval_body':
1524         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\nYour new %site membership also enables to you to login to other Drupal powered websites (e.g. http://www.drop.org/) without registering. Just use the following Drupal ID and password:\n\nDrupal ID: %username@%uri_brief\npassword: %password\n\n\n--  %site team", $variables);
1525       case 'pass_subject':
1526         return t('Replacement login information for %username at %site', $variables);
1527       case 'pass_body':
1528         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);
1529     }
1530   }
1531 }
1532
1533 function user_configure_settings() {
1534 }
1535
1536 /**
1537  * Menu callback: check an access rule
1538  */
1539 function user_admin_access_check() {
1540   $form['user'] = array('#type' => 'fieldset', '#title' => t('Username'));
1541   $form['user']['test'] = array('#type' => 'textfield', '#title' => '', '#description' => t('Enter a username to check if it will be denied or allowed.'), '#size' => 30, '#maxlength' => 64);
1542   $form['user']['type'] = array('#type' => 'hidden', '#value' => 'user');
1543   $form['user']['submit'] = array('#type' => 'submit', '#value' => t('Check username'));
1544   $output .= drupal_get_form('check_user', $form, 'user_admin_access_check');
1545   unset($form); // prevent endless loop?
1546
1547   $form['mail'] = array('#type' => 'fieldset', '#title' => t('E-mail'));
1548   $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' => 64);
1549   $form['mail']['type'] = array('#type' => 'hidden', '#value' => 'mail');
1550   $form['mail']['submit'] = array('#type' => 'submit', '#value' => t('Check e-mail'));
1551   $output .= drupal_get_form('check_mail', $form, 'user_admin_access_check');
1552   unset($form); // prevent endless loop?
1553
1554   $form['host'] = array('#type' => 'fieldset', '#title' => t('Hostname'));
1555   $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);
1556   $form['host']['type'] = array('#type' => 'hidden', '#value' => 'host');
1557   $form['host']['submit'] = array('#type' => 'submit', '#value' => t('Check hostname'));
1558   $output .= drupal_get_form('check_host', $form, 'user_admin_access_check');
1559   unset($form); // prevent endless loop?
1560
1561   return $output;
1562 }
1563
1564 function user_admin_access_check_validate($form_id, $edit) {
1565   if (empty($edit['test'])) {
1566     form_set_error($edit['type'], t('No value entered. Please enter a test string and try again.'));
1567   }
1568 }
1569
1570 function user_admin_access_check_submit($form_id, $edit) {
1571   switch ($edit['type']) {
1572     case 'user':
1573       if (drupal_is_denied('user', $edit['test'])) {
1574         drupal_set_message(t('The username %name is not allowed.', array('%name' => theme('placeholder', $edit['test']))));
1575       }
1576       else {
1577         drupal_set_message(t('The username %name is allowed.', array('%name' => theme('placeholder', $edit['test']))));
1578       }
1579       break;
1580     case 'mail':
1581       if (drupal_is_denied('mail', $edit['test'])) {
1582         drupal_set_message(t('The e-mail address %mail is not allowed.', array('%mail' => theme('placeholder', $edit['test']))));
1583       }
1584       else {
1585         drupal_set_message(t('The e-mail address %mail is allowed.', array('%mail' => theme('placeholder', $edit['test']))));
1586       }
1587       break;
1588     case 'host':
1589       if (drupal_is_denied('host', $edit['test'])) {
1590         drupal_set_message(t('The hostname %host is not allowed.', array('%host' => theme('placeholder', $edit['test']))));
1591       }
1592       else {
1593         drupal_set_message(t('The hostname %host is allowed.', array('%host' => theme('placeholder', $edit['test']))));
1594       }
1595       break;
1596     default:
1597       break;
1598   }
1599 }
1600
1601 /**
1602  * Menu callback: add an access rule
1603  */
1604 function user_admin_access_add($mask = NULL, $type = NULL) {
1605   if ($edit = $_POST['edit']) {
1606     if (!$edit['mask']) {
1607       form_set_error('mask', t('You must enter a mask.'));
1608     }
1609     else {
1610       $aid = db_next_id('{access}_aid');
1611       db_query("INSERT INTO {access} (aid, mask, type, status) VALUES ('%s', '%s', '%s', %d)", $aid, $edit['mask'], $edit['type'], $edit['status']);
1612       drupal_set_message(t('The access rule has been added.'));
1613       drupal_goto('admin/access/rules');
1614     }
1615   }
1616   else {
1617     $edit['mask'] = $mask;
1618     $edit['type'] = $type;
1619   }
1620
1621   $form = _user_admin_access_form($edit);
1622   $form['submit'] = array('#type' => 'submit', '#value' => t('Add rule'));
1623
1624   return drupal_get_form('access_rule', $form);
1625 }
1626
1627 /**
1628  * Menu callback: delete an access rule
1629  */
1630 function user_admin_access_delete($aid = 0) {
1631   $access_types = array('user' => t('username'), 'mail' => t('e-mail'));
1632   $edit = db_fetch_object(db_query('SELECT aid, type, status, mask FROM {access} WHERE aid = %d', $aid));
1633
1634   $form = array();
1635   $form['aid'] = array('#type' => 'hidden', '#value' => $aid);
1636   $output = confirm_form('user_admin_access_delete_confirm', $form,
1637                   t('Are you sure you want to delete the %type rule for %rule?', array('%type' => $access_types[$edit->type], '%rule' => theme('placeholder', $edit->mask))),
1638                   'admin/access/rules',
1639                   t('This action cannot be undone.'),
1640                   t('Delete'),
1641                   t('Cancel'));
1642   return $output;
1643 }
1644
1645 function user_admin_access_delete_confirm_submit($form_id, $edit) {
1646   db_query('DELETE FROM {access} WHERE aid = %d', $edit['aid']);
1647   drupal_set_message(t('The access rule has been deleted.'));
1648   return 'admin/access/rules';
1649 }
1650
1651 /**
1652  * Menu callback: edit an access rule
1653  */
1654 function user_admin_access_edit($aid = 0) {
1655   if ($edit = $_POST['edit']) {
1656     if (!$edit['mask']) {
1657       form_set_error('mask', t('You must enter a mask.'));
1658     }
1659     else {
1660       db_query("UPDATE {access} SET mask = '%s', type = '%s', status = '%s' WHERE aid = %d", $edit['mask'], $edit['type'], $edit['status'], $aid);
1661       drupal_set_message(t('The access rule has been saved.'));
1662       drupal_goto('admin/access/rules');
1663     }
1664   }
1665   else {
1666     $edit = db_fetch_array(db_query('SELECT aid, type, status, mask FROM {access} WHERE aid = %d', $aid));
1667   }
1668   $form = _user_admin_access_form($edit);
1669   $form['submit'] = array('#type' => 'submit', '#value' => t('Save rule'));
1670
1671   return drupal_get_form('access_rule', $form);
1672 }
1673
1674 function _user_admin_access_form($edit) {
1675   $form['status'] = array(
1676     '#type' => 'radios',
1677     '#title' => t('Access type'),
1678     '#default_value' => 0,
1679     '#options' => array('1' => t('Allow'), '0' => t('Deny')),
1680   );
1681   $type_options = array('user' => t('Username'), 'mail' => t('E-mail'), 'host' => t('Host'));
1682   $form['type'] = array(
1683     '#type' => 'radios',
1684     '#title' => t('Rule type'),
1685     '#default_value' => (isset($type_options[$edit['type']]) ? $edit['type'] : 'user'),
1686     '#options' => $type_options,
1687   );
1688   $form['mask'] = array(
1689     '#type' => 'textfield',
1690     '#title' => t('Mask'),
1691     '#size' => 30,
1692     '#maxlength' => 64,
1693     '#default_value' => $edit['mask'],
1694     '#description' => '%: '. t('Matches any number of characters, even zero characters') .'.<br />_: '. t('Matches exactly one character.'),
1695     '#required' => TRUE,
1696   );
1697   return $form;
1698 }
1699
1700 /**
1701  * Menu callback: list all access rules
1702  */
1703 function user_admin_access() {
1704   $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));
1705   $result = db_query("SELECT aid, type, status, mask FROM {access}". tablesort_sql($header));
1706   $access_types = array('user' => t('username'), 'mail' => t('e-mail'), 'host' => t('host'));
1707   $rows = array();
1708   while ($rule = db_fetch_object($result)) {
1709     $rows[] = array($rule->status ? t('allow') : t('deny'), $access_types[$rule->type], $rule->mask, l(t('edit'), 'admin/access/rules/edit/'. $rule->aid), l(t('delete'), 'admin/access/rules/delete/'. $rule->aid));
1710   }
1711   if (count($rows) == 0) {
1712     $rows[] = array(array('data' => '<em>'. t('There are currently no access rules.') .'</em>', 'colspan' => 5));
1713   }
1714   $output .= theme('table', $header, $rows);
1715
1716   return $output;
1717 }
1718
1719 /**
1720  * Retrieve an array of roles matching specified conditions.
1721  *
1722  * @param $membersonly
1723  *   Set this to TRUE to exclude the 'anonymous' role.
1724  * @param $permission
1725  *   A string containing a permission. If set, only roles containing that permission are returned.
1726  *
1727  * @return
1728  *   An associative array with the role id as the key and the role name as value.
1729  */
1730 function user_roles($membersonly = 0, $permission = 0) {
1731   $roles = array();
1732
1733   if ($permission) {
1734     $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);
1735   }
1736   else {
1737     $result = db_query('SELECT * FROM {role} ORDER BY name');
1738   }
1739   while ($role = db_fetch_object($result)) {
1740     if (!$membersonly || ($membersonly && $role->rid != DRUPAL_ANONYMOUS_RID)) {
1741       $roles[$role->rid] = $role->name;
1742     }
1743   }
1744   return $roles;
1745 }
1746
1747 /**
1748  * Menu callback: administer permissions.
1749  */
1750 function user_admin_perm($str_rids = NULL) {
1751   if (preg_match('/^([0-9]+[+ ])*[0-9]+$/', $str_rids)) {
1752     // The '+' character in a query string may be parsed as ' '.
1753     $rids = preg_split('/[+ ]/', $str_rids);
1754   }
1755
1756   if($rids) {
1757     $breadcrumbs = drupal_get_breadcrumb();
1758     $breadcrumbs[] = l(t('all roles'), 'admin/access');
1759     drupal_set_breadcrumb($breadcrumbs);
1760     $result = db_query('SELECT r.rid, p.perm FROM {role} r LEFT JOIN {permission} p ON r.rid = p.rid WHERE r.rid IN (%s) ORDER BY name', implode(', ', $rids));
1761   }
1762   else {
1763     $result = db_query('SELECT r.rid, p.perm FROM {role} r LEFT JOIN {permission} p ON r.rid = p.rid ORDER BY name');
1764   }
1765
1766   // Compile role array:
1767   $roles = array();
1768   while ($role = db_fetch_object($result)) {
1769     $role_permissions[$role->rid] = $role->perm;
1770   }
1771
1772   if($rids) {
1773     $result = db_query('SELECT rid, name FROM {role} r WHERE r.rid IN (%s) ORDER BY name', implode(', ', $rids));
1774   }
1775   else {
1776     $result = db_query('SELECT rid, name FROM {role} ORDER BY name');
1777   }
1778   $role_names = array();
1779   while ($role = db_fetch_object($result)) {
1780     $role_names[$role->rid] = $role->name;
1781   }
1782
1783   // Render role/permission overview:
1784   $options = array();
1785   foreach (module_list(FALSE, FALSE, TRUE) as $module) {
1786     if ($permissions = module_invoke($module, 'perm')) {
1787       $form['permission'][] = array('#type' => 'markup', '#value' => t('%module module', array('%module' => $module)));
1788       asort($permissions);
1789       foreach ($permissions as $perm) {
1790         $options[$perm] = '';
1791         $form['permission'][$perm] = array('#type' => 'markup', '#value' => t($perm));
1792         foreach ($role_names as $rid => $name) {
1793           // Builds arrays for checked boxes for each role
1794           if (strstr($role_permissions[$rid], $perm)) {
1795             $status[$rid][] = $perm;
1796           }
1797         }
1798       }
1799     }
1800   }
1801   // Have to build checkboxes here after checkbox arrays are built
1802   foreach ($role_names as $rid => $name) {
1803     $form['checkboxes'][$rid] = array('#type' => 'checkboxes', '#options' => $options, '#default_value' => $status[$rid]);
1804     $form['role_names'][$rid] = array('#type' => 'markup', '#value' => l($name, 'admin/access/'. $rid), '#tree' => TRUE);
1805   }
1806   $form['submit'] = array('#type' => 'submit', '#value' => t('Save permissions'));
1807
1808   return drupal_get_form('user_admin_perm', $form);
1809 }
1810
1811 function theme_user_admin_perm($form) {
1812   foreach (element_children($form['permission']) as $key) {
1813     // Don't take form control structures
1814     if (is_array($form['permission'][$key])) {
1815       $row = array();
1816       // Module name
1817       if (is_numeric($key)) {
1818         $row[] = array('data' => form_render($form['permission'][$key]), 'class' => 'module', 'colspan' => count($form['role_names']) + 1);
1819       // Permissions
1820       }
1821       else {
1822         $row[] = array('data' => form_render($form['permission'][$key]), 'class' => 'permission');
1823         foreach (element_children($form['checkboxes']) as $rid) {
1824           if (is_array($form['checkboxes'][$rid])) {
1825             $row[] = array('data' => form_render($form['checkboxes'][$rid][$key]), 'align' => 'center', 'title' => t($key));
1826           }
1827         }
1828       }
1829       $rows[] = $row;
1830     }
1831   }
1832   $header[] = (t('Permission'));
1833   foreach (element_children($form['role_names']) as $rid) {
1834     if (is_array($form['role_names'][$rid])) {
1835       $header[] = form_render($form['role_names'][$rid]);
1836     }
1837   }
1838   $output = theme('table', $header, $rows, array('id' => 'permissions'));
1839   $output .= form_render($form);
1840   return $output;
1841 }
1842
1843 function user_admin_perm_submit($form_id, $edit) {
1844   // Save permissions:
1845   $result = db_query('SELECT * FROM {role}');
1846   while ($role = db_fetch_object($result)) {
1847     if(isset($edit[$role->rid])) {
1848       // Delete, so if we clear every checkbox we reset that role;
1849       // otherwise permissions are active and denied everywhere.
1850       db_query('DELETE FROM {permission} WHERE rid = %d', $role->rid);
1851       foreach ($edit[$role->rid] as $key => $value) {
1852         if (!$value) {
1853           unset($edit[$role->rid][$key]);
1854         }
1855       }
1856       if (count($edit[$role->rid])) {
1857         db_query("INSERT INTO {permission} (rid, perm) VALUES (%d, '%s')", $role->rid, implode(', ', array_keys($edit[$role->rid])));
1858       }
1859     }
1860   }
1861
1862   drupal_set_message(t('The changes have been saved.'));
1863
1864   // Clear the cached pages and menus:
1865   menu_rebuild();
1866
1867 }
1868
1869 /**
1870  * Menu callback: administer roles.
1871  */
1872 function user_admin_role() {
1873   $edit = isset($_POST['edit']) ? $_POST['edit'] : '';
1874   $op = isset($_POST['op']) ? $_POST['op'] : '';
1875   $id = arg(4);
1876
1877   if ($op == t('Save role')) {
1878     if ($edit['name']) {
1879       db_query("UPDATE {role} SET name = '%s' WHERE rid = %d", $edit['name'], $id);
1880       drupal_set_message(t('The changes have been saved.'));
1881       drupal_goto('admin/access/roles');
1882     }
1883     else {
1884       form_set_error('name', t('You must specify a valid role name.'));
1885     }
1886   }
1887   else if ($op == t('Delete role')) {
1888     db_query('DELETE FROM {role} WHERE rid = %d', $id);
1889     db_query('DELETE FROM {permission} WHERE rid = %d', $id);
1890     // Update the users who have this role set:
1891     db_query('DELETE FROM {users_roles} WHERE rid = %d', $id);
1892
1893     drupal_set_message(t('The role has been deleted.'));
1894     drupal_goto('admin/access/roles');
1895   }
1896   else if ($op == t('Add role')) {
1897     if ($edit['name']) {
1898       db_query("INSERT INTO {role} (name) VALUES ('%s')", $edit['name']);
1899       drupal_set_message(t('The role has been added.'));
1900       drupal_goto('admin/access/roles');
1901     }
1902     else {
1903       form_set_error('name', t('You must specify a valid role name.'));
1904     }
1905   }
1906   else if ($id) {
1907     // Display the role form.
1908     $role = db_fetch_object(db_query('SELECT * FROM {role} WHERE rid = %d', $id));
1909     $form['name'] = array('#type' => 'textfield', '#title' => t('Role name'), '#default_value' => $role->name, '#size' => 30, '#maxlength' => 64, '#description' => t('The name for this role. Example: "moderator", "editorial board", "site architect".'));
1910     $form['submit'] = array('#type' => 'submit', '#value' => t('Save role'));
1911     $form['delete'] = array('#type' => 'submit', '#value' => t('Delete role'));
1912     return drupal_get_form('user_admin_role', $form);
1913   }
1914   $form['name'] = array('#type' => 'textfield', '#size' => 32, '#maxlength' => 64);
1915   $form['submit'] = array('#type' => 'submit', '#value' => t('Add role'));
1916   return drupal_get_form('user_admin_new_role', $form);
1917 }
1918
1919 function theme_user_admin_new_role($form) {
1920   $header = array(t('Name'), t('Operations'));
1921   foreach (user_roles() as $rid => $name) {
1922     if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
1923       $rows[] = array($name, l(t('edit'), 'admin/access/roles/edit/'. $rid));
1924     }
1925     else {
1926       $rows[] = array($name, '<span class="disabled">'. t('locked') .'</span>');
1927     }
1928   }
1929   $rows[] = array(form_render($form['name']), form_render($form['submit']));
1930
1931   return theme('table', $header, $rows);
1932 }
1933
1934 function user_admin_account() {
1935   $header = array(
1936     array('data' => t('Username'), 'field' => 'u.name'),
1937     array('data' => t('Status'), 'field' => 'u.status'),
1938     array('data' => t('Member for'), 'field' => 'u.created', 'sort' => 'desc'),
1939     array('data' => t('Last access'), 'field' => 'u.access'),
1940     t('Operations')
1941   );
1942   $sql = 'SELECT u.uid, u.name, u.status, u.created, u.access FROM {users} u WHERE uid != 0';
1943   $sql .= tablesort_sql($header);
1944   $result = pager_query($sql, 50);
1945
1946   $status = array(t('blocked'), t('active'));
1947   while ($account = db_fetch_object($result)) {
1948     $rows[] = array(theme('username', $account),
1949                     $status[$account->status],
1950                     format_interval(time() - $account->created),
1951                     $account->access ? t('%time ago', array('%time' => format_interval(time() - $account->access))) : t('never'),
1952                     l(t('edit'), "user/$account->uid/edit", array()));
1953   }
1954
1955   $output = theme('table', $header, $rows);
1956   $output .= theme('pager', NULL, 50, 0);
1957   return $output;
1958 }
1959
1960 function user_configure() {
1961   // User registration settings.
1962   $form['registration'] = array('#type' => 'fieldset', '#title' => t('User registration settings'));
1963   $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.')));
1964   $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.'));
1965
1966   // User e-mail settings.
1967   $form['email'] = array('#type' => 'fieldset', '#title' => t('User e-mail settings'));
1968   $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.');
1969   $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.');
1970   $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.');
1971   $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.');
1972   $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.');
1973   $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.');
1974   $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.');
1975   $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.');
1976
1977   // If picture support is enabled, check whether the picture directory exists:
1978   if (variable_get('user_pictures', 0)) {
1979     $picture_path = file_create_path(variable_get('user_picture_path', 'pictures'));
1980     file_check_directory($picture_path, 1, 'user_picture_path');
1981   }
1982
1983   $form['pictures'] = array('#type' => 'fieldset', '#title' => t('Pictures'));
1984   $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.'));
1985   $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() .'/')));
1986   $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.'));
1987   $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.'));
1988   $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.'));
1989   $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.'));
1990
1991   return system_settings_form('user_configure_settings', $form);
1992 }
1993
1994 function user_admin() {
1995   $edit = isset($_POST['edit']) ? $_POST['edit'] : '';
1996   $op = isset($_POST['op']) ? $_POST['op'] : '';
1997
1998   if (empty($op)) {
1999     $op = arg(2);
2000   }
2001
2002   switch ($op) {
2003     case 'search':
2004     case t('Search'):
2005       $output = search_form(url('admin/user/search'), $_POST['edit']['keys'], 'user') . search_data($_POST['edit']['keys'], 'user');
2006       break;
2007     case t('Create new account'):
2008     case 'create':
2009       $output = user_register();
2010       break;
2011     default:
2012       $output = user_admin_account();
2013   }
2014   return $output;
2015 }
2016
2017 /**
2018  * Implementation of hook_help().
2019  */
2020 function user_help($section) {
2021   global $user;
2022
2023   switch ($section) {
2024     case 'admin/help#user':
2025       $output = '<p>'. t('The user module allows users to register, login, and logout. 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>';
2026       $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>';
2027       $output .= t('<p>You can</p>
2028 <ul>
2029 <li>view your <a href="%user">user page</a>.</li>
2030 <li>administer users at <a href="%admin-user">administer &gt;&gt; user</a>.</li>
2031 <li>allow users who have the "select different theme" permission to select themes from their user account by enabling themes in <a href="%admin-themes">administer &gt;&gt; themes</a>.</li>
2032 <li>read user profile help at <a href="%admin-help-profile">administer &gt;&gt; help &gt;&gt; profile</a>.</li>
2033 <li>read about distributed authentication in the system module help at <a href="%admin-help-system">administer &gt;&gt; help &gt;&gt; system</a>.</li>
2034 </ul>
2035 ', array('%user' => url('user'), '%admin-user' => url('admin/user'), '%admin-themes' => url('admin/themes'), '%admin-help-profile' => url('admin/help/profile'), '%admin-help-system' => url('admin/help/system')));
2036       $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>';
2037       return $output;
2038     case 'admin/modules#description':
2039       return t('Manages the user registration and login system.');
2040     case 'admin/user':
2041       return t('<p>Drupal allows users to register, login, logout, maintain user profiles, etc. No participant can use his own name to post content until he signs up for a user account.</p>');
2042     case 'admin/user/create':
2043     case 'admin/user/account/create':
2044       return t('<p>This web page allows the administrators to register a 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>');
2045     case strstr($section, 'admin/access/rules'):
2046       return t('<p>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>');
2047     case 'admin/access':
2048       return t('<p>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.</p>', array('%role' => url('admin/access/roles')));
2049     case 'admin/access/roles':
2050       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>
2051       <ul>
2052       <li>Anonymous user: this role is used for users that don\'t have a user account or that are not authenticated.</li>
2053       <li>Authenticated user: this role is automatically granted to all logged in users.</li>
2054       </ul>', array('%permissions' => url('admin/access/permissions')));
2055     case 'admin/user/search':
2056       return t('<p>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>');
2057     case 'admin/user/configure':
2058     case 'admin/user/configure/settings':
2059       return t('<p>In order to use the full power of Drupal a visitor must sign up for an account. This page lets you setup how a user signs up, logs out, the guidelines from the system about user subscriptions, and the e-mails the system will send to the user.</p>');
2060     case 'user/help#user':
2061       $site = variable_get('site_name', 'this website');
2062
2063       $output = t("
2064       <h3>Distributed authentication<a id=\"da\"></a></h3>
2065       <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 %help-links. This capability is called <em>distributed authentication</em>, and is unique to <a href=\"%drupal\">Drupal</a>, the software which powers %site.</p>
2066       <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('%help-links' => (implode(', ', user_auth_help_links())), '%site' => "<em>$site</em>", '%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'));
2067
2068         foreach (module_list() as $module) {
2069           if (module_hook($module, 'auth')) {
2070             $output .= "<h4><a id=\"$module\"></a>". module_invoke($module, 'info', 'name') .'</h4>';
2071             $output .= module_invoke($module, 'help', "user/help#$module");
2072           }
2073         }
2074
2075         return $output;
2076   }
2077
2078 }
2079
2080 /**
2081  * Menu callback; Prints user-specific help information.
2082  */
2083 function user_help_page() {
2084   return user_help('user/help#user');
2085 }
2086
2087 /**
2088  * Retrieve a list of all user setting/information categories and sort them by weight.
2089  */
2090 function _user_categories($account) {
2091   $categories = array();
2092
2093   foreach (module_list() as $module) {
2094     if ($data = module_invoke($module, 'user', 'categories', NULL, $account, '')) {
2095       $categories = array_merge($data, $categories);
2096     }
2097   }
2098
2099   usort($categories, '_user_sort');
2100
2101   return $categories;
2102 }
2103
2104 function _user_sort($a, $b) {
2105   return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1));
2106 }
2107
2108 /**
2109  * Retrieve a list of all form elements for the specified category.
2110  */
2111 function _user_forms(&$edit, $account, $category, $hook = 'form') {
2112   $groups = array();
2113   foreach (module_list() as $module) {
2114     if ($data = module_invoke($module, 'user', $hook, $edit, $account, $category)) {
2115       $groups = array_merge_recursive($data, $groups);
2116     }
2117   }
2118   uasort($groups, '_user_sort');
2119
2120   return empty($groups) ? FALSE : $groups;
2121 }
2122
2123 /**
2124  * Retrieve a pipe delimited string of autocomplete suggestions for existing users
2125  */
2126 function user_autocomplete($string) {
2127   $matches = array();
2128   $result = db_query_range("SELECT name FROM {users} WHERE LOWER(name) LIKE LOWER('%s%%')", $string, 0, 10);
2129   while ($user = db_fetch_object($result)) {
2130     $matches[$user->name] = check_plain($user->name);
2131   }
2132   print drupal_to_js($matches);
2133   exit();
2134 }