? .bzr
? 118672-allow-or-in-UI.patch
? 268023-limit-number-of-pages.patch
? 329390-php-4-break-phrase.patch
? 503452-retool-export.patch
? 557372-sort-by-vocabulary.patch
? 586668-pluggable-pagers.patch
? 622608-attachment-inherit-pager_0_0.patch
? 623392-2.term-synonym-summary-links.patch
? 629984-fix-row-styles.patch
? 630194-plugin-option-definition.patch
? 648438-1.add_relationship_remember_alias.D6--3.patch
? 99370-reset-button-plus-some-corrections.patch
? alternatively-just-fix-the-help-text.patch
? compile_field.patch
? compile_field_0.patch
? exposed-form-plugin-full.patch
? exposed-form-plugin.patch
? exposed-form-plugin2.patch
? exposed-forms-pre_execute-nevel-called.patch
? getting-started.html__0.patch
? hack-views-module-2.txt
? quickfix.patch
? reorder-displays_5.patch
? retool-export.patch
? views-228510-exposed_sorts-2.patch
? views-268023_0.patch
? views-268023_0_0.patch
? views-324873.patch
? views-373615-6.patch
? views-383994.patch
? views-522318-display_description-human_name_0.patch
? views-6.x-timing_1.patch
? views-629840.patch
? views-645146.patch
? views-651244-views3.patch
? views-652712_2.patch
? views-652812_0.patch
? views-660140.patch
? views-99370-reset-button.patch
? views-DRUPAL-6--3_preprocess_views_css.patch
? views-exclude.patch
? views-exposed-form-plugin-quickfix.patch
? views-exposed-forms-form-alter_0.patch
? views-exposed_form-block-fix_0.patch
? views-get-view-result.patch
? views-group-by-fix.patch
? views-group-by.patch
? views-option_definition.patch
? views-paggers-pluggable_0.patch
? views-pluggable-exposed-forms_1.patch
? views-test-groupby_DRUPAL-6--3_0.patch
? views-user_field-signature_0.patch
? views-validate-term-cache.patch
? views_argument_summary_sortbycount_0.patch
? views_handler_argument_search_1.patch
? views_rename_display-D6-3.x_0.patch
? views_search_argument.patch
? views_segmented_cache.patch
? views_sort_by_vocab_id.patch
Index: views_ui.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/views_ui.module,v
retrieving revision 1.109.2.2
diff -u -p -r1.109.2.2 views_ui.module
--- views_ui.module	4 Dec 2009 01:08:09 -0000	1.109.2.2
+++ views_ui.module	28 Dec 2009 21:06:14 -0000
@@ -192,6 +192,10 @@ function views_ui_theme() {
       'arguments' => array('form' => NULL),
       'file' => 'includes/admin.inc',
     ),
+    'views_ui_rearrange_filter_form' => array(
+      'arguments' => array('form' => NULL),
+      'file' => 'includes/admin.inc',
+    ),
 
     // list views
     'views_ui_list_views' => array(
Index: css/views-admin.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/css/views-admin.css,v
retrieving revision 1.13.2.3
diff -u -p -r1.13.2.3 views-admin.css
--- css/views-admin.css	4 Dec 2009 01:08:09 -0000	1.13.2.3
+++ css/views-admin.css	28 Dec 2009 21:06:14 -0000
@@ -665,3 +665,34 @@ html.js .views-hidden {
   margin: 0 1em;
   border: 1px solid;
 }
+
+.group-populated {
+  display: none;
+}
+
+td.group {
+  /* this CSS mirrors what is found in core for blocks admin. */
+  background-color :#D4E7F3;
+  border-bottom: 1px solid #B4D7F0;
+  border-top: 1.5em solid #FFFFFF;
+  color: #455067;
+}
+
+td.group-title {
+  font-weight: bold;
+}
+
+tr.group-message {
+  font-style: italic;
+  color:#999999;
+}
+
+.group-message .form-submit,
+#views-add-group {
+  float: right;
+  clear: both;
+}
+
+html.js .views-group-select {
+  display: none;
+}
Index: includes/admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/admin.inc,v
retrieving revision 1.154.2.21
diff -u -p -r1.154.2.21 admin.inc
--- includes/admin.inc	24 Dec 2009 00:41:17 -0000	1.154.2.21
+++ includes/admin.inc	28 Dec 2009 21:06:16 -0000
@@ -1059,8 +1059,9 @@ function template_preprocess_views_ui_ed
 
   $vars['quick_links'] = theme('links', $vars['quick_links_raw']);
   views_add_css('views-admin');
+  drupal_add_js('misc/tabledrag.js', 'core');
   views_add_js('ajax');
-  drupal_add_js('misc/jquery.form.js');
+  drupal_add_js('misc/jquery.form.js', 'core');
 
   // Also add any js files required by plugins:
   $plugins = views_fetch_plugin_data();
@@ -1227,7 +1228,17 @@ function template_preprocess_views_ui_ed
     return;
   }
 
-  $vars['rearrange'] = l('<span>' . t('Rearrange') . '</span>', "admin/build/views/nojs/rearrange/$view->name/$display->id/$type", array('attributes' => array('class' => 'views-button-rearrange views-ajax-link', 'title' => t('Rearrange')), 'html' => true));
+  // Different types now have different rearrange forms, so we use this switch
+  // to get the right one.
+  switch ($type) {
+    case 'filter':
+      $rearrange_url = "admin/build/views/nojs/rearrange-$type/$view->name/$display->id/$type";
+      break;
+    default:
+      $rearrange_url = "admin/build/views/nojs/rearrange/$view->name/$display->id/$type";
+  }
+
+  $vars['rearrange'] = l('<span>' . t('Rearrange') . '</span>', $rearrange_url, array('attributes' => array('class' => 'views-button-rearrange views-ajax-link', 'title' => t('Rearrange')), 'html' => true));
 
   $vars['add'] = l('<span>' . t('Add') . '</span>', "admin/build/views/nojs/add-item/$view->name/$display->id/$type", array('attributes' => array('class' => 'views-button-add views-ajax-link', 'title' => t('Add')), 'html' => true));
 
@@ -1255,6 +1266,16 @@ function template_preprocess_views_ui_ed
     }
   }
 
+  // Filters can now be grouped so we do a little bit extra:
+  $grouping = FALSE;
+  if ($type == 'filter') {
+    $group_info = $view->display_handler->get_option('filter_groups');
+    if (!empty($group_info['groups']) && count($group_info['groups']) > 1) {
+      $grouping = TRUE;
+      $groups = array(0 => array());
+    }
+  }
+
   foreach ($display->handler->get_option($types[$type]['plural']) as $id => $field) {
     $fields[$id] = array();
 
@@ -1301,6 +1322,50 @@ function template_preprocess_views_ui_ed
       }
       $fields[$pid]['info'] = '';
     }
+
+    if ($grouping) {
+      $gid = $handler->options['group'];
+
+      // Show in default group if the group does not exist.
+      if (empty($group_info['groups'][$gid])) {
+        $gid = 0;
+      }
+      $groups[$gid][] = $id;
+    }
+  }
+
+  // If using grouping, re-order fields so that they show up properly in the list.
+  if ($type == 'filter') {
+    $store = $fields;
+    $fields = array();
+    foreach ($groups as $gid => $contents) {
+      if (!empty($fields)) {
+        $operator = ') ' . ($group_info['operator'] == 'OR' ? t('OR') : t('AND')) . ' (';
+      }
+      else {
+        $operator = '(';
+      }
+      $fields[] = array(
+        'class' => 'views-group-text',
+        'title' => $operator,
+        'info' => '',
+      );
+      $started = FALSE;
+      foreach ($contents as $pid) {
+        $operator = '&nbsp;&nbsp;';
+        if ($started) {
+          $operator .= ($group_info['groups'][$gid] == 'OR' ? t('OR') : t('AND')) . ' ';
+        }
+        $store[$pid]['title'] = $operator . $store[$pid]['title'];
+        $started = TRUE;
+        $fields[$pid] = $store[$pid];
+      }
+    }
+    $fields[] = array(
+      'class' => 'views-group-text',
+      'title' => ')',
+      'info' => '',
+    );
   }
 
   $vars['fields'] = $fields;
@@ -1467,6 +1532,10 @@ function views_ui_ajax_forms($key = NULL
       'form_id' => 'views_ui_rearrange_form',
       'args' => array('type'),
     ),
+    'rearrange-filter' => array(
+      'form_id' => 'views_ui_rearrange_filter_form',
+      'args' => array('type'),
+    ),
     'add-item' => array(
       'form_id' => 'views_ui_add_item_form',
       'args' => array('type'),
@@ -2262,8 +2331,7 @@ function views_ui_rearrange_form(&$form_
   foreach ($display->handler->get_option($types[$type]['plural']) as $id => $field) {
     $form[$id] = array('#tree' => TRUE);
     $form[$id]['weight'] = array(
-      '#type' => 'weight',
-      '#delta' => 200,
+      '#type' => 'textfield',
       '#default_value' => ++$count,
     );
     $handler = $display->handler->get_handler($type, $id);
@@ -2318,7 +2386,7 @@ function theme_views_ui_rearrange_form($
       $row[] = drupal_render($form[$id]['name']);
       $form[$id]['weight']['#attributes']['class'] = 'weight';
       $row[] = drupal_render($form[$id]['weight']);
-      $row[] = drupal_render($form[$id]['removed']) . l('<span>' . t('Remove') . '</span>', 'javascript:void()', array('attributes' => array('id' => 'views-remove-link-' . $id, 'class' => 'views-button-remove views-remove-link', 'alt' => t('Remove this item'), 'title' => t('Remove this item')), 'html' => true));
+      $row[] = drupal_render($form[$id]['removed']) . l('<span>' . t('Remove') . '</span>', 'javascript:void()', array('attributes' => array('id' => 'views-remove-link-' . $id, 'class' => 'views-hidden views-button-remove views-remove-link', 'alt' => t('Remove this item'), 'title' => t('Remove this item')), 'html' => true));
 
       $rows[] = array('data' => $row, 'class' => 'draggable', 'id' => 'views-row-' . $id);
     }
@@ -2369,6 +2437,331 @@ function views_ui_rearrange_form_submit(
 }
 
 /**
+ * Form to rearrange items in the views UI.
+ */
+function views_ui_rearrange_filter_form(&$form_state) {
+  $view = &$form_state['view'];
+  $display_id = $form_state['display_id'];
+  $type = $form_state['type'];
+
+  $types = views_object_types();
+  if (!$view->set_display($display_id)) {
+    views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
+  }
+  $display = &$view->display[$display_id];
+  $form['#title'] = check_plain($display->display_title) . ': ';
+  $form['#title'] .= t('Rearrange @type', array('@type' => $types[$type]['ltitle']));
+  $form['#section'] = $display_id . 'rearrange-item';
+
+  if ($display->handler->defaultable_sections($types[$type]['plural'])) {
+    $form_state['section'] = $types[$type]['plural'];
+    $display->handler->add_override_button($form, $form_state, $form_state['section']);
+  }
+
+  if (!empty($view->filter_cache)) {
+    $groups = $view->filter_cache['groups'];
+    $handlers = $view->filter_cache['handlers'];
+  }
+  else {
+    $groups = $display->handler->get_option('filter_groups');
+    $handlers = $display->handler->get_option($types[$type]['plural']);
+  }
+  $count = 0;
+
+  // Get relationship labels
+  $relationships = array();
+  foreach ($display->handler->get_handlers('relationship') as $id => $handler) {
+    $relationships[$id] = $handler->label();
+  }
+
+  $group_options = array();
+
+  /**
+   * Filter groups is an array that contains:
+   * array(
+   *   'operator' => 'and' || 'or',
+   *   'groups' => array(
+   *     $group_id => 'and' || 'or',
+   *   ),
+   * );
+   */
+
+  $form['filter_groups']['#tree'] = TRUE;
+  $form['filter_groups']['operator'] = array(
+    '#title' => t('Operator to use on all groups'),
+    '#type' => 'select',
+    '#options' => array (
+      'AND' => t('And'),
+      'OR' => t('Or'),
+    ),
+    '#default_value' => $groups['operator'],
+    '#attributes' => array(
+      'class' => 'warning-on-change',
+    ),
+    '#description' => t('Either "group 0 AND group 1 AND group 2" or "group 0 OR group 1 OR group 2", etc'),
+  );
+
+  $form['remove_groups']['#tree'] = TRUE;
+
+  foreach ($groups['groups'] as $id => $group) {
+    $form['filter_groups']['groups'][$id] = array(
+      '#title' => t('Group operator'),
+      '#type' => 'select',
+      '#options' => array(
+        'AND' => t('And'),
+        'OR' => t('Or'),
+      ),
+      '#default_value' => $group,
+      '#attributes' => array(
+        'class' => 'warning-on-change',
+      ),
+    );
+    $form['remove_groups'][$id] = array(); // to prevent a notice
+    if ($id != 0) {
+      $form['remove_groups'][$id] = array(
+        '#type' => 'submit',
+        '#value' => t('Remove group @group', array('@group' => $id)),
+        '#group' => $id,
+      );
+    }
+    $group_options[$id] = $id == 0 ? t('Default group') : t('Group @group', array('@group' => $id));
+    $form['#group_renders'][$id] = array();
+  }
+
+  $form['#group_options'] = $group_options;
+  $form['#groups'] = $groups;
+  // We don't use get_handlers() because we want items without handlers to
+  // appear and show up as 'broken' so that the user can see them.
+  $form['filters'] = array('#tree' => TRUE);
+  foreach ($handlers as $id => $field) {
+    // If the group does not exist, move the filters to the default group.
+    if (empty($field['group']) || empty($groups['groups'][$field['group']])) {
+      $field['group'] = 0;
+    }
+
+    // Place this item into the proper group for rendering.
+    $form['#group_renders'][$field['group']][] = $id;
+
+    $form['filters'][$id]['weight'] = array(
+      '#type' => 'textfield',
+      '#default_value' => ++$count,
+    );
+    $form['filters'][$id]['group'] = array(
+      '#type' => 'select',
+      '#options' => $group_options,
+      '#default_value' => $field['group'],
+      '#attributes' => array(
+        'class' => 'views-region-select views-region-' . $id,
+      ),
+    );
+
+    $handler = $display->handler->get_handler($type, $id);
+    if ($handler) {
+      $name = $handler->ui_name() . ' ' . $handler->admin_summary();
+      if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
+        $name = '(' . $relationships[$field['relationship']] . ') ' . $name;
+      }
+
+      $form['filters'][$id]['name'] = array(
+        '#value' => $name,
+      );
+    }
+    else {
+      $form['filters'][$id]['name'] = array('#value' => t('Broken field @id', array('@id' => $id)));
+    }
+    $form['filters'][$id]['removed'] = array(
+      '#type' => 'checkbox',
+      '#id' => 'views-removed-' . $id,
+      '#attributes' => array('class' => 'views-remove-checkbox'),
+      '#default_value' => 0,
+    );
+  }
+
+  $form['add_group'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add new group'),
+    '#id' => 'views-add-group',
+    '#group' => 'add',
+  );
+
+  // Add javascript settings that will be added via $.extend for tabledragging
+  // Equivalent: drupal_add_tabledrag('arrange', 'order', 'sibling', 'weight');
+  $form['#js']['tableDrag']['arrange']['weight'][0] = array(
+    'target' => 'weight',
+    'source' => NULL,
+    'relationship' => 'sibling',
+    'action' => 'order',
+    'hidden' => TRUE,
+    'limit' => 0,
+  );
+
+  foreach ($form['#group_renders'] as $group_id => $title) {
+    // Add javascript settings that will be added via $.extend for tabledragging
+    // Equivalent: drupal_add_tabledrag('arrange', 'match', 'sibling', 'views-group-select', 'views-group-' . $group_id);
+    $form['#js']['tableDrag']['arrange']['views-group-select'][] = array(
+      'target' => 'views-group-' . $group_id,
+      'source' => 'views-group-' . $group_id,
+      'relationship' => 'sibling',
+      'action' => 'match',
+      'hidden' => FALSE,
+      'limit' => 0,
+    );
+  }
+
+  if (isset($form_state['update_name'])) {
+    $name = $form_state['update_name'];
+  }
+
+  views_ui_standard_form_buttons($form, $form_state, 'views_ui_rearrange_filter_form');
+  return $form;
+}
+
+/**
+ * Turn the rearrange form into a proper table
+ */
+function theme_views_ui_rearrange_filter_form($form) {
+  $rows = array();
+  foreach ($form['#group_renders'] as $group_id => $contents) {
+    // Header row for the group.
+    $row = array();
+    $row[] = array('class' => 'group group-title', 'data' => $form['#group_options'][$group_id]);
+    $row[] = array('class' => 'group container-inline', 'data' => drupal_render($form['filter_groups']['groups'][$group_id]), 'colspan' => 3);
+    $rows[] = array('class' => 'views-group', 'data' => $row, 'id' => 'views-group-' . $group_id);
+
+    // Row which will only appear if the group has nothing in it:
+    $row = array();
+    $class = 'group-' . (count($contents) ? 'populated' : 'empty');
+    $row[] = array('colspan' => 4, 'data' => '<span>' . t('This group is empty') . '</span> ' . drupal_render($form['remove_groups'][$group_id]));
+    $rows[] = array('class' => "group-message group-$group_id-message " . $class, 'data' => $row, 'id' => 'views-group-' . $group_id);
+    foreach ($contents as $id) {
+      if (isset($form['filters'][$id]['name'])) {
+        $row = array();
+        $row[] = drupal_render($form['filters'][$id]['name']);
+        $form['filters'][$id]['weight']['#attributes']['class'] = 'weight';
+        $row[] = drupal_render($form['filters'][$id]['weight']);
+        $form['filters'][$id]['group']['#attributes']['class'] = 'views-group-select views-group-' . $group_id;
+        $row[] = drupal_render($form['filters'][$id]['group']);
+        $row[] = drupal_render($form['filters'][$id]['removed']) . l('<span>' . t('Remove') . '</span>', 'javascript:void()', array('attributes' => array('id' => 'views-remove-link-' . $id, 'class' => 'views-hidden views-button-remove views-groups-remove-link', 'alt' => t('Remove this item'), 'title' => t('Remove this item')), 'html' => true));
+
+        $rows[] = array('data' => $row, 'class' => 'draggable', 'id' => 'views-row-' . $id);
+      }
+    }
+  }
+  if (empty($rows)) {
+    $rows[] = array(array('data' => t('No fields available.'), 'colspan' => '2'));
+  }
+
+  $header = array('', t('Weight'), t('Remove'));
+  drupal_add_tabledrag('arrange', 'order', 'sibling', 'weight');
+  $output = drupal_render($form['override']);
+  $output .= drupal_render($form['filter_groups']['operator']);
+  $output .= theme('table', $header, $rows, array('id' => 'arrange'));
+  $output .= drupal_render($form);
+  return $output;
+
+}
+
+/**
+ * Submit handler for rearranging form
+ */
+function views_ui_rearrange_filter_form_submit($form, &$form_state) {
+  $types = views_object_types();
+  $display = &$form_state['view']->display[$form_state['display_id']];
+  $remember_groups = array();
+
+  if (!empty($form_state['view']->filter_cache)) {
+    $old_fields = $form_state['view']->filter_cache['handlers'];
+  }
+  else {
+    $old_fields = $display->handler->get_option($types[$form_state['type']]['plural']);
+  }
+  $count = 0;
+
+  $groups = $form_state['values']['filter_groups'];
+  // Whatever button was clicked, re-calculate field information.
+  $new_fields = $order = array();
+
+  // Make an array with the weights
+  foreach ($form_state['values']['filters'] as $field => $info) {
+    // add each value that is a field with a weight to our list, but only if
+    // it has had its 'removed' checkbox checked.
+    if (is_array($info) && empty($info['removed'])) {
+      if (isset($info['weight'])) {
+        $order[$field] = $info['weight'];
+      }
+
+      if (isset($info['group'])) {
+        $old_fields[$field]['group'] = $info['group'];
+        $remember_groups[$info['group']][] = $field;
+      }
+    }
+  }
+
+  // Sort the array
+  asort($order);
+
+  // Create a new list of fields in the new order.
+  foreach (array_keys($order) as $field) {
+    $new_fields[$field] = $old_fields[$field];
+  }
+
+  // Save if the actual update button was clicked.
+  if (!empty($form_state['clicked_button']['#group'])) {
+    if ($form_state['clicked_button']['#group'] == 'add') {
+      // Add a new group
+      $groups['groups'][] = 'AND';
+    }
+    else {
+      // Renumber groups above the removed one down.
+      foreach (array_keys($groups['groups']) as $group_id) {
+        if ($group_id >= $form_state['clicked_button']['#group']) {
+          $old_group = $group_id + 1;
+          if (isset($groups['groups'][$old_group])) {
+            $groups['groups'][$group_id] = $groups['groups'][$old_group];
+            if (isset($remember_groups[$old_group])) {
+              foreach ($remember_groups[$old_group] as $id) {
+                $new_fields[$id]['group'] = $group_id;
+              }
+            }
+          }
+          else {
+            // If this is the last one, just unset it.
+            unset($groups['groups'][$group_id]);
+          }
+        }
+      }
+    }
+    // Update our cache with values so that cancel still works the way
+    // people expect.
+    $form_state['view']->filter_cache = array(
+      'groups' => $groups,
+      'handlers' => $new_fields,
+    );
+
+    // Return to this form except on actual Update.
+    views_ui_add_form_to_stack('rearrange-filter', $form_state['view'], $form_state['display_id'], array($form_state['type']));
+  }
+  else {
+    // Actually write changed handler values.
+    $display->handler->set_option($types[$form_state['type']]['plural'], $new_fields);
+    $display->handler->set_option('filter_groups', $groups);
+    if (isset($form_state['view']->filter_cache)) {
+      unset($form_state['view']->filter_cache);
+    }
+  }
+
+  // Store in cache
+  views_ui_cache_set($form_state['view']);
+}
+
+function views_ui_rearrange_filter_form_cancel($form, &$form_state) {
+  if (isset($form_state['view']->filter_cache)) {
+    unset($form_state['view']->filter_cache);
+    views_ui_cache_set($form_state['view']);
+  }
+}
+
+/**
  * Form to add_item items in the views UI.
  */
 function views_ui_add_item_form(&$form_state) {
@@ -2468,6 +2861,10 @@ function views_ui_add_item_form_submit($
     }
   }
 
+  if (isset($form_state['view']->filter_cache)) {
+    unset($form_state['view']->filter_cache);
+  }
+
   // Store in cache
   views_ui_cache_set($form_state['view']);
 }
Index: includes/view.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/view.inc,v
retrieving revision 1.151.2.24
diff -u -p -r1.151.2.24 view.inc
--- includes/view.inc	3 Dec 2009 21:47:21 -0000	1.151.2.24
+++ includes/view.inc	28 Dec 2009 21:06:18 -0000
@@ -557,6 +557,15 @@ class view extends views_db_object {
     // Build all the relationships first thing.
     $this->_build('relationship');
 
+    // Set the filtering groups.
+    $filter_groups = $this->display_handler->get_option('filter_groups');
+    if ($filter_groups) {
+      $this->query->set_group_operator($filter_groups['operator']);
+      foreach($filter_groups['groups'] as $id => $operator) {
+        $this->query->set_where_group($operator, $id);
+      }
+    }
+
     // Build all the filters.
     $this->_build('filter');
 
Index: js/ajax.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/js/ajax.js,v
retrieving revision 1.25.2.4
diff -u -p -r1.25.2.4 ajax.js
--- js/ajax.js	3 Dec 2009 23:28:53 -0000	1.25.2.4
+++ js/ajax.js	28 Dec 2009 21:06:18 -0000
@@ -316,3 +316,71 @@ Drupal.behaviors.ViewsAjaxLinks = functi
 Drupal.theme.tableDragChangedWarning = function () {
   return '<div></div>';
 }
+
+// $Id$
+
+Drupal.behaviors.ViewsGroupedTableDrag = function(context) {
+  var table_id = 'arrange';
+  var table = $('table#arrange');
+  var tableDrag = Drupal.tableDrag[table_id];
+
+  if (tableDrag) {
+    // Add a handler for when a row is swapped, update empty regions.
+    tableDrag.row.prototype.onSwap = function(swappedRow) {
+      checkEmptyRegions(table, this);
+    };
+
+    $('a.views-groups-remove-link')
+      .addClass('views-processed')
+      .click(function() {
+        var id = $(this).attr('id').replace('views-remove-link-', '');
+        var $row = $('#views-row-' + id);
+        $row.hide().removeClass('draggable');
+        $('#views-removed-' + id).attr('checked', true);
+        tableDrag.rowObject = new tableDrag.row($row.get(0), 'mouse', tableDrag.indentEnabled, tableDrag.maxDepth, true);
+        // If there is a draggable row after the row we just removed, swap us
+        // down by one so that the empty region check does not see this row
+        // and think that the region is empty.
+        if ($row.next('tr').is('.draggable')) {
+          tableDrag.rowObject.swap('after', $row.next('tr').get(0));
+        }
+        checkEmptyRegions(table, tableDrag.rowObject);
+        return false;
+      });
+
+    // Add a handler so when a row is dropped, update fields dropped into new group.
+    tableDrag.onDrop = function() {
+      dragObject = this;
+      // If this occurs row is in an empty group or its is the first of the group
+      if ($(dragObject.rowObject.element).prev('tr').is('.group-message')) {
+        // Get the previous group, this contains the group id
+        var regionRow = $(dragObject.rowObject.element).prev('tr').get(0);
+        var groupId = regionRow.className.replace(/([^ ]+[ ]+)*group-([^ ]+)-message([ ]+[^ ]+)*/, '$2');
+        // Then, update the select group value
+        var selectGroupField = $('select.views-group-select', dragObject.rowObject.element);
+        selectGroupField.val(groupId);
+      }
+    }
+
+    var checkEmptyRegions = function(table, rowObject) {
+      $('tr.group-message', table).each(function() {
+        // If the dragged row is in this region, but above the message row, swap it down one space.
+        if ($(this).prev('tr').get(0) == rowObject.element) {
+          // Prevent a recursion problem when using the keyboard to move rows up.
+          if ((rowObject.method != 'keyboard' || rowObject.direction == 'down')) {
+            rowObject.swap('after', this);
+          }
+        }
+        // This region has become empty
+        if ($(this).next('tr').is(':not(.draggable)') || $(this).next('tr').size() == 0) {
+          $(this).removeClass('group-populated').addClass('group-empty');
+        }
+        // This region has become populated.
+        else if ($(this).is('.group-empty')) {
+          $(this).removeClass('group-empty').addClass('group-populated');
+        }
+      });
+    };
+  }
+}
+
Index: plugins/views_plugin_display.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/plugins/views_plugin_display.inc,v
retrieving revision 1.20.2.26
diff -u -p -r1.20.2.26 views_plugin_display.inc
--- plugins/views_plugin_display.inc	24 Dec 2009 00:41:17 -0000	1.20.2.26
+++ plugins/views_plugin_display.inc	28 Dec 2009 21:06:20 -0000
@@ -44,7 +44,7 @@ class views_plugin_display extends views
 
     // Translate changed settings:
     // Check if any of the previous values now managed by
-    // pluggable pagers have been changed. 
+    // pluggable pagers have been changed.
     // If yes, perform the conversion
     $items_per_page = $this->get_option('items_per_page');
     $offset = $this->get_option('offset');
@@ -238,7 +238,7 @@ class views_plugin_display extends views
       'fields' => array('fields'),
       'sorts' => array('sorts'),
       'arguments' => array('arguments'),
-      'filters' => array('filters'),
+      'filters' => array('filters', 'filter_groups'),
     );
     if ($section) {
       if (!empty($sections[$section])) {
@@ -315,6 +315,7 @@ class views_plugin_display extends views
           'sorts' => TRUE,
           'arguments' => TRUE,
           'filters' => TRUE,
+          'filter_groups' => TRUE,
         ),
         'export' => FALSE,
       ),
@@ -461,6 +462,12 @@ class views_plugin_display extends views
         'default' => array(),
         'export' => 'export_handler',
       ),
+      'filter_groups' => array(
+        'contains' => array(
+          'operator' => array('default' => 'AND'),
+          'groups' => array('default' => array(0 => 'AND')),
+        ),
+      ),
       'filters' => array(
         'default' => array(),
         'export' => 'export_handler',
Index: plugins/views_plugin_query.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/plugins/Attic/views_plugin_query.inc,v
retrieving revision 1.1.2.4
diff -u -p -r1.1.2.4 views_plugin_query.inc
--- plugins/views_plugin_query.inc	3 Dec 2009 01:47:31 -0000	1.1.2.4
+++ plugins/views_plugin_query.inc	28 Dec 2009 21:06:20 -0000
@@ -93,5 +93,45 @@ class views_plugin_query extends views_p
     return '';
   }
 
+  /**
+   * Create a new grouping for the WHERE or HAVING clause.
+   *
+   * @param $type
+   *   Either 'AND' or 'OR'. All items within this group will be added
+   *   to the WHERE clause with this logical operator.
+   * @param $group
+   *   An ID to use for this group. If unspecified, an ID will be generated.
+   * @param $where
+   *   'where' or 'having'.
+   *
+   * @return $group
+   *   The group ID generated.
+   */
+  function set_where_group($type = 'AND', $group = NULL, $where = 'where') {
+    // Set an alias.
+    $groups = &$this->$where;
+
+    if (!isset($group)) {
+      $group = empty($groups) ? 1 : max(array_keys($groups)) + 1;
+    }
+
+    // Create an empty group
+    if (empty($groups[$group])) {
+      $groups[$group] = array('clauses' => array(), 'args' => array());
+    }
+
+    $groups[$group]['type'] = strtoupper($type);
+    return $group;
+  }
+
+  /**
+   * Control how all WHERE and HAVING groups are put together.
+   *
+   * @param $type
+   *   Either 'AND' or 'OR'
+   */
+  function set_group_operator($type = 'AND') {
+    $this->group_operator = strtoupper($type);
+  }
 
 }
Index: plugins/views_plugin_query_default.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/plugins/Attic/views_plugin_query_default.inc,v
retrieving revision 1.1.2.15
diff -u -p -r1.1.2.15 views_plugin_query_default.inc
--- plugins/views_plugin_query_default.inc	23 Dec 2009 19:28:03 -0000	1.1.2.15
+++ plugins/views_plugin_query_default.inc	28 Dec 2009 21:06:20 -0000
@@ -679,47 +679,6 @@ class views_plugin_query_default extends
   }
 
   /**
-   * Create a new grouping for the WHERE or HAVING clause.
-   *
-   * @param $type
-   *   Either 'AND' or 'OR'. All items within this group will be added
-   *   to the WHERE clause with this logical operator.
-   * @param $group
-   *   An ID to use for this group. If unspecified, an ID will be generated.
-   * @param $where
-   *   'where' or 'having'.
-   *
-   * @return $group
-   *   The group ID generated.
-   */
-  function set_where_group($type = 'AND', $group = NULL, $where = 'where') {
-    // Set an alias.
-    $groups = &$this->$where;
-
-    if (!isset($group)) {
-      $group = empty($groups) ? 1 : max(array_keys($groups)) + 1;
-    }
-
-    // Create an empty group
-    if (empty($groups[$group])) {
-      $groups[$group] = array('clauses' => array(), 'args' => array());
-    }
-
-    $groups[$group]['type'] = strtoupper($type);
-    return $group;
-  }
-
-  /**
-   * Control how all WHERE and HAVING groups are put together.
-   *
-   * @param $type
-   *   Either 'AND' or 'OR'
-   */
-  function set_group_operator($type = 'AND') {
-    $this->group_operator = strtoupper($type);
-  }
-
-  /**
    * Add a simple WHERE clause to the query. The caller is responsible for
    * ensuring that all fields are fully qualified (TABLE.FIELD) and that
    * the table already exists in the query.
@@ -894,7 +853,7 @@ class views_plugin_query_default extends
     if ($clauses) {
       $keyword = drupal_strtoupper($where);
       if (count($clauses) > 1) {
-        return "$keyword (" . implode(")\n    " . $this->group_operator . ' (', $clauses) . ")\n";
+        return "$keyword (" . implode(")\n    " . $this->group_operator . ' (', array_filter($clauses)) . ")\n";
       }
       else {
         return "$keyword " . array_shift($clauses) . "\n";
