Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.491
diff -u -F^function -r1.491 common.inc
--- includes/common.inc	28 Oct 2005 01:06:36 -0000	1.491
+++ includes/common.inc	15 Nov 2005 18:06:41 -0000
@@ -1381,3 +1381,23 @@ function _drupal_bootstrap_full() {
   // Initialize the localization system.
   $locale = locale_initialize();
 }
+
+/**
+ * Flattens a nested array (array of arrays) so that all
+ * keys and values are at the top level.
+ *
+ * @param $array is the incoming array
+ * @return flattened array
+ */
+function drupal_array_flatten($array){
+  $return = array();
+  foreach($array as $key => $value){
+    if (is_array($value)){
+      $return += drupal_array_flatten($value);
+    }
+    else {
+      $return[$key] = $value;
+    }
+  }
+  return $return;
+}
\ No newline at end of file
Index: misc/drupal.css
===================================================================
RCS file: /cvs/drupal/drupal/misc/drupal.css,v
retrieving revision 1.124
diff -u -F^function -r1.124 drupal.css
--- misc/drupal.css	18 Oct 2005 14:41:27 -0000	1.124
+++ misc/drupal.css	15 Nov 2005 18:06:42 -0000
@@ -330,7 +330,7 @@
   font-style: normal;
   text-decoration: line-through;
 }
-#node-admin-filter ul {
+#node-admin-filter ul, #user-admin-filter ul {
   list-style-type: none;
   padding: 0px;
   margin: 0px;
Index: modules/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user.module,v
retrieving revision 1.530
diff -u -F^function -r1.530 user.module
--- modules/user.module	14 Nov 2005 21:49:47 -0000	1.530
+++ modules/user.module	15 Nov 2005 18:06:44 -0000
@@ -1731,6 +1731,11 @@ function theme_user_admin_new_role($form
 }
 
 function user_admin_account() {
+  global $form_values;
+  $output = user_filter_form();
+
+  $filter = user_build_filter_query();
+
   $header = array(
     array('data' => t('Username'), 'field' => 'u.name'),
     array('data' => t('Status'), 'field' => 'u.status'),
@@ -1738,9 +1743,9 @@ function user_admin_account() {
     array('data' => t('Last access'), 'field' => 'u.access'),
     t('Operations')
   );
-  $sql = 'SELECT u.uid, u.name, u.status, u.created, u.access FROM {users} u WHERE uid != 0';
+  $sql = 'SELECT DISTINCT u.uid, u.name, u.status, u.created, u.access FROM {users} u INNER JOIN {users_roles} ur ON u.uid = ur.uid '. $filter['join'].' WHERE u.uid != 0 '.$filter['where'];
   $sql .= tablesort_sql($header);
-  $result = pager_query($sql, 50);
+  $result = pager_query($sql, 50, 0, NULL, $filter['args']);
 
   $status = array(t('blocked'), t('active'));
   $destination = drupal_get_destination();
@@ -1752,7 +1757,7 @@ function user_admin_account() {
                     l(t('edit'), "user/$account->uid/edit", array(), $destination));
   }
 
-  $output = theme('table', $header, $rows);
+  $output .= theme('table', $header, $rows);
   $output .= theme('pager', NULL, 50, 0, tablesort_pager());
   return $output;
 }
@@ -1932,3 +1937,153 @@ function user_autocomplete($string) {
 }
 
 
+function user_filters() {
+  // Regular filters
+  $filters = array();
+  $roles = user_roles();
+  unset($roles[1]); // don't list anonymous role
+  $filters['role'] = array('title' => t('role'), 'where' => "ur.rid = %d",
+                        'options' => $roles);
+
+  $options = array();
+  foreach (module_list() as $module) {
+    if ($permissions = module_invoke($module, 'perm')) {
+      asort($permissions);
+      foreach($permissions as $permission){
+        $options[$module][$permission] = $permission;
+      }
+    }
+  }
+  $filters['permission'] = array('title' => t('permission'),
+                                 'join' => 'INNER JOIN {permission} p ON ur.rid = p.rid',
+                                 'where' => " (p.perm LIKE '%%%s%%' OR u.uid = 1) ",
+                                 'options' => $options);
+
+  $filters['status'] = array('title' => t('status'),
+                        'options' => array('status-1' => t('active'),
+                                           'status-0' => t('blocked')));
+  return $filters;
+}
+
+function user_build_filter_query() {
+  $filters = user_filters();
+
+  // Build query
+  $where = $args = $join = array();
+  foreach ($_SESSION['user_overview_filter'] as $filter) {
+    list($key, $value) = $filter;
+    if ($key == 'status') {
+      // Note: no exploit hole as $value has already been checked
+      list($key, $value) = explode('-', $value, 2);
+      $where[] = 'u.'. $key .' = %d';
+    }
+    else {
+      $where[] = $filters[$key]['where'];
+    }
+    $args[] = $value;
+    $join[] = $filters[$key]['join'];
+  }
+  $where = count($where) ? 'AND '. implode(' AND ', $where) : '';
+  $join = count($join) ? ' '. implode(' ', array_unique($join)) : '';
+
+  return array('where' => $where, 'join' => $join, 'args' => $args);
+}
+
+function user_filter_form() {
+  $session = &$_SESSION['user_overview_filter'];
+  $session = is_array($session) ? $session : array();
+  $filters = user_filters();
+
+  $i = 0;
+  $form['filters'] = array('#type' => 'fieldset', '#title' => t('Show only users where'), '#theme' => 'user_filters');
+  foreach ($session as $filter) {
+    list($type, $value) = $filter;
+    $string = ($i++ ? '<em>and</em> where <strong>%a</strong> is <strong>%b</strong>' : '<strong>%a</strong> is <strong>%b</strong>');
+    $options = drupal_array_flatten($filters[$type]['options']);
+    $form['filters']['current'][] = array('#value' => t($string, array('%a' => $filters[$type]['title'] , '%b' => $options[$value])));
+  }
+
+  foreach ($filters as $key => $filter) {
+    $names[$key] = $filter['title'];
+    $form['filters']['status'][$key] = array('#type' => 'select', '#options' => $filter['options']);
+  }
+
+  $form['filters']['filter'] = array('#type' => 'radios', '#options' => $names);
+  $form['filters']['buttons']['submit'] = array('#type' => 'submit', '#value' => (count($session) ? t('Refine') : t('Filter')));
+  $form['filters']['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Filter'));
+  if (count($session)) {
+    //$form['filters']['buttons']['undo'] = array('#type' => 'submit', '#value' => t('Undo'));
+    $form['filters']['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset'));
+  }
+
+  return drupal_get_form('user_filter_form', $form);
+}
+
+function theme_user_filter_form(&$form) {
+  $output .= '<div id="user-admin-filter">';
+  $output .= form_render($form['filters']);
+  $output .= '</div>';
+  $output .= form_render($form);
+  return $output;
+}
+
+function theme_user_filters(&$form) {
+  $output .= '<ul>';
+  if (sizeof($form['current'])) {
+    foreach (element_children($form['current']) as $key) {
+      $output .= '<li>' . form_render($form['current'][$key]) . '</li>';
+    }
+  }
+
+  //$output .= '<li><dl class="multiselect">' . (sizeof($form['current']) ? '<dt><em>and</em> where</dt>' : '') . '<dd class="a">';
+  $output .= '<li><dl class="multiselect"><dd class="a">';
+  foreach(element_children($form['filter']) as $key) {
+    $output .= form_render($form['filter'][$key]);
+  }
+  $output .= '</dd>';
+
+  $output .= '<dt>is</dt>' . '<dd class="b">';
+
+  foreach(element_children($form['status']) as $key) {
+    $output .= form_render($form['status'][$key]);
+  }
+  $output .= '</dd>';
+
+  $output .= '</dl>';
+  $output .= '<div class="container-inline" id="node-admin-buttons">'. form_render($form['buttons']) .'</div>';
+  $output .= '</li></ul><br class="clear" />';
+
+  return $output;
+}
+
+
+function user_filter_form_execute() {
+  global $form_values;
+  $op = $_POST['op'];
+  $filters = user_filters();
+  switch ($op) {
+    case t('Filter'): case t('Refine'):
+      if (isset($form_values['filter'])) {
+        $filter = $form_values['filter'];
+        $options = drupal_array_flatten($filters[$filter]['options']);
+        if (isset($options[$form_values[$filter]])) {
+          // Problems with SQL memory overflow have caused us to
+          // remove compound queries
+          $_SESSION['user_overview_filter'][0] = array($filter, $form_values[$filter]);
+        }
+      }
+      break;
+    case t('Undo'):
+      array_pop($_SESSION['user_overview_filter']);
+      break;
+    case t('Reset'):
+      $_SESSION['user_overview_filter'] = array();
+      break;
+    case t('Update'):
+      return;
+  }
+  if ($op != '') {
+    drupal_goto('admin/user');
+  }
+}
+
