[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/modules/user/ -> user.module (source)

   1  <?php
   2  // $Id: user.module,v 1.892.2.28 2010/11/04 09:16:45 goba Exp $
   3  
   4  /**
   5   * @file
   6   * Enables the user registration and login system.
   7   */
   8  
   9  define('USERNAME_MAX_LENGTH', 60);
  10  define('EMAIL_MAX_LENGTH', 64);
  11  
  12  /**
  13   * Invokes hook_user() in every module.
  14   *
  15   * We cannot use module_invoke() for this, because the arguments need to
  16   * be passed by reference.
  17   */
  18  function user_module_invoke($type, &$array, &$user, $category = NULL) {
  19    foreach (module_list() as $module) {
  20      $function = $module .'_user';
  21      if (function_exists($function)) {
  22        $function($type, $array, $user, $category);
  23      }
  24    }
  25  }
  26  
  27  /**
  28   * Implementation of hook_theme().
  29   */
  30  function user_theme() {
  31    return array(
  32      'user_picture' => array(
  33        'arguments' => array('account' => NULL),
  34        'template' => 'user-picture',
  35      ),
  36      'user_profile' => array(
  37        'arguments' => array('account' => NULL),
  38        'template' => 'user-profile',
  39        'file' => 'user.pages.inc',
  40      ),
  41      'user_profile_category' => array(
  42        'arguments' => array('element' => NULL),
  43        'template' => 'user-profile-category',
  44        'file' => 'user.pages.inc',
  45      ),
  46      'user_profile_item' => array(
  47        'arguments' => array('element' => NULL),
  48        'template' => 'user-profile-item',
  49        'file' => 'user.pages.inc',
  50      ),
  51      'user_list' => array(
  52        'arguments' => array('users' => NULL, 'title' => NULL),
  53      ),
  54      'user_admin_perm' => array(
  55        'arguments' => array('form' => NULL),
  56        'file' => 'user.admin.inc',
  57      ),
  58      'user_admin_new_role' => array(
  59        'arguments' => array('form' => NULL),
  60        'file' => 'user.admin.inc',
  61      ),
  62      'user_admin_account' => array(
  63        'arguments' => array('form' => NULL),
  64        'file' => 'user.admin.inc',
  65      ),
  66      'user_filter_form' => array(
  67        'arguments' => array('form' => NULL),
  68        'file' => 'user.admin.inc',
  69      ),
  70      'user_filters' => array(
  71        'arguments' => array('form' => NULL),
  72        'file' => 'user.admin.inc',
  73      ),
  74      'user_signature' => array(
  75        'arguments' => array('signature' => NULL),
  76      ),
  77    );
  78  }
  79  
  80  function user_external_load($authname) {
  81    $result = db_query("SELECT uid FROM {authmap} WHERE authname = '%s'", $authname);
  82  
  83    if ($user = db_fetch_array($result)) {
  84      return user_load($user);
  85    }
  86    else {
  87      return 0;
  88    }
  89  }
  90  
  91  /**
  92   * Perform standard Drupal login operations for a user object.
  93   *
  94   * The user object must already be authenticated. This function verifies
  95   * that the user account is not blocked/denied and then performs the login,
  96   * updates the login timestamp in the database, invokes hook_user('login'),
  97   * and regenerates the session.
  98   *
  99   * @param $account
 100   *    An authenticated user object to be set as the currently logged
 101   *    in user.
 102   * @param $edit
 103   *    The array of form values submitted by the user, if any.
 104   *    This array is passed to hook_user op login.
 105   * @return boolean
 106   *    TRUE if the login succeeds, FALSE otherwise.
 107   */
 108  function user_external_login($account, $edit = array()) {
 109    $form = drupal_get_form('user_login');
 110  
 111    $state['values'] = $edit;
 112    if (empty($state['values']['name'])) {
 113      $state['values']['name'] = $account->name;
 114    }
 115  
 116    // Check if user is blocked or denied by access rules.
 117    user_login_name_validate($form, $state, (array)$account);
 118    if (form_get_errors()) {
 119      // Invalid login.
 120      return FALSE;
 121    }
 122  
 123    // Valid login.
 124    global $user;
 125    $user = $account;
 126    user_authenticate_finalize($state['values']);
 127    return TRUE;
 128  }
 129  
 130  /**
 131   * Fetch a user object.
 132   *
 133   * @param $user_info
 134   *   Information about the user to load, consisting of one of the following:
 135   *   - An associative array whose keys are fields in the {users} table (such as
 136   *     uid, name, pass, mail, status) and whose values are the field's value.
 137   *   - A numeric user ID.
 138   *
 139   * @return
 140   *   A fully-loaded $user object upon successful user load or FALSE if user
 141   *   cannot be loaded.
 142   */
 143  function user_load($user_info = array()) {
 144    // Dynamically compose a SQL query:
 145    $query = array();
 146    $params = array();
 147  
 148    if (is_numeric($user_info)) {
 149      $user_info = array('uid' => $user_info);
 150    }
 151    elseif (!is_array($user_info)) {
 152      return FALSE;
 153    }
 154  
 155    foreach ($user_info as $key => $value) {
 156      if ($key == 'uid' || $key == 'status') {
 157        $query[] = "$key = %d";
 158        $params[] = $value;
 159      }
 160      else if ($key == 'pass') {
 161        $query[] = "pass = '%s'";
 162        $params[] = md5($value);
 163      }
 164      else {
 165        $query[]= "LOWER($key) = LOWER('%s')";
 166        $params[] = $value;
 167      }
 168    }
 169    $result = db_query('SELECT * FROM {users} u WHERE '. implode(' AND ', $query), $params);
 170  
 171    if ($user = db_fetch_object($result)) {
 172      $user = drupal_unpack($user);
 173  
 174      $user->roles = array();
 175      if ($user->uid) {
 176        $user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
 177      }
 178      else {
 179        $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
 180      }
 181      $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);
 182      while ($role = db_fetch_object($result)) {
 183        $user->roles[$role->rid] = $role->name;
 184      }
 185      user_module_invoke('load', $user_info, $user);
 186    }
 187    else {
 188      $user = FALSE;
 189    }
 190  
 191    return $user;
 192  }
 193  
 194  /**
 195   * Save changes to a user account or add a new user.
 196   *
 197   * @param $account
 198   *   The $user object for the user to modify or add. If $user->uid is
 199   *   omitted, a new user will be added.
 200   *
 201   * @param $array
 202   *   (optional) An array of fields and values to save. For example,
 203   *   array('name' => 'My name'); Setting a field to NULL deletes it from
 204   *   the data column.
 205   *
 206   * @param $category
 207   *   (optional) The category for storing profile information in.
 208   *
 209   * @return
 210   *   A fully-loaded $user object upon successful save or FALSE if the save failed.
 211   */
 212  function user_save($account, $array = array(), $category = 'account') {
 213    // Dynamically compose a SQL query:
 214    $user_fields = user_fields();
 215    if (is_object($account) && $account->uid) {
 216      user_module_invoke('update', $array, $account, $category);
 217      $query = '';
 218      $data = unserialize(db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $account->uid)));
 219      // Consider users edited by an administrator as logged in, if they haven't
 220      // already, so anonymous users can view the profile (if allowed).
 221      if (empty($array['access']) && empty($account->access) && user_access('administer users')) {
 222        $array['access'] = time();
 223      }
 224      foreach ($array as $key => $value) {
 225        if ($key == 'pass' && !empty($value)) {
 226          $query .= "$key = '%s', ";
 227          $v[] = md5($value);
 228        }
 229        else if ((substr($key, 0, 4) !== 'auth') && ($key != 'pass')) {
 230          if (in_array($key, $user_fields)) {
 231            // Save standard fields.
 232            $query .= "$key = '%s', ";
 233            $v[] = $value;
 234          }
 235          else if ($key != 'roles') {
 236            // Roles is a special case: it used below.
 237            if ($value === NULL) {
 238              unset($data[$key]);
 239            }
 240            elseif (!empty($key)) {
 241              $data[$key] = $value;
 242            }
 243          }
 244        }
 245      }
 246      $query .= "data = '%s' ";
 247      $v[] = serialize($data);
 248  
 249      $success = db_query("UPDATE {users} SET $query WHERE uid = %d", array_merge($v, array($account->uid)));
 250      if (!$success) {
 251        // The query failed - better to abort the save than risk further data loss.
 252        return FALSE;
 253      }
 254  
 255      // Reload user roles if provided.
 256      if (isset($array['roles']) && is_array($array['roles'])) {
 257        db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid);
 258  
 259        foreach (array_keys($array['roles']) as $rid) {
 260          if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
 261            db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid);
 262          }
 263        }
 264      }
 265  
 266      // Delete a blocked user's sessions to kick them if they are online.
 267      if (isset($array['status']) && $array['status'] == 0) {
 268        sess_destroy_uid($account->uid);
 269      }
 270  
 271      // If the password changed, delete all open sessions and recreate
 272      // the current one.
 273      if (!empty($array['pass'])) {
 274        sess_destroy_uid($account->uid);
 275        if ($account->uid == $GLOBALS['user']->uid) {
 276          sess_regenerate();
 277        }
 278      }
 279  
 280      // Refresh user object.
 281      $user = user_load(array('uid' => $account->uid));
 282  
 283      // Send emails after we have the new user object.
 284      if (isset($array['status']) && $array['status'] != $account->status) {
 285        // The user's status is changing; conditionally send notification email.
 286        $op = $array['status'] == 1 ? 'status_activated' : 'status_blocked';
 287        _user_mail_notify($op, $user);
 288      }
 289  
 290      user_module_invoke('after_update', $array, $user, $category);
 291    }
 292    else {
 293      // Allow 'created' to be set by the caller.
 294      if (!isset($array['created'])) {
 295        $array['created'] = time();
 296      }
 297      // Consider users created by an administrator as already logged in, so
 298      // anonymous users can view the profile (if allowed).
 299      if (empty($array['access']) && user_access('administer users')) {
 300        $array['access'] = time();
 301      }
 302  
 303      // Note: we wait to save the data column to prevent module-handled
 304      // fields from being saved there. We cannot invoke hook_user('insert') here
 305      // because we don't have a fully initialized user object yet.
 306      foreach ($array as $key => $value) {
 307        switch ($key) {
 308          case 'pass':
 309            $fields[] = $key;
 310            $values[] = md5($value);
 311            $s[] = "'%s'";
 312            break;
 313          case 'mode':       case 'sort':     case 'timezone':
 314          case 'threshold':  case 'created':  case 'access':
 315          case 'login':      case 'status':
 316            $fields[] = $key;
 317            $values[] = $value;
 318            $s[] = "%d";
 319            break;
 320          default:
 321            if (substr($key, 0, 4) !== 'auth' && in_array($key, $user_fields)) {
 322              $fields[] = $key;
 323              $values[] = $value;
 324              $s[] = "'%s'";
 325            }
 326            break;
 327        }
 328      }
 329      $success = db_query('INSERT INTO {users} ('. implode(', ', $fields) .') VALUES ('. implode(', ', $s) .')', $values);
 330      if (!$success) {
 331        // On a failed INSERT some other existing user's uid may be returned.
 332        // We must abort to avoid overwriting their account.
 333        return FALSE;
 334      }
 335  
 336      // Build the initial user object.
 337      $array['uid'] = db_last_insert_id('users', 'uid');
 338      $user = user_load(array('uid' => $array['uid']));
 339  
 340      user_module_invoke('insert', $array, $user, $category);
 341  
 342      // Build and save the serialized data field now.
 343      $data = array();
 344      foreach ($array as $key => $value) {
 345        if ((substr($key, 0, 4) !== 'auth') && ($key != 'roles') && (!in_array($key, $user_fields)) && ($value !== NULL)) {
 346          $data[$key] = $value;
 347        }
 348      }
 349      db_query("UPDATE {users} SET data = '%s' WHERE uid = %d", serialize($data), $user->uid);
 350  
 351      // Save user roles (delete just to be safe).
 352      if (isset($array['roles']) && is_array($array['roles'])) {
 353        db_query('DELETE FROM {users_roles} WHERE uid = %d', $array['uid']);
 354        foreach (array_keys($array['roles']) as $rid) {
 355          if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
 356            db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $array['uid'], $rid);
 357          }
 358        }
 359      }
 360  
 361      // Build the finished user object.
 362      $user = user_load(array('uid' => $array['uid']));
 363    }
 364  
 365    // Save distributed authentication mappings.
 366    $authmaps = array();
 367    foreach ($array as $key => $value) {
 368      if (substr($key, 0, 4) == 'auth') {
 369        $authmaps[$key] = $value;
 370      }
 371    }
 372    if (sizeof($authmaps) > 0) {
 373      user_set_authmaps($user, $authmaps);
 374    }
 375  
 376    return $user;
 377  }
 378  
 379  /**
 380   * Verify the syntax of the given name.
 381   */
 382  function user_validate_name($name) {
 383    if (!strlen($name)) {
 384      return t('You must enter a username.');
 385    }
 386    if (substr($name, 0, 1) == ' ') {
 387      return t('The username cannot begin with a space.');
 388    }
 389    if (substr($name, -1) == ' ') {
 390      return t('The username cannot end with a space.');
 391    }
 392    if (strpos($name, '  ') !== FALSE) {
 393      return t('The username cannot contain multiple spaces in a row.');
 394    }
 395    if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)) {
 396      return t('The username contains an illegal character.');
 397    }
 398    if (preg_match('/[\x{80}-\x{A0}'.          // Non-printable ISO-8859-1 + NBSP
 399                     '\x{AD}'.                 // Soft-hyphen
 400                     '\x{2000}-\x{200F}'.      // Various space characters
 401                     '\x{2028}-\x{202F}'.      // Bidirectional text overrides
 402                     '\x{205F}-\x{206F}'.      // Various text hinting characters
 403                     '\x{FEFF}'.               // Byte order mark
 404                     '\x{FF01}-\x{FF60}'.      // Full-width latin
 405                     '\x{FFF9}-\x{FFFD}'.      // Replacement characters
 406                     '\x{0}-\x{1F}]/u',        // NULL byte and control characters
 407                     $name)) {
 408      return t('The username contains an illegal character.');
 409    }
 410    if (drupal_strlen($name) > USERNAME_MAX_LENGTH) {
 411      return t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
 412    }
 413  }
 414  
 415  function user_validate_mail($mail) {
 416    if (!$mail) return t('You must enter an e-mail address.');
 417    if (!valid_email_address($mail)) {
 418      return t('The e-mail address %mail is not valid.', array('%mail' => $mail));
 419    }
 420  }
 421  
 422  function user_validate_picture(&$form, &$form_state) {
 423    // If required, validate the uploaded picture.
 424    $validators = array(
 425      'file_validate_is_image' => array(),
 426      'file_validate_image_resolution' => array(variable_get('user_picture_dimensions', '85x85')),
 427      'file_validate_size' => array(variable_get('user_picture_file_size', '30') * 1024),
 428    );
 429    if ($file = file_save_upload('picture_upload', $validators)) {
 430      // Remove the old picture.
 431      if (isset($form_state['values']['_account']->picture) && file_exists($form_state['values']['_account']->picture)) {
 432        file_delete($form_state['values']['_account']->picture);
 433      }
 434  
 435      // The image was saved using file_save_upload() and was added to the
 436      // files table as a temporary file. We'll make a copy and let the garbage
 437      // collector delete the original upload.
 438      $info = image_get_info($file->filepath);
 439      $destination = variable_get('user_picture_path', 'pictures') .'/picture-'. $form['#uid'] .'.'. $info['extension'];
 440      if (file_copy($file, $destination, FILE_EXISTS_REPLACE)) {
 441        $form_state['values']['picture'] = $file->filepath;
 442      }
 443      else {
 444        form_set_error('picture_upload', t("Failed to upload the picture image; the %directory directory doesn't exist or is not writable.", array('%directory' => variable_get('user_picture_path', 'pictures'))));
 445      }
 446    }
 447  }
 448  
 449  /**
 450   * Generate a random alphanumeric password.
 451   */
 452  function user_password($length = 10) {
 453    // This variable contains the list of allowable characters for the
 454    // password. Note that the number 0 and the letter 'O' have been
 455    // removed to avoid confusion between the two. The same is true
 456    // of 'I', 1, and 'l'.
 457    $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
 458  
 459    // Zero-based count of characters in the allowable list:
 460    $len = strlen($allowable_characters) - 1;
 461  
 462    // Declare the password as a blank string.
 463    $pass = '';
 464  
 465    // Loop the number of times specified by $length.
 466    for ($i = 0; $i < $length; $i++) {
 467  
 468      // Each iteration, pick a random character from the
 469      // allowable string and append it to the password:
 470      $pass .= $allowable_characters[mt_rand(0, $len)];
 471    }
 472  
 473    return $pass;
 474  }
 475  
 476  /**
 477   * Determine whether the user has a given privilege.
 478   *
 479   * @param $string
 480   *   The permission, such as "administer nodes", being checked for.
 481   * @param $account
 482   *   (optional) The account to check, if not given use currently logged in user.
 483   * @param $reset
 484   *   (optional) Resets the user's permissions cache, which will result in a
 485   *   recalculation of the user's permissions. This is necessary to support
 486   *   dynamically added user roles.
 487   *
 488   * @return
 489   *   Boolean TRUE if the current user has the requested permission.
 490   *
 491   * All permission checks in Drupal should go through this function. This
 492   * way, we guarantee consistent behavior, and ensure that the superuser
 493   * can perform all actions.
 494   */
 495  function user_access($string, $account = NULL, $reset = FALSE) {
 496    global $user;
 497    static $perm = array();
 498  
 499    if ($reset) {
 500      $perm = array();
 501    }
 502  
 503    if (!isset($account)) {
 504      $account = $user;
 505    }
 506  
 507    // User #1 has all privileges:
 508    if ($account->uid == 1) {
 509      return TRUE;
 510    }
 511  
 512    // To reduce the number of SQL queries, we cache the user's permissions
 513    // in a static variable.
 514    if (!isset($perm[$account->uid])) {
 515      $result = db_query("SELECT p.perm FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid WHERE r.rid IN (". db_placeholders($account->roles) .")", array_keys($account->roles));
 516  
 517      $perms = array();
 518      while ($row = db_fetch_object($result)) {
 519        $perms += array_flip(explode(', ', $row->perm));
 520      }
 521      $perm[$account->uid] = $perms;
 522    }
 523  
 524    return isset($perm[$account->uid][$string]);
 525  }
 526  
 527  /**
 528   * Checks for usernames blocked by user administration.
 529   *
 530   * @return boolean TRUE for blocked users, FALSE for active.
 531   */
 532  function user_is_blocked($name) {
 533    $deny = db_fetch_object(db_query("SELECT name FROM {users} WHERE status = 0 AND name = LOWER('%s')", $name));
 534  
 535    return $deny;
 536  }
 537  
 538  function user_fields() {
 539    static $fields;
 540  
 541    if (!$fields) {
 542      $result = db_query('SELECT * FROM {users} WHERE uid = 1');
 543      if ($field = db_fetch_array($result)) {
 544        $fields = array_keys($field);
 545      }
 546      else {
 547        // Make sure we return the default fields at least.
 548        $fields = array('uid', 'name', 'pass', 'mail', 'picture', 'mode', 'sort', 'threshold', 'theme', 'signature', 'signature_format', 'created', 'access', 'login', 'status', 'timezone', 'language', 'init', 'data');
 549      }
 550    }
 551  
 552    return $fields;
 553  }
 554  
 555  /**
 556   * Implementation of hook_perm().
 557   */
 558  function user_perm() {
 559    return array('administer permissions', 'administer users', 'access user profiles', 'change own username');
 560  }
 561  
 562  /**
 563   * Implementation of hook_file_download().
 564   *
 565   * Ensure that user pictures (avatars) are always downloadable.
 566   */
 567  function user_file_download($file) {
 568    if (strpos($file, variable_get('user_picture_path', 'pictures') .'/picture-') === 0) {
 569      $info = image_get_info(file_create_path($file));
 570      return array('Content-type: '. $info['mime_type']);
 571    }
 572  }
 573  
 574  /**
 575   * Implementation of hook_search().
 576   */
 577  function user_search($op = 'search', $keys = NULL, $skip_access_check = FALSE) {
 578    switch ($op) {
 579      case 'name':
 580        if ($skip_access_check || user_access('access user profiles')) {
 581          return t('Users');
 582        }
 583      case 'search':
 584        if (user_access('access user profiles')) {
 585          $find = array();
 586          // Replace wildcards with MySQL/PostgreSQL wildcards.
 587          $keys = preg_replace('!\*+!', '%', $keys);
 588          if (user_access('administer users')) {
 589            // Administrators can also search in the otherwise private email field.
 590            $result = pager_query("SELECT name, uid, mail FROM {users} WHERE LOWER(name) LIKE LOWER('%%%s%%') OR LOWER(mail) LIKE LOWER('%%%s%%')", 15, 0, NULL, $keys, $keys);
 591            while ($account = db_fetch_object($result)) {
 592              $find[] = array('title' => $account->name .' ('. $account->mail .')', 'link' => url('user/'. $account->uid, array('absolute' => TRUE)));
 593            }
 594          }
 595          else {
 596            $result = pager_query("SELECT name, uid FROM {users} WHERE LOWER(name) LIKE LOWER('%%%s%%')", 15, 0, NULL, $keys);
 597            while ($account = db_fetch_object($result)) {
 598              $find[] = array('title' => $account->name, 'link' => url('user/'. $account->uid, array('absolute' => TRUE)));
 599            }
 600          }
 601          return $find;
 602        }
 603    }
 604  }
 605  
 606  /**
 607   * Implementation of hook_elements().
 608   */
 609  function user_elements() {
 610    return array(
 611      'user_profile_category' => array(),
 612      'user_profile_item' => array(),
 613    );
 614  }
 615  
 616  /**
 617   * Implementation of hook_user().
 618   */
 619  function user_user($type, &$edit, &$account, $category = NULL) {
 620    if ($type == 'view') {
 621      $account->content['user_picture'] = array(
 622        '#value' => theme('user_picture', $account),
 623        '#weight' => -10,
 624      );
 625      if (!isset($account->content['summary'])) {
 626        $account->content['summary'] = array();
 627      }
 628      $account->content['summary'] += array(
 629        '#type' => 'user_profile_category',
 630        '#attributes' => array('class' => 'user-member'),
 631        '#weight' => 5,
 632        '#title' => t('History'),
 633      );
 634      $account->content['summary']['member_for'] = array(
 635        '#type' => 'user_profile_item',
 636        '#title' => t('Member for'),
 637        '#value' => format_interval(time() - $account->created),
 638      );
 639    }
 640    if ($type == 'form' && $category == 'account') {
 641      $form_state = array();
 642      return user_edit_form($form_state, (isset($account->uid) ? $account->uid : FALSE), $edit);
 643    }
 644  
 645    if ($type == 'validate' && $category == 'account') {
 646      return _user_edit_validate((isset($account->uid) ? $account->uid : FALSE), $edit);
 647    }
 648  
 649    if ($type == 'submit' && $category == 'account') {
 650      return _user_edit_submit((isset($account->uid) ? $account->uid : FALSE), $edit);
 651    }
 652  
 653    if ($type == 'categories') {
 654      return array(array('name' => 'account', 'title' => t('Account settings'), 'weight' => 1));
 655    }
 656  }
 657  
 658  function user_login_block() {
 659    $form = array(
 660      '#action' => url($_GET['q'], array('query' => drupal_get_destination())),
 661      '#id' => 'user-login-form',
 662      '#validate' => user_login_default_validators(),
 663      '#submit' => array('user_login_submit'),
 664    );
 665    $form['name'] = array('#type' => 'textfield',
 666      '#title' => t('Username'),
 667      '#maxlength' => USERNAME_MAX_LENGTH,
 668      '#size' => 15,
 669      '#required' => TRUE,
 670    );
 671    $form['pass'] = array('#type' => 'password',
 672      '#title' => t('Password'),
 673      '#maxlength' => 60,
 674      '#size' => 15,
 675      '#required' => TRUE,
 676    );
 677    $form['submit'] = array('#type' => 'submit',
 678      '#value' => t('Log in'),
 679    );
 680    $items = array();
 681    if (variable_get('user_register', 1)) {
 682      $items[] = l(t('Create new account'), 'user/register', array('attributes' => array('title' => t('Create a new user account.'))));
 683    }
 684    $items[] = l(t('Request new password'), 'user/password', array('attributes' => array('title' => t('Request new password via e-mail.'))));
 685    $form['links'] = array('#value' => theme('item_list', $items));
 686    return $form;
 687  }
 688  
 689  /**
 690   * Implementation of hook_block().
 691   */
 692  function user_block($op = 'list', $delta = 0, $edit = array()) {
 693    global $user;
 694  
 695    if ($op == 'list') {
 696      $blocks[0]['info'] = t('User login');
 697      // Not worth caching.
 698      $blocks[0]['cache'] = BLOCK_NO_CACHE;
 699  
 700      $blocks[1]['info'] = t('Navigation');
 701      // Menu blocks can't be cached because each menu item can have
 702      // a custom access callback. menu.inc manages its own caching.
 703      $blocks[1]['cache'] = BLOCK_NO_CACHE;
 704  
 705      $blocks[2]['info'] = t('Who\'s new');
 706  
 707      // Too dynamic to cache.
 708      $blocks[3]['info'] = t('Who\'s online');
 709      $blocks[3]['cache'] = BLOCK_NO_CACHE;
 710      return $blocks;
 711    }
 712    else if ($op == 'configure' && $delta == 2) {
 713      $form['user_block_whois_new_count'] = array(
 714        '#type' => 'select',
 715        '#title' => t('Number of users to display'),
 716        '#default_value' => variable_get('user_block_whois_new_count', 5),
 717        '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)),
 718      );
 719      return $form;
 720    }
 721    else if ($op == 'configure' && $delta == 3) {
 722      $period = drupal_map_assoc(array(30, 60, 120, 180, 300, 600, 900, 1800, 2700, 3600, 5400, 7200, 10800, 21600, 43200, 86400), 'format_interval');
 723      $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.'));
 724      $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.'));
 725  
 726      return $form;
 727    }
 728    else if ($op == 'save' && $delta == 2) {
 729      variable_set('user_block_whois_new_count', $edit['user_block_whois_new_count']);
 730    }
 731    else if ($op == 'save' && $delta == 3) {
 732      variable_set('user_block_seconds_online', $edit['user_block_seconds_online']);
 733      variable_set('user_block_max_list_count', $edit['user_block_max_list_count']);
 734    }
 735    else if ($op == 'view') {
 736      $block = array();
 737  
 738      switch ($delta) {
 739        case 0:
 740          // For usability's sake, avoid showing two login forms on one page.
 741          if (!$user->uid && !(arg(0) == 'user' && !is_numeric(arg(1)))) {
 742  
 743            $block['subject'] = t('User login');
 744            $block['content'] = drupal_get_form('user_login_block');
 745          }
 746          return $block;
 747  
 748        case 1:
 749          if ($menu = menu_tree()) {
 750            $block['subject'] = $user->uid ? check_plain($user->name) : t('Navigation');
 751            $block['content'] = $menu;
 752          }
 753          return $block;
 754  
 755        case 2:
 756          if (user_access('access content')) {
 757            // Retrieve a list of new users who have subsequently accessed the site successfully.
 758            $result = db_query_range('SELECT uid, name FROM {users} WHERE status != 0 AND access != 0 ORDER BY created DESC', 0, variable_get('user_block_whois_new_count', 5));
 759            while ($account = db_fetch_object($result)) {
 760              $items[] = $account;
 761            }
 762            $output = theme('user_list', $items);
 763  
 764            $block['subject'] = t('Who\'s new');
 765            $block['content'] = $output;
 766          }
 767          return $block;
 768  
 769        case 3:
 770          if (user_access('access content')) {
 771            // Count users active within the defined period.
 772            $interval = time() - variable_get('user_block_seconds_online', 900);
 773  
 774            // Perform database queries to gather online user lists.  We use s.timestamp
 775            // rather than u.access because it is much faster.
 776            $anonymous_count = sess_count($interval);
 777            $authenticated_users = db_query('SELECT DISTINCT u.uid, u.name, s.timestamp FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.timestamp >= %d AND s.uid > 0 ORDER BY s.timestamp DESC', $interval);
 778            $authenticated_count = 0;
 779            $max_users = variable_get('user_block_max_list_count', 10);
 780            $items = array();
 781            while ($account = db_fetch_object($authenticated_users)) {
 782              if ($max_users > 0) {
 783                $items[] = $account;
 784                $max_users--;
 785              }
 786              $authenticated_count++;
 787            }
 788  
 789            // Format the output with proper grammar.
 790            if ($anonymous_count == 1 && $authenticated_count == 1) {
 791              $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')));
 792            }
 793            else {
 794              $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')));
 795            }
 796  
 797            // Display a list of currently online users.
 798            $max_users = variable_get('user_block_max_list_count', 10);
 799            if ($authenticated_count && $max_users) {
 800              $output .= theme('user_list', $items, t('Online users'));
 801            }
 802  
 803            $block['subject'] = t('Who\'s online');
 804            $block['content'] = $output;
 805          }
 806          return $block;
 807      }
 808    }
 809  }
 810  
 811  /**
 812   * Process variables for user-picture.tpl.php.
 813   *
 814   * The $variables array contains the following arguments:
 815   * - $account
 816   *
 817   * @see user-picture.tpl.php
 818   */
 819  function template_preprocess_user_picture(&$variables) {
 820    $variables['picture'] = '';
 821    if (variable_get('user_pictures', 0)) {
 822      $account = $variables['account'];
 823      if (!empty($account->picture) && file_exists($account->picture)) {
 824        $picture = file_create_url($account->picture);
 825      }
 826      else if (variable_get('user_picture_default', '')) {
 827        $picture = variable_get('user_picture_default', '');
 828      }
 829  
 830      if (isset($picture)) {
 831        $alt = t("@user's picture", array('@user' => $account->name ? $account->name : variable_get('anonymous', t('Anonymous'))));
 832        $variables['picture'] = theme('image', $picture, $alt, $alt, '', FALSE);
 833        if (!empty($account->uid) && user_access('access user profiles')) {
 834          $attributes = array('attributes' => array('title' => t('View user profile.')), 'html' => TRUE);
 835          $variables['picture'] = l($variables['picture'], "user/$account->uid", $attributes);
 836        }
 837      }
 838    }
 839  }
 840  
 841  /**
 842   * Make a list of users.
 843   *
 844   * @param $users
 845   *   An array with user objects. Should contain at least the name and uid.
 846   * @param $title
 847   *  (optional) Title to pass on to theme_item_list().
 848   *
 849   * @ingroup themeable
 850   */
 851  function theme_user_list($users, $title = NULL) {
 852    if (!empty($users)) {
 853      foreach ($users as $user) {
 854        $items[] = theme('username', $user);
 855      }
 856    }
 857    return theme('item_list', $items, $title);
 858  }
 859  
 860  function user_is_anonymous() {
 861    // Menu administrators can see items for anonymous when administering.
 862    return !$GLOBALS['user']->uid || !empty($GLOBALS['menu_admin']);
 863  }
 864  
 865  function user_is_logged_in() {
 866    return (bool)$GLOBALS['user']->uid;
 867  }
 868  
 869  function user_register_access() {
 870    return user_is_anonymous() && variable_get('user_register', 1);
 871  }
 872  
 873  function user_view_access($account) {
 874    return $account && $account->uid &&
 875      (
 876        // Always let users view their own profile.
 877        ($GLOBALS['user']->uid == $account->uid) ||
 878        // Administrators can view all accounts.
 879        user_access('administer users') ||
 880        // The user is not blocked and logged in at least once.
 881        ($account->access && $account->status && user_access('access user profiles'))
 882      );
 883  }
 884  
 885  /**
 886   * Access callback for user account editing.
 887   */
 888  function user_edit_access($account) {
 889    return (($GLOBALS['user']->uid == $account->uid) || user_access('administer users')) && $account->uid > 0;
 890  }
 891  
 892  function user_load_self($arg) {
 893    $arg[1] = user_load($GLOBALS['user']->uid);
 894    return $arg;
 895  }
 896  
 897  /**
 898   * Implementation of hook_menu().
 899   */
 900  function user_menu() {
 901    $items['user/autocomplete'] = array(
 902      'title' => 'User autocomplete',
 903      'page callback' => 'user_autocomplete',
 904      'access callback' => 'user_access',
 905      'access arguments' => array('access user profiles'),
 906      'type' => MENU_CALLBACK,
 907      'file' => 'user.pages.inc',
 908    );
 909  
 910    // Registration and login pages.
 911    $items['user'] = array(
 912      'title' => 'User account',
 913      'page callback' => 'user_page',
 914      'access callback' => TRUE,
 915      'type' => MENU_CALLBACK,
 916      'file' => 'user.pages.inc',
 917    );
 918  
 919    $items['user/login'] = array(
 920      'title' => 'Log in',
 921      'access callback' => 'user_is_anonymous',
 922      'type' => MENU_DEFAULT_LOCAL_TASK,
 923    );
 924  
 925    $items['user/register'] = array(
 926      'title' => 'Create new account',
 927      'page callback' => 'drupal_get_form',
 928      'page arguments' => array('user_register'),
 929      'access callback' => 'user_register_access',
 930      'type' => MENU_LOCAL_TASK,
 931      'file' => 'user.pages.inc',
 932    );
 933  
 934    $items['user/password'] = array(
 935      'title' => 'Request new password',
 936      'page callback' => 'drupal_get_form',
 937      'page arguments' => array('user_pass'),
 938      'access callback' => 'user_is_anonymous',
 939      'type' => MENU_LOCAL_TASK,
 940      'file' => 'user.pages.inc',
 941    );
 942    $items['user/reset/%/%/%'] = array(
 943      'title' => 'Reset password',
 944      'page callback' => 'drupal_get_form',
 945      'page arguments' => array('user_pass_reset', 2, 3, 4),
 946      'access callback' => TRUE,
 947      'type' => MENU_CALLBACK,
 948      'file' => 'user.pages.inc',
 949    );
 950  
 951    // Admin user pages.
 952    $items['admin/user'] = array(
 953      'title' => 'User management',
 954      'description' => "Manage your site's users, groups and access to site features.",
 955      'position' => 'left',
 956      'page callback' => 'system_admin_menu_block_page',
 957      'access arguments' => array('access administration pages'),
 958      'file' => 'system.admin.inc',
 959      'file path' => drupal_get_path('module', 'system'),
 960    );
 961    $items['admin/user/user'] = array(
 962      'title' => 'Users',
 963      'description' => 'List, add, and edit users.',
 964      'page callback' => 'user_admin',
 965      'page arguments' => array('list'),
 966      'access arguments' => array('administer users'),
 967      'file' => 'user.admin.inc',
 968    );
 969    $items['admin/user/user/list'] = array(
 970      'title' => 'List',
 971      'type' => MENU_DEFAULT_LOCAL_TASK,
 972      'weight' => -10,
 973    );
 974    $items['admin/user/user/create'] = array(
 975      'title' => 'Add user',
 976      'page arguments' => array('create'),
 977      'access arguments' => array('administer users'),
 978      'type' => MENU_LOCAL_TASK,
 979      'file' => 'user.admin.inc',
 980    );
 981    $items['admin/user/settings'] = array(
 982      'title' => 'User settings',
 983      'description' => 'Configure default behavior of users, including registration requirements, e-mails, and user pictures.',
 984      'page callback' => 'drupal_get_form',
 985      'page arguments' => array('user_admin_settings'),
 986      'access arguments' => array('administer users'),
 987      'file' => 'user.admin.inc',
 988    );
 989  
 990    // Admin access pages.
 991    $items['admin/user/permissions'] = array(
 992      'title' => 'Permissions',
 993      'description' => 'Determine access to features by selecting permissions for roles.',
 994      'page callback' => 'drupal_get_form',
 995      'page arguments' => array('user_admin_perm'),
 996      'access arguments' => array('administer permissions'),
 997      'file' => 'user.admin.inc',
 998    );
 999    $items['admin/user/roles'] = array(
1000      'title' => 'Roles',
1001      'description' => 'List, edit, or add user roles.',
1002      'page callback' => 'drupal_get_form',
1003      'page arguments' => array('user_admin_new_role'),
1004      'access arguments' => array('administer permissions'),
1005      'file' => 'user.admin.inc',
1006    );
1007    $items['admin/user/roles/edit'] = array(
1008      'title' => 'Edit role',
1009      'page arguments' => array('user_admin_role'),
1010      'access arguments' => array('administer permissions'),
1011      'type' => MENU_CALLBACK,
1012      'file' => 'user.admin.inc',
1013    );
1014    $items['admin/user/rules'] = array(
1015      'title' => 'Access rules',
1016      'description' => 'List and create rules to disallow usernames, e-mail addresses, and IP addresses.',
1017      'page callback' => 'user_admin_access',
1018      'access arguments' => array('administer permissions'),
1019      'file' => 'user.admin.inc',
1020    );
1021    $items['admin/user/rules/list'] = array(
1022      'title' => 'List',
1023      'type' => MENU_DEFAULT_LOCAL_TASK,
1024      'weight' => -10,
1025    );
1026    $items['admin/user/rules/add'] = array(
1027      'title' => 'Add rule',
1028      'page callback' => 'user_admin_access_add',
1029      'access arguments' => array('administer permissions'),
1030      'type' => MENU_LOCAL_TASK,
1031      'file' => 'user.admin.inc',
1032    );
1033    $items['admin/user/rules/check'] = array(
1034      'title' => 'Check rules',
1035      'page callback' => 'user_admin_access_check',
1036      'access arguments' => array('administer permissions'),
1037      'type' => MENU_LOCAL_TASK,
1038      'file' => 'user.admin.inc',
1039    );
1040    $items['admin/user/rules/edit'] = array(
1041      'title' => 'Edit rule',
1042      'page callback' => 'user_admin_access_edit',
1043      'access arguments' => array('administer permissions'),
1044      'type' => MENU_CALLBACK,
1045      'file' => 'user.admin.inc',
1046    );
1047    $items['admin/user/rules/delete'] = array(
1048      'title' => 'Delete rule',
1049      'page callback' => 'drupal_get_form',
1050      'page arguments' => array('user_admin_access_delete_confirm'),
1051      'access arguments' => array('administer permissions'),
1052      'type' => MENU_CALLBACK,
1053      'file' => 'user.admin.inc',
1054    );
1055  
1056    $items['logout'] = array(
1057      'title' => 'Log out',
1058      'access callback' => 'user_is_logged_in',
1059      'page callback' => 'user_logout',
1060      'weight' => 10,
1061      'file' => 'user.pages.inc',
1062    );
1063  
1064    $items['user/%user_uid_optional'] = array(
1065      'title' => 'My account',
1066      'title callback' => 'user_page_title',
1067      'title arguments' => array(1),
1068      'page callback' => 'user_view',
1069      'page arguments' => array(1),
1070      'access callback' => 'user_view_access',
1071      'access arguments' => array(1),
1072      'parent' => '',
1073      'file' => 'user.pages.inc',
1074    );
1075  
1076    $items['user/%user/view'] = array(
1077      'title' => 'View',
1078      'type' => MENU_DEFAULT_LOCAL_TASK,
1079      'weight' => -10,
1080    );
1081  
1082    $items['user/%user/delete'] = array(
1083      'title' => 'Delete',
1084      'page callback' => 'drupal_get_form',
1085      'page arguments' => array('user_confirm_delete', 1),
1086      'access callback' => 'user_access',
1087      'access arguments' => array('administer users'),
1088      'type' => MENU_CALLBACK,
1089      'file' => 'user.pages.inc',
1090    );
1091  
1092    $items['user/%user_category/edit'] = array(
1093      'title' => 'Edit',
1094      'page callback' => 'user_edit',
1095      'page arguments' => array(1),
1096      'access callback' => 'user_edit_access',
1097      'access arguments' => array(1),
1098      'type' => MENU_LOCAL_TASK,
1099      'load arguments' => array('%map', '%index'),
1100      'file' => 'user.pages.inc',
1101    );
1102  
1103    $items['user/%user_category/edit/account'] = array(
1104      'title' => 'Account',
1105      'type' => MENU_DEFAULT_LOCAL_TASK,
1106      'load arguments' => array('%map', '%index'),
1107    );
1108  
1109    $empty_account = new stdClass();
1110    if (($categories = _user_categories($empty_account)) && (count($categories) > 1)) {
1111      foreach ($categories as $key => $category) {
1112        // 'account' is already handled by the MENU_DEFAULT_LOCAL_TASK.
1113        if ($category['name'] != 'account') {
1114          $items['user/%user_category/edit/'. $category['name']] = array(
1115            'title callback' => 'check_plain',
1116            'title arguments' => array($category['title']),
1117            'page callback' => 'user_edit',
1118            'page arguments' => array(1, 3),
1119            'access callback' => isset($category['access callback']) ? $category['access callback'] : 'user_edit_access',
1120            'access arguments' => isset($category['access arguments']) ? $category['access arguments'] : array(1),
1121            'type' => MENU_LOCAL_TASK,
1122            'weight' => $category['weight'],
1123            'load arguments' => array('%map', '%index'),
1124            'tab_parent' => 'user/%/edit',
1125            'file' => 'user.pages.inc',
1126          );
1127        }
1128      }
1129    }
1130    return $items;
1131  }
1132  
1133  /**
1134   * Implementation of hook_init().
1135   */
1136  function user_init() {
1137    drupal_add_css(drupal_get_path('module', 'user') .'/user.css', 'module');
1138  }
1139  
1140  /**
1141   * Load either a specified or the current user account.
1142   *
1143   * @param $uid
1144   *   An optional user ID of the user to load. If not provided, the current
1145   *   user's ID will be used.
1146   * @return
1147   *   A fully-loaded $user object upon successful user load, FALSE if user
1148   *   cannot be loaded.
1149   *
1150   * @see user_load()
1151   */
1152  function user_uid_optional_load($uid = NULL) {
1153    if (!isset($uid)) {
1154      $uid = $GLOBALS['user']->uid;
1155    }
1156    return user_load($uid);
1157  }
1158  
1159  /**
1160   * Return a user object after checking if any profile category in the path exists.
1161   */
1162  function user_category_load($uid, &$map, $index) {
1163    static $user_categories, $accounts;
1164  
1165    // Cache $account - this load function will get called for each profile tab.
1166    if (!isset($accounts[$uid])) {
1167      $accounts[$uid] = user_load($uid);
1168    }
1169    $valid = TRUE;
1170    if (($account = $accounts[$uid]) && isset($map[$index + 1]) && $map[$index + 1] == 'edit') {
1171      // Since the path is like user/%/edit/category_name, the category name will
1172      // be at a position 2 beyond the index corresponding to the % wildcard.
1173      $category_index = $index + 2;
1174      // Valid categories may contain slashes, and hence need to be imploded.
1175      $category_path = implode('/', array_slice($map, $category_index));
1176      if ($category_path) {
1177        // Check that the requested category exists.
1178        $valid = FALSE;
1179        if (!isset($user_categories)) {
1180          $empty_account = new stdClass();
1181          $user_categories = _user_categories($empty_account);
1182        }
1183        foreach ($user_categories as $category) {
1184          if ($category['name'] == $category_path) {
1185            $valid = TRUE;
1186            // Truncate the map array in case the category name had slashes.
1187            $map = array_slice($map, 0, $category_index);
1188            // Assign the imploded category name to the last map element.
1189            $map[$category_index] = $category_path;
1190            break;
1191          }
1192        }
1193      }
1194    }
1195    return $valid ? $account : FALSE;
1196  }
1197  
1198  /**
1199   * Returns the user id of the currently logged in user.
1200   */
1201  function user_uid_optional_to_arg($arg) {
1202    // Give back the current user uid when called from eg. tracker, aka.
1203    // with an empty arg. Also use the current user uid when called from
1204    // the menu with a % for the current account link.
1205    return empty($arg) || $arg == '%' ? $GLOBALS['user']->uid : $arg;
1206  }
1207  
1208  /**
1209   * Menu item title callback - use the user name if it's not the current user.
1210   */
1211  function user_page_title($account) {
1212    if ($account->uid == $GLOBALS['user']->uid) {
1213      return t('My account');
1214    }
1215    return $account->name;
1216  }
1217  
1218  /**
1219   * Discover which external authentication module(s) authenticated a username.
1220   *
1221   * @param $authname
1222   *   A username used by an external authentication module.
1223   * @return
1224   *   An associative array with module as key and username as value.
1225   */
1226  function user_get_authmaps($authname = NULL) {
1227    $result = db_query("SELECT authname, module FROM {authmap} WHERE authname = '%s'", $authname);
1228    $authmaps = array();
1229    $has_rows = FALSE;
1230    while ($authmap = db_fetch_object($result)) {
1231      $authmaps[$authmap->module] = $authmap->authname;
1232      $has_rows = TRUE;
1233    }
1234    return $has_rows ? $authmaps : 0;
1235  }
1236  
1237  /**
1238   * Save mappings of which external authentication module(s) authenticated
1239   * a user. Maps external usernames to user ids in the users table.
1240   *
1241   * @param $account
1242   *   A user object.
1243   * @param $authmaps
1244   *   An associative array with a compound key and the username as the value.
1245   *   The key is made up of 'authname_' plus the name of the external authentication
1246   *   module.
1247   * @see user_external_login_register()
1248   */
1249  function user_set_authmaps($account, $authmaps) {
1250    foreach ($authmaps as $key => $value) {
1251      $module = explode('_', $key, 2);
1252      if ($value) {
1253        db_query("UPDATE {authmap} SET authname = '%s' WHERE uid = %d AND module = '%s'", $value, $account->uid, $module[1]);
1254        if (!db_affected_rows()) {
1255          @db_query("INSERT INTO {authmap} (authname, uid, module) VALUES ('%s', %d, '%s')", $value, $account->uid, $module[1]);
1256        }
1257      }
1258      else {
1259        db_query("DELETE FROM {authmap} WHERE uid = %d AND module = '%s'", $account->uid, $module[1]);
1260      }
1261    }
1262  }
1263  
1264  /**
1265   * Form builder; the main user login form.
1266   *
1267   * @ingroup forms
1268   */
1269  function user_login(&$form_state) {
1270    global $user;
1271  
1272    // If we are already logged on, go to the user page instead.
1273    if ($user->uid) {
1274      drupal_goto('user/'. $user->uid);
1275    }
1276  
1277    // Display login form:
1278    $form['name'] = array('#type' => 'textfield',
1279      '#title' => t('Username'),
1280      '#size' => 60,
1281      '#maxlength' => USERNAME_MAX_LENGTH,
1282      '#required' => TRUE,
1283    );
1284  
1285    $form['name']['#description'] = t('Enter your @s username.', array('@s' => variable_get('site_name', 'Drupal')));
1286    $form['pass'] = array('#type' => 'password',
1287      '#title' => t('Password'),
1288      '#description' => t('Enter the password that accompanies your username.'),
1289      '#required' => TRUE,
1290    );
1291    $form['#validate'] = user_login_default_validators();
1292    $form['submit'] = array('#type' => 'submit', '#value' => t('Log in'), '#weight' => 2);
1293  
1294    return $form;
1295  }
1296  
1297  /**
1298   * Set up a series for validators which check for blocked/denied users,
1299   * then authenticate against local database, then return an error if
1300   * authentication fails. Distributed authentication modules are welcome
1301   * to use hook_form_alter() to change this series in order to
1302   * authenticate against their user database instead of the local users
1303   * table.
1304   *
1305   * We use three validators instead of one since external authentication
1306   * modules usually only need to alter the second validator.
1307   *
1308   * @see user_login_name_validate()
1309   * @see user_login_authenticate_validate()
1310   * @see user_login_final_validate()
1311   * @return array
1312   *   A simple list of validate functions.
1313   */
1314  function user_login_default_validators() {
1315    return array('user_login_name_validate', 'user_login_authenticate_validate', 'user_login_final_validate');
1316  }
1317  
1318  /**
1319   * A FAPI validate handler. Sets an error if supplied username has been blocked
1320   * or denied access.
1321   */
1322  function user_login_name_validate($form, &$form_state) {
1323    if (isset($form_state['values']['name'])) {
1324      if (user_is_blocked($form_state['values']['name'])) {
1325        // blocked in user administration
1326        form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
1327      }
1328      else if (drupal_is_denied('user', $form_state['values']['name'])) {
1329        // denied by access controls
1330        form_set_error('name', t('The name %name is a reserved username.', array('%name' => $form_state['values']['name'])));
1331      }
1332    }
1333  }
1334  
1335  /**
1336   * A validate handler on the login form. Check supplied username/password
1337   * against local users table. If successful, sets the global $user object.
1338   */
1339  function user_login_authenticate_validate($form, &$form_state) {
1340    user_authenticate($form_state['values']);
1341  }
1342  
1343  /**
1344   * A validate handler on the login form. Should be the last validator. Sets an
1345   * error if user has not been authenticated yet.
1346   */
1347  function user_login_final_validate($form, &$form_state) {
1348    global $user;
1349    if (!$user->uid) {
1350      form_set_error('name', t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password'))));
1351    }
1352  }
1353  
1354  /**
1355   * Try to log in the user locally.
1356   *
1357   * @param $form_values
1358   *   Form values with at least 'name' and 'pass' keys, as well as anything else
1359   *   which should be passed along to hook_user op 'login'.
1360   *
1361   * @return
1362   *  A $user object, if successful.
1363   */
1364  function user_authenticate($form_values = array()) {
1365    global $user;
1366  
1367    // Load the account to check if the e-mail is denied by an access rule.
1368    // Doing this check here saves us a user_load() in user_login_name_validate()
1369    // and introduces less code change for a security fix.
1370    $account = user_load(array('name' => $form_values['name'], 'pass' => trim($form_values['pass']), 'status' => 1));
1371    if ($account && drupal_is_denied('mail', $account->mail)) {
1372      form_set_error('name', t('The name %name is registered using a reserved e-mail address and therefore could not be logged in.', array('%name' => $account->name)));
1373    }
1374  
1375    // Name and pass keys are required.
1376    // The user is about to be logged in, so make sure no error was previously
1377    // encountered in the validation process.
1378    if (!form_get_errors() && !empty($form_values['name']) && !empty($form_values['pass']) && $account) {
1379      $user = $account;
1380      user_authenticate_finalize($form_values);
1381      return $user;
1382    }
1383    else {
1384      watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_values['name']));
1385    }
1386  }
1387  
1388  /**
1389   * Finalize the login process. Must be called when logging in a user.
1390   *
1391   * The function records a watchdog message about the new session, saves the
1392   * login timestamp, calls hook_user op 'login' and generates a new session.
1393   *
1394   * $param $edit
1395   *   This array is passed to hook_user op login.
1396   */
1397  function user_authenticate_finalize(&$edit) {
1398    global $user;
1399    watchdog('user', 'Session opened for %name.', array('%name' => $user->name));
1400    // Update the user table timestamp noting user has logged in.
1401    // This is also used to invalidate one-time login links.
1402    $user->login = time();
1403    db_query("UPDATE {users} SET login = %d WHERE uid = %d", $user->login, $user->uid);
1404  
1405    // Regenerate the session ID to prevent against session fixation attacks.
1406    sess_regenerate();
1407    user_module_invoke('login', $edit, $user);
1408  }
1409  
1410  /**
1411   * Submit handler for the login form. Redirects the user to a page.
1412   *
1413   * The user is redirected to the My Account page. Setting the destination in
1414   * the query string (as done by the user login block) overrides the redirect.
1415   */
1416  function user_login_submit($form, &$form_state) {
1417    global $user;
1418    if ($user->uid) {
1419      $form_state['redirect'] = 'user/'. $user->uid;
1420      return;
1421    }
1422  }
1423  
1424  /**
1425   * Helper function for authentication modules. Either login in or registers
1426   * the current user, based on username. Either way, the global $user object is
1427   * populated based on $name.
1428   */
1429  function user_external_login_register($name, $module) {
1430    global $user;
1431  
1432    $existing_user = user_load(array('name' => $name));
1433    if (isset($existing_user->uid)) {
1434      $user = $existing_user;
1435    }
1436    else {
1437      // Register this new user.
1438      $userinfo = array(
1439        'name' => $name,
1440        'pass' => user_password(),
1441        'init' => $name,
1442        'status' => 1,
1443        "authname_$module" => $name,
1444        'access' => time()
1445      );
1446      $account = user_save('', $userinfo);
1447      // Terminate if an error occured during user_save().
1448      if (!$account) {
1449        drupal_set_message(t("Error saving user account."), 'error');
1450        return;
1451      }
1452      $user = $account;
1453      watchdog('user', 'New external user: %name using module %module.', array('%name' => $name, '%module' => $module), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $user->uid .'/edit'));
1454    }
1455  }
1456  
1457  /**
1458   * Generates a unique URL for a user to login and reset their password.
1459   *
1460   * @param object $account
1461   *   An object containing the user account.
1462   *
1463   * @return
1464   *   A unique URL that provides a one-time log in for the user, from which
1465   *   they can change their password.
1466   */
1467  function user_pass_reset_url($account) {
1468    $timestamp = time();
1469    return url("user/reset/$account->uid/$timestamp/". user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE));
1470  }
1471  
1472  function user_pass_rehash($password, $timestamp, $login) {
1473    return md5($timestamp . $password . $login);
1474  }
1475  
1476  function user_edit_form(&$form_state, $uid, $edit, $register = FALSE) {
1477    _user_password_dynamic_validation();
1478    $admin = user_access('administer users');
1479  
1480    // Account information:
1481    $form['account'] = array('#type' => 'fieldset',
1482      '#title' => t('Account information'),
1483      '#weight' => -10,
1484    );
1485    // Only show name field when: registration page; or user is editing own account and can change username; or an admin user.
1486    if ($register || ($GLOBALS['user']->uid == $uid && user_access('change own username')) || $admin) {
1487      $form['account']['name'] = array('#type' => 'textfield',
1488        '#title' => t('Username'),
1489        '#default_value' => $edit['name'],
1490        '#maxlength' => USERNAME_MAX_LENGTH,
1491        '#description' => t('Spaces are allowed; punctuation is not allowed except for periods, hyphens, and underscores.'),
1492        '#required' => TRUE,
1493      );
1494    }
1495    $form['account']['mail'] = array('#type' => 'textfield',
1496      '#title' => t('E-mail address'),
1497      '#default_value' => $edit['mail'],
1498      '#maxlength' => EMAIL_MAX_LENGTH,
1499      '#description' => t('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.'),
1500      '#required' => TRUE,
1501    );
1502    if (!$register) {
1503      $form['account']['pass'] = array('#type' => 'password_confirm',
1504        '#description' => t('To change the current user password, enter the new password in both fields.'),
1505        '#size' => 25,
1506      );
1507    }
1508    elseif (!variable_get('user_email_verification', TRUE) || $admin) {
1509      $form['account']['pass'] = array(
1510        '#type' => 'password_confirm',
1511        '#description' => t('Provide a password for the new account in both fields.'),
1512        '#required' => TRUE,
1513        '#size' => 25,
1514      );
1515    }
1516    if ($admin) {
1517      $form['account']['status'] = array(
1518        '#type' => 'radios',
1519        '#title' => t('Status'),
1520        '#default_value' => isset($edit['status']) ? $edit['status'] : 1,
1521        '#options' => array(t('Blocked'), t('Active'))
1522      );
1523    }
1524    if (user_access('administer permissions')) {
1525      $roles = user_roles(TRUE);
1526  
1527      // The disabled checkbox subelement for the 'authenticated user' role
1528      // must be generated separately and added to the checkboxes element,
1529      // because of a limitation in D6 FormAPI not supporting a single disabled
1530      // checkbox within a set of checkboxes.
1531      // TODO: This should be solved more elegantly. See issue #119038.
1532      $checkbox_authenticated = array(
1533        '#type' => 'checkbox',
1534        '#title' => $roles[DRUPAL_AUTHENTICATED_RID],
1535        '#default_value' => TRUE,
1536        '#disabled' => TRUE,
1537      );
1538  
1539      unset($roles[DRUPAL_AUTHENTICATED_RID]);
1540      if ($roles) {
1541        $default = empty($edit['roles']) ? array() : array_keys($edit['roles']);
1542        $form['account']['roles'] = array(
1543          '#type' => 'checkboxes',
1544          '#title' => t('Roles'),
1545          '#default_value' => $default,
1546          '#options' => $roles,
1547          DRUPAL_AUTHENTICATED_RID => $checkbox_authenticated,
1548        );
1549      }
1550    }
1551  
1552    // Signature:
1553    if (variable_get('user_signatures', 0) && module_exists('comment') && !$register) {
1554      $form['signature_settings'] = array(
1555        '#type' => 'fieldset',
1556        '#title' => t('Signature settings'),
1557        '#weight' => 1,
1558      );
1559      $form['signature_settings']['signature'] = array(
1560        '#type' => 'textarea',
1561        '#title' => t('Signature'),
1562        '#default_value' => $edit['signature'],
1563        '#description' => t('Your signature will be publicly displayed at the end of your comments.'),
1564      );
1565  
1566      // Prevent a "validation error" message when the user attempts to save with a default value they
1567      // do not have access to.
1568      if (!filter_access($edit['signature_format']) && empty($_POST)) {
1569        drupal_set_message(t("The signature input format has been set to a format you don't have access to. It will be changed to a format you have access to when you save this page."));
1570        $edit['signature_format'] = FILTER_FORMAT_DEFAULT;
1571      }
1572  
1573      $form['signature_settings']['signature_format'] = filter_form($edit['signature_format'], NULL, array('signature_format'));
1574    }
1575  
1576    // Picture/avatar:
1577    if (variable_get('user_pictures', 0) && !$register) {
1578      $form['picture'] = array('#type' => 'fieldset', '#title' => t('Picture'), '#weight' => 1);
1579      $picture = theme('user_picture', (object)$edit);
1580      if ($edit['picture']) {
1581        $form['picture']['current_picture'] = array('#value' => $picture);
1582        $form['picture']['picture_delete'] = array('#type' => 'checkbox', '#title' => t('Delete picture'), '#description' => t('Check this box to delete your current picture.'));
1583      }
1584      else {
1585        $form['picture']['picture_delete'] = array('#type' => 'hidden');
1586      }
1587      $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', ''));
1588      $form['#validate'][] = 'user_profile_form_validate';
1589      $form['#validate'][] = 'user_validate_picture';
1590    }
1591    $form['#uid'] = $uid;
1592  
1593    return $form;
1594  }
1595  
1596  function _user_edit_validate($uid, &$edit) {
1597    // Validate the username when: new user account; or user is editing own account and can change username; or an admin user.
1598    if (!$uid || ($GLOBALS['user']->uid == $uid && user_access('change own username')) || user_access('administer users')) {
1599      if ($error = user_validate_name($edit['name'])) {
1600        form_set_error('name', $error);
1601      }
1602      else if (db_result(db_query("SELECT COUNT(*) FROM {users} WHERE uid != %d AND LOWER(name) = LOWER('%s')", $uid, $edit['name'])) > 0) {
1603        form_set_error('name', t('The name %name is already taken.', array('%name' => $edit['name'])));
1604      }
1605      else if (drupal_is_denied('user', $edit['name'])) {
1606        form_set_error('name', t('The name %name has been denied access.', array('%name' => $edit['name'])));
1607      }
1608    }
1609  
1610    // Validate the e-mail address:
1611    if ($error = user_validate_mail($edit['mail'])) {
1612      form_set_error('mail', $error);
1613    }
1614    else if (db_result(db_query("SELECT COUNT(*) FROM {users} WHERE uid != %d AND LOWER(mail) = LOWER('%s')", $uid, $edit['mail'])) > 0) {
1615      form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $edit['mail'], '@password' => url('user/password'))));
1616    }
1617    else if (drupal_is_denied('mail', $edit['mail'])) {
1618      form_set_error('mail', t('The e-mail address %email has been denied access.', array('%email' => $edit['mail'])));
1619    }
1620  }
1621  
1622  function _user_edit_submit($uid, &$edit) {
1623    $account = user_load($uid);
1624    // Delete picture if requested, and if no replacement picture was given.
1625    if (!empty($edit['picture_delete'])) {
1626      if ($account->picture && file_exists($account->picture)) {
1627        file_delete($account->picture);
1628      }
1629      $edit['picture'] = '';
1630    }
1631    if (isset($edit['roles'])) {
1632      $edit['roles'] = array_filter($edit['roles']);
1633    }
1634  }
1635  
1636  /**
1637   * Delete a user.
1638   *
1639   * @param $edit An array of submitted form values.
1640   * @param $uid The user ID of the user to delete.
1641   */
1642  function user_delete($edit, $uid) {
1643    $account = user_load(array('uid' => $uid));
1644    sess_destroy_uid($uid);
1645    _user_mail_notify('status_deleted', $account);
1646    db_query('DELETE FROM {users} WHERE uid = %d', $uid);
1647    db_query('DELETE FROM {users_roles} WHERE uid = %d', $uid);
1648    db_query('DELETE FROM {authmap} WHERE uid = %d', $uid);
1649    $variables = array('%name' => $account->name, '%email' => '<'. $account->mail .'>');
1650    watchdog('user', 'Deleted user: %name %email.', $variables, WATCHDOG_NOTICE);
1651    user_module_invoke('delete', $edit, $account);
1652  }
1653  
1654  /**
1655   * Builds a structured array representing the profile content.
1656   *
1657   * @param $account
1658   *   A user object.
1659   *
1660   * @return
1661   *   A structured array containing the individual elements of the profile.
1662   */
1663  function user_build_content(&$account) {
1664    $edit = NULL;
1665    user_module_invoke('view', $edit, $account);
1666    // Allow modules to modify the fully-built profile.
1667    drupal_alter('profile', $account);
1668  
1669    return $account->content;
1670  }
1671  
1672  /**
1673   * Implementation of hook_mail().
1674   */
1675  function user_mail($key, &$message, $params) {
1676    $language = $message['language'];
1677    $variables = user_mail_tokens($params['account'], $language);
1678    $message['subject'] .= _user_mail_text($key .'_subject', $language, $variables);
1679    $message['body'][] = _user_mail_text($key .'_body', $language, $variables);
1680  }
1681  
1682  /**
1683   * Returns a mail string for a variable name.
1684   *
1685   * Used by user_mail() and the settings forms to retrieve strings.
1686   */
1687  function _user_mail_text($key, $language = NULL, $variables = array()) {
1688    $langcode = isset($language) ? $language->language : NULL;
1689  
1690    if ($admin_setting = variable_get('user_mail_'. $key, FALSE)) {
1691      // An admin setting overrides the default string.
1692      return strtr($admin_setting, $variables);
1693    }
1694    else {
1695      // No override, return default string.
1696      switch ($key) {
1697        case 'register_no_approval_required_subject':
1698          return t('Account details for !username at !site', $variables, $langcode);
1699        case 'register_no_approval_required_body':
1700          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\n\n--  !site team", $variables, $langcode);
1701        case 'register_admin_created_subject':
1702          return t('An administrator created an account for you at !site', $variables, $langcode);
1703        case 'register_admin_created_body':
1704          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\n\n--  !site team", $variables, $langcode);
1705        case 'register_pending_approval_subject':
1706        case 'register_pending_approval_admin_subject':
1707          return t('Account details for !username at !site (pending admin approval)', $variables, $langcode);
1708        case 'register_pending_approval_body':
1709          return t("!username,\n\nThank you for registering at !site. Your application for an account is currently pending approval. Once it has been approved, you will receive another e-mail containing information about how to log in, set your password, and other details.\n\n\n--  !site team", $variables, $langcode);
1710        case 'register_pending_approval_admin_body':
1711          return t("!username has applied for an account.\n\n!edit_uri", $variables, $langcode);
1712        case 'password_reset_subject':
1713          return t('Replacement login information for !username at !site', $variables, $langcode);
1714        case 'password_reset_body':
1715          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 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. 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, $langcode);
1716        case 'status_activated_subject':
1717          return t('Account details for !username at !site (approved)', $variables, $langcode);
1718        case 'status_activated_body':
1719          return t("!username,\n\nYour account at !site has been activated.\n\nYou may now 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\nOnce you have set your own password, you will be able to log in to !login_uri in the future using:\n\nusername: !username\n", $variables, $langcode);
1720        case 'status_blocked_subject':
1721          return t('Account details for !username at !site (blocked)', $variables, $langcode);
1722        case 'status_blocked_body':
1723          return t("!username,\n\nYour account on !site has been blocked.", $variables, $langcode);
1724        case 'status_deleted_subject':
1725          return t('Account details for !username at !site (deleted)', $variables, $langcode);
1726        case 'status_deleted_body':
1727          return t("!username,\n\nYour account on !site has been deleted.", $variables, $langcode);
1728      }
1729    }
1730  }
1731  
1732  /*** Administrative features ***********************************************/
1733  
1734  /**
1735   * Retrieve an array of roles matching specified conditions.
1736   *
1737   * @param $membersonly
1738   *   Set this to TRUE to exclude the 'anonymous' role.
1739   * @param $permission
1740   *   A string containing a permission. If set, only roles containing that
1741   *   permission are returned.
1742   *
1743   * @return
1744   *   An associative array with the role id as the key and the role name as
1745   *   value.
1746   */
1747  function user_roles($membersonly = FALSE, $permission = NULL) {
1748    // System roles take the first two positions.
1749    $roles = array(
1750      DRUPAL_ANONYMOUS_RID => NULL,
1751      DRUPAL_AUTHENTICATED_RID => NULL,
1752    );
1753  
1754    if (!empty($permission)) {
1755      $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);
1756    }
1757    else {
1758      $result = db_query('SELECT * FROM {role} ORDER BY name');
1759    }
1760  
1761    while ($role = db_fetch_object($result)) {
1762      switch ($role->rid) {
1763        // We only translate the built in role names
1764        case DRUPAL_ANONYMOUS_RID:
1765          if (!$membersonly) {
1766            $roles[$role->rid] = t($role->name);
1767          }
1768          break;
1769        case DRUPAL_AUTHENTICATED_RID:
1770          $roles[$role->rid] = t($role->name);
1771          break;
1772        default:
1773          $roles[$role->rid] = $role->name;
1774      }
1775    }
1776  
1777    // Filter to remove unmatched system roles.
1778    return array_filter($roles);
1779  }
1780  
1781  /**
1782   * Implementation of hook_user_operations().
1783   */
1784  function user_user_operations($form_state = array()) {
1785    $operations = array(
1786      'unblock' => array(
1787        'label' => t('Unblock the selected users'),
1788        'callback' => 'user_user_operations_unblock',
1789      ),
1790      'block' => array(
1791        'label' => t('Block the selected users'),
1792        'callback' => 'user_user_operations_block',
1793      ),
1794      'delete' => array(
1795        'label' => t('Delete the selected users'),
1796      ),
1797    );
1798  
1799    if (user_access('administer permissions')) {
1800      $roles = user_roles(TRUE);
1801      unset($roles[DRUPAL_AUTHENTICATED_RID]);  // Can't edit authenticated role.
1802  
1803      $add_roles = array();
1804      foreach ($roles as $key => $value) {
1805        $add_roles['add_role-'. $key] = $value;
1806      }
1807  
1808      $remove_roles = array();
1809      foreach ($roles as $key => $value) {
1810        $remove_roles['remove_role-'. $key] = $value;
1811      }
1812  
1813      if (count($roles)) {
1814        $role_operations = array(
1815          t('Add a role to the selected users') => array(
1816            'label' => $add_roles,
1817          ),
1818          t('Remove a role from the selected users') => array(
1819            'label' => $remove_roles,
1820          ),
1821        );
1822  
1823        $operations += $role_operations;
1824      }
1825    }
1826  
1827    // If the form has been posted, we need to insert the proper data for
1828    // role editing if necessary.
1829    if (!empty($form_state['submitted'])) {
1830      $operation_rid = explode('-', $form_state['values']['operation']);
1831      $operation = $operation_rid[0];
1832      if ($operation == 'add_role' || $operation == 'remove_role') {
1833        $rid = $operation_rid[1];
1834        if (user_access('administer permissions')) {
1835          $operations[$form_state['values']['operation']] = array(
1836            'callback' => 'user_multiple_role_edit',
1837            'callback arguments' => array($operation, $rid),
1838          );
1839        }
1840        else {
1841          watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
1842          return;
1843        }
1844      }
1845    }
1846  
1847    return $operations;
1848  }
1849  
1850  /**
1851   * Callback function for admin mass unblocking users.
1852   */
1853  function user_user_operations_unblock($accounts) {
1854    foreach ($accounts as $uid) {
1855      $account = user_load(array('uid' => (int)$uid));
1856      // Skip unblocking user if they are already unblocked.
1857      if ($account !== FALSE && $account->status == 0) {
1858        user_save($account, array('status' => 1));
1859      }
1860    }
1861  }
1862  
1863  /**
1864   * Callback function for admin mass blocking users.
1865   */
1866  function user_user_operations_block($accounts) {
1867    foreach ($accounts as $uid) {
1868      $account = user_load(array('uid' => (int)$uid));
1869      // Skip blocking user if they are already blocked.
1870      if ($account !== FALSE && $account->status == 1) {
1871        user_save($account, array('status' => 0));
1872      }
1873    }
1874  }
1875  
1876  /**
1877   * Callback function for admin mass adding/deleting a user role.
1878   */
1879  function user_multiple_role_edit($accounts, $operation, $rid) {
1880    // The role name is not necessary as user_save() will reload the user
1881    // object, but some modules' hook_user() may look at this first.
1882    $role_name = db_result(db_query('SELECT name FROM {role} WHERE rid = %d', $rid));
1883  
1884    switch ($operation) {
1885      case 'add_role':
1886        foreach ($accounts as $uid) {
1887          $account = user_load(array('uid' => (int)$uid));
1888          // Skip adding the role to the user if they already have it.
1889          if ($account !== FALSE && !isset($account->roles[$rid])) {
1890            $roles = $account->roles + array($rid => $role_name);
1891            user_save($account, array('roles' => $roles));
1892          }
1893        }
1894        break;
1895      case 'remove_role':
1896        foreach ($accounts as $uid) {
1897          $account = user_load(array('uid' => (int)$uid));
1898          // Skip removing the role from the user if they already don't have it.
1899          if ($account !== FALSE && isset($account->roles[$rid])) {
1900            $roles = array_diff($account->roles, array($rid => $role_name));
1901            user_save($account, array('roles' => $roles));
1902          }
1903        }
1904        break;
1905    }
1906  }
1907  
1908  function user_multiple_delete_confirm(&$form_state) {
1909    $edit = $form_state['post'];
1910  
1911    $form['accounts'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
1912    // array_filter() returns only elements with TRUE values.
1913    foreach (array_filter($edit['accounts']) as $uid => $value) {
1914      $user = db_result(db_query('SELECT name FROM {users} WHERE uid = %d', $uid));
1915      $form['accounts'][$uid] = array('#type' => 'hidden', '#value' => $uid, '#prefix' => '<li>', '#suffix' => check_plain($user) ."</li>\n");
1916    }
1917    $form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
1918  
1919    return confirm_form($form,
1920                        t('Are you sure you want to delete these users?'),
1921                        'admin/user/user', t('This action cannot be undone.'),
1922                        t('Delete all'), t('Cancel'));
1923  }
1924  
1925  function user_multiple_delete_confirm_submit($form, &$form_state) {
1926    if ($form_state['values']['confirm']) {
1927      foreach ($form_state['values']['accounts'] as $uid => $value) {
1928        user_delete($form_state['values'], $uid);
1929      }
1930      drupal_set_message(t('The users have been deleted.'));
1931    }
1932    $form_state['redirect'] = 'admin/user/user';
1933    return;
1934  }
1935  
1936  /**
1937   * Implementation of hook_help().
1938   */
1939  function user_help($path, $arg) {
1940    global $user;
1941  
1942    switch ($path) {
1943      case 'admin/help#user':
1944        $output = '<p>'. t('The user module allows users to register, login, and log out. 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 establish 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>';
1945        $output .= '<p>'. t("Users can use their own name or handle and can specify personal configuration settings through their individual <em>My account</em> page. Users must authenticate by supplying a local username and password or through their OpenID, an optional and secure method for logging into many websites with a single username and password. In some configurations, users may authenticate using a username and password from another Drupal site, or through some other site-specific mechanism.") .'</p>';
1946        $output .= '<p>'. t('A visitor accessing your website is assigned a unique ID, or session ID, which is stored in a cookie. The cookie does not contain personal information, but acts as a key to retrieve information from your site. Users should have cookies enabled in their web browser when using your site.') .'</p>';
1947        $output .= '<p>'. t('For more information, see the online handbook entry for <a href="@user">User module</a>.', array('@user' => 'http://drupal.org/handbook/modules/user/')) .'</p>';
1948        return $output;
1949      case 'admin/user/user':
1950        return '<p>'. t('Drupal allows users to register, login, log out, maintain user profiles, etc. Users of the site may not use their own names to post content until they have signed up for a user account.') .'</p>';
1951      case 'admin/user/user/create':
1952      case 'admin/user/user/account/create':
1953        return '<p>'. t("This web page allows administrators to register new users. Users' e-mail addresses and usernames must be unique.") .'</p>';
1954      case 'admin/user/rules':
1955        return '<p>'. t('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>';
1956      case 'admin/user/permissions':
1957        return '<p>'. t('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.', array('@role' => url('admin/user/roles'))) .'</p>';
1958      case 'admin/user/roles':
1959        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>
1960        <ul>
1961        <li>Anonymous user: this role is used for users that don\'t have a user account or that are not authenticated.</li>
1962        <li>Authenticated user: this role is automatically granted to all logged in users.</li>
1963        </ul>', array('@permissions' => url('admin/user/permissions')));
1964      case 'admin/user/search':
1965        return '<p>'. t('Enter a simple pattern ("*" may be used as a wildcard match) to search for a username or e-mail address. For example, one may search for "br" and Drupal might return "brian", "brad", and "brenda@example.com".') .'</p>';
1966    }
1967  }
1968  
1969  /**
1970   * Retrieve a list of all user setting/information categories and sort them by weight.
1971   */
1972  function _user_categories($account) {
1973    $categories = array();
1974  
1975    // Only variables can be passed by reference workaround.
1976    $null = NULL;
1977    foreach (module_list() as $module) {
1978      $function = $module .'_user';
1979      // $null and $account need to be passed by reference.
1980      if (function_exists($function) && ($data = $function('categories', $null, $account, ''))) {
1981        $categories = array_merge($data, $categories);
1982      }
1983    }
1984  
1985    usort($categories, '_user_sort');
1986  
1987    return $categories;
1988  }
1989  
1990  function _user_sort($a, $b) {
1991    $a = (array)$a + array('weight' => 0, 'title' => '');
1992    $b = (array)$b + array('weight' => 0, 'title' => '');
1993    return $a['weight'] < $b['weight'] ? -1 : ($a['weight'] > $b['weight'] ? 1 : ($a['title'] < $b['title'] ? -1 : 1));
1994  }
1995  
1996  /**
1997   * List user administration filters that can be applied.
1998   */
1999  function user_filters() {
2000    // Regular filters
2001    $filters = array();
2002    $roles = user_roles(TRUE);
2003    unset($roles[DRUPAL_AUTHENTICATED_RID]); // Don't list authorized role.
2004    if (count($roles)) {
2005      $filters['role'] = array(
2006        'title' => t('role'),
2007        'where' => "ur.rid = %d",
2008        'options' => $roles,
2009        'join' => '',
2010      );
2011    }
2012  
2013    $options = array();
2014    foreach (module_list() as $module) {
2015      if ($permissions = module_invoke($module, 'perm')) {
2016        asort($permissions);
2017        foreach ($permissions as $permission) {
2018          $options[t('@module module', array('@module' => $module))][$permission] = t($permission);
2019        }
2020      }
2021    }
2022    ksort($options);
2023    $filters['permission'] = array(
2024      'title' => t('permission'),
2025      'join' => 'LEFT JOIN {permission} p ON ur.rid = p.rid',
2026      'where' => " ((p.perm IS NOT NULL AND p.perm LIKE '%%%s%%') OR u.uid = 1) ",
2027      'options' => $options,
2028    );
2029  
2030    $filters['status'] = array(
2031      'title' => t('status'),
2032      'where' => 'u.status = %d',
2033      'join' => '',
2034      'options' => array(1 => t('active'), 0 => t('blocked')),
2035    );
2036    return $filters;
2037  }
2038  
2039  /**
2040   * Build query for user administration filters based on session.
2041   */
2042  function user_build_filter_query() {
2043    $filters = user_filters();
2044  
2045    // Build query
2046    $where = $args = $join = array();
2047    foreach ($_SESSION['user_overview_filter'] as $filter) {
2048      list($key, $value) = $filter;
2049      // This checks to see if this permission filter is an enabled permission for
2050      // the authenticated role. If so, then all users would be listed, and we can
2051      // skip adding it to the filter query.
2052      if ($key == 'permission') {
2053        $account = new stdClass();
2054        $account->uid = 'user_filter';
2055        $account->roles = array(DRUPAL_AUTHENTICATED_RID => 1);
2056        if (user_access($value, $account)) {
2057          continue;
2058        }
2059      }
2060      $where[] = $filters[$key]['where'];
2061      $args[] = $value;
2062      $join[] = $filters[$key]['join'];
2063    }
2064    $where = !empty($where) ? 'AND '. implode(' AND ', $where) : '';
2065    $join = !empty($join) ? ' '. implode(' ', array_unique($join)) : '';
2066  
2067    return array('where' => $where,
2068             'join' => $join,
2069             'args' => $args,
2070           );
2071  }
2072  
2073  /**
2074   * Implementation of hook_forms().
2075   */
2076  function user_forms() {
2077    $forms['user_admin_access_add_form']['callback'] = 'user_admin_access_form';
2078    $forms['user_admin_access_edit_form']['callback'] = 'user_admin_access_form';
2079    $forms['user_admin_new_role']['callback'] = 'user_admin_role';
2080    return $forms;
2081  }
2082  
2083  /**
2084   * Implementation of hook_comment().
2085   */
2086  function user_comment(&$comment, $op) {
2087    // Validate signature.
2088    if ($op == 'view') {
2089      if (variable_get('user_signatures', 0) && !empty($comment->signature)) {
2090        $comment->signature = check_markup($comment->signature, $comment->signature_format, FALSE);
2091      }
2092      else {
2093        $comment->signature = '';
2094      }
2095    }
2096  }
2097  
2098  /**
2099   * Theme output of user signature.
2100   *
2101   * @ingroup themeable
2102   */
2103  function theme_user_signature($signature) {
2104    $output = '';
2105    if ($signature) {
2106      $output .= '<div class="clear">';
2107      $output .= '<div>—</div>';
2108      $output .= $signature;
2109      $output .= '</div>';
2110    }
2111  
2112    return $output;
2113  }
2114  
2115  /**
2116   * Return an array of token to value mappings for user e-mail messages.
2117   *
2118   * @param $account
2119   *  The user object of the account being notified.  Must contain at
2120   *  least the fields 'uid', 'name', and 'mail'.
2121   * @param $language
2122   *  Language object to generate the tokens with.
2123   * @return
2124   *  Array of mappings from token names to values (for use with strtr()).
2125   */
2126  function user_mail_tokens($account, $language) {
2127    global $base_url;
2128    $tokens = array(
2129      '!username' => $account->name,
2130      '!site' => variable_get('site_name', 'Drupal'),
2131      '!login_url' => user_pass_reset_url($account),
2132      '!uri' => $base_url,
2133      '!uri_brief' => preg_replace('!^https?://!', '', $base_url),
2134      '!mailto' => $account->mail,
2135      '!date' => format_date(time(), 'medium', '', NULL, $language->language),
2136      '!login_uri' => url('user', array('absolute' => TRUE, 'language' => $language)),
2137      '!edit_uri' => url('user/'. $account->uid .'/edit', array('absolute' => TRUE, 'language' => $language)),
2138    );
2139    if (!empty($account->password)) {
2140      $tokens['!password'] = $account->password;
2141    }
2142    return $tokens;
2143  }
2144  
2145  /**
2146   * Get the language object preferred by the user. This user preference can
2147   * be set on the user account editing page, and is only available if there
2148   * are more than one languages enabled on the site. If the user did not
2149   * choose a preferred language, or is the anonymous user, the $default
2150   * value, or if it is not set, the site default language will be returned.
2151   *
2152   * @param $account
2153   *   User account to look up language for.
2154   * @param $default
2155   *   Optional default language object to return if the account
2156   *   has no valid language.
2157   */
2158  function user_preferred_language($account, $default = NULL) {
2159    $language_list = language_list();
2160    if (!empty($account->language) && isset($language_list[$account->language])) {
2161      return $language_list[$account->language];
2162    }
2163    else {
2164      return $default ? $default : language_default();
2165    }
2166  }
2167  
2168  /**
2169   * Conditionally create and send a notification email when a certain
2170   * operation happens on the given user account.
2171   *
2172   * @see user_mail_tokens()
2173   * @see drupal_mail()
2174   *
2175   * @param $op
2176   *  The operation being performed on the account.  Possible values:
2177   *  'register_admin_created': Welcome message for user created by the admin
2178   *  'register_no_approval_required': Welcome message when user self-registers
2179   *  'register_pending_approval': Welcome message, user pending admin approval
2180   *  'password_reset': Password recovery request
2181   *  'status_activated': Account activated
2182   *  'status_blocked': Account blocked
2183   *  'status_deleted': Account deleted
2184   *
2185   * @param $account
2186   *  The user object of the account being notified.  Must contain at
2187   *  least the fields 'uid', 'name', and 'mail'.
2188   * @param $language
2189   *  Optional language to use for the notification, overriding account language.
2190   * @return
2191   *  The return value from drupal_mail_send(), if ends up being called.
2192   */
2193  function _user_mail_notify($op, $account, $language = NULL) {
2194    // By default, we always notify except for deleted and blocked.
2195    $default_notify = ($op != 'status_deleted' && $op != 'status_blocked');
2196    $notify = variable_get('user_mail_'. $op .'_notify', $default_notify);
2197    if ($notify) {
2198      $params['account'] = $account;
2199      $language = $language ? $language : user_preferred_language($account);
2200      $mail = drupal_mail('user', $op, $account->mail, $language, $params);
2201      if ($op == 'register_pending_approval') {
2202        // If a user registered requiring admin approval, notify the admin, too.
2203        // We use the site default language for this.
2204        drupal_mail('user', 'register_pending_approval_admin', variable_get('site_mail', ini_get('sendmail_from')), language_default(), $params);
2205      }
2206    }
2207    return empty($mail) ? NULL : $mail['result'];
2208  }
2209  
2210  /**
2211   * Add javascript and string translations for dynamic password validation
2212   * (strength and confirmation checking).
2213   *
2214   * This is an internal function that makes it easier to manage the translation
2215   * strings that need to be passed to the javascript code.
2216   */
2217  function _user_password_dynamic_validation() {
2218    static $complete = FALSE;
2219    global $user;
2220    // Only need to do once per page.
2221    if (!$complete) {
2222      drupal_add_js(drupal_get_path('module', 'user') .'/user.js', 'module');
2223  
2224      drupal_add_js(array(
2225        'password' => array(
2226          'strengthTitle' => t('Password strength:'),
2227          'lowStrength' => t('Low'),
2228          'mediumStrength' => t('Medium'),
2229          'highStrength' => t('High'),
2230          'tooShort' => t('It is recommended to choose a password that contains at least six characters. It should include numbers, punctuation, and both upper and lowercase letters.'),
2231          'needsMoreVariation' => t('The password does not include enough variation to be secure. Try:'),
2232          'addLetters' => t('Adding both upper and lowercase letters.'),
2233          'addNumbers' => t('Adding numbers.'),
2234          'addPunctuation' => t('Adding punctuation.'),
2235          'sameAsUsername' => t('It is recommended to choose a password different from the username.'),
2236          'confirmSuccess' => t('Yes'),
2237          'confirmFailure' => t('No'),
2238          'confirmTitle' => t('Passwords match:'),
2239          'username' => (isset($user->name) ? $user->name : ''))),
2240        'setting');
2241      $complete = TRUE;
2242    }
2243  }
2244  
2245  /**
2246   * Implementation of hook_hook_info().
2247   */
2248  function user_hook_info() {
2249    return array(
2250      'user' => array(
2251        'user' => array(
2252          'insert' => array(
2253            'runs when' => t('After a user account has been created'),
2254          ),
2255          'update' => array(
2256            'runs when' => t("After a user's profile has been updated"),
2257          ),
2258          'delete' => array(
2259            'runs when' => t('After a user has been deleted')
2260          ),
2261          'login' => array(
2262            'runs when' => t('After a user has logged in')
2263          ),
2264          'logout' => array(
2265            'runs when' => t('After a user has logged out')
2266          ),
2267          'view' => array(
2268            'runs when' => t("When a user's profile is being viewed")
2269          ),
2270        ),
2271      ),
2272    );
2273  }
2274  
2275  /**
2276   * Implementation of hook_action_info().
2277   */
2278  function user_action_info() {
2279    return array(
2280      'user_block_user_action' => array(
2281        'description' => t('Block current user'),
2282        'type' => 'user',
2283        'configurable' => FALSE,
2284        'hooks' => array(),
2285      ),
2286      'user_block_ip_action' => array(
2287        'description' => t('Ban IP address of current user'),
2288        'type' => 'user',
2289        'configurable' => FALSE,
2290        'hooks' => array(),
2291      ),
2292    );
2293  }
2294  
2295  /**
2296   * Implementation of a Drupal action.
2297   * Blocks the current user.
2298   */
2299  function user_block_user_action(&$object, $context = array()) {
2300    if (isset($object->uid)) {
2301      $uid = $object->uid;
2302    }
2303    elseif (isset($context['uid'])) {
2304      $uid = $context['uid'];
2305    }
2306    else {
2307      global $user;
2308      $uid = $user->uid;
2309    }
2310    db_query("UPDATE {users} SET status = 0 WHERE uid = %d", $uid);
2311    sess_destroy_uid($uid);
2312    watchdog('action', 'Blocked user %name.', array('%name' => check_plain($user->name)));
2313  }
2314  
2315  /**
2316   * Implementation of a Drupal action.
2317   * Adds an access rule that blocks the user's IP address.
2318   */
2319  function user_block_ip_action() {
2320    $ip = ip_address();
2321    db_query("INSERT INTO {access} (mask, type, status) VALUES ('%s', '%s', %d)", $ip, 'host', 0);
2322    watchdog('action', 'Banned IP address %ip', array('%ip' => $ip));
2323  }
2324  
2325  /**
2326   * Submit handler for the user registration form.
2327   *
2328   * This function is shared by the installation form and the normal registration form,
2329   * which is why it can't be in the user.pages.inc file.
2330   */
2331  function user_register_submit($form, &$form_state) {
2332    global $base_url;
2333    $admin = user_access('administer users');
2334  
2335    $mail = $form_state['values']['mail'];
2336    $name = $form_state['values']['name'];
2337    if (!variable_get('user_email_verification', TRUE) || $admin) {
2338      $pass = $form_state['values']['pass'];
2339    }
2340    else {
2341      $pass = user_password();
2342    };
2343    $notify = isset($form_state['values']['notify']) ? $form_state['values']['notify'] : NULL;
2344    $from = variable_get('site_mail', ini_get('sendmail_from'));
2345    if (isset($form_state['values']['roles'])) {
2346      // Remove unset roles.
2347      $roles = array_filter($form_state['values']['roles']);
2348    }
2349    else {
2350      $roles = array();
2351    }
2352  
2353    if (!$admin && array_intersect(array_keys($form_state['values']), array('uid', 'roles', 'init', 'session', 'status'))) {
2354      watchdog('security', 'Detected malicious attempt to alter protected user fields.', array(), WATCHDOG_WARNING);
2355      $form_state['redirect'] = 'user/register';
2356      return;
2357    }
2358    // The unset below is needed to prevent these form values from being saved as
2359    // user data.
2360    unset($form_state['values']['form_token'], $form_state['values']['submit'], $form_state['values']['op'], $form_state['values']['notify'], $form_state['values']['form_id'], $form_state['values']['affiliates'], $form_state['values']['destination']);
2361  
2362    $merge_data = array('pass' => $pass, 'init' => $mail, 'roles' => $roles);
2363    if (!$admin) {
2364      // Set the user's status because it was not displayed in the form.
2365      $merge_data['status'] = variable_get('user_register', 1) == 1;
2366    }
2367    $account = user_save('', array_merge($form_state['values'], $merge_data));
2368    // Terminate if an error occured during user_save().
2369    if (!$account) {
2370      drupal_set_message(t("Error saving user account."), 'error');
2371      $form_state['redirect'] = '';
2372      return;
2373    }
2374    $form_state['user'] = $account;
2375  
2376    watchdog('user', 'New user: %name (%email).', array('%name' => $name, '%email' => $mail), WATCHDOG_NOTICE, l(t('edit'), 'user/'. $account->uid .'/edit'));
2377  
2378    // The first user may login immediately, and receives a customized welcome e-mail.
2379    if ($account->uid == 1) {
2380      drupal_set_message(t('Welcome to Drupal. You are now logged in as user #1, which gives you full control over your website.'));
2381      if (variable_get('user_email_verification', TRUE)) {
2382        drupal_set_message(t('</p><p> Your password is <strong>%pass</strong>. You may change your password below.</p>', array('%pass' => $pass)));
2383      }
2384  
2385      user_authenticate(array_merge($form_state['values'], $merge_data));
2386  
2387      $form_state['redirect'] = 'user/1/edit';
2388      return;
2389    }
2390    else {
2391      // Add plain text password into user account to generate mail tokens.
2392      $account->password = $pass;
2393      if ($admin && !$notify) {
2394        drupal_set_message(t('Created a new user account for <a href="@url">%name</a>. No e-mail has been sent.', array('@url' => url("user/$account->uid"), '%name' => $account->name)));
2395      }
2396      else if (!variable_get('user_email_verification', TRUE) && $account->status && !$admin) {
2397        // No e-mail verification is required, create new user account, and login
2398        // user immediately.
2399        _user_mail_notify('register_no_approval_required', $account);
2400        if (user_authenticate(array_merge($form_state['values'], $merge_data))) {
2401          drupal_set_message(t('Registration successful. You are now logged in.'));
2402        }
2403        $form_state['redirect'] = '';
2404        return;
2405      }
2406      else if ($account->status || $notify) {
2407        // Create new user account, no administrator approval required.
2408        $op = $notify ? 'register_admin_created' : 'register_no_approval_required';
2409        _user_mail_notify($op, $account);
2410        if ($notify) {
2411          drupal_set_message(t('Password and further instructions have been e-mailed to the new user <a href="@url">%name</a>.', array('@url' => url("user/$account->uid"), '%name' => $account->name)));
2412        }
2413        else {
2414          drupal_set_message(t('Your password and further instructions have been sent to your e-mail address.'));
2415          $form_state['redirect'] = '';
2416          return;
2417        }
2418      }
2419      else {
2420        // Create new user account, administrator approval required.
2421        _user_mail_notify('register_pending_approval', $account);
2422        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, a welcome message with further instructions has been sent to your e-mail address.'));
2423        $form_state['redirect'] = '';
2424        return;
2425  
2426      }
2427    }
2428  }
2429  
2430  /**
2431   * Form builder; The user registration form.
2432   *
2433   * @ingroup forms
2434   * @see user_register_validate()
2435   * @see user_register_submit()
2436   */
2437  function user_register() {
2438    global $user;
2439  
2440    $admin = user_access('administer users');
2441  
2442    // If we aren't admin but already logged on, go to the user page instead.
2443    if (!$admin && $user->uid) {
2444      drupal_goto('user/'. $user->uid);
2445    }
2446  
2447    $form = array();
2448  
2449    // Display the registration form.
2450    if (!$admin) {
2451      $form['user_registration_help'] = array(
2452        '#value' => filter_xss_admin(variable_get('user_registration_help', '')),
2453        // Ensure that user registration help appears above profile fields.
2454        '#weight' => -20,
2455      );
2456    }
2457  
2458    // Merge in the default user edit fields.
2459    $form = array_merge($form, user_edit_form($form_state, NULL, NULL, TRUE));
2460    if ($admin) {
2461      $form['account']['notify'] = array(
2462       '#type' => 'checkbox',
2463       '#title' => t('Notify user of new account')
2464      );
2465      // Redirect back to page which initiated the create request;
2466      // usually admin/user/user/create.
2467      $form['destination'] = array('#type' => 'hidden', '#value' => $_GET['q']);
2468    }
2469  
2470    // Create a dummy variable for pass-by-reference parameters.
2471    $null = NULL;
2472    $extra = _user_forms($null, NULL, NULL, 'register');
2473  
2474    // Remove form_group around default fields if there are no other groups.
2475    if (!$extra) {
2476      foreach (array('name', 'mail', 'pass', 'status', 'roles', 'notify') as $key) {
2477        if (isset($form['account'][$key])) {
2478          $form[$key] = $form['account'][$key];
2479        }
2480      }
2481      unset($form['account']);
2482    }
2483    else {
2484      $form = array_merge($form, $extra);
2485    }
2486  
2487    if (variable_get('configurable_timezones', 1)) {
2488      // Override field ID, so we only change timezone on user registration,
2489      // and never touch it on user edit pages.
2490      $form['timezone'] = array(
2491        '#type' => 'hidden',
2492        '#default_value' => variable_get('date_default_timezone', NULL),
2493        '#id' => 'edit-user-register-timezone',
2494      );
2495  
2496      // Add the JavaScript callback to automatically set the timezone.
2497      drupal_add_js('
2498  // Global Killswitch
2499  if (Drupal.jsEnabled) {
2500    $(document).ready(function() {
2501      Drupal.setDefaultTimezone();
2502    });
2503  }', 'inline');
2504    }
2505  
2506    $form['submit'] = array('#type' => 'submit', '#value' => t('Create new account'), '#weight' => 30);
2507    $form['#validate'][] = 'user_register_validate';
2508  
2509    return $form;
2510  }
2511  
2512  function user_register_validate($form, &$form_state) {
2513    user_module_invoke('validate', $form_state['values'], $form_state['values'], 'account');
2514  }
2515  
2516  /**
2517   * Retrieve a list of all form elements for the specified category.
2518   */
2519  function _user_forms(&$edit, $account, $category, $hook = 'form') {
2520    $groups = array();
2521    foreach (module_list() as $module) {
2522      $function = $module .'_user';
2523      // $edit and $account need to be passed by reference.
2524      if (function_exists($function) && ($data = $function($hook, $edit, $account, $category))) {
2525        $groups = array_merge_recursive($data, $groups);
2526      }
2527    }
2528    uasort($groups, '_user_sort');
2529  
2530    return empty($groups) ? FALSE : $groups;
2531  }
2532  
2533  /**
2534   * Prepare a destination query string for use in combination with drupal_goto().
2535   *
2536   * Used to direct the user back to the referring page after completing
2537   * the openid login.  This function prevents the login page from being
2538   * returned because that page will give an access denied message to an
2539   * authenticated user.
2540   *
2541   * @see drupal_get_destination()
2542   */
2543  function user_login_destination() {
2544    $destination = drupal_get_destination();
2545    return $destination == 'destination=user%2Flogin' ? 'destination=user' : $destination;
2546  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7