summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.txt2
-rw-r--r--database/database.mysql10
-rw-r--r--database/database.pgsql10
-rw-r--r--database/updates.inc10
-rw-r--r--includes/session.inc11
-rw-r--r--modules/comment.module15
-rw-r--r--modules/comment/comment.module15
-rw-r--r--modules/user.module79
-rw-r--r--modules/user/user.module79
-rw-r--r--update.php35
10 files changed, 214 insertions, 52 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index a142b205e..88f95a5e1 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -6,6 +6,8 @@ Drupal x.x.x, xxxx-xx-xx
* made it possible to browse the profiles by field.
- menu module:
* made it possible to customize menus.
+- user management:
+ * added support for multiple roles per user.
- refactored 403 (forbidden) handling and added support for custom 403 pages.
- syndication:
* added support for RSS ping-notifications of http://technorati.com/.
diff --git a/database/database.mysql b/database/database.mysql
index d8073ad79..6d900b653 100644
--- a/database/database.mysql
+++ b/database/database.mysql
@@ -598,6 +598,16 @@ CREATE TABLE users (
) TYPE=MyISAM;
--
+-- Table structure for table 'users_roles'
+--
+
+CREATE TABLE users_roles (
+ uid int(10) unsigned NOT NULL default '0',
+ rid int(10) unsigned NOT NULL default '0',
+ PRIMARY KEY (uid, rid)
+) TYPE=MyISAM;
+
+--
-- Table structure for table 'variable'
--
diff --git a/database/database.pgsql b/database/database.pgsql
index 317324bd8..a3d8a686b 100644
--- a/database/database.pgsql
+++ b/database/database.pgsql
@@ -593,6 +593,16 @@ CREATE INDEX users_changed_idx ON users(changed);
CREATE SEQUENCE users_uid_seq INCREMENT 1 START 1;
--
+-- Table structure for users_roles
+--
+
+CREATE TABLE users_roles (
+ uid integer NOT NULL default '0',
+ rid integer NOT NULL default '0',
+ PRIMARY KEY (uid, rid)
+);
+
+--
-- Table structure for variable
--
diff --git a/database/updates.inc b/database/updates.inc
index b96d29863..7e437fd4f 100644
--- a/database/updates.inc
+++ b/database/updates.inc
@@ -56,7 +56,8 @@ $sql_updates = array(
"2004-02-27" => "update_82",
"2004-04-15" => "update_83",
"2004-04-21" => "update_84",
- "2004-04-27" => "update_85"
+ "2004-04-27" => "update_85",
+ "2004-05-10" => "update_86"
);
function update_32() {
@@ -1075,6 +1076,13 @@ function update_85() {
return $ret;
}
+function update_86() {
+ $ret = array();
+ $ret[] = update_sql("INSERT INTO {users_roles} (uid, rid) SELECT uid, rid FROM {users}");
+ // TODO: should we verify the insert above worked before dropping rid?
+ $ret[] = update_sql("ALTER TABLE {users} DROP rid");
+}
+
function update_sql($sql) {
$edit = $_POST["edit"];
$result = db_query($sql);
diff --git a/includes/session.inc b/includes/session.inc
index 14f98cf92..277885eae 100644
--- a/includes/session.inc
+++ b/includes/session.inc
@@ -17,15 +17,22 @@ function sess_close() {
function sess_read($key) {
global $user;
- $result = db_query_range("SELECT u.*, s.*, r.name AS role FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid LEFT JOIN {role} r ON u.rid = r.rid WHERE s.sid = '%s' AND u.status < 3", $key, 0, 1);
+ $result = db_query_range("SELECT u.*, s.* FROM {users} u INNER JOIN {sessions} s ON u.uid = s.uid WHERE s.sid = '%s' AND u.status < 3", $key, 0, 1);
if (!db_num_rows($result)) {
- $result = db_query("SELECT u.*, r.name AS role FROM {users} u INNER JOIN {role} r ON u.rid = r.rid WHERE u.uid = 0");
+ $result = db_query("SELECT u.* FROM {users} u WHERE u.uid = 0");
db_query("INSERT INTO {sessions} (uid, sid, hostname, timestamp) values(%d, '%s', '%s', %d)", $user->uid, $key, $_SERVER["REMOTE_ADDR"], time());
}
$user = db_fetch_object($result);
$user = drupal_unpack($user);
+ $user->roles = array();
+
+ $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;
+ }
return !empty($user->session) ? $user->session : '';
}
diff --git a/modules/comment.module b/modules/comment.module
index d5239fcd1..841197d3f 100644
--- a/modules/comment.module
+++ b/modules/comment.module
@@ -322,7 +322,12 @@ function comment_post($edit) {
$status = user_access("post comments without approval") ? 0 : 1;
$roles = variable_get("comment_roles", array());
- $score = $roles[$user->rid] ? $roles[$user->rid] : 0;
+ $score = 0;
+
+ foreach (array_intersect(array_keys($roles), array_keys($user->roles)) as $rid) {
+ $score = max($roles[$rid], $score);
+ }
+
$users = serialize(array(0 => $score));
/*
@@ -371,13 +376,13 @@ function comment_post($edit) {
*/
// Get the parent comment:
- $parent = db_fetch_object(db_query("SELECT * FROM {comments} WHERE cid = '%d'", $edit["pid"]));
+ $parent = db_fetch_object(db_query("SELECT * FROM {comments} WHERE cid = %d", $edit['pid']));
// Strip the "/" from the end of the parent thread:
$parent->thread = (string)rtrim((string)$parent->thread, "/");
// Get the max value in _this_ thread:
- $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = '%d'", $parent->thread, $edit["nid"]));
+ $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = %d", $parent->thread, $edit['nid']));
if ($max == "") {
// First child of this parent
@@ -1342,7 +1347,7 @@ function theme_comment_moderation_form($comment) {
// comment hasn't been moderated yet:
if (!isset($votes)) {
- $result = db_query("SELECT v.mid, v.vote, r.value FROM {moderation_votes} v, {moderation_roles} r WHERE v.mid = r.mid AND r.rid = %d ORDER BY weight", $user->rid);
+ $result = db_query("SELECT v.mid, v.vote, MAX(r.value) AS value FROM {moderation_votes} v INNER JOIN {moderation_roles} r ON r.mid = v.mid WHERE r.rid IN (%s) GROUP BY v.mid, v.vote ORDER BY weight", implode(", ", array_keys($user->roles)));
$votes = array();
while ($vote = db_fetch_object($result)) {
if ($vote->value != 0) {
@@ -1452,7 +1457,7 @@ function comment_moderate() {
$moderation = $_POST["moderation"];
if ($moderation) {
- $result = db_query("SELECT mid, value FROM {moderation_roles} WHERE rid = %d", $user->rid);
+ $result = db_query("SELECT mid, MAX(value) AS value FROM {moderation_roles} WHERE rid IN (%s) GROUP BY mid", implode(", ", array_keys($user->roles)));
while ($mod = db_fetch_object($result)) {
$votes[$mod->mid] = $mod->value;
}
diff --git a/modules/comment/comment.module b/modules/comment/comment.module
index d5239fcd1..841197d3f 100644
--- a/modules/comment/comment.module
+++ b/modules/comment/comment.module
@@ -322,7 +322,12 @@ function comment_post($edit) {
$status = user_access("post comments without approval") ? 0 : 1;
$roles = variable_get("comment_roles", array());
- $score = $roles[$user->rid] ? $roles[$user->rid] : 0;
+ $score = 0;
+
+ foreach (array_intersect(array_keys($roles), array_keys($user->roles)) as $rid) {
+ $score = max($roles[$rid], $score);
+ }
+
$users = serialize(array(0 => $score));
/*
@@ -371,13 +376,13 @@ function comment_post($edit) {
*/
// Get the parent comment:
- $parent = db_fetch_object(db_query("SELECT * FROM {comments} WHERE cid = '%d'", $edit["pid"]));
+ $parent = db_fetch_object(db_query("SELECT * FROM {comments} WHERE cid = %d", $edit['pid']));
// Strip the "/" from the end of the parent thread:
$parent->thread = (string)rtrim((string)$parent->thread, "/");
// Get the max value in _this_ thread:
- $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = '%d'", $parent->thread, $edit["nid"]));
+ $max = db_result(db_query("SELECT MAX(thread) FROM {comments} WHERE thread LIKE '%s.%%' AND nid = %d", $parent->thread, $edit['nid']));
if ($max == "") {
// First child of this parent
@@ -1342,7 +1347,7 @@ function theme_comment_moderation_form($comment) {
// comment hasn't been moderated yet:
if (!isset($votes)) {
- $result = db_query("SELECT v.mid, v.vote, r.value FROM {moderation_votes} v, {moderation_roles} r WHERE v.mid = r.mid AND r.rid = %d ORDER BY weight", $user->rid);
+ $result = db_query("SELECT v.mid, v.vote, MAX(r.value) AS value FROM {moderation_votes} v INNER JOIN {moderation_roles} r ON r.mid = v.mid WHERE r.rid IN (%s) GROUP BY v.mid, v.vote ORDER BY weight", implode(", ", array_keys($user->roles)));
$votes = array();
while ($vote = db_fetch_object($result)) {
if ($vote->value != 0) {
@@ -1452,7 +1457,7 @@ function comment_moderate() {
$moderation = $_POST["moderation"];
if ($moderation) {
- $result = db_query("SELECT mid, value FROM {moderation_roles} WHERE rid = %d", $user->rid);
+ $result = db_query("SELECT mid, MAX(value) AS value FROM {moderation_roles} WHERE rid IN (%s) GROUP BY mid", implode(", ", array_keys($user->roles)));
while ($mod = db_fetch_object($result)) {
$votes[$mod->mid] = $mod->value;
}
diff --git a/modules/user.module b/modules/user.module
index 2c07b5aa0..0187142cc 100644
--- a/modules/user.module
+++ b/modules/user.module
@@ -37,11 +37,17 @@ function user_load($array = array()) {
$query .= "u.$key = '". check_query($value) ."' AND ";
}
}
- $result = db_query_range("SELECT u.*, r.name AS role FROM {role} r INNER JOIN {users} u ON r.rid = u.rid WHERE $query u.status < 3", 0, 1);
+ $result = db_query_range("SELECT u.* FROM {users} u WHERE $query u.status < 3", 0, 1);
$user = db_fetch_object($result);
$user = drupal_unpack($user);
+ $user->roles = array();
+ $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);
@@ -80,6 +86,13 @@ function user_save($account, $array = array()) {
db_query("UPDATE {users} SET $query changed = %d WHERE uid = %d", array_merge($v, array(time(), $account->uid)));
+ // reload user roles
+ db_query("DELETE FROM {users_roles} WHERE uid = %d", $account->uid);
+
+ foreach ($array['rid'] as $rid) {
+ db_query("INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)", $account->uid, $rid);
+ }
+
$user = user_load(array('uid' => $account->uid));
}
else {
@@ -111,6 +124,13 @@ function user_save($account, $array = array()) {
db_query("INSERT INTO {users} (". implode(", ", $fields) .") VALUES (". implode(", ", $s) .")", $values);
+ // reload user roles (delete just to be safe)
+ db_query("DELETE FROM {users_roles} WHERE uid = %d", $array['uid']);
+
+ foreach ($array['rid'] as $rid) {
+ db_query("INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)", $array['uid'], $rid);
+ }
+
$user = user_load(array('name' => $array['name']));
module_invoke_all('user', 'insert', $array, $user);
@@ -232,10 +252,14 @@ function user_access($string) {
*/
if ($perm === 0) {
- $perm = db_result(db_query("SELECT p.perm FROM {role} r, {permission} p WHERE r.rid = p.rid AND r.rid = %d", $user->rid), 0);
+ $result = db_query("SELECT DISTINCT p.perm FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d", $user->uid);
+
+ while ($row = db_fetch_object($result)) {
+ $perm .= "$row->perm, ";
+ }
}
- return strstr($perm, $string);
+ return strstr($perm, "$string, ");
}
function user_mail($mail, $subject, $message, $header) {
@@ -314,7 +338,7 @@ function user_fields() {
}
else {
// Make sure we return the default fields at least
- $fields = array('uid', 'name', 'pass', "mail", "picture", "mode", "sort", "threshold", "theme", "signature", "created", "changed", "status", "timezone", "language", "init", "data", "rid");
+ $fields = array('uid', 'name', 'pass', 'mail', 'picture', 'mode', 'sort', 'threshold', 'theme', 'signature', 'created', 'changed', 'status', 'timezone', 'language', 'init', 'data');
}
}
@@ -690,7 +714,7 @@ function user_login($edit = array(), $msg = "") {
if (module_hook($module, "auth")) {
if (module_invoke($module, "auth", $name, $pass, $server)) {
if (variable_get("user_register", 1) == 1 && !user_load(array('name' => "$name@$server"))) { //register this new user
- $user = user_save("", array('name' => "$name@$server", 'pass' => user_password(), "init" => "$name@$server", "status" => 1, "authname_$module" => "$name@$server", "rid" => _user_authenticated_id()));
+ $user = user_save('', array('name' => "$name@$server", 'pass' => user_password(), 'init' => "$name@$server", 'status' => 1, "authname_$module" => "$name@$server", 'rid' => array(_user_authenticated_id())));
watchdog('user', "new user: $name@$server ($module ID)", l(t("edit user"), "admin/user/edit/$user->uid"));
break;
}
@@ -925,7 +949,7 @@ function user_register($edit = array()) {
// TODO: is this necessary? Won't session_write replicate this?
unset($edit["session"]);
- $account = user_save("", array_merge(array('name' => $edit['name'], 'pass' => $pass, "init" => $edit['mail'], "mail" => $edit['mail'], "rid" => _user_authenticated_id(), "status" => (variable_get("user_register", 1) == 1 ? 1 : 0)), $data));
+ $account = user_save('', array_merge(array('name' => $edit['name'], 'pass' => $pass, 'init' => $edit['mail'], 'mail' => $edit['mail'], 'rid' => array(_user_authenticated_id()), 'status' => (variable_get('user_register', 1) == 1 ? 1 : 0)), $data));
watchdog('user', "new user: '". $edit['name'] ."' &lt;". $edit['mail'] ."&gt;", l(t("edit user"), "admin/user/edit/$account->uid"));
$variables = array("%username" => $edit['name'], "%site" => variable_get("site_name", "drupal"), "%password" => $pass, "%uri" => $base_url, "%uri_brief" => substr($base_url, strlen("http://")), "%mailto" => $edit['mail'], "%date" => format_date(time()), "%login_uri" => url('user/login', NULL, NULL, TRUE), "%edit_uri" => url("user/edit", NULL, NULL, TRUE));
@@ -1041,9 +1065,9 @@ function user_edit($edit = array()) {
watchdog("warning", "detected malicious attempt to alter a protected database field");
}
- $edit["rid"] = $user->rid;
- $edit["init"] = $user->init;
- $edit["session"] = $user->session;
+ $edit['rid'] = array_keys($user->roles);
+ $edit['init'] = $user->init;
+ $edit['session'] = $user->session;
}
/*
@@ -1319,7 +1343,7 @@ function user_admin_create($edit = array()) {
if ($success) {
watchdog('user', "new user: '". $edit['name'] ."' &lt;". $edit['mail'] ."&gt;");
- user_save("", array('name' => $edit['name'], 'pass' => $edit['pass'], "init" => $edit['mail'], "mail" => $edit['mail'], "rid" => _user_authenticated_id(), "status" => 1));
+ user_save('', array('name' => $edit['name'], 'pass' => $edit['pass'], 'init' => $edit['mail'], 'mail' => $edit['mail'], 'rid' => array(_user_authenticated_id()), 'status' => 1));
drupal_set_message(t('Created a new user account. No e-mail has been sent.'));
}
@@ -1486,9 +1510,17 @@ function user_admin_role($edit = array()) {
db_query("DELETE FROM {role} WHERE rid = %d", $id);
db_query("DELETE FROM {permission} WHERE rid = %d", $id);
- // Users with the deleted role are put back in the authenticated users pool
- $aid = db_result(db_query("SELECT rid FROM {role} WHERE name = 'Authenticated user'"));
- db_query("UPDATE {users} SET rid = %d WHERE rid = %d", $aid, $id);
+ // Users with only the deleted role are put back in the authenticated users pool
+ $aid = _user_authenticated_id();
+ $result = db_query("SELECT DISTINCT ur1.uid FROM {users_roles} ur1 LEFT JOIN {users_roles} ur2 ON ur2.uid = ur1.uid WHERE ur1.rid = %d AND ur2.rid != ur1.rid", $id);
+ $uid = array();
+
+ while ($u = db_fetch_object($result)) {
+ $uid[] = $u->uid;
+ }
+
+ db_query("DELETE FROM {users_roles} WHERE rid = %d AND uid IN (%s)", $id, implode(', ', $uid));
+ db_query("UPDATE {users_roles} SET rid = %d WHERE rid = %d", $aid, $id);
}
else if ($op == t("Add role")) {
db_query("INSERT INTO {role} (name) VALUES ('%s')", $edit['name']);
@@ -1546,6 +1578,9 @@ function user_admin_edit($edit = array()) {
else if ($error = user_validate_mail($edit['mail'])) {
// do nothing
}
+ else if (count($edit['rid']) < 1) {
+ $error = t('The user must have at least one role.');
+ }
else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(name) = LOWER('%s')", $account->uid, $edit['name'])) > 0) {
$error = t("The name '%s' is already taken.", array("%s" => $edit['name']));
}
@@ -1604,6 +1639,7 @@ function user_admin_edit($edit = array()) {
else if ($op == t("Delete account")) {
if ($edit['status'] == 0) {
db_query("DELETE FROM {users} WHERE uid = %d", $account->uid);
+ db_query("DELETE FROM {users_roles} WHERE uid = %d", $account->uid);
db_query("DELETE FROM {authmap} WHERE uid = %d", $account->uid);
drupal_set_message(t("the account has been deleted."));
module_invoke_all('user', 'delete', $edit, $account);
@@ -1628,7 +1664,7 @@ function user_admin_edit($edit = array()) {
$group .= form_textfield(t("E-mail address"), "mail", $account->mail, 30, 55, t("Insert 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."));
$group .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter a new password twice if you want to change the current password for this user or leave it blank if you are happy with the current password."));
$group .= form_radios(t("Status"), "status", $account->status, array(t("Blocked"), t("Active")));
- $group .= form_radios(t("Role"), "rid", $account->rid, user_roles(1));
+ $group .= form_select(t('Roles'), 'rid', array_keys($account->roles), user_roles(1), t("Select at least one role. The user receives the combined permissions of all of the selected roles."), 0, 1);
$output = form_group(t('Account information'), $group);
@@ -1660,17 +1696,24 @@ function user_admin_account() {
array("data" => t("ID"), "field" => "u.uid"),
array("data" => t("username"), "field" => "u.name"),
array("data" => t("status"), "field" => "u.status"),
- array("data" => t("role"), "field" => "u.rid"),
+ array('data' => t('roles')),
array("data" => t("last access"), "field" => "u.changed", "sort" => "desc"),
t("operations")
);
- $sql = "SELECT u.uid, u.name, u.status, u.changed, r.name AS rolename FROM {role} r INNER JOIN {users} u ON r.rid = u.rid WHERE uid != 0";
+ $sql = "SELECT u.uid, u.name, u.status, u.changed FROM {users} u WHERE uid != 0";
$sql .= tablesort_sql($header);
$result = pager_query($sql, 50);
$status = array(t("blocked"), t("active"));
while ($account = db_fetch_object($result)) {
- $rows[] = array($account->uid, format_name($account), $status[$account->status], $account->rolename, format_date($account->changed, "small"), l(t("edit account"), "admin/user/edit/$account->uid"));
+ $rolesresult = db_query("SELECT r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d", $account->uid);
+ $roles = array();
+
+ while ($role = db_fetch_object($rolesresult)) {
+ $roles[] = $role->name;
+ }
+
+ $rows[] = array($account->uid, format_name($account), $status[$account->status], implode(',<br />', $roles), format_date($account->changed, 'small'), l(t('edit account'), "admin/user/edit/$account->uid"));
}
$pager = theme("pager", NULL, 50, 0, tablesort_pager());
@@ -1774,7 +1817,7 @@ function user_help($section = "admin/help#user") {
// Start of user_help_admin
$output = t("
<h3>Introduction</h3>
- <p>Drupal offers a powerful access system that allows users to register, login, logout, maintain user profiles, etc. By using <a href=\"%user-role\">roles</a> you can setup fine grained <a href=\"%user-permission\">permissions</a> allowing each role to do only what you want them to. Each user is assigned to a role. By default there are two roles \"anonymous\" - a user who has not logged in, and \"authorized\" a user who has signed up and who has been authorized. As anonymous users, participants suffer numerous disadvantages, for example they cannot sign their names to nodes, and their moderated posts beginning at a lower score.</p>
+ <p>Drupal offers a powerful access system that allows users to register, login, logout, maintain user profiles, etc. By using <a href=\"%user-role\">roles</a> you can setup fine grained <a href=\"%user-permission\">permissions</a> allowing each role to do only what you want them to. Each user is assigned to one or more roles. By default there are two roles \"anonymous\" - a user who has not logged in, and \"authorized\" a user who has signed up and who has been authorized. As anonymous users, participants suffer numerous disadvantages, for example they cannot sign their names to nodes, and their moderated posts beginning at a lower score.</p>
<p>In contrast, those with a user account can use their own name or handle and are granted various privileges: the most important is probably the ability to moderate new submissions, to rate comments, and to fine-tune the site to their personal liking, with saved personal settings. Drupal themes make fine tuning quite a pleasure.</p>
<p>Registered users need to authenticate by supplying either a local username and password, or a remote username and password such as a <a href=\"%jabber\">Jabber ID</a>, <a href=\"%delphiforums\">DelphiForums ID</a>, or one from a <a href=\"%drupal\">Drupal powered</a> website. See the <a href=\"%da-auth\">distributed authentication help</a> for more information on this innovative feature.
The local username and password, hashed with Message Digest 5 (MD5), are stored in your database. When you enter a password it is also hashed with MD5 and compaired with what is in the database. If the hashes match, the username and password are correct. Once a user authenticated session is started, and until that session is over, the user won't have to re-authenticate. To keep track of the individual sessions, Drupal relies on <a href=\"%php-sess\">PHP sessions</a>. A visitor accessing your website is assigned an unique ID, the so-called session ID, which is stored in a cookie. For security's sake, the cookie does not contain personal information but acts as a key to retrieve the information stored on your server. When a visitor accesses your site, Drupal will check whether a specific session ID has been sent with the request. If this is the case, the prior saved environment is recreated.</p>
diff --git a/modules/user/user.module b/modules/user/user.module
index 2c07b5aa0..0187142cc 100644
--- a/modules/user/user.module
+++ b/modules/user/user.module
@@ -37,11 +37,17 @@ function user_load($array = array()) {
$query .= "u.$key = '". check_query($value) ."' AND ";
}
}
- $result = db_query_range("SELECT u.*, r.name AS role FROM {role} r INNER JOIN {users} u ON r.rid = u.rid WHERE $query u.status < 3", 0, 1);
+ $result = db_query_range("SELECT u.* FROM {users} u WHERE $query u.status < 3", 0, 1);
$user = db_fetch_object($result);
$user = drupal_unpack($user);
+ $user->roles = array();
+ $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);
@@ -80,6 +86,13 @@ function user_save($account, $array = array()) {
db_query("UPDATE {users} SET $query changed = %d WHERE uid = %d", array_merge($v, array(time(), $account->uid)));
+ // reload user roles
+ db_query("DELETE FROM {users_roles} WHERE uid = %d", $account->uid);
+
+ foreach ($array['rid'] as $rid) {
+ db_query("INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)", $account->uid, $rid);
+ }
+
$user = user_load(array('uid' => $account->uid));
}
else {
@@ -111,6 +124,13 @@ function user_save($account, $array = array()) {
db_query("INSERT INTO {users} (". implode(", ", $fields) .") VALUES (". implode(", ", $s) .")", $values);
+ // reload user roles (delete just to be safe)
+ db_query("DELETE FROM {users_roles} WHERE uid = %d", $array['uid']);
+
+ foreach ($array['rid'] as $rid) {
+ db_query("INSERT INTO {users_roles} (uid, rid) VALUES (%d, %d)", $array['uid'], $rid);
+ }
+
$user = user_load(array('name' => $array['name']));
module_invoke_all('user', 'insert', $array, $user);
@@ -232,10 +252,14 @@ function user_access($string) {
*/
if ($perm === 0) {
- $perm = db_result(db_query("SELECT p.perm FROM {role} r, {permission} p WHERE r.rid = p.rid AND r.rid = %d", $user->rid), 0);
+ $result = db_query("SELECT DISTINCT p.perm FROM {role} r INNER JOIN {permission} p ON p.rid = r.rid INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d", $user->uid);
+
+ while ($row = db_fetch_object($result)) {
+ $perm .= "$row->perm, ";
+ }
}
- return strstr($perm, $string);
+ return strstr($perm, "$string, ");
}
function user_mail($mail, $subject, $message, $header) {
@@ -314,7 +338,7 @@ function user_fields() {
}
else {
// Make sure we return the default fields at least
- $fields = array('uid', 'name', 'pass', "mail", "picture", "mode", "sort", "threshold", "theme", "signature", "created", "changed", "status", "timezone", "language", "init", "data", "rid");
+ $fields = array('uid', 'name', 'pass', 'mail', 'picture', 'mode', 'sort', 'threshold', 'theme', 'signature', 'created', 'changed', 'status', 'timezone', 'language', 'init', 'data');
}
}
@@ -690,7 +714,7 @@ function user_login($edit = array(), $msg = "") {
if (module_hook($module, "auth")) {
if (module_invoke($module, "auth", $name, $pass, $server)) {
if (variable_get("user_register", 1) == 1 && !user_load(array('name' => "$name@$server"))) { //register this new user
- $user = user_save("", array('name' => "$name@$server", 'pass' => user_password(), "init" => "$name@$server", "status" => 1, "authname_$module" => "$name@$server", "rid" => _user_authenticated_id()));
+ $user = user_save('', array('name' => "$name@$server", 'pass' => user_password(), 'init' => "$name@$server", 'status' => 1, "authname_$module" => "$name@$server", 'rid' => array(_user_authenticated_id())));
watchdog('user', "new user: $name@$server ($module ID)", l(t("edit user"), "admin/user/edit/$user->uid"));
break;
}
@@ -925,7 +949,7 @@ function user_register($edit = array()) {
// TODO: is this necessary? Won't session_write replicate this?
unset($edit["session"]);
- $account = user_save("", array_merge(array('name' => $edit['name'], 'pass' => $pass, "init" => $edit['mail'], "mail" => $edit['mail'], "rid" => _user_authenticated_id(), "status" => (variable_get("user_register", 1) == 1 ? 1 : 0)), $data));
+ $account = user_save('', array_merge(array('name' => $edit['name'], 'pass' => $pass, 'init' => $edit['mail'], 'mail' => $edit['mail'], 'rid' => array(_user_authenticated_id()), 'status' => (variable_get('user_register', 1) == 1 ? 1 : 0)), $data));
watchdog('user', "new user: '". $edit['name'] ."' &lt;". $edit['mail'] ."&gt;", l(t("edit user"), "admin/user/edit/$account->uid"));
$variables = array("%username" => $edit['name'], "%site" => variable_get("site_name", "drupal"), "%password" => $pass, "%uri" => $base_url, "%uri_brief" => substr($base_url, strlen("http://")), "%mailto" => $edit['mail'], "%date" => format_date(time()), "%login_uri" => url('user/login', NULL, NULL, TRUE), "%edit_uri" => url("user/edit", NULL, NULL, TRUE));
@@ -1041,9 +1065,9 @@ function user_edit($edit = array()) {
watchdog("warning", "detected malicious attempt to alter a protected database field");
}
- $edit["rid"] = $user->rid;
- $edit["init"] = $user->init;
- $edit["session"] = $user->session;
+ $edit['rid'] = array_keys($user->roles);
+ $edit['init'] = $user->init;
+ $edit['session'] = $user->session;
}
/*
@@ -1319,7 +1343,7 @@ function user_admin_create($edit = array()) {
if ($success) {
watchdog('user', "new user: '". $edit['name'] ."' &lt;". $edit['mail'] ."&gt;");
- user_save("", array('name' => $edit['name'], 'pass' => $edit['pass'], "init" => $edit['mail'], "mail" => $edit['mail'], "rid" => _user_authenticated_id(), "status" => 1));
+ user_save('', array('name' => $edit['name'], 'pass' => $edit['pass'], 'init' => $edit['mail'], 'mail' => $edit['mail'], 'rid' => array(_user_authenticated_id()), 'status' => 1));
drupal_set_message(t('Created a new user account. No e-mail has been sent.'));
}
@@ -1486,9 +1510,17 @@ function user_admin_role($edit = array()) {
db_query("DELETE FROM {role} WHERE rid = %d", $id);
db_query("DELETE FROM {permission} WHERE rid = %d", $id);
- // Users with the deleted role are put back in the authenticated users pool
- $aid = db_result(db_query("SELECT rid FROM {role} WHERE name = 'Authenticated user'"));
- db_query("UPDATE {users} SET rid = %d WHERE rid = %d", $aid, $id);
+ // Users with only the deleted role are put back in the authenticated users pool
+ $aid = _user_authenticated_id();
+ $result = db_query("SELECT DISTINCT ur1.uid FROM {users_roles} ur1 LEFT JOIN {users_roles} ur2 ON ur2.uid = ur1.uid WHERE ur1.rid = %d AND ur2.rid != ur1.rid", $id);
+ $uid = array();
+
+ while ($u = db_fetch_object($result)) {
+ $uid[] = $u->uid;
+ }
+
+ db_query("DELETE FROM {users_roles} WHERE rid = %d AND uid IN (%s)", $id, implode(', ', $uid));
+ db_query("UPDATE {users_roles} SET rid = %d WHERE rid = %d", $aid, $id);
}
else if ($op == t("Add role")) {
db_query("INSERT INTO {role} (name) VALUES ('%s')", $edit['name']);
@@ -1546,6 +1578,9 @@ function user_admin_edit($edit = array()) {
else if ($error = user_validate_mail($edit['mail'])) {
// do nothing
}
+ else if (count($edit['rid']) < 1) {
+ $error = t('The user must have at least one role.');
+ }
else if (db_num_rows(db_query("SELECT uid FROM {users} WHERE uid != %d AND LOWER(name) = LOWER('%s')", $account->uid, $edit['name'])) > 0) {
$error = t("The name '%s' is already taken.", array("%s" => $edit['name']));
}
@@ -1604,6 +1639,7 @@ function user_admin_edit($edit = array()) {
else if ($op == t("Delete account")) {
if ($edit['status'] == 0) {
db_query("DELETE FROM {users} WHERE uid = %d", $account->uid);
+ db_query("DELETE FROM {users_roles} WHERE uid = %d", $account->uid);
db_query("DELETE FROM {authmap} WHERE uid = %d", $account->uid);
drupal_set_message(t("the account has been deleted."));
module_invoke_all('user', 'delete', $edit, $account);
@@ -1628,7 +1664,7 @@ function user_admin_edit($edit = array()) {
$group .= form_textfield(t("E-mail address"), "mail", $account->mail, 30, 55, t("Insert 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."));
$group .= form_item(t("Password"), "<input type=\"password\" name=\"edit[pass1]\" size=\"12\" maxlength=\"24\" /> <input type=\"password\" name=\"edit[pass2]\" size=\"12\" maxlength=\"24\" />", t("Enter a new password twice if you want to change the current password for this user or leave it blank if you are happy with the current password."));
$group .= form_radios(t("Status"), "status", $account->status, array(t("Blocked"), t("Active")));
- $group .= form_radios(t("Role"), "rid", $account->rid, user_roles(1));
+ $group .= form_select(t('Roles'), 'rid', array_keys($account->roles), user_roles(1), t("Select at least one role. The user receives the combined permissions of all of the selected roles."), 0, 1);
$output = form_group(t('Account information'), $group);
@@ -1660,17 +1696,24 @@ function user_admin_account() {
array("data" => t("ID"), "field" => "u.uid"),
array("data" => t("username"), "field" => "u.name"),
array("data" => t("status"), "field" => "u.status"),
- array("data" => t("role"), "field" => "u.rid"),
+ array('data' => t('roles')),
array("data" => t("last access"), "field" => "u.changed", "sort" => "desc"),
t("operations")
);
- $sql = "SELECT u.uid, u.name, u.status, u.changed, r.name AS rolename FROM {role} r INNER JOIN {users} u ON r.rid = u.rid WHERE uid != 0";
+ $sql = "SELECT u.uid, u.name, u.status, u.changed FROM {users} u WHERE uid != 0";
$sql .= tablesort_sql($header);
$result = pager_query($sql, 50);
$status = array(t("blocked"), t("active"));
while ($account = db_fetch_object($result)) {
- $rows[] = array($account->uid, format_name($account), $status[$account->status], $account->rolename, format_date($account->changed, "small"), l(t("edit account"), "admin/user/edit/$account->uid"));
+ $rolesresult = db_query("SELECT r.name FROM {role} r INNER JOIN {users_roles} ur ON ur.rid = r.rid WHERE ur.uid = %d", $account->uid);
+ $roles = array();
+
+ while ($role = db_fetch_object($rolesresult)) {
+ $roles[] = $role->name;
+ }
+
+ $rows[] = array($account->uid, format_name($account), $status[$account->status], implode(',<br />', $roles), format_date($account->changed, 'small'), l(t('edit account'), "admin/user/edit/$account->uid"));
}
$pager = theme("pager", NULL, 50, 0, tablesort_pager());
@@ -1774,7 +1817,7 @@ function user_help($section = "admin/help#user") {
// Start of user_help_admin
$output = t("
<h3>Introduction</h3>
- <p>Drupal offers a powerful access system that allows users to register, login, logout, maintain user profiles, etc. By using <a href=\"%user-role\">roles</a> you can setup fine grained <a href=\"%user-permission\">permissions</a> allowing each role to do only what you want them to. Each user is assigned to a role. By default there are two roles \"anonymous\" - a user who has not logged in, and \"authorized\" a user who has signed up and who has been authorized. As anonymous users, participants suffer numerous disadvantages, for example they cannot sign their names to nodes, and their moderated posts beginning at a lower score.</p>
+ <p>Drupal offers a powerful access system that allows users to register, login, logout, maintain user profiles, etc. By using <a href=\"%user-role\">roles</a> you can setup fine grained <a href=\"%user-permission\">permissions</a> allowing each role to do only what you want them to. Each user is assigned to one or more roles. By default there are two roles \"anonymous\" - a user who has not logged in, and \"authorized\" a user who has signed up and who has been authorized. As anonymous users, participants suffer numerous disadvantages, for example they cannot sign their names to nodes, and their moderated posts beginning at a lower score.</p>
<p>In contrast, those with a user account can use their own name or handle and are granted various privileges: the most important is probably the ability to moderate new submissions, to rate comments, and to fine-tune the site to their personal liking, with saved personal settings. Drupal themes make fine tuning quite a pleasure.</p>
<p>Registered users need to authenticate by supplying either a local username and password, or a remote username and password such as a <a href=\"%jabber\">Jabber ID</a>, <a href=\"%delphiforums\">DelphiForums ID</a>, or one from a <a href=\"%drupal\">Drupal powered</a> website. See the <a href=\"%da-auth\">distributed authentication help</a> for more information on this innovative feature.
The local username and password, hashed with Message Digest 5 (MD5), are stored in your database. When you enter a password it is also hashed with MD5 and compaired with what is in the database. If the hashes match, the username and password are correct. Once a user authenticated session is started, and until that session is over, the user won't have to re-authenticate. To keep track of the individual sessions, Drupal relies on <a href=\"%php-sess\">PHP sessions</a>. A visitor accessing your website is assigned an unique ID, the so-called session ID, which is stored in a cookie. For security's sake, the cookie does not contain personal information but acts as a key to retrieve the information stored on your server. When a visitor accesses your site, Drupal will check whether a specific session ID has been sent with the request. If this is the case, the prior saved environment is recreated.</p>
diff --git a/update.php b/update.php
index b92e011d7..4936880a5 100644
--- a/update.php
+++ b/update.php
@@ -109,7 +109,29 @@ function update_info() {
print "</ol>";
print "Notes:";
print "<ol>";
- print " <li>If you <strong>upgrade from Drupal 4.3.x</strong>, you have will need to add the <code>bootstrap</code> and <code>throttle</code> fields to the <code>system</code> table manually before upgrading. To add the required fields, issue the following SQL commands:
+ print " <li>If you <strong>upgrade from Drupal 4.4.x</strong>, you will need to create the <code>users_roles</code> table manually before upgrading. To create the <code>users_roles</code> table, issue the following SQL commands:
+
+ <p>MySQL specific example:
+ <pre>
+ CREATE TABLE users_roles (
+ uid int(10) unsigned NOT NULL default '0',
+ rid int(10) unsigned NOT NULL default '0',
+ PRIMARY KEY (uid, rid)
+ );
+ </pre>
+ </p>
+
+ <p>PostgreSQL specific example:
+ <pre>
+ CREATE TABLE users_roles (
+ uid integer NOT NULL default '0',
+ rid integer NOT NULL default '0',
+ PRIMARY KEY (uid, rid)
+ );
+ </pre>
+ </p>
+ </li>";
+ print " <li>If you <strong>upgrade from Drupal 4.3.x</strong>, you will need to add the <code>bootstrap</code> and <code>throttle</code> fields to the <code>system</code> table manually before upgrading. To add the required fields, issue the following SQL commands:
<p>MySQL specific example:
<pre>
@@ -128,7 +150,11 @@ function update_info() {
</pre>
</p>
</li>";
- print " <li>If you <strong>upgrade from Drupal 4.2.0</strong>, you have to create the <code>sessions</code> table manually before upgrading. After you created the table, you'll want to log in and immediately continue the upgrade. To create the <code>sessions</code> table, issue the following SQL command (MySQL specific example):<pre>CREATE TABLE sessions (
+ print " <li>If you <strong>upgrade from Drupal 4.2.0</strong>, you will need to create the <code>sessions</code> table manually before upgrading. After creating the table, you will want to log in and immediately continue the upgrade. To create the <code>sessions</code> table, issue the following SQL command:
+
+ <p>MySQL specific example:
+ <pre>
+ CREATE TABLE sessions (
uid int(10) unsigned NOT NULL,
sid varchar(32) NOT NULL default '',
hostname varchar(128) NOT NULL default '',
@@ -136,7 +162,10 @@ function update_info() {
session text,
KEY uid (uid),
KEY sid (sid(4)),
- KEY timestamp (timestamp));</pre></li>";
+ KEY timestamp (timestamp));
+ </pre>
+ </p>
+ </li>";
print "</ol>";
print update_page_footer();
}