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