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