Skip to content
user.module 103 KiB
Newer Older
Dries Buytaert's avatar
 
Dries Buytaert committed
<?php
// $Id$

Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * @file
 * Enables the user registration and login system.
 */

/**
 * Maximum length of username text field.
 */

/**
 * Maximum length of user e-mail text field.
 */
/**
 * Invokes hook_user() in every module.
 *
 * We cannot use module_invoke() for this, because the arguments need to
 * be passed by reference.
 */
function user_module_invoke($type, &$array, &$user, $category = NULL) {
  foreach (module_implements('user_' . $type) as $module) {
    $function = $module . '_user_' . $type;
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
}

 */
function user_theme() {
  return array(
    'user_picture' => array(
      'arguments' => array('account' => NULL),
      'arguments' => array('elements' => NULL),
    ),
    'user_profile_category' => array(
      'arguments' => array('element' => NULL),
      'template' => 'user-profile-category',
    ),
    'user_profile_item' => array(
      'arguments' => array('element' => NULL),
    ),
    'user_list' => array(
      'arguments' => array('users' => NULL, 'title' => NULL),
    ),
    'user_admin_perm' => array(
      'arguments' => array('form' => NULL),
    ),
    'user_admin_new_role' => array(
      'arguments' => array('form' => NULL),
    ),
    'user_admin_account' => array(
      'arguments' => array('form' => NULL),
    ),
    'user_filter_form' => array(
      'arguments' => array('form' => NULL),
    ),
    'user_filters' => array(
      'arguments' => array('form' => NULL),
    'user_signature' => array(
      'arguments' => array('signature' => NULL),
    ),
Dries Buytaert's avatar
 
Dries Buytaert committed
/**
 * Implementation of hook_fieldable_info().
 */
function user_fieldable_info() {
  $return = array(
    'user' => array(
      'name' => t('User'),
      'id key' => 'uid',
    ),
  );
  return $return;
}

/**
 * Implementation of hook_field_build_modes().
 */
function user_field_build_modes($obj_type) {
  $modes = array();
  if ($obj_type == 'user') {
    $modes = array(
      'full' => t('User account'),
    );
  }
  return $modes;
}

Dries Buytaert's avatar
 
Dries Buytaert committed
function user_external_load($authname) {
  $uid = db_query("SELECT uid FROM {authmap} WHERE authname = :authname", array(':authname' => $authname))->fetchField();
Dries Buytaert's avatar
 
Dries Buytaert committed

Dries Buytaert's avatar
 
Dries Buytaert committed
  }
  else {
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
}

 * Perform standard Drupal login operations for a user object.
 *
 * The user object must already be authenticated. This function verifies
 * that the user account is not blocked and then performs the login,
 * updates the login timestamp in the database, invokes hook_user('login'),
 *
 * @param $account
 *    An authenticated user object to be set as the currently logged
 *    in user.
 * @param $edit
 *    The array of form values submitted by the user, if any.
 *    This array is passed to hook_user op login.
 * @return boolean
 *    TRUE if the login succeeds, FALSE otherwise.
 */
function user_external_login($account, $edit = array()) {
  $form = drupal_render(drupal_get_form('user_login'));
  $state['values'] = $edit;
  if (empty($state['values']['name'])) {
    $state['values']['name'] = $account->name;
  }

  user_login_name_validate($form, $state, (array)$account);
  if (form_get_errors()) {
  user_authenticate_finalize($state['values']);
 * Load multiple users based on certain conditions.
 * This function should be used whenever you need to load more than one user
 * from the database. Users are loaded into memory and will not require
 * database access if loaded again during the same page request.
 * @param $uids
 *   An array of user IDs.
 * @param $conditions
 *   An array of conditions to match against the {users} table. These
 *   should be supplied in the form array('field_name' => 'field_value').
 * @param $reset
 *   A boolean indicating that the internal cache should be reset. Use this if
 *   loading a user object which has been altered during the page request.
 *   An array of user objects, indexed by uid.
 *
 * @see user_load()
 * @see user_load_by_mail()
 * @see user_load_by_name()
function user_load_multiple($uids = array(), $conditions = array(), $reset = FALSE) {
  static $user_cache = array();
  if ($reset) {
    $user_cache = array();
  $users = array();

  // Create a new variable which is either a prepared version of the $uids
  // array for later comparison with the user cache, or FALSE if no $uids were
  // passed. The $uids array is reduced as items are loaded from cache, and we
  // need to know if it's empty for this reason to avoid querying the database
  // when all requested users are loaded from cache.
  $passed_uids = !empty($uids) ? array_flip($uids) : FALSE;

  // Load any available users from the internal cache.
  if ($user_cache) {
    if ($uids && !$conditions) {
      $users += array_intersect_key($user_cache, $passed_uids);
      // If any users were loaded, remove them from the $uids still to load.
      $uids = array_keys(array_diff_key($passed_uids, $users));
Dries Buytaert's avatar
 
Dries Buytaert committed
    }
  }

  // Load any remaining users from the database, this is necessary if we have
  // $uids still to load, or if $conditions was passed without $uids.
  if ($uids || ($conditions && !$passed_uids)) {
    $query = db_select('users', 'u')->fields('u');
Dries Buytaert's avatar
 
Dries Buytaert committed

    // If the $uids array is populated, add those to the query.
    if ($uids) {
      $query->condition('u.uid', $uids, 'IN');
    // If the conditions array is populated, add those to the query.
    if ($conditions) {
      // TODO D7: Using LIKE() to get a case insensitive comparison because Crell
      // and chx promise that dbtng will map it to ILIKE in postgres.
        $query->condition('u.name', $conditions['name'], 'LIKE');
        unset($conditions['name']);
      }
      if (isset($conditions['mail'])) {
        $query->condition('u.mail', $conditions['mail'], 'LIKE');
        unset($conditions['mail']);
      }
      foreach ($conditions as $field => $value) {
        $query->condition('u.' . $field, $value);
      }
    $result = $query->execute();

    $queried_users = array();
    // Build an array of user picture IDs so that these can be fetched later.
    $picture_fids = array();
    foreach ($result as $record) {
      $picture_fids[] = $record->picture;
      $queried_users[$record->uid] = drupal_unpack($record);
      $queried_users[$record->uid]->roles = array();
      if ($record->uid) {
        $queried_users[$record->uid]->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
      }
      else {
        $queried_users[$record->uid]->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
      }
    if (!empty($queried_users)) {
      // Add any additional roles from the database.
      $result = db_query('SELECT r.rid, r.name, ur.uid FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid IN (:uids)', array(':uids' => array_keys($queried_users)));
      foreach ($result as $record) {
        $queried_users[$record->uid]->roles[$record->rid] = $record->name;
      }
Dries Buytaert's avatar
 
Dries Buytaert committed

      // Add the full file objects for user pictures if enabled.
      if (!empty($picture_fids) && variable_get('user_pictures', 1) == 1) {
        $pictures = file_load_multiple($picture_fids);
        foreach ($queried_users as $account) {
          if (!empty($account->picture) && isset($pictures[$account->picture])) {
            $account->picture = $pictures[$account->picture];
          }
          else {
            $account->picture = NULL;
          }
        }
      }

      // Invoke hook_user_load() on the users loaded from the database
      // and add them to the static cache.
      foreach (module_implements('user_load') as $module) {
        $function = $module . '_user_load';
        $function($queried_users);
      }

      $users = $users + $queried_users;
      $user_cache = $user_cache + $queried_users;
    }

  // Ensure that the returned array is ordered the same as the original $uids
  // array if this was passed in and remove any invalid uids.
  if ($passed_uids) {
    // Remove any invalid uids from the array.
    $passed_uids = array_intersect_key($passed_uids, $users);
    foreach ($users as $user) {
      $passed_uids[$user->uid] = $user;
    }
    $users = $passed_uids;
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
Dries Buytaert's avatar
 
Dries Buytaert committed

  return $users;
}


/**
 * Fetch a user object.
 *
 * @param $uid
 *   Integer specifying the user id.
 * @param $reset
 *   A boolean indicating that the internal cache should be reset.
 * @return
 *   A fully-loaded $user object upon successful user load or FALSE if user
 *   cannot be loaded.
 *
 * @see user_load_multiple()
 */
function user_load($uid, $reset = FALSE) {
  $users = user_load_multiple(array($uid), array(), $reset);
  return reset($users);
}

/**
 * Fetch a user object by email address.
 *
 * @param $mail
 *   String with the account's e-mail address.
 * @return
 *   A fully-loaded $user object upon successful user load or FALSE if user
 *   cannot be loaded.
 *
 * @see user_load_multiple()
 */
function user_load_by_mail($mail) {
  $users = user_load_multiple(array(), array('mail' => $mail));
  return reset($users);
}

/**
 * Fetch a user object by account name.
 *
 * @param $name
 *   String with the account's user name.
 * @return
 *   A fully-loaded $user object upon successful user load or FALSE if user
 *   cannot be loaded.
 *
 * @see user_load_multiple()
 */
function user_load_by_name($name) {
  $users = user_load_multiple(array(), array('name' => $name));
  return reset($users);
Dries Buytaert's avatar
 
Dries Buytaert committed
}

 * Save changes to a user account or add a new user.
 *   The $user object for the user to modify or add. If $user->uid is
 *   omitted (or $user->is_new == TRUE), a new user will be added.
 *   An array of fields and values to save. For example array('name'
 *   => 'My name'). Keys that do not belong to columns in the user-related
 *   tables are added to the a serialized array in the 'data' column
 *   and will be loaded in the $user->data array by user_load().
 *   Setting a field to NULL deletes it from the data column.
 *
 * @param $category
 *   (optional) The category for storing profile information in.
 *   A fully-loaded $user object upon successful save or FALSE if the save failed.
function user_save($account, $edit = array(), $category = 'account') {
    // Allow alternate password hashing schemes.
    require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
    $edit['pass'] = user_hash_password(trim($edit['pass']));
    // Abort if the hashing failed and returned FALSE.
  }
  else {
    // Avoid overwriting an existing password with a blank password.
Dries Buytaert's avatar
 
Dries Buytaert committed
  // Get the fields form so we can recognize the fields in the $edit
  // form that should not go into the serialized data array.
  $field_form = array();
  $field_form_state = array();
  $edit = (object) $edit;
  field_attach_form('user', $edit, $field_form, $field_form_state);

  // Presave fields.
  field_attach_presave('user', $edit);

  $edit = (array) $edit;

    $account->is_new = empty($account->uid);
    user_module_invoke('update', $edit, $account, $category);
    $data = unserialize(db_query('SELECT data FROM {users} WHERE uid = :uid', array(':uid' => $account->uid))->fetchField());
    // Consider users edited by an administrator as logged in, if they haven't
    // already, so anonymous users can view the profile (if allowed).
    if (empty($edit['access']) && empty($account->access) && user_access('administer users')) {
Dries Buytaert's avatar
 
Dries Buytaert committed
      // Form fields that don't pertain to the users, user_roles, or
      // Field API are automatically serialized into the users.data
Dries Buytaert's avatar
 
Dries Buytaert committed
      // column.
      if (!in_array($key, array('roles', 'is_new')) && empty($user_fields[$key]) && empty($field_form[$key])) {
Dries Buytaert's avatar
 
Dries Buytaert committed
        }
Dries Buytaert's avatar
 
Dries Buytaert committed
        }
Dries Buytaert's avatar
 
Dries Buytaert committed
      }
    }


    // Process picture uploads.
    if (!empty($edit['picture']->fid)) {
      $picture = $edit['picture'];
      // If the picture is a temporary file move it to its final location and
      // make it permanent.
      if (($picture->status & FILE_STATUS_PERMANENT) == 0) {
        $info = image_get_info($picture->filepath);
        $destination = file_create_path(variable_get('user_picture_path', 'pictures') . '/picture-' . $account->uid . '.' . $info['extension']);
        if ($picture = file_move($picture, $destination, FILE_EXISTS_REPLACE)) {
          $picture->status |= FILE_STATUS_PERMANENT;
          $edit['picture'] = file_save($picture);
        }
      }
    }
    $edit['picture'] = empty($edit['picture']->fid) ? 0 : $edit['picture']->fid;

    $edit['data'] = $data;
    $edit['uid'] = $account->uid;
    $success = drupal_write_record('users', $edit, 'uid');
Dries Buytaert's avatar
 
Dries Buytaert committed
      // The query failed - better to abort the save than risk further
      // data loss.

      // TODO: Fields change: I think this is a bug.  If no columns in
      // the user table are changed, drupal_write_record returns
Dries Buytaert's avatar
 
Dries Buytaert committed
      // FALSE because rowCount() (rows changed) is 0.  However,
      // non-users data may have been changed, e.g. fields.
Dries Buytaert's avatar
 
Dries Buytaert committed
      // return FALSE;
Dries Buytaert's avatar
 
Dries Buytaert committed

    // If the picture changed or was unset, remove the old one. This step needs
    // to occur after updating the {users} record so that user_file_references()
    // doesn't report it in use and block the deletion.
    if (!empty($account->picture->fid) && ($edit['picture'] != $account->picture->fid)) {
      file_delete($account->picture);
    }

    if (isset($edit['roles']) && is_array($edit['roles'])) {
      db_delete('users_roles')
        ->condition('uid', $account->uid)
        ->execute();
Dries Buytaert's avatar
 
Dries Buytaert committed

      $query = db_insert('users_roles')->fields(array('uid', 'rid'));
      foreach (array_keys($edit['roles']) as $rid) {
        if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
          $query->values(array(
            'uid' => $account->uid,
            'rid' => $rid,
          ));
Dries Buytaert's avatar
 
Dries Buytaert committed
    }

    // Delete a blocked user's sessions to kick them if they are online.
    if (isset($edit['status']) && $edit['status'] == 0) {
    // If the password changed, delete all open sessions and recreate
    // the current one.
      if ($account->uid == $GLOBALS['user']->uid) {
        drupal_session_regenerate();
      }
Dries Buytaert's avatar
 
Dries Buytaert committed
    // Save Field data.
    $object = (object) $edit;
    field_attach_update('user', $object);
Dries Buytaert's avatar
 
Dries Buytaert committed


    // Send emails after we have the new user object.
    if (isset($edit['status']) && $edit['status'] != $account->status) {
      // The user's status is changing; conditionally send notification email.
      $op = $edit['status'] == 1 ? 'status_activated' : 'status_blocked';
    user_module_invoke('after_update', $edit, $user, $category);
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
  else {
    // Consider users created by an administrator as already logged in, so
    // anonymous users can view the profile (if allowed).
    if (empty($edit['access']) && user_access('administer users')) {
    $success = drupal_write_record('users', $edit);
    if (!$success) {
      // On a failed INSERT some other existing user's uid may be returned.
      // We must abort to avoid overwriting their account.
      return FALSE;
    }
    // Build the initial user object.
Dries Buytaert's avatar
 
Dries Buytaert committed

    $object = (object) $edit;
    field_attach_insert('user', $object);
Dries Buytaert's avatar
 
Dries Buytaert committed

    user_module_invoke('insert', $edit, $user, $category);
    // Note, we wait with saving the data column to prevent module-handled
    // fields from being saved there.
      // Form fields that don't pertain to the users, user_roles, or
      // Field API are automatically serialized into the user.data
Dries Buytaert's avatar
 
Dries Buytaert committed
      // column.
      if ((!in_array($key, array('roles', 'is_new'))) && (empty($user_fields[$key]) && empty($field_form[$key])) && ($value !== NULL)) {
    if (!empty($data)) {
      $data_array = array('uid' => $user->uid, 'data' => $data);
      drupal_write_record('users', $data_array, 'uid');
    // Save user roles (delete just to be safe).
    if (isset($edit['roles']) && is_array($edit['roles'])) {
      db_delete('users_roles')
        ->condition('uid', $edit['uid'])
        ->execute();
      $query = db_insert('users_roles')->fields(array('uid', 'rid'));
      foreach (array_keys($edit['roles']) as $rid) {
        if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
          $query->values(array(
            'uid' => $edit['uid'],
            'rid' => $rid,
          ));
    // Build the finished user object.
Dries Buytaert's avatar
 
Dries Buytaert committed
  }

  return $user;
}

/**
 * Verify the syntax of the given name.
 */
Dries Buytaert's avatar
 
Dries Buytaert committed
function user_validate_name($name) {
  if (!$name) {
    return t('You must enter a username.');
  }
  if (substr($name, 0, 1) == ' ') {
    return t('The username cannot begin with a space.');
  }
  if (substr($name, -1) == ' ') {
    return t('The username cannot end with a space.');
  }
  if (strpos($name, '  ') !== FALSE) {
    return t('The username cannot contain multiple spaces in a row.');
  }
  if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)) {
    return t('The username contains an illegal character.');
  }
  if (preg_match('/[\x{80}-\x{A0}' .         // Non-printable ISO-8859-1 + NBSP
                  '\x{AD}' .                // Soft-hyphen
                  '\x{2000}-\x{200F}' .     // Various space characters
                  '\x{2028}-\x{202F}' .     // Bidirectional text overrides
                  '\x{205F}-\x{206F}' .     // Various text hinting characters
                  '\x{FEFF}' .              // Byte order mark
                  '\x{FF01}-\x{FF60}' .     // Full-width latin
                  '\x{FFF9}-\x{FFFD}' .     // Replacement characters
                  '\x{0}-\x{1F}]/u',        // NULL byte and control characters
                  $name)) {
    return t('The username contains an illegal character.');
  }
  if (drupal_strlen($name) > USERNAME_MAX_LENGTH) {
    return t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
  }
Dries Buytaert's avatar
 
Dries Buytaert committed
}

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

function user_validate_picture(&$form, &$form_state) {
  // If required, validate the uploaded picture.
  $validators = array(
    'file_validate_is_image' => array(),
    'file_validate_image_resolution' => array(variable_get('user_picture_dimensions', '85x85')),
    'file_validate_size' => array(variable_get('user_picture_file_size', '30') * 1024),
  );
  // Save the file as a temporary file.
  $file = file_save_upload('picture_upload', $validators);
  if ($file === FALSE) {
    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'))));
  }
  elseif ($file !== NULL) {
    $form_state['values']['picture_upload'] = $file;
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
}

/**
 * Generate a random alphanumeric password.
 */
Dries Buytaert's avatar
 
Dries Buytaert committed
function user_password($length = 10) {
  // This variable contains the list of allowable characters for the
  // password. Note that the number 0 and the letter 'O' have been
  // removed to avoid confusion between the two. The same is true
  $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
  // Zero-based count of characters in the allowable list:
  $len = strlen($allowable_characters) - 1;
Dries Buytaert's avatar
 
Dries Buytaert committed

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

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

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

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

/**
 * Determine the permissions for one or more roles.
 *
 * @param $roles
 *   An array whose keys are the role IDs of interest, such as $user->roles.
 * @param $reset
 *   Optional parameter - if TRUE data in the static variable is rebuilt.
 *
 * @return
 *   An array indexed by role ID. Each value is an array whose keys are the
 *   permission strings for the given role ID.
 */
function user_role_permissions($roles = array(), $reset = FALSE) {
  static $stored_permissions = array();

  if ($reset) {
    // Clear the data cached in the static variable.
    $stored_permissions = array();
  }

  $role_permissions = $fetch = array();

  if ($roles) {
    foreach ($roles as $rid => $name) {
      if (isset($stored_permissions[$rid])) {
        $role_permissions[$rid] = $stored_permissions[$rid];
      }
      else {
        // Add this rid to the list of those needing to be fetched.
        $fetch[] = $rid;
        // Prepare in case no permissions are returned.
        $stored_permissions[$rid] = array();
      }
    }

    if ($fetch) {
      // Get from the database permissions that were not in the static variable.
      // Only role IDs with at least one permission assigned will return rows.
      $result = db_query("SELECT rid, permission FROM {role_permission} WHERE rid IN (:fetch)", array(':fetch' => $fetch));
      foreach ($result as $row) {
        $stored_permissions[$row->rid][$row->permission] = TRUE;
      }
      foreach ($fetch as $rid) {
        // For every rid, we know we at least assigned an empty array.
        $role_permissions[$rid] = $stored_permissions[$rid];
      }
    }
  }

  return $role_permissions;
}

/**
 * Determine whether the user has a given privilege.
 *
 * @param $string
 *   The permission, such as "administer nodes", being checked for.
Dries Buytaert's avatar
 
Dries Buytaert committed
 * @param $account
 *   (optional) The account to check, if not given use currently logged in user.
 * @param $reset
 *   (optional) Resets the user's permissions cache, which will result in a
 *   recalculation of the user's permissions. This is necessary to support
 *   dynamically added user roles.
 *   Boolean TRUE if the current user has the requested permission.
 *
 * All permission checks in Drupal should go through this function. This
 * way, we guarantee consistent behavior, and ensure that the superuser
 * can perform all actions.
 */
function user_access($string, $account = NULL, $reset = FALSE) {
Dries Buytaert's avatar
 
Dries Buytaert committed
  global $user;
Dries Buytaert's avatar
 
Dries Buytaert committed
  static $perm = array();
Dries Buytaert's avatar
 
Dries Buytaert committed

  if (is_null($account)) {
    $account = $user;
  }

Dries Buytaert's avatar
 
Dries Buytaert committed
  }

  // To reduce the number of SQL queries, we cache the user's permissions
  // in a static variable.
    $role_permissions = user_role_permissions($account->roles, $reset);
Dries Buytaert's avatar
 
Dries Buytaert committed

    foreach ($role_permissions as $one_role) {
      $perms += $one_role;
Dries Buytaert's avatar
 
Dries Buytaert committed
    }
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
  return isset($perm[$account->uid][$string]);
Dries Buytaert's avatar
 
Dries Buytaert committed
}

 * Checks for usernames blocked by user administration.
 * @return boolean TRUE for blocked users, FALSE for active.
  $deny = db_query("SELECT name FROM {users} WHERE status = 0 AND name = LOWER(:name)", array(':name' => $name))->fetchObject();
/**
 * Implementation of hook_perm().
 */
Dries Buytaert's avatar
 
Dries Buytaert committed
function user_perm() {
  return array(
    'administer permissions' =>  array(
      'title' => t('Administer permissions'),
      'description' => t('Manage the permissions assigned to user roles. %warning', array('%warning' => t('Warning: Give to trusted roles only; this permission has security implications.'))),
    ),
    'administer users' => array(
      'title' => t('Administer users'),
      'description' => t('Manage or block users, and manage their role assignments.'),
    ),
    'access user profiles' => array(
      'title' => t('Access user profiles'),
      'description' => t('View profiles of users on the site, which may contain personal information.'),
    ),
    'change own username' => array(
      'title' => t('Change own username'),
      'description' => t('Select a different username.'),
    ),
    'cancel account' => array(
      'title' => t('Cancel account'),
      'description' => t('Remove or disable own user account and unpublish, anonymize, or remove own submissions depending on the configured <a href="@user-settings-url">user settings</a>.', array('@user-settings-url' => url('admin/user/settings'))),
    ),
    'select account cancellation method' => array(
      'title' => t('Select method for cancelling own account'),
      'description' => t('Select the method for cancelling own user account. %warning', array('%warning' => t('Warning: Give to trusted roles only; this permission has security implications.'))),
    ),
  );
Dries Buytaert's avatar
 
Dries Buytaert committed
}

/**
 * Implementation of hook_file_download().
 *
 * Ensure that user pictures (avatars) are always downloadable.
 */
function user_file_download($filepath) {
  if (strpos($filepath, variable_get('user_picture_path', 'pictures') . '/picture-') === 0) {
    $info = image_get_info(file_create_path($filepath));
    return array('Content-Type' => $info['mime_type']);
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
}

/**
 * Implementation of hook_file_references().
 */
function user_file_references($file) {
  // Determine if the file is used by this module.
  $file_used = (bool) db_query_range('SELECT 1 FROM {users} WHERE picture = :fid', array(':fid' => $file->fid), 0, 1)->fetchField();
  if ($file_used) {
    // Return the name of the module and how many references it has to the file.
    return array('user' => $count);
  }
}

/**
 * Implementation of hook_file_delete().
 */
function user_file_delete($file) {
  // Remove any references to the file.
    ->fields(array('picture' => 0))
    ->condition('picture', $file->fid)
    ->execute();
}

/**
 * Implementation of hook_search().
 */
function user_search($op = 'search', $keys = NULL, $skip_access_check = FALSE) {
      if ($skip_access_check || user_access('access user profiles')) {
      if (user_access('access user profiles')) {
        $find = array();
        // Replace wildcards with MySQL/PostgreSQL wildcards.
        $keys = preg_replace('!\*+!', '%', $keys);
        $query = db_select('users')->extend('PagerDefault');
        $query->fields('users', array('name', 'uid', 'mail'));
        if (user_access('administer users')) {
          // Administrators can also search in the otherwise private email field.
            where('LOWER(name) LIKE LOWER(:name)', array(':name' => "%$keys%"))->
            where('LOWER(mail) LIKE LOWER(:mail)', array(':mail' => "%$keys%")));
          $query->where('LOWER(name) LIKE LOWER(:name)', array(':name' => "%$keys%"));
        }
        foreach ($result as $account) {
          $find[] = array('title' => $account->name . ' (' . $account->mail . ')', 'link' => url('user/' . $account->uid, array('absolute' => TRUE)));
Dries Buytaert's avatar
 
Dries Buytaert committed
  }
}

/**
 * Implementation of hook_elements().
 */
function user_elements() {
  return array(
    'user_profile_category' => array(
      '#theme_wrapper' => 'user_profile_category'
    ),
    'user_profile_item' => array(
      '#theme' => 'user_profile_item'
    ),
function user_user_view(&$edit, &$account, $category = NULL) {
  $account->content['user_picture'] = array(
    '#markup' => theme('user_picture', $account),
    '#weight' => -10,
  );
  if (!isset($account->content['summary'])) {
    $account->content['summary'] = array();
  }
  $account->content['summary'] += array(
    '#type' => 'user_profile_category',
    '#attributes' => array('class' => 'user-member'),
    '#weight' => 5,
    '#title' => t('History'),
  );
  $account->content['summary']['member_for'] =  array(
    '#type' => 'user_profile_item',
    '#title' => t('Member for'),
    '#markup' => format_interval(REQUEST_TIME - $account->created),
  );
}

/**
 * Implementation of hook_user_form.
 */
function user_user_form(&$edit, &$account, $category = NULL) {
  if ($category == 'account') {
    return user_edit_form($form_state, (isset($account->uid) ? $account->uid : FALSE), $edit);
 * Implementation of hook_user_validate().
 */
function user_user_validate(&$edit, &$account, $category = NULL) {
  if ($category == 'account') {
    $uid = isset($account->uid) ? $account->uid : FALSE;
    // Validate the username when: new user account; or user is editing own account and can change username; or an admin user.
    if (!$uid || ($GLOBALS['user']->uid == $uid && user_access('change own username')) || user_access('administer users')) {
      if ($error = user_validate_name($edit['name'])) {
        form_set_error('name', $error);
      }
      elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(name) = LOWER(:name)", array(':uid' => $uid, ':name' => $edit['name']), 0, 1)->fetchField()) {
        form_set_error('name', t('The name %name is already taken.', array('%name' => $edit['name'])));
      }
    }

    // Validate the e-mail address, and check if it is taken by an existing user.
    if ($error = user_validate_mail($edit['mail'])) {
      form_set_error('mail', $error);
    }
    elseif ((bool) db_query_range("SELECT 1 FROM {users} WHERE uid <> :uid AND LOWER(mail) = LOWER(:mail)", array(':uid' => $uid, ':mail' => $edit['mail']), 0, 1)->fetchField()) {
      // Format error message dependent on whether the user is logged in or not.
      if ($GLOBALS['user']->uid) {
        form_set_error('mail', t('The e-mail address %email is already taken.', array('%email' => $edit['mail'])));
      }
      else {
        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'))));
      }

    // Make sure the signature isn't longer than the size of the database field.
    // Signatures are disabled by default, so make sure it exists first.
    if (isset($edit['signature'])) {
      $user_schema = drupal_get_schema('users');
      if (strlen($edit['signature']) > $user_schema['fields']['signature']['length']) {
        form_set_error('signature', t('The signature is too long: it must be %max characters or less.', array('%max' => $user_schema['fields']['signature']['length'])));
      }
    }
 * Implementation of hook_user_submit().
 */
function user_user_submit(&$edit, &$account, $category = NULL) {
  if ($category == 'account') {
    if (!empty($edit['picture_upload'])) {
      $edit['picture'] = $edit['picture_upload'];
    }
    // Delete picture if requested, and if no replacement picture was given.
    elseif (!empty($edit['picture_delete'])) {
      $edit['picture'] = NULL;
    // Remove these values so they don't end up serialized in the data field.
    $edit['picture_upload'] = NULL;
    $edit['picture_delete'] = NULL;

    if (isset($edit['roles'])) {
      $edit['roles'] = array_filter($edit['roles']);
    }