Index: includes/install.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/install.inc,v retrieving revision 1.31 diff -u -p -r1.31 install.inc --- includes/install.inc 2 Jan 2007 05:05:38 -0000 1.31 +++ includes/install.inc 30 Jan 2007 18:07:09 -0000 @@ -739,3 +739,28 @@ function drupal_check_module($module) { } return TRUE; } + +/** + * Add a permission. + * This function will add a permission to the permission table and return the pid. + * If the permission already exists, its pid will be returned instead. + */ +function drupal_add_permission($perm, $module='') { + if ($pid = db_result(db_query("SELECT pid FROM {permission} WHERE perm = '%s'",$perm))) { + return $pid; + } + $pid = db_next_id('{permission}_pid'); + db_query("INSERT INTO {permission} (pid,perm,module) VALUES (%d,'%s','%s')",$pid,$perm,$module); + return $pid; +} + +/** + * Delete a permission. + * This function will permanetly remove a permission. + */ +function drupal_del_permission($perm) { + if ($pid = db_result(db_query("SELECT pid FROM {permission} WHERE perm = '%s'",$perm))) { + db_query("DELETE FROM {permission_role} WHERE pid = %d",$pid); + db_query("DELETE FROM {permission} WHERE pid = %d",$pid); + } +} Index: includes/module.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/module.inc,v retrieving revision 1.96 diff -u -p -r1.96 module.inc --- includes/module.inc 25 Jan 2007 22:14:06 -0000 1.96 +++ includes/module.inc 30 Jan 2007 18:07:09 -0000 @@ -253,6 +253,8 @@ function module_enable($module_list) { module_list(TRUE, FALSE); // Force to regenerate the stored list of hook implementations. module_implements('', FALSE, TRUE); + // Check for new permissions. + user_update_permissions(); } foreach ($invoke_modules as $module) { Index: modules/system/system.install =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.install,v retrieving revision 1.74 diff -u -p -r1.74 system.install --- modules/system/system.install 29 Jan 2007 12:30:11 -0000 1.74 +++ modules/system/system.install 30 Jan 2007 18:07:09 -0000 @@ -430,14 +430,21 @@ function system_install() { ) /*!40100 DEFAULT CHARACTER SET UTF8 */ "); db_query("CREATE TABLE {permission} ( - rid int unsigned NOT NULL default '0', - perm longtext, - tid int unsigned NOT NULL default '0', - KEY rid (rid) + pid int(10) unsigned NOT NULL default '0', + perm varchar(255) NOT NULL default '', + module varchar(255) NOT NULL default '', + PRIMARY KEY (pid), + UNIQUE KEY perm (perm) + ) /*!40100 DEFAULT CHARACTER SET UTF8 */ "); + + db_query("CREATE TABLE {permission_role} ( + pid int(10) unsigned NOT NULL, + rid int(10) unsigned NOT NULL, + PRIMARY KEY (pid,rid) ) /*!40100 DEFAULT CHARACTER SET UTF8 */ "); db_query("CREATE TABLE {role} ( - rid int unsigned NOT NULL auto_increment, + rid int unsigned NOT NULL, name varchar(64) NOT NULL default '', PRIMARY KEY (rid), UNIQUE KEY name (name) @@ -898,19 +905,26 @@ function system_install() { )"); db_query("CREATE INDEX {url_alias}_src_idx ON {url_alias} (src)"); + db_query("CREATE TABLE {role} ( + rid int_unsigned NOT NULL default '0', + name varchar(64) NOT NULL default '', + PRIMARY KEY (rid), + UNIQUE (name) + )"); + db_query("CREATE TABLE {permission} ( rid int_unsigned NOT NULL default '0', - perm text, - tid int_unsigned NOT NULL default '0' + perm varchar(255) NOT NULL default '', + module varchar(255) NOT NULL default '', )"); db_query("CREATE INDEX {permission}_rid_idx ON {permission} (rid)"); + db_query("CREATE INDEX {permission}_perm_idx ON {permission} (perm)"); - db_query("CREATE TABLE {role} ( - rid serial CHECK (rid >= 0), - name varchar(64) NOT NULL default '', - PRIMARY KEY (rid), - UNIQUE (name) + db_query("CREATE TABLE {permission_role} ( + pid int_unsigned NOT NULL default '0', + rid int_unsigned NOT NULL default '0' )"); + db_query("CREATE INDEX {permission_role}_pid_rid_idx ON {permission_role} (rid, pid)"); db_query("CREATE TABLE {blocks_roles} ( module varchar(64) NOT NULL, @@ -1082,11 +1096,21 @@ function system_install() { db_query("INSERT INTO {users} (uid,name,mail) VALUES(0,'','')"); - db_query("INSERT INTO {role} (name) VALUES ('anonymous user')"); - db_query("INSERT INTO {role} (name) VALUES ('authenticated user')"); - - db_query("INSERT INTO {permission} VALUES (1,'access content',0)"); - db_query("INSERT INTO {permission} VALUES (2,'access comments, access content, post comments, post comments without approval',0)"); + db_query("INSERT INTO {role} (rid, name) VALUES (1, 'anonymous user')"); + db_query("INSERT INTO {role} (rid, name) VALUES (2, 'authenticated user')"); + db_query("INSERT INTO {sequences} (name, id) VALUES ('{role}_rid', 2)"); + + // Default permissions for anonymous users. + $access_content = drupal_add_permission('access content','node'); + db_query('INSERT INTO {permission_role} (pid, rid) VALUES (%d, %d)', $access_content, 1); + // Default permissions for auth users. + $auth_perms = array($access_content); + $auth_perms[] = drupal_add_permission('access comments','comment'); + $auth_perms[] = drupal_add_permission('post comments','comment'); + $auth_perms[] = drupal_add_permission('post comments without approval','comment'); + foreach ($auth_perms as $pid) { + db_query('INSERT INTO {permission_role} (pid, rid) VALUES (%d, %d)', $pid, 2); + } db_query("INSERT INTO {variable} (name,value) VALUES('theme_default', 's:7:\"garland\";')"); Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.750 diff -u -p -r1.750 user.module --- modules/user/user.module 29 Jan 2007 19:17:55 -0000 1.750 +++ modules/user/user.module 30 Jan 2007 18:07:09 -0000 @@ -348,33 +348,29 @@ function user_password($length = 10) { */ function user_access($string, $account = NULL) { global $user; - static $perm = array(); + static $perm = array(), $role_string = array(); if (is_null($account)) { $account = $user; } - // User #1 has all privileges: + // User #1 has all privileges. if ($account->uid == 1) { return TRUE; } - // To reduce the number of SQL queries, we cache the user's permissions - // in a static variable. - 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, "; - } + // Fold user's roles into an SQL fragment. + if (!isset($role_string[$account->uid])) { + $role_string[$account->uid] = implode(',', array_keys($account->roles)); } - if (isset($perm[$account->uid])) { - return strpos($perm[$account->uid], "$string, ") !== FALSE; + // If we don't have this permission cached yet, get it. + if (!isset($perm[$account->uid][$string])) { + $perm[$account->uid][$string] = db_num_rows(db_query_range("SELECT p.pid FROM {permission_role} pr INNER JOIN {permission} p ON pr.pid = p.pid WHERE perm = '%s' AND rid IN (%s)", $string, $role_string[$account->uid], 0, 1)); } - return FALSE; + // If the permission was set and granted to the role, access is granted. + return isset($perm[$account->uid][$string]) && $perm[$account->uid][$string]; } /** @@ -1843,7 +1839,7 @@ function user_roles($membersonly = 0, $p $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); + $result = db_query("SELECT r.* FROM {role} r INNER JOIN {permission_role} pr ON r.rid = pr.rid INNER JOIN {permission} p ON pr.pid = p.pid WHERE p.perm = '%s' ORDER BY r.name", $permission); } else { $result = db_query('SELECT * FROM {role} ORDER BY name'); @@ -1860,106 +1856,120 @@ function user_roles($membersonly = 0, $p * Menu callback: administer permissions. */ function user_admin_perm($rid = NULL) { + // Compile permissions map. + $status = array(); if (is_numeric($rid)) { - $result = db_query('SELECT r.rid, p.perm FROM {role} r LEFT JOIN {permission} p ON r.rid = p.rid WHERE r.rid = %d', $rid); + $result = db_query('SELECT pr.rid, pr.pid FROM {permission_role} pr INNER JOIN {permission} p ON pr.pid = p.pid WHERE pr.rid = %d', $rid); } else { - $result = db_query('SELECT r.rid, p.perm FROM {role} r LEFT JOIN {permission} p ON r.rid = p.rid ORDER BY name'); + $result = db_query('SELECT pr.rid, pr.pid FROM {permission_role} pr INNER JOIN {permission} p ON pr.pid = p.pid'); } - - // Compile role array: - // Add a comma at the end so when searching for a permission, we can - // always search for "$perm," to make sure we do not confuse - // permissions that are substrings of each other. - while ($role = db_fetch_object($result)) { - $role_permissions[$role->rid] = $role->perm .','; + while ($row = db_fetch_object($result)) { + $status[$row->rid][$row->pid] = TRUE; } + // Compile role list. + $role_names = array(); if (is_numeric($rid)) { - $result = db_query('SELECT rid, name FROM {role} r WHERE r.rid = %d ORDER BY name', $rid); + $result = db_query('SELECT rid, name FROM {role} WHERE rid = %d',$rid); } else { - $result = db_query('SELECT rid, name FROM {role} ORDER BY name'); + $result = db_query('SELECT rid, name FROM {role}'); } - - $role_names = array(); while ($role = db_fetch_object($result)) { $role_names[$role->rid] = $role->name; } + $form['permission'] = array( + '#type' => 'markup', + '#theme' => 'user_admin_perm_table', + '#header' => array_merge(t('Permission'), $role_names), + ); + // Render role/permission overview: $options = array(); foreach (module_list(FALSE, FALSE, TRUE) as $module) { if ($permissions = module_invoke($module, 'perm')) { - $form['permission'][] = array( - '#value' => $module, + $form['permission'][$module] = array( + '#type' => 'fieldset', + '#title' => t('@module module',array('@module'=>$module)), ); asort($permissions); foreach ($permissions as $perm) { - $options[$perm] = ''; - $form['permission'][$perm] = array('#value' => t($perm)); - foreach ($role_names as $rid => $name) { - // Builds arrays for checked boxes for each role - if (strpos($role_permissions[$rid], $perm .',') !== FALSE) { - $status[$rid][] = $perm; - } + $result = db_query("SELECT pid FROM {permission} WHERE perm = '%s'",$perm); + $pid = db_result($result); + + // Add permission as an option + $form['permission'][$module][$pid] = array( + '#type' => 'fieldset', + '#title' => t($perm), + '#tree' => TRUE, + ); + foreach ($role_names as $k => $v) { + $form['permission'][$module][$pid][$k] = array( + '#type' => 'checkbox', + '#title' => '', + '#default_value' => (isset($status[$k]) && isset($status[$k][$pid])), + ); } } } } - // Have to build checkboxes here after checkbox arrays are built - foreach ($role_names as $rid => $name) { - $form['checkboxes'][$rid] = array('#type' => 'checkboxes', '#options' => $options, '#default_value' => $status[$rid]); - $form['role_names'][$rid] = array('#value' => $name, '#tree' => TRUE); - } $form['submit'] = array('#type' => 'submit', '#value' => t('Save permissions')); - return $form; } -function theme_user_admin_perm($form) { - foreach (element_children($form['permission']) as $key) { - // Don't take form control structures - if (is_array($form['permission'][$key])) { +function theme_user_admin_perm_table($element) { + $ncols = count($element['#header']); + $rows = array(); + foreach (element_children($element) as $module) { + // Module header + $rows[] = array( + 'data' => array( + array( + 'data' => $element[$module]['#title'], + 'colspan' => $ncols, + 'id' => 'module-'. $element[$module]['#title'], + 'class' => 'module', + ), + ), + ); + + // Permissions of the module + foreach (element_children($element[$module]) as $permission) { $row = array(); - // Module name - if (is_numeric($key)) { - $row[] = array('data' => t('@module module', array('@module' => drupal_render($form['permission'][$key]))), 'class' => 'module', 'id' => 'module-'. $form['permission'][$key]['#value'], 'colspan' => count($form['role_names']) + 1); - } - else { - $row[] = array('data' => drupal_render($form['permission'][$key]), 'class' => 'permission'); - foreach (element_children($form['checkboxes']) as $rid) { - if (is_array($form['checkboxes'][$rid])) { - $row[] = array('data' => drupal_render($form['checkboxes'][$rid][$key]), 'align' => 'center', 'title' => t($key)); - } - } + // Title + $row[] = array( + 'data' => $element[$module][$permission]['#title'], + 'class' => 'permission', + ); + foreach (element_children($element[$module][$permission]) as $role) { + // Role + $row[] = array( + 'align' => 'center', + 'title' => $element[$module][$permission]['#title'], + 'data' => drupal_render($element[$module][$permission][$role]), + ); } $rows[] = $row; } } - $header[] = (t('Permission')); - foreach (element_children($form['role_names']) as $rid) { - if (is_array($form['role_names'][$rid])) { - $header[] = drupal_render($form['role_names'][$rid]); - } - } - $output = theme('table', $header, $rows, array('id' => 'permissions')); - $output .= drupal_render($form); - return $output; + return theme('table', $element['#header'], $rows, array('id' => 'permissions')); } function user_admin_perm_submit($form_id, $form_values) { // Save permissions: - $result = db_query('SELECT * FROM {role}'); - while ($role = db_fetch_object($result)) { - if (isset($form_values[$role->rid])) { - // Delete, so if we clear every checkbox we reset that role; - // otherwise permissions are active and denied everywhere. - db_query('DELETE FROM {permission} WHERE rid = %d', $role->rid); - $form_values[$role->rid] = array_filter($form_values[$role->rid]); - if (count($form_values[$role->rid])) { - db_query("INSERT INTO {permission} (rid, perm) VALUES (%d, '%s')", $role->rid, implode(', ', array_keys($form_values[$role->rid]))); + $result = db_query('SELECT * FROM {permission}'); + while ($perm = db_fetch_object($result)) { + if (isset($form_values[$perm->pid])) { + foreach ($form_values[$perm->pid] as $rid => $val) { + // We have to remove it first in case the box state went from checked -> unchecked. + db_query('DELETE FROM {permission_role} WHERE pid = %d AND rid = %d', $perm->pid, $rid); + if ($val) { + // Add if checked. + db_query('INSERT INTO {permission_role} (pid, rid) VALUES (%d, %d)', $perm->pid, $rid); + } } } } @@ -2043,10 +2053,10 @@ function user_admin_role_submit($form_id drupal_set_message(t('The role has been renamed.')); } else if ($form_values['op'] == t('Delete role')) { - db_query('DELETE FROM {role} WHERE rid = %d', $form_values['rid']); - db_query('DELETE FROM {permission} WHERE rid = %d', $form_values['rid']); + db_query('DELETE FROM {permission_role} WHERE rid = %d',$form_values['rid']); // Update the users who have this role set: db_query('DELETE FROM {users_roles} WHERE rid = %d', $form_values['rid']); + db_query('DELETE FROM {role} WHERE rid = %d', $form_values['rid']); drupal_set_message(t('The role has been deleted.')); } @@ -2741,3 +2751,41 @@ function user_forms() { return $forms; } +/** + * Pick up any new permissions that modules are advertising. + */ +function user_update_permissions() { + foreach (module_implements('perm') as $module) { + foreach (module_invoke($module, 'perm') as $permission) { + if (!db_result(db_query("SELECT COUNT(*) FROM {permission} WHERE perm = '%s'", $permission))) { + db_query("INSERT INTO {permission} (pid,perm,module) VALUES (%d,'%s','%s')",db_next_id('{permission}_pid'), $permission, $module); + } + } + } +} + +/** + * Get the list of permissions. + */ +/* +function user_permissions($all = FALSE) { + $table = array(); + $result = db_query('SELECT pid, perm, module FROM {permission}'); + while ($perm = db_fetch_object($result)) { + $table[$perm->module][$perm->pid] = $perm->perm; + } + if ($all) { + // "All" means displaying everything in the database, even defunct permissions. + $permissions = $table; + unset($table); + } + else { + $permissions = array(); + foreach (module_implements('perm') as $module) { + $permissions[$module] = array(); + foreach (module_invoke($module, 'perm') as $perm) { + $permissions[$module][$perm + + + +*/