user_foo

user 也是模組之一,管理使用者的登入登出,或是權限的認證。
總之想作好一個有管理使用者的模組,這個部分的函數就必須有所了解。
這些函數不需要特別去引用,直接呼叫函數的名稱就可以了。

user_access

定義

user_access($string, $account = NULL)
modules/user/user.module, 第 351 行開始

描述

判斷使用者是否具有某種權限。
所有的權限判斷,都應該要例用這個函數。一來是讓所有的程式有一致性,而且可以保證SuperUser能擁有所有的權限。

參數

$string: 要判定的權限名字,如"administer nodes"
$account (預設): 一個 User 物件。在要判斷的使用者並不是「目前登入的那個人」時使用。

傳回值

如果判定是「有這個權限」,則傳回布林值 TRUE。

範例

檢查目前使用者是否有「存取管理頁面(administer comments)」的權限

$has_permission = user_access("administer comments");    // 第二個參數省略

原始碼

<?php
function user_access($string, $account = NULL) {
  global
$user;
  static
$perm = array();

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

 
// 超級使用者 SuperUser 擁有所有的權限
 
if ($account->uid == 1) {
    return
TRUE;
  }

 
// 為了滅少到資料庫作查詢連線數,會把作查過的資料放入靜態變數中
 
if (!isset($perm[$account->uid])) {
   
$result = db_query("SELECT DISTINCT(p.perm) FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid WHERE r.rid IN (%s)", implode(',', array_keys($account->roles)));

   
$perm[$account->uid] = '';
    while (
$row = db_fetch_object($result)) {
     
$perm[$account->uid] .= "$row->perm, ";
    }
  }

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

  return
FALSE;
}
?>

user_load

定義

user_load($array = array())
modules/user/user.module

描述

取得一個 user 物件

參數

$array 一個關連式陣列,讓這個函數可以找得到你想找的使用者資料,可能是帳號名稱或是e-mail位址。

傳回值

如果找到的話,傳回一個"完整"的 user 物件,否則傳回 FALSE

範例

其實Drupal 的登入就是靠它來檢查,輸入帳號密碼,來作搜尋有沒有符合的條件。

$account = user_load(array('name' => '納格髓', 'pass' => 'very secret', 'status' => 1))

name: 帳號
pass: 密碼
status:狀態 (1 代表啟用中)

如果有找到,就表示帳號密碼正確,$account 就會傳回一個 user 物件。再下兩行

global $user;
$user = $account;  // 把找到的 user 物件"存"起來

這樣就算是完成登入了。很簡單吧!!

附帶一提,登出的語法也很簡單。

global $user;
$user = drupal_anonymous_user();

程式碼

<?php
function user_load($array = array()) {
 
// Dynamically compose a SQL query:
 
$query = array();
 
$params = array();

  foreach (
$array as $key => $value) {
    if (
$key == 'uid' || $key == 'status') {
     
$query[] = "$key = %d";
     
$params[] = $value;
    }
    else if (
$key == 'pass') {
     
$query[] = "pass = '%s'";
     
$params[] = md5($value);
    }
    else {
     
$query[]= "LOWER($key) = LOWER('%s')";
     
$params[] = $value;
    }
  }
 
$result = db_query('SELECT * FROM {users} u WHERE '. implode(' AND ', $query), $params);

  if (
db_num_rows($result)) {
   
$user = db_fetch_object($result);
   
$user = drupal_unpack($user);

   
$user->roles = array();
    if (
$user->uid) {
     
$user->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';
    }
    else {
     
$user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
    }
   
$result = db_query('SELECT r.rid, r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d', $user->uid);
    while (
$role = db_fetch_object($result)) {
     
$user->roles[$role->rid] = $role->name;
    }
   
user_module_invoke('load', $array, $user);
  }
  else {
   
$user = FALSE;
  }

  return
$user;
}
?>

user_roles

定義

user_roles($membersonly = 0, $permission = 0)
modules/user/user.module, 第 1762 行開始

描述

傳回符合條件的「群組(身分)」陣列
(在 Drupal 裡,群組(身分)被叫作"role")

參數

$membersonly: 如果設為 TRUE ,則傳回的時候會排除「訪客群組(身分)」
$permission: 一個字串,如果加到它,則只傳回包含這個權限的群組(身分)回來

傳回值

一個關連陣列,鍵是該群組在資料庫的ID,值是它的名字。

範例

列出現在所有的群組: (假設我事前建立一個叫作 power user 的群組)

print_r(user_roles());

傳回值:
Array
(
    [1] => anonymous user
    [2] => authenticated user
    [3] => power user
)

程式碼

<?php
function user_roles($membersonly = 0, $permission = 0) {
 
$roles = array();

  if (
$permission) {
   
$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);
  }
  else {
   
$result = db_query('SELECT * FROM {role} ORDER BY name');
  }
  while (
$role = db_fetch_object($result)) {
    if (!
$membersonly || ($membersonly && $role->rid != DRUPAL_ANONYMOUS_RID)) {
     
$roles[$role->rid] = $role->name;
    }
  }
  return
$roles;
}
?>

user_save

定義

user_save($account, $array = array(), $category = 'account')
modules/user/user.module, 第 106 行開始

描述

更新一個使用者帳號內容,或是建立一個新的帳號

參數

$account 一個 user 物件,如果 $user->uid(帳號 ID)為空,則建立新的帳號,如果有東西的話,就更新這個 uid 的帳號資料
$array 一個帶有帳號資訊的陣列。例如: array('name' => 'My name'); 值為 NULL 的話,就表示把這個欄位清空。
(註: 在更新中,沒有列在這個陣列裡的欄位會保持不變)
$category (選擇性參數) 用來作註記的參數。在這個函數中並沒有用到,主要是用來當參數傳給相關的 hook 函數。

範例

第一個參數要求的是一個 user 物件,但是其實只用到裡面的 uid 屬性。
所以要新增一個帳號的時候,並不用費心去建立一個"空的 user 物件",給它一個 '' (空字串)就好了。
如:

$roles = array(
   '3' => '角色名稱',
   '4' => '角色名稱',
   '5' => '角色名稱',
);
user_save('', array("name" => '納格髓', "pass" => 'unknow', "status" => 1, 'roles' => $roles , 'profile_tel' => '0800 123456'));;

name: 帳號名稱
pass: 密碼 (不需要作加密動作)
status: 狀態(1 表示啟用中)
roles: 權限,也許叫它"群組"比較適合,這裡要放array。(例子中,是加入編號3, 4, 5 的群組)
profile_tel: 這個是自訂的使用者欄位,名字叫作"tel"

註:
在 Drupal 的群組(roles)中,1 表示訪客,2 表示已註冊帳號。這兩個資訊在記錄的時候會"自動跳過"。因為有沒有帳號就已經足以作為這兩者的區分。所以就算你在roles放入 1 或 2 也不會被記入資料庫中。
(原來文章的例子是直接打 array(3, 4, 5) 來加入編號 3, 4, 5的角色,這種寫法是錯誤的)

原始碼

<?php
function user_save($account, $array = array(), $category = 'account') {
 
// Dynamically compose a SQL query:
 
$user_fields = user_fields();
  if (
$account->uid) {
   
user_module_invoke('update', $array, $account, $category);

   
$data = unserialize(db_result(db_query('SELECT data FROM {users} WHERE uid = %d', $account->uid)));
    foreach (
$array as $key => $value) {
      if (
$key == 'pass' && !empty($value)) {
       
$query .= "$key = '%s', ";
       
$v[] = md5($value);
      }
      else if ((
substr($key, 0, 4) !== 'auth') && ($key != 'pass')) {
        if (
in_array($key, $user_fields)) {
         
// Save standard fields
         
$query .= "$key = '%s', ";
         
$v[] = $value;
        }
        else if (
$key != 'roles') {
         
// Roles is a special case: it used below.
         
if ($value === NULL) {
            unset(
$data[$key]);
          }
          else {
           
$data[$key] = $value;
          }
        }
      }
    }
   
$query .= "data = '%s' ";
   
$v[] = serialize($data);

   
db_query("UPDATE {users} SET $query WHERE uid = %d", array_merge($v, array($account->uid)));

   
// Reload user roles if provided
   
if (is_array($array['roles'])) {
     
db_query('DELETE FROM {users_roles} WHERE uid = %d', $account->uid);

      foreach (
array_keys($array['roles']) as $rid) {
        if (!
in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
         
db_query('INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)', $account->uid, $rid);
        }
      }
    }

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

   
// If the password changed, delete all open sessions and recreate
    // the current one.
   
if (isset($array['pass'])) {
     
sess_destroy_uid($account->uid);
     
sess_regenerate();
    }

   
// Refresh user object
   
$user = user_load(array('uid' => $account->uid));
   
user_module_invoke('after_update', $array, $user, $category);
  }
  else {
   
$array['uid'] = db_next_id('{users}_uid');

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

   
// Note, we wait with saving the data column to prevent module-handled
    // fields from being saved there. We cannot invoke hook_user('insert') here
    // because we don't have a fully initialized user object yet.
   
foreach ($array as $key => $value) {
      switch (
$key) {
        case
'pass':
         
$fields[] = $key;
         
$values[] = md5($value);
         
$s[] = "'%s'";
          break;
        case
'uid':        case 'mode':     case 'sort':
        case
'threshold':  case 'created':  case 'access':
        case
'login':      case 'status':
         
$fields[] = $key;
         
$values[] = $value;
         
$s[] = "%d";
          break;
        default:
          if (
substr($key, 0, 4) !== 'auth' && in_array($key, $user_fields)) {
           
$fields[] = $key;
           
$values[] = $value;
           
$s[] = "'%s'";
          }
          break;
      }
    }
   
db_query('INSERT INTO {users} ('. implode(', ', $fields) .') VALUES ('. implode(', ', $s) .')', $values);

   
// Build the initial user object.
   
$user = user_load(array('uid' => $array['uid']));

   
user_module_invoke('insert', $array, $user, $category);

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

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

   
// Build the finished user object.
   
$user = user_load(array('uid' => $array['uid']));
  }

 
// Save distributed authentication mappings
 
$authmaps = array();
  foreach (
$array as $key => $value) {
    if (
substr($key, 0, 4) == 'auth') {
     
$authmaps[$key] = $value;
    }
  }
  if (
sizeof($authmaps) > 0) {
   
user_set_authmaps($user, $authmaps);
  }

  return
$user;
}
?>