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
+      
+ 
+    
+*/
