user.module 108 KB
Newer Older
Dries Buytaert's avatar
   
Dries Buytaert committed
1
2
3
<?php
// $Id$

Dries Buytaert's avatar
   
Dries Buytaert committed
4
5
6
7
8
/**
 * @file
 * Enables the user registration and login system.
 */

9
10
11
/**
 * Invokes hook_user() in every module.
 *
12
 * We cannot use module_invoke() for this, because the arguments need to
13
14
 * be passed by reference.
 */
15
function user_module_invoke($type, &$array, &$user, $category = NULL) {
Dries Buytaert's avatar
   
Dries Buytaert committed
16
17
  foreach (module_list() as $module) {
    $function = $module .'_user';
18
19
20
    if (function_exists($function)) {
      $function($type, $array, $user, $category);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
21
22
23
  }
}

Dries Buytaert's avatar
   
Dries Buytaert committed
24
function user_external_load($authname) {
Dries Buytaert's avatar
   
Dries Buytaert committed
25
  $result = db_query("SELECT uid FROM {authmap} WHERE authname = '%s'", $authname);
Dries Buytaert's avatar
   
Dries Buytaert committed
26

Dries Buytaert's avatar
   
Dries Buytaert committed
27
28
  if ($user = db_fetch_object($result)) {
    return user_load($user);
Dries Buytaert's avatar
   
Dries Buytaert committed
29
30
31
32
33
34
  }
  else {
    return 0;
  }
}

35
36
37
38
39
/**
 * Fetch a user object.
 *
 * @param $array
 *   An associative array of attributes to search for in selecting the
40
 *   user, such as user name or e-mail address.
41
42
 *
 * @return
43
 *   A fully-loaded $user object upon successful user load or FALSE if user cannot be loaded.
44
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
45
function user_load($array = array()) {
46
  // Dynamically compose a SQL query:
47
  $query = array();
48
  $params = array();
49

Dries Buytaert's avatar
   
Dries Buytaert committed
50
  foreach ($array as $key => $value) {
51
52
    if ($key == 'uid' || $key == 'status') {
      $query[] = "$key = %d";
53
      $params[] = $value;
54
    }
55
56
57
58
    else if ($key == 'pass') {
      $query[] = "pass = '%s'";
      $params[] = md5($value);
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
59
    else {
60
      $query[]= "LOWER($key) = LOWER('%s')";
61
      $params[] = $value;
Dries Buytaert's avatar
   
Dries Buytaert committed
62
63
    }
  }
64
  $result = db_query('SELECT * FROM {users} u WHERE ' . implode(' AND ', $query), $params);
Dries Buytaert's avatar
   
Dries Buytaert committed
65

66
67
68
  if (db_num_rows($result)) {
    $user = db_fetch_object($result);
    $user = drupal_unpack($user);
Dries Buytaert's avatar
   
Dries Buytaert committed
69

70
    $user->roles = array();
71
72
73
74
75
76
    if ($user->uid) {
      $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
    }
    else {
      $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
    }
77
78
79
80
    $result = db_query('SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d', $user->uid);
    while ($role = db_fetch_object($result)) {
      $user->roles[$role->rid] = $role->name;
    }
81
    user_module_invoke('load', $array, $user);
82
83
  }
  else {
84
    $user = FALSE;
Dries Buytaert's avatar
   
Dries Buytaert committed
85
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
86
87
88
89

  return $user;
}

90
/**
91
 * Save changes to a user account or add a new user.
92
93
 *
 * @param $account
94
95
 *   The $user object for the user to modify or add. If $user->uid is
 *   omitted, a new user will be added.
96
97
98
 *
 * @param $array
 *   An array of fields and values to save. For example array('name' => 'My name');
99
 *   Setting a field to NULL deletes it from the data column.
100
101
102
103
 *
 * @param $category
 *   (optional) The category for storing profile information in.
 */
104
function user_save($account, $array = array(), $category = 'account') {
105
  // Dynamically compose a SQL query:
106
  $user_fields = user_fields();
Dries Buytaert's avatar
   
Dries Buytaert committed
107
  if ($account->uid) {
108
    user_module_invoke('update', $array, $account, $category);
Dries Buytaert's avatar
   
Dries Buytaert committed
109

110
    $data = unserialize(db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $account->uid)));
Dries Buytaert's avatar
   
Dries Buytaert committed
111
    foreach ($array as $key => $value) {
112
      if ($key == 'pass' && !empty($value)) {
Dries Buytaert's avatar
   
Dries Buytaert committed
113
114
        $query .= "$key = '%s', ";
        $v[] = md5($value);
Dries Buytaert's avatar
   
Dries Buytaert committed
115
      }
116
      else if ((substr($key, 0, 4) !== 'auth') && ($key != 'pass')) {
117
        if (in_array($key, $user_fields)) {
118
          // Save standard fields
Dries Buytaert's avatar
   
Dries Buytaert committed
119
120
          $query .= "$key = '%s', ";
          $v[] = $value;
Dries Buytaert's avatar
   
Dries Buytaert committed
121
        }
Dries Buytaert's avatar
   
Dries Buytaert committed
122
        else if ($key != 'roles') {
123
          // Roles is a special case: it used below.
124
          if ($value === NULL) {
125
126
127
128
129
            unset($data[$key]);
          }
          else {
            $data[$key] = $value;
          }
Dries Buytaert's avatar
   
Dries Buytaert committed
130
        }
Dries Buytaert's avatar
   
Dries Buytaert committed
131
132
      }
    }
133
    $query .= "data = '%s' ";
Dries Buytaert's avatar
   
Dries Buytaert committed
134
    $v[] = serialize($data);
Dries Buytaert's avatar
   
Dries Buytaert committed
135

136
    db_query("UPDATE {users} SET $query WHERE uid = %d", array_merge($v, array($account->uid)));
Dries Buytaert's avatar
   
Dries Buytaert committed
137

138
    // Reload user roles if provided
139
    if (is_array($array['roles'])) {
140
      db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid);
Dries Buytaert's avatar
   
Dries Buytaert committed
141

142
      foreach (array_keys($array['roles']) as $rid) {
143
144
145
        if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
          db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid);
        }
146
      }
Dries Buytaert's avatar
   
Dries Buytaert committed
147
148
    }

149
    // Delete a blocked user's sessions to kick them if they are online.
150
    if (isset($array['status']) && $array['status'] == 0) {
151
      sess_destroy($account->uid);
152
153
    }

154
    // Refresh user object
Dries Buytaert's avatar
   
Dries Buytaert committed
155
    $user = user_load(array('uid' => $account->uid));
156
    user_module_invoke('after_update', $array, $user, $category);
Dries Buytaert's avatar
   
Dries Buytaert committed
157
158
  }
  else {
159
    $array['uid'] = db_next_id('{users}_uid');
Dries Buytaert's avatar
   
Dries Buytaert committed
160

161
162
163
164
    if (!isset($array['created'])) {    // Allow 'created' to be set by hook_auth
      $array['created'] = time();
    }

165
166
167
    // Note, we wait with saving the data column to prevent module-handled
    // fields from being saved there. We cannot invoke hook_user('insert') here
    // because we don't have a fully initialized user object yet.
Dries Buytaert's avatar
   
Dries Buytaert committed
168
    foreach ($array as $key => $value) {
169
170
171
172
      switch($key) {
        case 'pass':
          $fields[] = $key;
          $values[] = md5($value);
Dries Buytaert's avatar
   
Dries Buytaert committed
173
          $s[] = "'%s'";
174
          break;
175
176
177
178
179
180
181
182
183
184
185
186
187
188
        case 'uid':        case 'mode':     case 'sort':
        case 'threshold':  case 'created':  case 'access':
        case 'login':      case 'status':
          $fields[] = $key;
          $values[] = $value;
          $s[] = "%d";
          break;
        default:
          if (substr($key, 0, 4) !== 'auth' && in_array($key, $user_fields)) {
            $fields[] = $key;
            $values[] = $value;
            $s[] = "'%s'";
          }
          break;
Dries Buytaert's avatar
   
Dries Buytaert committed
189
190
      }
    }
191
    db_query('INSERT INTO {users} ('. implode(', ', $fields) .') VALUES ('. implode(', ', $s) .')', $values);
Dries Buytaert's avatar
   
Dries Buytaert committed
192

193
194
    // Build the initial user object.
    $user = user_load(array('uid' => $array['uid']));
Dries Buytaert's avatar
   
Dries Buytaert committed
195

196
197
198
199
200
    user_module_invoke('insert', $array, $user, $category);

    // Build and save the serialized data field now
    $data = array();
    foreach ($array as $key => $value) {
201
      if ((substr($key, 0, 4) !== 'auth') && ($key != 'roles') && (!in_array($key, $user_fields)) && ($value !== NULL)) {
202
203
204
205
206
        $data[$key] = $value;
      }
    }
    db_query("UPDATE {users} SET data = '%s' WHERE uid = %d", serialize($data), $user->uid);

207
    // Save user roles (delete just to be safe).
208
209
210
211
212
213
    if (is_array($array['roles'])) {
      db_query('DELETE FROM {users_roles} WHERE uid = %d', $array['uid']);
      foreach (array_keys($array['roles']) as $rid) {
        if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
          db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $array['uid'], $rid);
        }
214
215
216
      }
    }

217
218
    // Build the finished user object.
    $user = user_load(array('uid' => $array['uid']));
Dries Buytaert's avatar
   
Dries Buytaert committed
219
220
  }

221
  // Save distributed authentication mappings
222
  $authmaps = array();
Dries Buytaert's avatar
   
Dries Buytaert committed
223
  foreach ($array as $key => $value) {
Dries Buytaert's avatar
   
Dries Buytaert committed
224
    if (substr($key, 0, 4) == 'auth') {
Dries Buytaert's avatar
   
Dries Buytaert committed
225
226
227
      $authmaps[$key] = $value;
    }
  }
228
  if (sizeof($authmaps) > 0) {
Dries Buytaert's avatar
   
Dries Buytaert committed
229
    user_set_authmaps($user, $authmaps);
Dries Buytaert's avatar
   
Dries Buytaert committed
230
231
232
233
234
  }

  return $user;
}

235
236
237
/**
 * Verify the syntax of the given name.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
238
function user_validate_name($name) {
239
  if (!strlen($name)) return t('You must enter a username.');
240
241
242
  if (substr($name, 0, 1) == ' ') return t('The username cannot begin with a space.');
  if (substr($name, -1) == ' ') return t('The username cannot end with a space.');
  if (ereg('  ', $name)) return t('The username cannot contain multiple spaces in a row.');
243
  if (ereg("[^\x80-\xF7 [:alnum:]@_.-]", $name)) return t('The username contains an illegal character.');
244
245
246
247
248
249
250
251
252
253
254
  if (preg_match('/[\x{80}-\x{A0}'.          // Non-printable ISO-8859-1 + NBSP
                   '\x{AD}'.                 // Soft-hyphen
                   '\x{2000}-\x{200F}'.      // Various space characters
                   '\x{2028}-\x{202F}'.      // Bidirectional text overrides
                   '\x{205F}-\x{206F}'.      // Various text hinting characters
                   '\x{FEFF}'.               // Byte order mark
                   '\x{FF01}-\x{FF60}'.      // Full-width latin
                   '\x{FFF9}-\x{FFFD}]/u',   // Replacement characters
                   $name)) {
    return t('The username contains an illegal character.');
  }
255
  if (ereg('@', $name) && !eregi('@([0-9a-z](-?[0-9a-z])*.)+[a-z]{2}([zmuvtg]|fo|me)?$', $name)) return t('The username is not a valid authentication ID.');
256
  if (strlen($name) > 56) return t('The username %name is too long: it must be less than 56 characters.', array('%name' => $name));
Dries Buytaert's avatar
   
Dries Buytaert committed
257
258
259
}

function user_validate_mail($mail) {
260
  if (!$mail) return t('You must enter an e-mail address.');
261
  if (!valid_email_address($mail)) {
262
    return t('The e-mail address %mail is not valid.', array('%mail' => $mail));
Dries Buytaert's avatar
   
Dries Buytaert committed
263
264
265
  }
}

Dries Buytaert's avatar
   
Dries Buytaert committed
266
function user_validate_picture($file, &$edit, $user) {
267
  global $form_values;
268
  // Initialize the picture:
269
  $form_values['picture'] = $user->picture;
Dries Buytaert's avatar
   
Dries Buytaert committed
270

271
272
  // Check that uploaded file is an image, with a maximum file size
  // and maximum height/width.
273
  $info = image_get_info($file->filepath);
274
  list($maxwidth, $maxheight) = explode('x', variable_get('user_picture_dimensions', '85x85'));
Dries Buytaert's avatar
   
Dries Buytaert committed
275

276
  if (!$info || !$info['extension']) {
277
    form_set_error('picture_upload', t('The uploaded file was not an image.'));
Dries Buytaert's avatar
   
Dries Buytaert committed
278
  }
279
280
  else if (image_get_toolkit()) {
    image_scale($file->filepath, $file->filepath, $maxwidth, $maxheight);
Dries Buytaert's avatar
   
Dries Buytaert committed
281
  }
282
  else if (filesize($file->filepath) > (variable_get('user_picture_file_size', '30') * 1000)) {
283
    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'))));
284
  }
285
  else if ($info['width'] > $maxwidth || $info['height'] > $maxheight) {
286
    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'))));
Dries Buytaert's avatar
   
Dries Buytaert committed
287
  }
288
289

  if (!form_get_errors()) {
290
    if ($file = file_save_upload('picture_upload', variable_get('user_picture_path', 'pictures') .'/picture-'. $user->uid .'.'. $info['extension'], 1)) {
291
      $form_values['picture'] = $file->filepath;
292
293
    }
    else {
294
      form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist.", array('%directory' => variable_get('user_picture_path', 'pictures'))));
295
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
296
297
298
  }
}

299
300
301
/**
 * Generate a random alphanumeric password.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
302
303
function user_password($length = 10) {
  // This variable contains the list of allowable characters for the
304
305
  // password. Note that the number 0 and the letter 'O' have been
  // removed to avoid confusion between the two. The same is true
306
307
  // of 'I', 1, and l.
  $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
308

309
310
  // Zero-based count of characters in the allowable list:
  $len = strlen($allowable_characters) - 1;
Dries Buytaert's avatar
   
Dries Buytaert committed
311

312
313
  // Declare the password as a blank string.
  $pass = '';
Dries Buytaert's avatar
   
Dries Buytaert committed
314

315
  // Loop the number of times specified by $length.
Dries Buytaert's avatar
   
Dries Buytaert committed
316
317
318
319
  for ($i = 0; $i < $length; $i++) {

    // Each iteration, pick a random character from the
    // allowable string and append it to the password:
320
    $pass .= $allowable_characters[mt_rand(0, $len)];
Dries Buytaert's avatar
   
Dries Buytaert committed
321
322
323
  }

  return $pass;
Dries Buytaert's avatar
   
Dries Buytaert committed
324
325
}

326
327
328
329
330
/**
 * Determine whether the user has a given privilege.
 *
 * @param $string
 *   The permission, such as "administer nodes", being checked for.
Dries Buytaert's avatar
   
Dries Buytaert committed
331
332
 * @param $account
 *   (optional) The account to check, if not given use currently logged in user.
333
334
 *
 * @return
335
 *   boolean TRUE if the current user has the requested permission.
336
337
338
339
340
 *
 * All permission checks in Drupal should go through this function. This
 * way, we guarantee consistent behavior, and ensure that the superuser
 * can perform all actions.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
341
function user_access($string, $account = NULL) {
Dries Buytaert's avatar
   
Dries Buytaert committed
342
  global $user;
Dries Buytaert's avatar
   
Dries Buytaert committed
343
  static $perm = array();
Dries Buytaert's avatar
   
Dries Buytaert committed
344

345
346
347
348
  if (is_null($account)) {
    $account = $user;
  }

349
  // User #1 has all privileges:
350
  if ($account->uid == 1) {
351
    return TRUE;
Dries Buytaert's avatar
   
Dries Buytaert committed
352
353
  }

354
355
  // To reduce the number of SQL queries, we cache the user's permissions
  // in a static variable.
356
  if (!isset($perm[$account->uid])) {
357
    $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)));
Dries Buytaert's avatar
   
Dries Buytaert committed
358

Steven Wittens's avatar
Steven Wittens committed
359
    $perm[$account->uid] = '';
Dries Buytaert's avatar
   
Dries Buytaert committed
360
    while ($row = db_fetch_object($result)) {
361
      $perm[$account->uid] .= "$row->perm, ";
Dries Buytaert's avatar
   
Dries Buytaert committed
362
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
363
  }
364

365
  if (isset($perm[$account->uid])) {
366
    return strpos($perm[$account->uid], "$string, ") !== FALSE;
367
  }
368

369
  return FALSE;
Dries Buytaert's avatar
   
Dries Buytaert committed
370
371
}

372
373
374
/**
 * Checks for usernames blocked by user administration
 *
375
 * @return boolean TRUE for blocked users, FALSE for active
376
377
378
379
380
381
382
383
 */
function user_is_blocked($name) {
  $allow = db_fetch_object(db_query("SELECT * FROM {users} WHERE status = 1 AND name = LOWER('%s')", $name));
  $deny  = db_fetch_object(db_query("SELECT * FROM {users} WHERE status = 0 AND name = LOWER('%s')", $name));

  return $deny && !$allow;
}

Dries Buytaert's avatar
   
Dries Buytaert committed
384
385
function user_fields() {
  static $fields;
Dries Buytaert's avatar
   
Dries Buytaert committed
386

Dries Buytaert's avatar
   
Dries Buytaert committed
387
  if (!$fields) {
388
    $result = db_query('SELECT * FROM {users} WHERE uid = 1');
389
390
391
    if (db_num_rows($result)) {
      $fields = array_keys(db_fetch_array($result));
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
392
393
    else {
      // Make sure we return the default fields at least
394
      $fields = array('uid', 'name', 'pass', 'mail', 'picture', 'mode', 'sort', 'threshold', 'theme', 'signature', 'created', 'access', 'login', 'status', 'timezone', 'language', 'init', 'data');
Dries Buytaert's avatar
   
Dries Buytaert committed
395
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
396
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
397

Dries Buytaert's avatar
   
Dries Buytaert committed
398
  return $fields;
Dries Buytaert's avatar
   
Dries Buytaert committed
399
400
}

401
402
403
/**
 * Implementation of hook_perm().
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
404
function user_perm() {
405
  return array('administer access control', 'administer users', 'access user profiles', 'change own username');
Dries Buytaert's avatar
   
Dries Buytaert committed
406
407
}

408
409
410
411
412
/**
 * Implementation of hook_file_download().
 *
 * Ensure that user pictures (avatars) are always downloadable.
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
413
function user_file_download($file) {
Steven Wittens's avatar
Steven Wittens committed
414
  if (strpos($file, variable_get('user_picture_path', 'pictures') .'/picture-') === 0) {
415
416
    $info = image_get_info(file_create_path($file));
    return array('Content-type: '. $info['mime_type']);
Dries Buytaert's avatar
   
Dries Buytaert committed
417
418
419
  }
}

420
421
422
/**
 * Implementation of hook_search().
 */
423
function user_search($op = 'search', $keys = NULL) {
424
425
  switch ($op) {
    case 'name':
426
427
428
      if (user_access('access user profiles')) {
        return t('users');
      }
429
    case 'search':
430
431
432
433
434
435
      if (user_access('access user profiles')) {
        $find = array();
        // Replace wildcards with MySQL/PostgreSQL wildcards.
        $keys = preg_replace('!\*+!', '%', $keys);
        $result = pager_query("SELECT * FROM {users} WHERE LOWER(name) LIKE LOWER('%%%s%%')", 15, 0, NULL, $keys);
        while ($account = db_fetch_object($result)) {
436
          $find[] = array('title' => $account->name, 'link' => url('user/'. $account->uid));
437
438
        }
        return $find;
439
      }
Dries Buytaert's avatar
   
Dries Buytaert committed
440
441
442
  }
}

443
444
445
/**
 * Implementation of hook_user().
 */
446
function user_user($type, &$edit, &$user, $category = NULL) {
447
  if ($type == 'view') {
448
    $items['history'] = array('title' => t('Member for'),
449
450
451
452
453
      'value' => format_interval(time() - $user->created),
      'class' => 'member',
    );

    return array(t('History') => $items);
454
  }
455
456
457
458
459
  if ($type == 'form' && $category == 'account') {
    return user_edit_form(arg(1), $edit);
  }

  if ($type == 'validate' && $category == 'account') {
460
    return _user_edit_validate(arg(1), $edit);
461
462
  }

463
464
465
466
  if ($type == 'submit' && $category == 'account') {
    return _user_edit_submit(arg(1), $edit);
  }

467
468
469
  if ($type == 'categories') {
    return array(array('name' => 'account', 'title' => t('account settings'), 'weight' => 1));
  }
470
471
}

472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
function user_login_block() {
  $form = array(
    '#action' => url($_GET['q'], drupal_get_destination()),
    '#id' => 'user-login-form',
    '#base' => 'user_login',
  );
  $form['name'] = array('#type' => 'textfield',
    '#title' => t('Username'),
    '#maxlength' => 60,
    '#size' => 15,
    '#required' => TRUE,
  );
  $form['pass'] = array('#type' => 'password',
    '#title' => t('Password'),
    '#size' => 15,
    '#required' => TRUE,
  );
  $form['submit'] = array('#type' => 'submit',
    '#value' => t('Log in'),
  );
  $items = array();
  if (variable_get('user_register', 1)) {
    $items[] = l(t('Create new account'), 'user/register', array('title' => t('Create a new user account.')));
  }
  $items[] = l(t('Request new password'), 'user/password', array('title' => t('Request new password via e-mail.')));
  $form['links'] = array('#value' => theme('item_list', $items));
  return $form;
}

501
502
503
/**
 * Implementation of hook_block().
 */
504
function user_block($op = 'list', $delta = 0, $edit = array()) {
Dries Buytaert's avatar
   
Dries Buytaert committed
505
506
  global $user;

507
508
509
510
511
  if ($op == 'list') {
     $blocks[0]['info'] = t('User login');
     $blocks[1]['info'] = t('Navigation');
     $blocks[2]['info'] = t('Who\'s new');
     $blocks[3]['info'] = t('Who\'s online');
512

513
     return $blocks;
514
  }
515
516
  else if ($op == 'configure' && $delta == 3) {
    $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), 'format_interval');
517
518
    $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.'));
    $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.'));
519

520
    return $form;
521
522
523
524
525
526
  }
  else if ($op == 'save' && $delta == 3) {
    variable_set('user_block_seconds_online', $edit['user_block_seconds_online']);
    variable_set('user_block_max_list_count', $edit['user_block_max_list_count']);
  }
  else if ($op == 'view') {
Dries Buytaert's avatar
   
Dries Buytaert committed
527
528
    $block = array();

Dries Buytaert's avatar
   
Dries Buytaert committed
529
530
    switch ($delta) {
      case 0:
Dries Buytaert's avatar
Dries Buytaert committed
531
532
        // For usability's sake, avoid showing two login forms on one page.
        if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
Dries Buytaert's avatar
   
Dries Buytaert committed
533

534
          $block['subject'] = t('User login');
535
          $block['content'] = drupal_get_form('user_login_block');
Dries Buytaert's avatar
   
Dries Buytaert committed
536
        }
Dries Buytaert's avatar
Dries Buytaert committed
537
        return $block;
Dries Buytaert's avatar
Dries Buytaert committed
538

539
      case 1:
Dries Buytaert's avatar
   
Dries Buytaert committed
540
        if ($menu = theme('menu_tree')) {
541
           $block['subject'] = $user->uid ? $user->name : t('Navigation');
542
           $block['content'] = $menu;
Dries Buytaert's avatar
   
Dries Buytaert committed
543
        }
544
        return $block;
Dries Buytaert's avatar
Dries Buytaert committed
545

Dries Buytaert's avatar
   
Dries Buytaert committed
546
      case 2:
547
        if (user_access('access content')) {
Steven Wittens's avatar
Steven Wittens committed
548
          // Retrieve a list of new users who have subsequently accessed the site successfully.
549
          $result = db_query_range('SELECT uid, name FROM {users} WHERE status != 0 AND access != 0 ORDER BY created DESC', 0, 5);
550
          while ($account = db_fetch_object($result)) {
551
            $items[] = $account;
552
          }
553
          $output = theme('user_list', $items);
Dries Buytaert's avatar
   
Dries Buytaert committed
554

555
556
          $block['subject'] = t('Who\'s new');
          $block['content'] = $output;
557
        }
Dries Buytaert's avatar
Dries Buytaert committed
558
559
        return $block;

Dries Buytaert's avatar
   
Dries Buytaert committed
560
      case 3:
561
        if (user_access('access content')) {
562
          // Count users with activity in the past defined period.
563
          $time_period = time() - variable_get('user_block_seconds_online', 900);
Dries Buytaert's avatar
   
Dries Buytaert committed
564

565
          // Perform database queries to gather online user lists.
566
567
568
569
          $anonymous_count = sess_count($time_period);
          $authenticated_count = sess_count($time_period, false);
          $authenticated_users = db_query('SELECT uid, name, access FROM {users} WHERE access >= %d AND uid != 0 ORDER BY access DESC', time() - $time_period);

Dries Buytaert's avatar
   
Dries Buytaert committed
570

571
          // Format the output with proper grammar.
572
573
          if ($anonymous_count == 1 && $authenticated_count == 1) {
            $output = t('There is currently %members and %visitors online.', array('%members' => format_plural($authenticated_count, '1 user', '@count users'), '%visitors' => format_plural($anonymous_count, '1 guest', '@count guests')));
Dries Buytaert's avatar
   
Dries Buytaert committed
574
575
          }
          else {
576
            $output = t('There are currently %members and %visitors online.', array('%members' => format_plural($authenticated_count, '1 user', '@count users'), '%visitors' => format_plural($anonymous_count, '1 guest', '@count guests')));
Dries Buytaert's avatar
   
Dries Buytaert committed
577
578
          }

579
580
          // Display a list of currently online users.
          $max_users = variable_get('user_block_max_list_count', 10);
581
          if ($authenticated_count && $max_users) {
582
            $items = array();
583

584
            while ($max_users-- && $account = db_fetch_object($authenticated_users)) {
585
586
              $items[] = $account;
            }
587

588
589
            $output .= theme('user_list', $items, t('Online users'));
          }
590

591
592
          $block['subject'] = t('Who\'s online');
          $block['content'] = $output;
Dries Buytaert's avatar
   
Dries Buytaert committed
593
        }
Dries Buytaert's avatar
   
Dries Buytaert committed
594
        return $block;
Dries Buytaert's avatar
   
Dries Buytaert committed
595
596
    }
  }
597
598
}

Dries Buytaert's avatar
   
Dries Buytaert committed
599
600
601
602
603
604
605
606
607
function theme_user_picture($account) {
  if (variable_get('user_pictures', 0)) {
    if ($account->picture && file_exists($account->picture)) {
      $picture = file_create_url($account->picture);
    }
    else if (variable_get('user_picture_default', '')) {
      $picture = variable_get('user_picture_default', '');
    }

608
    if (isset($picture)) {
609
      $alt = t('@user\'s picture', array('@user' => $account->name ? $account->name : variable_get('anonymous', 'Anonymous')));
610
      $picture = theme('image', $picture, $alt, $alt, '', FALSE);
611
      if (!empty($account->uid) && user_access('access user profiles')) {
612
        $picture = l($picture, "user/$account->uid", array('title' => t('View user profile.')), NULL, NULL, FALSE, TRUE);
Dries Buytaert's avatar
   
Dries Buytaert committed
613
614
615
616
617
618
619
      }

      return "<div class=\"picture\">$picture</div>";
    }
  }
}

620
621
622
/**
 * Theme a user page
 * @param $account the user object
623
624
625
626
627
 * @param $fields a multidimensional array for the fields, in the form of array (
 *   'category1' => array(item_array1, item_array2), 'category2' => array(item_array3,
 *    .. etc.). Item arrays are formatted as array(array('title' => 'item title',
 * 'value' => 'item value', 'class' => 'class-name'), ... etc.). Module names are incorporated
 * into the CSS class.
628
629
630
 *
 * @ingroup themeable
 */
631
function theme_user_profile($account, $fields) {
632
  $output = '<div class="profile">';
Dries Buytaert's avatar
   
Dries Buytaert committed
633
  $output .= theme('user_picture', $account);
634
  foreach ($fields as $category => $items) {
635
    if (strlen($category) > 0) {
636
      $output .= '<h2 class="title">'. $category .'</h2>';
637
    }
638
639
    $output .= '<dl>';
    foreach ($items as $item) {
640
      if (isset($item['title'])) {
641
        $output .= '<dt class="'. $item['class'] .'">'. $item['title'] .'</dt>';
642
643
      }
      $output .= '<dd class="'. $item['class'] .'">'. $item['value'] .'</dd>';
644
645
    }
    $output .= '</dl>';
646
  }
647
  $output .= '</div>';
Dries Buytaert's avatar
   
Dries Buytaert committed
648
649
650
651

  return $output;
}

652
653
654
655
656
657
658
/**
 * Make a list of users.
 * @param $items an array with user objects. Should contain at least the name and uid
 *
 * @ingroup themeable
 */
function theme_user_list($users, $title = NULL) {
659
660
661
662
  if (!empty($users)) {
    foreach ($users as $user) {
      $items[] = theme('username', $user);
    }
663
  }
664
  return theme('item_list', $items, $title);
Dries Buytaert's avatar
   
Dries Buytaert committed
665
666
}

Dries Buytaert's avatar
   
Dries Buytaert committed
667
/**
Dries Buytaert's avatar
   
Dries Buytaert committed
668
 * Implementation of hook_menu().
Dries Buytaert's avatar
   
Dries Buytaert committed
669
 */
Dries Buytaert's avatar
   
Dries Buytaert committed
670
function user_menu($may_cache) {
Dries Buytaert's avatar
   
Dries Buytaert committed
671
  global $user;
Dries Buytaert's avatar
   
Dries Buytaert committed
672

Dries Buytaert's avatar
   
Dries Buytaert committed
673
  $items = array();
Dries Buytaert's avatar
   
Dries Buytaert committed
674

675
  $admin_access = user_access('administer users');
676
  $access_access = user_access('administer access control');
677
  $view_access = user_access('access user profiles');
Dries Buytaert's avatar
   
Dries Buytaert committed
678

Dries Buytaert's avatar
   
Dries Buytaert committed
679
  if ($may_cache) {
Dries Buytaert's avatar
   
Dries Buytaert committed
680
    $items[] = array('path' => 'user', 'title' => t('user account'),
681
      'callback' => 'drupal_get_form', 'callback arguments' => array('user_login'), 'access' => TRUE, 'type' => MENU_CALLBACK);
682

Steven Wittens's avatar
Steven Wittens committed
683
    $items[] = array('path' => 'user/autocomplete', 'title' => t('user autocomplete'),
684
      'callback' => 'user_autocomplete', 'access' => $view_access, 'type' => MENU_CALLBACK);
Steven Wittens's avatar
Steven Wittens committed
685

Steven Wittens's avatar
Steven Wittens committed
686
    // Registration and login pages.
Dries Buytaert's avatar
   
Dries Buytaert committed
687
    $items[] = array('path' => 'user/login', 'title' => t('log in'),
688
      'callback' => 'drupal_get_form', 'callback arguments' => array('user_login'), 'type' => MENU_DEFAULT_LOCAL_TASK);
689
    $items[] = array('path' => 'user/register', 'title' => t('create new account'),
690
      'callback' => 'drupal_get_form', 'callback arguments' => array('user_register'), 'access' => $user->uid == 0 && variable_get('user_register', 1), 'type' => MENU_LOCAL_TASK);
Dries Buytaert's avatar
   
Dries Buytaert committed
691
    $items[] = array('path' => 'user/password', 'title' => t('request new password'),
692
      'callback' => 'drupal_get_form', 'callback arguments' => array('user_pass'), 'access' => $user->uid == 0, 'type' => MENU_LOCAL_TASK);
693
    $items[] = array('path' => 'user/reset', 'title' => t('reset password'),
694
      'callback' => 'drupal_get_form', 'callback arguments' => array('user_pass_reset'), 'access' => TRUE, 'type' => MENU_CALLBACK);
Dries Buytaert's avatar
   
Dries Buytaert committed
695
696
    $items[] = array('path' => 'user/help', 'title' => t('help'),
      'callback' => 'user_help_page', 'type' => MENU_CALLBACK);
Dries Buytaert's avatar
   
Dries Buytaert committed
697

Steven Wittens's avatar
Steven Wittens committed
698
    // Admin user pages
699
700
701
702
703
704
705
706
707
    $items[] = array('path' => 'admin/user',
      'title' => t('user management'),
      'description' => t('Manage your site\'s users, groups and access to site features.'),
      'position' => 'left',
      'callback' => 'system_admin_menu_block_page',
      'access' => user_access('access configuration pages'),
    );
    $items[] = array('path' => 'admin/user/user', 'title' => t('users'),
      'description' => t('List, add, and edit users.'),
708
      'callback' => 'user_admin', 'callback arguments' => array('list'), 'access' => $admin_access);
709
    $items[] = array('path' => 'admin/user/user/list', 'title' => t('list'),
Dries Buytaert's avatar
   
Dries Buytaert committed
710
      'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
711
    $items[] = array('path' => 'admin/user/user/create', 'title' => t('add user'),
712
      'callback' => 'user_admin', 'callback arguments' => array('create'), 'access' => $admin_access,
Dries Buytaert's avatar
   
Dries Buytaert committed
713
      'type' => MENU_LOCAL_TASK);
714
715
    $items[] = array('path' => 'admin/user/settings', 'title' => t('user settings'),
      'description' => t('Configure default behavior of users, including registration requirements, e-mails, and user pictures.'),
716
      'callback' => 'drupal_get_form', 'callback arguments' => array('user_admin_settings'));
717

Steven Wittens's avatar
Steven Wittens committed
718
    // Admin access pages
719
720
    $items[] = array('path' => 'admin/user/access', 'title' => t('access control'),
      'description' => t('Determine access to features by selecting permissions for roles.'),
721
      'callback' => 'drupal_get_form', 'callback arguments' => array('user_admin_perm'), 'access' => $access_access);
722
723
    $items[] = array('path' => 'admin/user/roles', 'title' => t('roles'),
      'description' => t('List, edit, or add user roles.'),
724
      'callback' => 'drupal_get_form', 'callback arguments' => array('user_admin_new_role'), 'access' => $access_access,
725
726
      'type' => MENU_NORMAL_ITEM);
    $items[] = array('path' => 'admin/user/roles/edit', 'title' => t('edit role'),
727
       'callback' => 'drupal_get_form', 'callback arguments' => array('user_admin_role'), 'access' => $access_access,
728
      'type' => MENU_CALLBACK);
729
730
731
732
    $items[] = array('path' => 'admin/user/rules', 'title' => t('access rules'),
      'description' => t('List and create rules to disallow usernames, e-mail addresses, and IP addresses.'),
      'callback' => 'user_admin_access', 'access' => $access_access);
    $items[] = array('path' => 'admin/user/rules/list', 'title' => t('list'),
733
      'access' => $access_access, 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
734
    $items[] = array('path' => 'admin/user/rules/add', 'title' => t('add rule'),
735
      'callback' => 'user_admin_access_add', 'access' => $access_access,
Dries Buytaert's avatar
   
Dries Buytaert committed
736
      'type' => MENU_LOCAL_TASK);
737
    $items[] = array('path' => 'admin/user/rules/check', 'title' => t('check rules'),
738
      'callback' => 'user_admin_access_check', 'access' => $access_access,
Dries Buytaert's avatar
   
Dries Buytaert committed
739
      'type' => MENU_LOCAL_TASK);
740
    $items[] = array('path' => 'admin/user/rules/edit', 'title' => t('edit rule'),
741
      'callback' => 'user_admin_access_edit', 'access' => $access_access,
742
      'type' => MENU_CALLBACK);
743
    $items[] = array('path' => 'admin/user/rules/delete', 'title' => t('delete rule'),
744
745
      'callback' => 'drupal_get_form', 'callback arguments' => array('user_admin_access_delete_confirm'),
      'access' => $access_access, 'type' => MENU_CALLBACK);
746

747
    if (module_exists('search')) {
748
749
      $items[] = array('path' => 'admin/user/search', 'title' => t('search users'),
        'description' => t('Search users by name.'),
750
        'callback' => 'user_admin', 'callback arguments' => array('search'), 'access' => $admin_access,
751
        'type' => MENU_NORMAL_ITEM);
752
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
753

Steven Wittens's avatar
Steven Wittens committed
754
    // Your personal page
Dries Buytaert's avatar
   
Dries Buytaert committed
755
756
    if ($user->uid) {
      $items[] = array('path' => 'user/'. $user->uid, 'title' => t('my account'),
757
        'callback' => 'user_view', 'callback arguments' => array(arg(1)), 'access' => TRUE,
758
        'type' => MENU_DYNAMIC_ITEM);
Dries Buytaert's avatar
   
Dries Buytaert committed
759
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
760
761
762
763
764

    $items[] = array('path' => 'logout', 'title' => t('log out'),
      'access' => $user->uid != 0,
      'callback' => 'user_logout',
      'weight' => 10);
Dries Buytaert's avatar
   
Dries Buytaert committed
765
766
  }
  else {
767
768
769
770
    // Add the CSS for this module. We put this in !$may_cache so it is only
    // added once per request.
    drupal_add_css(drupal_get_path('module', 'user') .'/user.css', 'core');

771
    if (arg(0) == 'user' && is_numeric(arg(1)) && arg(1) > 0) {
772
      $account = user_load(array('uid' => arg(1)));
773

774
775
776
777
      if ($user !== FALSE) {
        // Always let a user view their own account
        $view_access |= $user->uid == arg(1);
        // Only admins can view blocked accounts
778
        $view_access &= $account->status || $admin_access;
779
780
781

        $items[] = array('path' => 'user/'. arg(1), 'title' => t('user'),
          'type' => MENU_CALLBACK, 'callback' => 'user_view',
782
          'callback arguments' => array(arg(1)), 'access' => $view_access);
783
784
785

        $items[] = array('path' => 'user/'. arg(1) .'/view', 'title' => t('view'),
          'access' => $view_access, 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
786

787
        $items[] = array('path' => 'user/'. arg(1) .'/edit', 'title' => t('edit'),
788
789
          'callback' => 'drupal_get_form', 'callback arguments' => array('user_edit'),
          'access' => $admin_access || $user->uid == arg(1), 'type' => MENU_LOCAL_TASK);
790
791
792
793
794
        $items[] = array('path' => 'user/'. arg(1) .'/delete', 'title' => t('delete'),
          'callback' => 'user_edit', 'access' => $admin_access,
          'type' => MENU_CALLBACK);

        if (arg(2) == 'edit') {
795
          if (($categories = _user_categories($account)) && (count($categories) > 1)) {
796
797
798
799
800
801
802
803
            foreach ($categories as $key => $category) {
              $items[] = array(
                'path' => 'user/'. arg(1) .'/edit/'. $category['name'],
                'title' => $category['title'],
                'type' => $category['name'] == 'account' ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
                'weight' => $category['weight'],
                'access' => ($admin_access || $user->uid == arg(1)));
            }
Dries Buytaert's avatar
   
Dries Buytaert committed
804
805
806
807
          }
        }
      }
    }
Dries Buytaert's avatar
   
Dries Buytaert committed
808
  }
Dries Buytaert's avatar
   
Dries Buytaert committed
809
810

  return $items;
Dries Buytaert's avatar
   
Dries Buytaert committed
811
812
}

813
814
815
816
/**
 * Accepts an user object, $account, or a DA name and returns an associative
 * array of modules and DA names. Called at external login.
 */
817
function user_get_authmaps($authname = NULL) {
Dries Buytaert's avatar
   
Dries Buytaert committed
818
  $result = db_query("SELECT authname, module FROM {authmap} WHERE authname = '%s'", $authname);
Dries Buytaert's avatar
   
Dries Buytaert committed
819
820
821
822
823
824
825
826
827
828
829
830
831
  if (db_num_rows($result) > 0) {
    while ($authmap = db_fetch_object($result)) {
      $authmaps[$authmap->module] = $authmap->authname;
    }
    return $authmaps;
  }
  else {
    return 0;
  }
}

function user_set_authmaps($account, $authmaps) {
  foreach ($authmaps as $key => $value) {
832
    $module = explode('_', $key, 2);
Dries Buytaert's avatar
   
Dries Buytaert committed
833
    if ($value) {
Dries Buytaert's avatar