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

/*** Common functions ******************************************************/

Dries's avatar
 
Dries committed
6 7 8 9
// we cannot use module_invoke because we need passing by reference
function user_module_invoke($type, &$array, &$user) {
  foreach (module_list() as $module) {
    $function = $module .'_user';
Dries's avatar
 
Dries committed
10
    if (function_exists($function)) $function($type, $array, $user);
Dries's avatar
 
Dries committed
11 12 13
  }
}

Dries's avatar
 
Dries committed
14
function user_external_load($authname) {
Dries's avatar
 
Dries committed
15
  $result = db_query("SELECT uid FROM {authmap} WHERE authname = '%s'", $authname);
Dries's avatar
 
Dries committed
16

Dries's avatar
 
Dries committed
17 18
  if ($user = db_fetch_object($result)) {
    return user_load($user);
Dries's avatar
 
Dries committed
19 20 21 22 23 24 25 26 27 28 29
  }
  else {
    return 0;
  }
}

function user_load($array = array()) {
  /*
  ** Dynamically compose a SQL query:
  */

Dries's avatar
 
Dries committed
30 31
  $query = "";

Dries's avatar
 
Dries committed
32
  foreach ($array as $key => $value) {
Dries's avatar
 
Dries committed
33
    if ($key == 'pass') {
Kjartan's avatar
Kjartan committed
34
      $query .= "u.$key = '". md5($value) ."' AND ";
35
    }
Dries's avatar
 
Dries committed
36
    else {
Dries's avatar
 
Dries committed
37
      $query .= "u.$key = '". check_query($value) ."' AND ";
Dries's avatar
 
Dries committed
38 39
    }
  }
Dries's avatar
 
Dries committed
40
  $result = db_query_range("SELECT u.* FROM {users} u WHERE $query u.status < 3", 0, 1);
Dries's avatar
 
Dries committed
41 42

  $user = db_fetch_object($result);
Dries's avatar
 
Dries committed
43
  $user = drupal_unpack($user);
Dries's avatar
 
Dries committed
44
  $user->roles = array();
Dries's avatar
 
Dries committed
45

Dries's avatar
 
Dries committed
46 47 48 49 50
  $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;
  }
Dries's avatar
 
Dries committed
51

Dries's avatar
 
Dries committed
52 53
  user_module_invoke("load", $array, $user);

Dries's avatar
 
Dries committed
54 55 56 57 58 59 60 61
  return $user;
}

function user_save($account, $array = array()) {
  /*
  ** Dynamically compose a SQL query:
  */

Kjartan's avatar
Kjartan committed
62
  $user_fields = user_fields();
Dries's avatar
 
Dries committed
63
  if ($account->uid) {
Dries's avatar
 
Dries committed
64 65
    user_module_invoke("update", $array, $account);

Dries's avatar
 
Dries committed
66
    $data = unserialize(db_result(db_query("SELECT data FROM {users} WHERE uid = %d", $account->uid)));
Dries's avatar
 
Dries committed
67
    foreach ($array as $key => $value) {
Dries's avatar
 
Dries committed
68
      if ($key == 'pass') {
Dries's avatar
 
Dries committed
69 70
        $query .= "$key = '%s', ";
        $v[] = md5($value);
Dries's avatar
 
Dries committed
71 72
      }
      else if (substr($key, 0, 4) !== "auth") {
Kjartan's avatar
Kjartan committed
73
        if (in_array($key, $user_fields)) {
Dries's avatar
 
Dries committed
74 75
          // escape '%'s:
          $value = str_replace("%", "%%", $value);
Dries's avatar
 
Dries committed
76 77
          $query .= "$key = '%s', ";
          $v[] = $value;
Dries's avatar
 
Dries committed
78 79 80 81
        }
        else {
          $data[$key] = $value;
        }
Dries's avatar
 
Dries committed
82 83
      }
    }
Dries's avatar
 
Dries committed
84 85
    $query .= "data = '%s', ";
    $v[] = serialize($data);
Dries's avatar
 
Dries committed
86

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

89 90 91
    // reload user roles if provided
    if (is_array($array['rid'])) {
      db_query("DELETE FROM {users_roles} WHERE uid = %d", $account->uid);
Dries's avatar
 
Dries committed
92

93 94 95
      foreach ($array['rid'] as $rid) {
        db_query("INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)", $account->uid, $rid);
      }
Dries's avatar
 
Dries committed
96 97
    }

Dries's avatar
 
Dries committed
98
    $user = user_load(array('uid' => $account->uid));
Dries's avatar
 
Dries committed
99 100
  }
  else {
Dries's avatar
 
Dries committed
101 102
    $array['created'] = time();
    $array['changed'] = time();
Dries's avatar
 
Dries committed
103
    $array['uid'] = db_next_id("{users}_uid");
Dries's avatar
 
Dries committed
104 105

    foreach ($array as $key => $value) {
Dries's avatar
 
Dries committed
106
      if ($key == 'pass') {
Dries's avatar
 
Dries committed
107
        $fields[] = check_query($key);
Dries's avatar
 
Dries committed
108 109
        $values[] = md5($value);
        $s[] = "'%s'";
Dries's avatar
 
Dries committed
110 111
      }
      else if (substr($key, 0, 4) !== "auth") {
Kjartan's avatar
Kjartan committed
112
        if (in_array($key, $user_fields)) {
Dries's avatar
 
Dries committed
113
          $fields[] = check_query($key);
Dries's avatar
 
Dries committed
114 115
          $values[] = $value;
          $s[] = "'%s'";
Dries's avatar
 
Dries committed
116 117 118 119
        }
        else {
          $data[$key] = $value;
        }
Dries's avatar
 
Dries committed
120 121 122
      }
    }

Dries's avatar
 
Dries committed
123
    $fields[] = "data";
Dries's avatar
 
Dries committed
124 125
    $values[] = serialize($data);
    $s[] = "'%s'";
Dries's avatar
 
Dries committed
126

Dries's avatar
 
Dries committed
127
    db_query("INSERT INTO {users} (". implode(", ", $fields) .") VALUES (". implode(", ", $s) .")", $values);
Dries's avatar
 
Dries committed
128

Dries's avatar
 
Dries committed
129 130 131 132 133 134 135
    // reload user roles (delete just to be safe)
    db_query("DELETE FROM {users_roles} WHERE uid = %d", $array['uid']);

    foreach ($array['rid'] as $rid) {
      db_query("INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)", $array['uid'], $rid);
    }

Dries's avatar
 
Dries committed
136
    $user = user_load(array('name' => $array['name']));
Dries's avatar
 
Dries committed
137

Dries's avatar
 
Dries committed
138
    module_invoke_all('user', 'insert', $array, $user);
Dries's avatar
 
Dries committed
139 140 141
  }

  foreach ($array as $key => $value) {
Dries's avatar
 
Dries committed
142
    if (substr($key, 0, 4) == 'auth') {
Dries's avatar
 
Dries committed
143 144 145 146 147
      $authmaps[$key] = $value;
    }
  }

  if ($authmaps) {
Dries's avatar
 
Dries committed
148
    user_set_authmaps($user, $authmaps);
Dries's avatar
 
Dries committed
149 150 151 152 153 154 155 156 157 158
  }

  return $user;
}

function user_validate_name($name) {
  /*
  ** Verify the syntax of the given name:
  */

Dries's avatar
 
Dries committed
159
  if (!$name) return t("You must enter a username.");
Dries's avatar
 
Dries committed
160 161
  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.");
Dries's avatar
 
Dries committed
162
  if (ereg('  ', $name)) return t("The username cannot contain multiple spaces in a row.");
Dries's avatar
 
Dries committed
163 164
  if (ereg('[^ [:alnum:]@_.-]', $name)) return t("The username contains an illegal character.");
  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.");
Dries's avatar
 
Dries committed
165
  if (strlen($name) > 56) return t("The username '%name' is too long: it must be less than 56 characters.", array("%name" => $name));
Dries's avatar
 
Dries committed
166 167 168
}

function user_validate_mail($mail) {
Dries's avatar
 
Dries committed
169
  if (!$mail) return t("You must enter an e-mail address.");
Dries's avatar
 
Dries committed
170
  if ($mail && !valid_email_address($mail)) {
Dries's avatar
 
Dries committed
171
    return t("The e-mail address '%mail' is not valid.", array("%mail" => $mail));
Dries's avatar
 
Dries committed
172 173 174
  }
}

Dries's avatar
 
Dries committed
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
function user_validate_picture($file, &$edit, $user) {

  // initialize the picture:
  $edit['picture'] = $user->picture;

  // check that uploaded file is an image, with a maximum file size and maximum height/width
  $extension = strtolower(strrchr($file->name, "."));
  $size = getimagesize($file->path);
  list($maxwidth, $maxheight) = explode("x", variable_get('user_picture_dimensions', "85x85"));

  if ((!in_array($size[2], array(1, 2, 3))) || (!in_array($extension, array(".gif", ".jpg", ".png", ".jpeg")))) {
    $error = t("The uploaded file was not an image.");
  }
  else if ($file->size > (variable_get('user_picture_file_size', "30") * 1000)) {
    $error = t("The uploaded image is too large; the maximum file size is %a kB.", array("%a" => variable_get('user_picture_file_size', "30")));
  }
  else if ($size[0] > $maxwidth || $size[1] > $maxheight) {
    $error = t("The uploaded image is too large; the maximum dimensions are %a pixels.", array("%a" => variable_get('user_picture_dimensions', "85x85")));
  }
  else if ($file = file_save_upload('picture', variable_get('user_picture_path', "pictures") . FILE_SEPARATOR .'picture-'. $user->uid . $extension, 1)) {
    $edit['picture'] = $file->path;
  }
  else {
    $error = t("Failed to upload the picture image; the '%directory' directory doesn't exist.", array("%directory" => variable_get('user_picture_path', "pictures")));
  }

  return $error;
}

204
function user_validate_authmap($account, $authname, $module) {
Dries's avatar
 
Dries committed
205
  $result = db_query("SELECT COUNT(*) from {authmap} WHERE uid != %d AND authname = '%s'", $account->uid, $authname);
Dries's avatar
 
Dries committed
206
  if (db_result($result) > 0) {
Dries's avatar
 
Dries committed
207
    $name = module_invoke($module, 'info', 'name');
Dries's avatar
 
Dries committed
208 209
    return t("The %u ID %s is already taken.", array("%u" => ucfirst($name), "%s" => "<i>$authname</i>"));
  }
Dries's avatar
 
Dries committed
210 211
}

Dries's avatar
 
Dries committed
212
function user_password($length = 10) {
Dries's avatar
 
Dries committed
213
  /*
Dries's avatar
 
Dries committed
214
  ** Generate a random alphanumeric password.
Dries's avatar
 
Dries committed
215 216
  */

Dries's avatar
 
Dries committed
217 218 219 220 221 222 223 224 225
  // This variable contains the list of allowable characters for the
  // password.  Note that the number 0 and the letter 'O' have been
  // removed to avoid confusion between the two.  The same is true
  // of 'I' and 1.
  $allowable_characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789";
  // We see how many characters are in the allowable list:
  $len = strlen($allowable_characters);

  // Seed the random number generator with the microtime stamp:
Dries's avatar
 
Dries committed
226
  mt_srand((double)microtime() * 1000000);
Dries's avatar
 
Dries committed
227 228 229 230 231 232 233 234 235 236 237 238 239

  // Declare the password as a blank string:
  $pass = "";

  // Loop the number of times specified by $length:
  for ($i = 0; $i < $length; $i++) {

    // Each iteration, pick a random character from the
    // allowable string and append it to the password:
    $pass .= $allowable_characters[mt_rand(0, $len - 1)];
  }

  return $pass;
Dries's avatar
 
Dries committed
240 241 242 243
}

function user_access($string) {
  global $user;
Dries's avatar
 
Dries committed
244
  static $perm = 0;
Dries's avatar
 
Dries committed
245

Dries's avatar
 
Dries committed
246
  // User #1 has all priveleges:
Dries's avatar
 
Dries committed
247 248 249 250
  if ($user->uid == 1) {
    return 1;
  }

Dries's avatar
 
Dries committed
251 252 253 254
  /*
  ** To reduce the number of SQL queries, we cache the user's permissions
  ** in a static variable.
  */
Dries's avatar
 
Dries committed
255

Dries's avatar
 
Dries committed
256
  if ($perm === 0) {
Dries's avatar
 
Dries committed
257 258 259 260 261
    $result = db_query("SELECT DISTINCT p.perm FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d", $user->uid);

    while ($row = db_fetch_object($result)) {
      $perm .= "$row->perm, ";
    }
Dries's avatar
 
Dries committed
262 263
  }

Dries's avatar
 
Dries committed
264
  return strstr($perm, "$string, ");
Dries's avatar
 
Dries committed
265 266 267 268 269
}

function user_mail($mail, $subject, $message, $header) {
  if (variable_get("smtp_library", "") && file_exists(variable_get("smtp_library", ""))) {
    include_once variable_get("smtp_library", "");
270
    return user_mail_wrapper($mail, $subject, $message, $header);
Dries's avatar
 
Dries committed
271 272
  }
  else {
273 274 275 276 277 278 279
    /*
    ** Note: if you are having problems with sending mail, or mails look wrong
    ** when they are recieved you may have to modify the str_replace to suit
    ** your systems.
    **  - \r\n will work under dos and windows.
    **  - \n will work for linux, unix and BSDs.
    **  - \r will work for macs.
Dries's avatar
 
Dries committed
280 281 282 283 284 285 286 287 288 289 290 291
    **
    ** According to RFC 2646, it's quite rude to not wrap your e-mails:
    **
    ** "The Text/Plain media type is the lowest common denominator of
    ** Internet email, with lines of no more than 997 characters (by
    ** convention usually no more than 80), and where the CRLF sequence
    ** represents a line break [MIME-IMT]."
    **
    ** CRLF === \r\n
    **
    ** http://www.rfc-editor.org/rfc/rfc2646.txt
    **
292
    */
Dries's avatar
 
Dries committed
293 294 295 296
    return mail(
      $mail,
      user_mail_encode($subject),
      str_replace("\r", "", $message),
Dries's avatar
 
Dries committed
297
      "MIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8; format=flowed\nContent-transfer-encoding: 8Bit\n" . $header
Dries's avatar
 
Dries committed
298
    );
Dries's avatar
 
Dries committed
299 300 301
  }
}

Kjartan's avatar
Kjartan committed
302 303 304 305 306
function user_mail_encode($string, $charset = "UTF-8") {
  /*
  ** Used to encodes mail headers that contain non US- ASCII
  ** characters.
  ** http://www.rfc-editor.org/rfc/rfc2047.txt
Kjartan's avatar
Kjartan committed
307 308
  **
  ** Notes:
309
  **   - Only encode strings that contain non-ASCII characters.
Kjartan's avatar
Kjartan committed
310 311 312 313 314 315
  **   - The chunks come in groupings of 4 bytes when using base64
  **     encoded.
  **   - trim() is used to ensure that no extra spacing is added by
  **     chunk_split() or preg_replace().
  **   - Using \n as the chunk separator may cause problems on some
  **     systems and may have to be changed to \r\n or \r.
Kjartan's avatar
Kjartan committed
316
  */
317 318 319 320 321 322
  if (!preg_match('/^[\x20-\x7E]*$/', $string)) {
    $chunk_size = 75 - 7 - strlen($charset);
    $chunk_size -= $chunk_size % 4;
    $string = trim(chunk_split(base64_encode($string), $chunk_size, "\n"));
    $string = trim(preg_replace('/^(.*)$/m', " =?$charset?B?\\1?=", $string));
  }
Kjartan's avatar
Kjartan committed
323
  return $string;
Dries's avatar
 
Dries committed
324 325
}

Dries's avatar
 
Dries committed
326
function user_deny($type, $mask) {
Dries's avatar
 
Dries committed
327 328
  $allow = db_fetch_object(db_query("SELECT * FROM {access} WHERE status = '1' AND type = '%s' AND LOWER('%s') LIKE LOWER(mask)", $type, $mask));
  $deny = db_fetch_object(db_query("SELECT * FROM {access} WHERE status = '0' AND type = '%s' AND LOWER('%s') LIKE LOWER(mask)", $type, $mask));
Dries's avatar
 
Dries committed
329

Dries's avatar
 
Dries committed
330
  return $deny && !$allow;
Dries's avatar
 
Dries committed
331 332
}

Dries's avatar
 
Dries committed
333 334
function user_fields() {
  static $fields;
Dries's avatar
 
Dries committed
335

Dries's avatar
 
Dries committed
336
  if (!$fields) {
Dries's avatar
 
Dries committed
337
    $result = db_query("SELECT * FROM {users} WHERE uid = 1");
Kjartan's avatar
Kjartan committed
338 339 340
    if (db_num_rows($result)) {
      $fields = array_keys(db_fetch_array($result));
    }
Dries's avatar
 
Dries committed
341 342
    else {
      // Make sure we return the default fields at least
Dries's avatar
 
Dries committed
343
      $fields = array('uid', 'name', 'pass', 'mail', 'picture', 'mode', 'sort', 'threshold', 'theme', 'signature', 'created', 'changed', 'status', 'timezone', 'language', 'init', 'data');
Dries's avatar
 
Dries committed
344
    }
Dries's avatar
 
Dries committed
345
  }
Dries's avatar
 
Dries committed
346

Dries's avatar
 
Dries committed
347
  return $fields;
Dries's avatar
 
Dries committed
348 349
}

Dries's avatar
 
Dries committed
350 351 352
/*** Module hooks **********************************************************/

function user_perm() {
Dries's avatar
 
Dries committed
353
  return array("administer users", "access user list");
Dries's avatar
 
Dries committed
354 355
}

Dries's avatar
 
Dries committed
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
function user_file_download($file) {
  if (strpos($file, variable_get('user_picture_path', "pictures") . FILE_SEPARATOR . 'picture-') === 0) {
    list($width, $height, $type, $attr) = getimagesize(file_create_path($file));
    $types = array(
      IMAGETYPE_GIF => 'image/gif',
      IMAGETYPE_JPEG => 'image/jpeg',
      IMAGETYPE_PNG => 'image/png',
      IMAGETYPE_SWF => 'application/x-shockwave-flash',
      IMAGETYPE_PSD => 'image/psd',
      IMAGETYPE_BMP => 'image/bmp',
      IMAGETYPE_TIFF_II => 'image/tiff',
      IMAGETYPE_TIFF_MM  => 'image/tiff',
      IMAGETYPE_JPC => 'application/octet-stream',
      IMAGETYPE_JP2 => 'image/jp2',
      IMAGETYPE_JPX => 'application/octet-stream',
      IMAGETYPE_JB2 => 'application/octet-stream',
      IMAGETYPE_SWC => 'application/x-shockwave-flash',
      IMAGETYPE_IFF => 'image/iff',
      IMAGETYPE_WBMP => 'image/vnd.wap.wbmp',
      IMAGETYPE_XBM => 'image/xbm'
    );
    return array('Content-type: '. $types[$type]);
  }
}

Dries's avatar
 
Dries committed
381
function user_search($keys) {
Dries's avatar
 
Dries committed
382
  $find = array();
383 384 385 386

  // Replace wildcards with mysql wildcards
  $keys = str_replace("*", "%", $keys);

Dries's avatar
 
Dries committed
387
  $result = db_query_range("SELECT * FROM {users} WHERE name LIKE '%%%s%%'", $keys, 0, 20);
Dries's avatar
 
Dries committed
388
  while ($account = db_fetch_object($result)) {
Dries's avatar
 
Dries committed
389
    $find[] = array("title" => $account->name, "link" => (strstr(request_uri(), "admin") ? url("admin/user/edit/$account->uid") : url("user/view/$account->uid")), 'user' => $account->name);
Dries's avatar
 
Dries committed
390
  }
Dries's avatar
 
Dries committed
391
  return array(t("Matching users"), $find);
Dries's avatar
 
Dries committed
392 393
}

Dries's avatar
Dries committed
394
function user_user($type, &$edit, &$user) {
Dries's avatar
Dries committed
395
  if ($type == 'view') {
Dries's avatar
Dries committed
396
    return array(t('History') => form_item(t('Member for'), format_interval(time() - $user->created)));
Dries's avatar
Dries committed
397 398 399
  }
}

Dries's avatar
 
Dries committed
400
function user_block($op = "list", $delta = 0) {
Dries's avatar
 
Dries committed
401 402
  global $user;

Dries's avatar
 
Dries committed
403
  if ($op == "list") {
Dries's avatar
 
Dries committed
404 405 406 407
     $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");
408

409
     return $blocks;
410 411
  }
  else {
Dries's avatar
 
Dries committed
412 413
    $block = array();

Dries's avatar
 
Dries committed
414 415
    switch ($delta) {
      case 0:
Dries's avatar
 
Dries committed
416

417
        if (!$user->uid) {
Dries's avatar
 
Dries committed
418 419 420 421 422
          /*
          ** For usability's sake, avoid showing two login forms on one
          ** page.
          */

Dries's avatar
 
Dries committed
423
          if (arg(0) == 'user' && arg(1) != "view") {
Dries's avatar
 
Dries committed
424 425 426
            return;
          }

Dries's avatar
 
Dries committed
427 428
          $edit = $_POST["edit"];

Dries's avatar
 
Dries committed
429 430
          $output = "<div class=\"user-login-block\">\n";

431
          /*
Dries's avatar
Dries committed
432 433
          ** Save the referer.  We record where the user came from such
          ** that we/ can redirect him after having completed the login
434 435
          ** form.
          */
Dries's avatar
Dries committed
436

437
          if (empty($edit)) {
Dries's avatar
Dries committed
438
            $edit["destination"] = $_GET["q"];
439 440
          }
          // NOTE: special care needs to be taken because on pages with forms, such as node and comment submission pages, the $edit variable might already be set.
Dries's avatar
Dries committed
441

Dries's avatar
 
Dries committed
442
          $output .= form_hidden("destination", $edit["destination"]);
Dries's avatar
 
Dries committed
443 444
          $output .= form_textfield(t("Username"), 'name', $edit['name'], 15, 64);
          $output .= form_password(t("Password"), 'pass', $pass, 15, 64);
Dries's avatar
 
Dries committed
445
          $output .= form_submit(t("Log in"));
Dries's avatar
 
Dries committed
446
          $output .= "</div>\n";
Dries's avatar
 
Dries committed
447

Dries's avatar
 
Dries committed
448 449
          $output  = form($output, "post", url("user/login"));

Dries's avatar
 
Dries committed
450
          if (variable_get("user_register", 1)) {
Dries's avatar
 
Dries committed
451
            $items[] = l(t("Create new account"), "user/register", array("title" => t("Create a new user account.")));
Dries's avatar
 
Dries committed
452
          }
Dries's avatar
 
Dries committed
453
          $items[] = l(t("Request new password"), "user/password", array("title" => t("Request new password via e-mail.")));
Dries's avatar
 
Dries committed
454

Dries's avatar
 
Dries committed
455
          $output .= theme("item_list", $items);
456

Dries's avatar
 
Dries committed
457
          $block["subject"] = t("User login");
Dries's avatar
 
Dries committed
458
          $block["content"] = $output;
Dries's avatar
 
Dries committed
459
        }
Dries's avatar
Dries committed
460
        return $block;
461
      case 1:
Dries's avatar
 
Dries committed
462 463
        if ($menu = menu_tree()) {
           $block["subject"] = $user->uid ? $user->name : t("Navigation");
Dries's avatar
 
Dries committed
464
           $block["content"] = "<div class=\"menu\">". $menu ."</div>";
Dries's avatar
 
Dries committed
465
        }
466
        return $block;
Dries's avatar
 
Dries committed
467
      case 2:
468 469 470
        if (user_access("access content")) {
          $result = db_query_range("SELECT uid, name FROM {users} WHERE status != '0' ORDER BY uid DESC", 0, 5);
          while ($account = db_fetch_object($result)) {
471
            $items[] = format_name($account);
472
          }
Dries's avatar
 
Dries committed
473

Dries's avatar
 
Dries committed
474
          $output = theme("user_list", $items);
Dries's avatar
 
Dries committed
475

476 477 478 479
          $block["subject"] = t("Who's new");
          $block["content"] = $output;
          return $block;
        }
Dries's avatar
 
Dries committed
480 481
      case 3:
        if (user_access("access content")) {
Dries's avatar
 
Dries committed
482 483
          /* count users with activity in the past defined period */
          $time_period = variable_get("user_block_seconds_online", 2700);
Dries's avatar
 
Dries committed
484

Dries's avatar
 
Dries committed
485 486 487
          /* perform database queries to gather online user lists */
          $guests = db_fetch_object(db_query("SELECT COUNT(DISTINCT sid) AS count FROM {sessions} WHERE timestamp >= %d AND uid = 0", time() - $time_period));
          $users = db_query("SELECT DISTINCT uid, MAX(timestamp) AS max_timestamp FROM {sessions} WHERE timestamp >= %d AND uid != 0 GROUP BY uid ORDER BY max_timestamp DESC", time() - $time_period );
Dries's avatar
 
Dries committed
488
          $total_users = db_num_rows($users);
Dries's avatar
 
Dries committed
489 490 491 492

          /* format the output with proper grammar */
          if ($total_users == 1 && $guests->count == 1) {
            $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")));
Dries's avatar
 
Dries committed
493 494
          }
          else {
Dries's avatar
 
Dries committed
495 496 497 498
            $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")));
          }

          if (user_access("access user list") && $total_users) {
Dries's avatar
 
Dries committed
499

Dries's avatar
 
Dries committed
500
            // Display a list of currently online users
Dries's avatar
 
Dries committed
501
            $max_users = variable_get("user_block_max_list_count", 10);
Dries's avatar
 
Dries committed
502 503
            if ($max_users) {
              $items = array();
Dries's avatar
 
Dries committed
504

Dries's avatar
 
Dries committed
505
              while ($max_users-- && $uid = db_fetch_object($users)) {
Dries's avatar
 
Dries committed
506
                $items[] = format_name(user_load(array('uid' => $uid->uid)));
Dries's avatar
 
Dries committed
507 508 509
              }

              if ($items) {
Dries's avatar
 
Dries committed
510 511 512
                if (db_fetch_object($users)) {
                  $items[] = "...";
                }
Dries's avatar
 
Dries committed
513 514
                $output .= theme("item_list", $items, t("Online users:"));
              }
Dries's avatar
 
Dries committed
515
            }
Dries's avatar
 
Dries committed
516
          }
Dries's avatar
 
Dries committed
517 518
          $block["subject"] = t("Who's online");
          $block["content"] = $output;
Dries's avatar
 
Dries committed
519
        }
Dries's avatar
 
Dries committed
520
        return $block;
Dries's avatar
 
Dries committed
521 522
    }
  }
523 524
}

Dries's avatar
 
Dries committed
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
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', '');
    }

    if ($picture) {
      $picture = "<img src=\"$picture\" alt=\"" . t("%user's picture", array("%user" => $account->name ? $account->name : t(variable_get("anonymous", "Anonymous")))) . "\" />";
      if ($account->uid) {
        $picture = l($picture, "user/view/$account->uid", array("title" => t("View user profile.")));
      }

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

Dries's avatar
Dries committed
545
function theme_user_profile($account, $fields) {
Dries's avatar
 
Dries committed
546 547
  $output = "<div class=\"profile\">\n";
  $output .= theme('user_picture', $account);
Dries's avatar
Dries committed
548 549 550
  foreach ($fields as $category => $value) {
    $output .= "<h2>$category</h2>$value";
  }
Dries's avatar
 
Dries committed
551 552 553 554 555 556 557 558 559 560

  if (user_access("administer users")) {
    $output .= form_item(t("Administration"), l(t("edit account"), "admin/user/edit/$account->uid"));
  }

  $output .= "</div>\n";

  return $output;
}

Dries's avatar
 
Dries committed
561
function theme_user_list($items, $title = NULL) {
Dries's avatar
 
Dries committed
562
  return theme("item_list", $items, $title);
Dries's avatar
 
Dries committed
563 564
}

Dries's avatar
 
Dries committed
565 566 567
/**
 * Implementation of hook_link().
 */
Dries's avatar
 
Dries committed
568
function user_link($type) {
Dries's avatar
 
Dries committed
569
  global $user;
Dries's avatar
 
Dries committed
570

Dries's avatar
 
Dries committed
571
  if ($type == 'system') {
572 573 574 575 576 577 578 579 580
    if ($user->uid) {
      menu('user', t('my account'), 'user_page', 0);
      menu('user/edit', t('edit account'), 'user_page', 0);
      menu('logout', t('log out'), 'user_logout', 10);
    }
    else {
      menu('user', t('user'), 'user_page', 0, MENU_HIDE);
      menu('user/edit', t('edit account'), MENU_DENIED);
      menu('logout', t('log out'), MENU_DENIED);
Dries's avatar
 
Dries committed
581
    }
Dries's avatar
 
Dries committed
582 583 584 585 586 587 588 589 590 591 592 593 594

    $access = user_access('administer users');
    menu('admin/user', t('accounts'), $access ? 'user_admin' : MENU_DENIED, 2);
    menu('admin/user/create', t('new user'), $access ? 'user_admin' : MENU_DENIED, 1);
    menu('admin/user/access', t('access rules'), $access ? 'user_admin' : MENU_DENIED, 3);
    menu('admin/user/access/mail', t('e-mail rules'), $access ? 'user_admin' : MENU_DENIED);
    menu('admin/user/access/user', t('name rules'), $access ? 'user_admin' : MENU_DENIED);
    menu('admin/user/role', t('roles'), $access ? 'user_admin' : MENU_DENIED, 4);
    menu('admin/user/permission', t('permissions'), $access ? 'user_admin' : MENU_DENIED, 5);
    menu('admin/user/help', t('help'), $access ? 'user_help_page' : MENU_DENIED, 9);
    menu('admin/user/edit', t('edit user account'), $access ? 'user_admin' : MENU_DENIED, 0, MENU_HIDE, MENU_LOCKED);
    if (module_exist('search')) {
      menu('admin/user/search', t('search'), $access ? 'user_admin' : MENU_DENIED, 8);
Dries's avatar
 
Dries committed
595
    }
Dries's avatar
 
Dries committed
596 597 598 599 600
  }
}

/*** Authentication methods ************************************************/

601
function user_get_authname($account, $module) {
Dries's avatar
 
Dries committed
602 603

  /*
604
  **  Called by authentication modules in order to edit/view their authmap information.
Dries's avatar
 
Dries committed
605 606
  */

Dries's avatar
 
Dries committed
607
  $result = db_query("SELECT authname FROM {authmap} WHERE uid = %d AND module = '%s'", $account->uid, $module);
608 609 610
  return db_result($result);
}

Dries's avatar
 
Dries committed
611

612 613 614 615 616 617 618
function user_get_authmaps($authname = NULL) {

  /*
  ** Accepts an user object, $account, or an DA name and returns an
  ** associtive array of modules and DA names. Called at external login.
  */

Dries's avatar
 
Dries committed
619
  $result = db_query("SELECT authname, module FROM {authmap} WHERE authname = '%s'", $authname);
Dries's avatar
 
Dries committed
620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
  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) {
    $module = explode("_", $key, 2);
    if ($value) {
Dries's avatar
 
Dries committed
635 636 637
      db_query("UPDATE {authmap} SET authname = '%s' WHERE uid = %d AND module = '%s'", $value, $account->uid, $module["1"]);
      if (!db_affected_rows()) {
        db_query("INSERT INTO {authmap} (authname, uid, module) VALUES ('%s', %d, '%s')", $value, $account->uid, $module[1]);
Dries's avatar
 
Dries committed
638 639 640
      }
    }
    else {
Dries's avatar
 
Dries committed
641
      db_query("DELETE FROM {authmap} WHERE uid = %d AND module = '%s'", $account->uid, $module["1"]);
Dries's avatar
 
Dries committed
642 643 644 645 646
    }
  }
}

function user_auth_help_links() {
647
  $links = array();
Dries's avatar
 
Dries committed
648
  foreach (module_list() as $module) {
Dries's avatar
 
Dries committed
649
    if (module_hook($module, "auth")) {
Dries's avatar
 
Dries committed
650
      $links[] = l(module_invoke($module, 'info', 'name'), "user/help#$module");
Dries's avatar
 
Dries committed
651 652 653 654 655 656 657
    }
  }
  return $links;
}

/*** User features *********************************************************/

Dries's avatar
 
Dries committed
658
function user_login($edit = array(), $msg = "") {
Dries's avatar
 
Dries committed
659
  global $user, $base_url;
Dries's avatar
 
Dries committed
660 661 662 663 664 665

  /*
  ** If we are already logged on, go to the user page instead.
  */

  if ($user->uid) {
Dries's avatar
Dries committed
666
    drupal_goto('user');
Dries's avatar
 
Dries committed
667 668
  }

Dries's avatar
 
Dries committed
669 670
  if (user_deny('user', $edit['name'])) {
    $error = t("The name '%s' has been denied access.", array("%s" => $edit['name']));
Dries's avatar
 
Dries committed
671
  }
Dries's avatar
 
Dries committed
672
  else if ($edit['name'] && $edit['pass']) {
Dries's avatar
 
Dries committed
673 674

    /*
Dries's avatar
 
Dries committed
675
    ** Try to log in the user locally:
Dries's avatar
 
Dries committed
676 677
    */

Dries's avatar
 
Dries committed
678
    if (!$user->uid) {
Dries's avatar
 
Dries committed
679 680 681
      $name = $edit['name'];
      $pass = $edit['pass'];
      $user = user_load(array('name' => $name, 'pass' => $pass, "status" => 1));
Dries's avatar
 
Dries committed
682 683 684 685 686 687
    }

    /*
    ** Strip name and server from ID:
    */

Dries's avatar
 
Dries committed
688 689
    if ($server = strrchr($edit['name'], "@")) {
      $name = substr($edit['name'], 0, strlen($edit['name']) - strlen($server));
690
      $server = substr($server, 1);
Dries's avatar
 
Dries committed
691
      $pass = $edit['pass'];
Dries's avatar
 
Dries committed
692
    }
Dries's avatar
 
Dries committed
693

Dries's avatar
 
Dries committed
694
    /*
Dries's avatar
 
Dries committed
695
    ** When possible, determine corresponding external auth source. Invoke
Dries's avatar
 
Dries committed
696
    ** source, and login user if successful:
Dries's avatar
 
Dries committed
697 698
    */

Dries's avatar
 
Dries committed
699
    if (!$user->uid && $server && $result = user_get_authmaps("$name@$server")) {
Dries's avatar
 
Dries committed
700 701
      if (module_invoke(key($result), "auth", $name, $pass, $server)) {
        $user = user_external_load("$name@$server");
Dries's avatar
 
Dries committed
702
        watchdog('user', "external load: $name@$server, module: ". key($result));
Dries's avatar
 
Dries committed
703 704
      }
      else {
Dries's avatar
 
Dries committed
705
        $error = t("Invalid password for %s.", array("%s" => "<i>$name@$server</i>"));
Dries's avatar
 
Dries committed
706 707 708
      }
    }

Dries's avatar
 
Dries committed
709
    /*
Dries's avatar
 
Dries committed
710
    ** Try each external authentication source in series. Register user if
Dries's avatar
 
Dries committed
711
    ** successful.
Dries's avatar
 
Dries committed
712 713
    */

Dries's avatar
 
Dries committed
714
    else if (!$user->uid && $server) {
Dries's avatar
 
Dries committed
715 716 717
      foreach (module_list() as $module) {
        if (module_hook($module, "auth")) {
          if (module_invoke($module, "auth", $name, $pass, $server)) {
Dries's avatar
 
Dries committed
718
            if (variable_get("user_register", 1) == 1 && !user_load(array('name' => "$name@$server"))) { //register this new user
Dries's avatar
 
Dries committed
719
              $user = user_save('', array('name' => "$name@$server", 'pass' => user_password(), 'init' => "$name@$server", 'status' => 1, "authname_$module" => "$name@$server", 'rid' => array(_user_authenticated_id())));
Dries's avatar
 
Dries committed
720
              watchdog('user', "new user: $name@$server ($module ID)", l(t("edit user"), "admin/user/edit/$user->uid"));
Dries's avatar
 
Dries committed
721 722 723 724 725 726 727 728
              break;
            }
          }
        }
      }
    }

    if ($user->uid) {
Dries's avatar
 
Dries committed
729
      watchdog('user', "session opened for '$user->name'");
Dries's avatar
 
Dries committed
730

Dries's avatar
 
Dries committed
731
      // update the user table timestamp noting user has logged in
Dries's avatar
 
Dries committed
732
      db_query("UPDATE {users} SET changed = '%d' WHERE uid = '%s'", time(), $user->uid);
Dries's avatar
 
Dries committed
733

Dries's avatar
 
Dries committed
734 735
      user_module_invoke("login", $edit, $user);

Dries's avatar
 
Dries committed
736 737 738 739 740
      /*
      ** If the user wants to be remembered, set the proper cookie such
      ** that the session won't expire.
      */

Dries's avatar
 
Dries committed
741
      $path = preg_replace("/.+\/\/[^\/]+(.*)/", "\$1/", $base_url);
Dries's avatar
 
Dries committed
742
      setcookie(session_name(), session_id(), FALSE, $path);
Dries's avatar
 
Dries committed
743 744

      /*
Dries's avatar
 
Dries committed
745
      ** Redirect the user to the page he logged on from.
Dries's avatar
 
Dries committed
746 747
      */

Dries's avatar
 
Dries committed
748
      drupal_goto($edit["destination"]);
Dries's avatar
 
Dries committed
749 750 751
    }
    else {
      if (!$error) {
Dries's avatar
 
Dries committed
752
        $error = t("Sorry.  Unrecognized username or password.") ." ". l(t("Have you forgotten your password?"), "user/password");
Dries's avatar
 
Dries committed
753 754
      }
      if ($server) {
Dries's avatar
 
Dries committed
755
        watchdog('user', "failed login for '$name@$server': $error");
Dries's avatar
 
Dries committed
756 757
      }
      else {
Dries's avatar
 
Dries committed
758
        watchdog('user', "failed login for '$name': $error");
Dries's avatar
 
Dries committed
759 760 761 762 763 764 765 766 767
      }
    }
  }

  /*
  ** Display error message (if any):
  */

  if ($error) {
Dries's avatar
 
Dries committed
768
    drupal_set_message($error, 'error');
Dries's avatar
 
Dries committed
769 770
  }

Dries's avatar
 
Dries committed
771
  /*
Dries's avatar
 
Dries committed
772
  ** Save the referrer.  We record where the user came from such that we
Dries's avatar
 
Dries committed
773 774 775
  ** can redirect him after having completed the login form.
  */

776
  if (empty($edit)) {
Dries's avatar
Dries committed
777
    $edit["destination"] = $_GET["q"];
Dries's avatar
 
Dries committed
778 779 780
  }
  $output .= form_hidden("destination", $edit["destination"]);

Dries's avatar
 
Dries committed
781 782 783 784
  /*
  ** Display login form:
  */

Dries's avatar
 
Dries committed
785 786 787
  if ($msg) {
    $output .= "<p>$msg</p>";
  }
Dries's avatar
 
Dries committed
788
  if (count(user_auth_help_links()) > 0) {
Dries's avatar
 
Dries committed
789
    $output .= form_textfield(t("Username"), 'name', $edit['name'], 30, 64, 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()))));
Dries's avatar
 
Dries committed
790 791
  }
  else {
Dries's avatar
 
Dries committed
792
    $output .= form_textfield(t("Username"), 'name', $edit['name'], 30, 64, t("Enter your %s username.", array("%s" => variable_get("site_name", "local"))));
Dries's avatar
 
Dries committed
793
  }
Dries's avatar
 
Dries committed
794
  $output .= form_password(t("Password"), 'pass', $pass, 30, 64, t("Enter the password that accompanies your username."));
Dries's avatar
 
Dries committed
795
  $output .= form_submit(t("Log in"));
Dries's avatar
 
Dries committed
796
  $items[] = l(t("Request new password"), "user/password");
797
  if (variable_get("user_register", 1)) {
Dries's avatar
 
Dries committed
798
    $items[] = l(t("Create new account"), "user/register");
799
  }
Dries's avatar
 
Dries committed
800
  $output .= theme("item_list", $items);
Dries's avatar
 
Dries committed
801

Dries's avatar
 
Dries committed
802 803
  $output  = form_group(t('User login'), $output);

804
  return form($output, "post", url('user/login'));
Dries's avatar
 
Dries committed
805 806
}

807
function _user_authenticated_id() {
Dries's avatar
 
Dries committed
808
  return db_result(db_query("SELECT rid FROM {role} WHERE name = 'authenticated user'"));
809 810
}

Dries's avatar
 
Dries committed
811 812 813 814
function user_logout() {
  global $user;

  if ($user->uid) {
Dries's avatar
 
Dries committed
815
    watchdog('user', "session closed for '$user->name'");
Dries's avatar
 
Dries committed
816 817 818 819 820 821

    /*
    ** Destroy the current session:
    */

    session_destroy();
Dries's avatar
 
Dries committed
822
    module_invoke_all('user', 'logout', NULL, $user);
Dries's avatar
 
Dries committed
823 824 825
    unset($user);
  }

Dries's avatar
Dries committed
826
  drupal_goto();
Dries's avatar
 
Dries committed
827 828 829
}

function user_pass($edit = array()) {
Dries's avatar
 
Dries committed
830 831
  global $base_url;

Dries's avatar
 
Dries committed
832 833 834
  if ($edit['name']) {
    $account = db_fetch_object(db_query("SELECT uid, name, mail FROM {users} WHERE status = 1 AND name = '%s'", $edit['name']));
    if (!$account) $error = t("Sorry. The username <i>%s</i> is not recognized.", array("%s" => $edit['name']));
835
  }
Dries's avatar
 
Dries committed
836 837 838
  else if ($edit['mail']) {
    $account = db_fetch_object(db_query("SELECT uid, name, mail FROM {users} WHERE status = 1 AND mail = '%s'", $edit['mail']));
    if (!$account) $error = t("Sorry. The e-mail address <i>%s</i> is not recognized.", array("%s" => $edit['mail']));
Kjartan's avatar
Kjartan committed
839 840
  }
  if ($account) {
Dries's avatar
 
Dries committed
841

842
      $from = variable_get("site_mail", ini_get("sendmail_from"));
Dries's avatar
 
Dries committed
843 844 845 846 847 848
      $pass = user_password();

      /*
      ** Save new password:
      */

Dries's avatar
 
Dries committed
849
      user_save($account, array('pass' => $pass));
Dries's avatar
 
Dries committed
850 851 852 853 854

      /*
      ** Mail new password:
      */

Dries's avatar
Dries committed
855
      $variables = array("%username" => $account->name, "%site" => variable_get("site_name", "drupal"), "%password" => $pass, "%uri" => $base_url, "%uri_brief" => substr($base_url, strlen("http://")), "%mailto" => $account->mail, "%date" => format_date(time()), '%login_uri' => url('user/login', NULL, NULL, TRUE), '%edit_uri' => url('user/edit', NULL, NULL, TRUE));
Dries's avatar
 
Dries committed
856 857
      $subject = _user_mail_text("pass_subject", $variables);
      $body = _user_mail_text("pass_body", $variables);
858
      $headers = "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from";
Dries's avatar
 
Dries committed
859
      $mail_success = user_mail($account->mail, $subject, $body, $headers);
Dries's avatar
 
Dries committed
860

Dries's avatar
 
Dries committed
861
      if ($mail_success) {
Dries's avatar
 
Dries committed
862
        watchdog('user', "mail password: '". $account->name ."' &lt;". $account->mail ."&gt;");
Dries's avatar
 
Dries committed
863 864 865
        return t("Your password and further instructions have been sent to your e-mail address.");
      }
      else {
Dries's avatar
 
Dries committed
866
        watchdog('error', "error mailing new password: '". $account->name ."' &lt;". $account->mail ."&gt;");
Dries's avatar
 
Dries committed
867 868
        return t("Unable to send mail. Please contact the site admin.");
      }
Dries's avatar
 
Dries committed
869 870
    }
    else {
871

Kjartan's avatar
Kjartan committed
872 873
    // Display error message if necessary.
    if ($error) {
Dries's avatar
 
Dries committed
874
      drupal_set_message($error, 'error');
Dries's avatar
 
Dries committed
875 876 877 878 879 880
    }

    /*
    ** Display form:
    */

Dries's avatar
 
Dries committed
881
    $output .= "<p>". t("Enter your username <strong><em>or</em></strong> your e-mail address.") ."</p>";
Dries's avatar
 
Dries committed
882 883
    $output .= form_textfield(t("Username"), 'name', $edit['name'], 30, 64);
    $output .= form_textfield(t("E-mail address"), "mail", $edit['mail'], 30, 64);
Dries's avatar