Index: includes/filter.inc =================================================================== RCS file: includes/filter.inc diff -N includes/filter.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ includes/filter.inc 3 Jun 2009 09:10:56 -0000 @@ -0,0 +1,310 @@ +name = $name; + $this->initialize(); + + return $this; + } + + protected function initialize() { + if (!isset($_SESSION['filter'][$this->name])) { + $_SESSION['filter'][$this->name] = array(); + } + $this->filter =& $_SESSION['filter'][$this->name]; + $this->elements = module_invoke_all('filter_elements_' . $this->name, $this); + $this->addTag('filter_' . $this->name); + $this->addMetaData('filter', $this->filter); + $this->addMetaData('elements', $this->elements); + } + + public function execute() { + // Add simple conditions of the currently active filters to the query. + // @todo: Implement additional simple filter configurations. + foreach ($this->filter as $name => $values) { + foreach ($values as $value) { + if (isset($this->elements[$name]['field'])) { + // Check if we need to do a LIKE comparison. + if (preg_match('/([\*%]+)/', $value)) { + // Replace wildcards with PDO wildcards. + $this->condition($this->elements[$name]['field'], preg_replace('!\*+!', '%', $value), 'LIKE'); + } + else { + $this->condition($this->elements[$name]['field'], $value); + } + } + } + } + + return $this->query->execute(); + } + + /** + * Return the defined filter name. + * + * @return + * The previously defined filter. + */ + public function getFilterName() { + return $this->name; + } + + /** + * Return the loaded, currently active filters for that query. + * + * @return + * Array with active filters. + */ + public function getFilter() { + return $this->filter; + } + + /** + * Get the form definition of the active filter. + * + * @see filter_extender_form + * + * @param $title + * Fieldset title that should be used for that form. + */ + public function getForm($title = NULL) { + + // Only display a form if filter elements were found. + if (empty($this->elements)) { + return array(); + } + + if ($title == NULL) { + $title = t('Only display items where'); + } + + // Note: #query is a workaround becase we can't directly call a object + // method for the form submit. + $form = array( + '#title' => $title, + '#type' => 'fieldset', + '#weight' => -50, + '#theme' => 'filter_selection_form', + '#query' => $this, + ); + + $i = 0; + foreach ($this->filter as $name => $values) { + $params = array( + '%property' => $this->elements[$name]['title'], + '%value' => join(t(' and '), $this->getFilterDescription($name, $values) + )); + if ($i++ > 0) { + $form['current'][] = array('#markup' => t('and where %property is %value', $params)); + } + else { + $form['current'][] = array('#markup' => t('%property is %value', $params)); + } + } + + // Create a select for each filter element. + foreach ($this->elements as $name => $element) { + $names[$name] = $element['title']; + + // Display a select if pre-defined options are available. + if (isset($element['options'])) { + $form['status'][$name] = array( + '#type' => 'select', + '#options' => $element['options'], + ); + } else { + // If not, use a textfield, optionally with an autocomplete path. + $form['status'][$name] = array( + '#type' => 'textfield', + '#size' => 19, + ); + if (isset($element['autocomplete_path'])) { + $form['status'][$name]['#autocomplete_path'] = $element['autocomplete_path']; + } + } + } + + // Create list of filter elements. + $form['filter'] = array( + '#type' => 'radios', + '#options' => $names, + ); + + $form['buttons']['submit'] = array( + '#type' => 'submit', + '#value' => (count($this->filter) ? t('Refine') : t('Filter')), + '#submit' => array('filter_extender_form_submit'), + ); + if (count($this->filter)) { + $form['buttons']['undo'] = array( + '#type' => 'submit', + '#value' => t('Undo'), + '#submit' => array('filter_extender_form_submit'), + ); + $form['buttons']['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset'), + '#submit' => array('filter_extender_form_submit'), + ); + } + return $form; + } + + /** + * Handle the filter form submit. + * + * Because this can't be called directly, it's called by the helper function + * filter_extender_form_submit. + * + * @see filter_extender_form_submit() + */ + public function submitForm($form, &$form_state) { + switch ($form_state['values']['op']) { + case t('Filter'): + case t('Refine'): + if (!empty($form_state['values']['filter'])) { + $name = $form_state['values']['filter']; + $value = $form_state['values'][$name]; + + // Only add the new filter of multiple is enabled, override the + // current value if not. + if (isset($this->elements[$name]['multiple']) && $this->elements[$name]['multiple']) { + $this->filter[$name][$value] = $value; + } + else { + $this->filter[$name] = array($value => $value); + } + } + break; + case t('Undo'): + array_pop($this->filter); + break; + case t('Reset'): + $this->filter = array(); + break; + } + } + + /** + * Return an array of filter values that should be displayed in the form. + * + * @param $name + * Name of the filter. + * @param $values + * Array of values that should be displayed. + * @return + * Array of filter values, either the values itself or the text of the + * selected options. + */ + protected function getFilterDescription($name, $values) { + if (isset($this->elements[$name]['options'])) { + $options = $this->elements[$name]['options']; + if (count(array_filter($this->elements[$name]['options'], 'is_array')) > 0) { + $options = call_user_func_array('array_merge', $this->elements[$name]['options']); + } + return array_intersect_key($options, $values); + } else { + return $values; + } + } +} + +/** + * Theme filter selector form. + * + * @ingroup themeable + */ +function theme_filter_selection_form($form) { + $output = ''; + if (!empty($form['current'])) { + $output .= '

'; + foreach (element_children($form['current']) as $key) { + $output .= '' . drupal_render($form['current'][$key]) . '
'; + } + $output .= '

'; + } + + $output .= '

' . (!empty($form['current']) ? '
' . t('and') . ' ' . t('where') . '
' : '') . '
'; + foreach (element_children($form['filter']) as $key) { + $output .= drupal_render($form['filter'][$key]); + } + $output .= '
' . t('is') . '
'; + + foreach (element_children($form['status']) as $key) { + $output .= drupal_render($form['status'][$key]); + } + $output .= '
'; + $output .= '
' . drupal_render($form['buttons']) . '

'; + return $output; +} + +/** + * Form for the filter of the passed in $query object. + * + * This is a helper function because we can't directly call a object method with + * drupal_get_form(). Only use this if you don't already have a $form object + * that could be extended. + * + * @param $query + * SelectQuery object that is already extend with Filter. + * @return + */ +function filter_extender_form($query) { + return $query->getForm(); +} + +/** + * Helper function for submitting the filter form. + * + * @see filter_extender_form() + */ +function filter_extender_form_submit($form, &$form_state) { + $form['filter']['#query']->submitForm($form, $form_state); +} \ No newline at end of file Index: modules/user/user.admin.inc =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.admin.inc,v retrieving revision 1.54 diff -u -p -r1.54 user.admin.inc --- modules/user/user.admin.inc 2 Jun 2009 06:58:17 -0000 1.54 +++ modules/user/user.admin.inc 3 Jun 2009 09:10:56 -0000 @@ -19,7 +19,6 @@ function user_admin($callback_arg = '') $build['user_multiple_cancel_confirm'] = drupal_get_form('user_multiple_cancel_confirm'); } else { - $build['user_filter_form'] = drupal_get_form('user_filter_form'); $build['user_admin_account'] = drupal_get_form('user_admin_account'); } } @@ -27,98 +26,6 @@ function user_admin($callback_arg = '') } /** - * Form builder; Return form for user administration filters. - * - * @ingroup forms - * @see user_filter_form_submit() - */ -function user_filter_form() { - $session = isset($_SESSION['user_overview_filter']) ? $_SESSION['user_overview_filter'] : 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; - // Merge an array of arrays into one if necessary. - $options = $type == 'permission' ? call_user_func_array('array_merge', $filters[$type]['options']) : $filters[$type]['options']; - $params = array('%property' => $filters[$type]['title'] , '%value' => $options[$value]); - if ($i++ > 0) { - $form['filters']['current'][] = array('#markup' => t('and where %property is %value', $params)); - } - else { - $form['filters']['current'][] = array('#markup' => t('%property is %value', $params)); - } - } - - 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')), - ); - if (count($session)) { - $form['filters']['buttons']['undo'] = array( - '#type' => 'submit', - '#value' => t('Undo'), - ); - $form['filters']['buttons']['reset'] = array( - '#type' => 'submit', - '#value' => t('Reset'), - ); - } - - drupal_add_js('misc/form.js'); - - return $form; -} - -/** - * Process result from user administration filter form. - */ -function user_filter_form_submit($form, &$form_state) { - $op = $form_state['values']['op']; - $filters = user_filters(); - switch ($op) { - case t('Filter'): case t('Refine'): - if (isset($form_state['values']['filter'])) { - $filter = $form_state['values']['filter']; - // Merge an array of arrays into one if necessary. - $options = $filter == 'permission' ? call_user_func_array('array_merge', $filters[$filter]['options']) : $filters[$filter]['options']; - if (isset($options[$form_state['values'][$filter]])) { - $_SESSION['user_overview_filter'][] = array($filter, $form_state['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; - } - - $form_state['redirect'] = 'admin/user/user'; - return; -} - -/** * Form builder; User administration page. * * @ingroup forms @@ -137,14 +44,16 @@ function user_admin_account() { t('Operations') ); - $query = db_select('users', 'u'); - $query->leftJoin('users_roles', 'ur', 'u.uid = ur.uid'); - $query->condition('u.uid', 0, '<>'); - user_build_filter_query($query); + // Extend with Filter early so that the count query gets the same filters applied. + $query = db_select('users', 'u')->extend('Filter'); + $query + ->filterName('user_admin') + ->condition('u.uid', 0, '<>'); $count_query = clone $query; $count_query->addExpression('COUNT(DISTINCT u.uid)'); + // Extend with PagerDefault and Tablesort now, as the count query doesn't need them. $query = $query->extend('PagerDefault')->extend('TableSort'); $query ->fields('u', array('uid', 'name', 'status', 'created', 'access')) @@ -152,6 +61,8 @@ function user_admin_account() { ->orderByHeader($header) ->setCountQuery($count_query); $result = $query->execute(); + // Load filter form. + $form['filter'] = $query->getForm(t('Only display users where...')); $form['options'] = array( '#type' => 'fieldset', @@ -171,6 +82,9 @@ function user_admin_account() { $form['options']['submit'] = array( '#type' => 'submit', '#value' => t('Update'), + // Only run the update validate and submit function for the Update button. + '#submit' => array('user_admin_account_update'), + '#validate' => array('user_admin_account_update_validate'), ); $destination = drupal_get_destination(); @@ -205,7 +119,7 @@ function user_admin_account() { /** * Submit the user administration update form. */ -function user_admin_account_submit($form, &$form_state) { +function user_admin_account_update($form, &$form_state) { $operations = module_invoke_all('user_operations', $form_state); $operation = $operations[$form_state['values']['operation']]; // Filter out unchecked accounts. @@ -224,7 +138,10 @@ function user_admin_account_submit($form } } -function user_admin_account_validate($form, &$form_state) { +/** + * Validate the user administration update form. + */ +function user_admin_account_update_validate($form, &$form_state) { $form_state['values']['accounts'] = array_filter($form_state['values']['accounts']); if (count($form_state['values']['accounts']) == 0) { form_set_error('', t('No users selected.')); @@ -813,7 +730,8 @@ function theme_user_admin_account($form) t('Operations') ); - $output = drupal_render($form['options']); + $output = drupal_render($form['filter']); + $output .= drupal_render($form['options']); if (isset($form['name']) && is_array($form['name'])) { foreach (element_children($form['name']) as $key) { $rows[] = array( @@ -864,49 +782,3 @@ function theme_user_admin_new_role($form return $output; } - -/** - * Theme user administration filter form. - * - * @ingroup themeable - */ -function theme_user_filter_form($form) { - $output = '
'; - $output .= drupal_render($form['filters']); - $output .= '
'; - $output .= drupal_render_children($form); - return $output; -} - -/** - * Theme user administration filter selector. - * - * @ingroup themeable - */ -function theme_user_filters($form) { - $output = ''; - - return $output; -} Index: modules/user/user.css =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.css,v retrieving revision 1.10 diff -u -p -r1.10 user.css --- modules/user/user.css 9 Oct 2008 04:19:44 -0000 1.10 +++ modules/user/user.css 3 Jun 2009 09:10:56 -0000 @@ -12,12 +12,6 @@ #user-login-form { text-align: center; } -#user-admin-filter ul { - list-style-type: none; - padding: 0; - margin: 0; - width: 100%; -} #user-admin-buttons { float: left; /* LTR */ margin-left: 0.5em; /* LTR */ Index: modules/user/user.module =================================================================== RCS file: /cvs/drupal/drupal/modules/user/user.module,v retrieving revision 1.998 diff -u -p -r1.998 user.module --- modules/user/user.module 3 Jun 2009 07:28:28 -0000 1.998 +++ modules/user/user.module 3 Jun 2009 09:10:58 -0000 @@ -69,14 +69,6 @@ function user_theme() { 'arguments' => array('form' => NULL), 'file' => 'user.admin.inc', ), - 'user_filter_form' => array( - 'arguments' => array('form' => NULL), - 'file' => 'user.admin.inc', - ), - 'user_filters' => array( - 'arguments' => array('form' => NULL), - 'file' => 'user.admin.inc', - ), 'user_signature' => array( 'arguments' => array('signature' => NULL), ), @@ -2430,18 +2422,24 @@ function _user_sort($a, $b) { } /** - * List user administration filters that can be applied. + * Implementation of hook_filter_elements_filtername(). */ -function user_filters() { +function user_filter_elements_user_admin() { // Regular filters $filters = array(); + $filters['name'] = array( + 'title' => t('username'), + 'autocomplete_path' => 'user/autocomplete', + 'field' => 'u.name', + ); + $roles = user_roles(TRUE); unset($roles[DRUPAL_AUTHENTICATED_RID]); // Don't list authorized role. if (count($roles)) { $filters['role'] = array( 'title' => t('role'), - 'field' => 'ur.rid', 'options' => $roles, + 'multiple' => TRUE, ); } @@ -2459,6 +2457,7 @@ function user_filters() { $filters['permission'] = array( 'title' => t('permission'), 'options' => $options, + 'multiple' => TRUE, ); $filters['status'] = array( @@ -2470,32 +2469,39 @@ function user_filters() { } /** - * Extends a query object for user administration filters based on session. + * Extends a query object for user permission filters. * * @param $query * Query object that should be filtered. */ -function user_build_filter_query(SelectQuery $query) { - $filters = user_filters(); - - // Extend Query with filter conditions. - foreach (isset($_SESSION['user_overview_filter']) ? $_SESSION['user_overview_filter'] : array() as $filter) { - list($key, $value) = $filter; - // This checks to see if this permission filter is an enabled permission for - // the authenticated role. If so, then all users would be listed, and we can - // skip adding it to the filter query. - if ($key == 'permission') { +function user_query_filter_user_admin_alter($query) { + // Extend Query with permission conditions. + $filter = $query->getMetaData('filter'); + if (isset($filter['permission'])) { + $i = 0; + $query->leftJoin('users_roles', 'ur', 'u.uid = ur.uid'); + foreach ($filter['permission'] as $value) { $account = new stdClass(); $account->uid = 'user_filter'; $account->roles = array(DRUPAL_AUTHENTICATED_RID => 1); if (user_access($value, $account)) { continue; } - $query->leftJoin('role_permission', 'p', 'ur.rid = p.rid'); - $query->condition(db_or()->condition('u.uid', 1)->condition('p.permission', $value)); + $query->leftjoin('role_permission', "p$i", "ur.rid = p$i.rid"); + $query->condition(db_or() + ->condition("u.uid", 1) + ->condition("p$i.permission", $value) + ); + $i++; } - else { - $query->condition($filters[$key]['field'], $value); + } + + if (isset($filter['role'])) { + $i = 0; + foreach ($filter['role'] as $value) { + $table = $query->leftJoin('users_roles', "ur$i", "ur$i.uid = u.uid"); + $query->condition("ur$i.rid", $value); + $i++; } } }