Index: CHANGELOG.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/CHANGELOG.txt,v
retrieving revision 1.344.4.6
diff -u -p -r1.344.4.6 CHANGELOG.txt
--- CHANGELOG.txt	10 Nov 2009 22:44:17 -0000	1.344.4.6
+++ CHANGELOG.txt	13 Nov 2009 18:55:17 -0000
@@ -7,7 +7,11 @@ Views 3.x-7.x-dev
     o #622602: Missing ORDER BY in some views with tables due to incorrect test for default table sorting.
     o #623498 by marcp: total_rows should be filled in if fetching all records.
 
-Views 3.x-6.x-dev
+Views 6.x-3.x-dev
+    o #396380 by merlinofchaos, dereine and dagmar: Initial support for GROUP BY queries!!!!!!!!!!!!
+    o #535868 by dagmar: Exposed forms as plugins, helping to isolate exposed form code and increase the ability for modules to change the behavior of exposed forms.
+
+Views 6.x-3.0-alpha1
   Bug fixes:
     o Table style when not overriding sorts put sorts in wrong order.
     o #488908 by sl27257: "Node: Has new content" only worked when exposed.
Index: views.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/views.module,v
retrieving revision 1.341.4.2
diff -u -p -r1.341.4.2 views.module
--- views.module	3 Nov 2009 08:33:16 -0000	1.341.4.2
+++ views.module	13 Nov 2009 18:55:18 -0000
@@ -13,7 +13,14 @@
  * Advertise the current views api version
  */
 function views_api_version() {
-  return 2.0;
+  return '3.0-alpha1';
+}
+
+/**
+ * Views will not load plugins advertising a version older than this.
+ */
+function views_api_minimum_version() {
+  return '2';
 }
 
 /**
@@ -538,7 +545,8 @@ function views_get_module_apis() {
     $cache = array();
     foreach (module_implements('views_api') as $module) {
       $info = module_invoke($module, 'views_api');
-      if (isset($info['api']) && $info['api'] == 2.000) {
+      if (version_compare($info['api'], views_api_minimum_version(), '>=') &&
+          version_compare($info['api'], views_api_version(), '<=')) {
         $cache[$module] = $info;
       }
     }
@@ -628,19 +636,35 @@ function views_include_default_views() {
  *   The name of the field this handler is from.
  * @param $key
  *   The type of handler. i.e, sort, field, argument, filter, relationship
+ * @param $override
+ *   Override the actual handler object with this class. Used for
+ *   aggregation when the handler is redirected to the aggregation
+ *   handler.
  *
  * @return
  *   An instance of a handler object. May be views_handler_broken.
  */
-function views_get_handler($table, $field, $key) {
+function views_get_handler($table, $field, $key, $override = NULL) {
   $data = views_fetch_data($table);
+  $handler = NULL;
+
   if (isset($data[$field][$key])) {
     // Set up a default handler:
     if (empty($data[$field][$key]['handler'])) {
       $data[$field][$key]['handler'] = 'views_handler_' . $key;
     }
-    return _views_prepare_handler($data[$field][$key], $data, $field);
+
+    if ($override) {
+      $data[$field][$key]['override handler'] = $override;
+    }
+
+    $handler = _views_prepare_handler($data[$field][$key], $data, $field);
   }
+
+  if ($handler) {
+    return $handler;
+  }
+
   // DEBUG -- identify missing handlers
   debug("Missing handler: $table $field $key");
   $broken = array(
Index: views_ui.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/views_ui.module,v
retrieving revision 1.109.6.2
diff -u -p -r1.109.6.2 views_ui.module
--- views_ui.module	9 Nov 2009 22:35:58 -0000	1.109.6.2
+++ views_ui.module	13 Nov 2009 18:55:19 -0000
@@ -261,6 +261,7 @@ function views_ui_cache_set(&$view) {
   unset($view->display_handler);
   unset($view->current_display);
   unset($view->default_display);
+  $view->query = NULL;
   foreach (array_keys($view->display) as $id) {
     unset($view->display[$id]->handler);
     unset($view->display[$id]->default_display);
Index: handlers/views_handler_field.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/handlers/views_handler_field.inc,v
retrieving revision 1.33.4.2
diff -u -p -r1.33.4.2 views_handler_field.inc
--- handlers/views_handler_field.inc	2 Nov 2009 23:31:09 -0000	1.33.4.2
+++ handlers/views_handler_field.inc	13 Nov 2009 18:55:23 -0000
@@ -94,7 +94,12 @@ class views_handler_field extends views_
           else {
             $table_alias = $this->table_alias;
           }
-          $this->aliases[$identifier] = $this->query->add_field($table_alias, $info['field']);
+          $params = array();
+          if (!empty($info['params'])) {
+            $params = $info['params'];
+          }
+
+          $this->aliases[$identifier] = $this->query->add_field($table_alias, $info['field'], NULL, $params);
         }
         else {
           $this->aliases[$info] = $this->query->add_field($this->table_alias, $info);
@@ -173,6 +178,8 @@ class views_handler_field extends views_
    * should have.
    */
   function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+
     $form['label'] = array(
       '#type' => 'textfield',
       '#title' => t('Label'),
Index: handlers/views_handler_filter.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/handlers/views_handler_filter.inc,v
retrieving revision 1.10.4.2
diff -u -p -r1.10.4.2 views_handler_filter.inc
--- handlers/views_handler_filter.inc	2 Nov 2009 23:17:00 -0000	1.10.4.2
+++ handlers/views_handler_filter.inc	13 Nov 2009 18:55:23 -0000
@@ -271,7 +271,7 @@ class views_handler_filter extends views
     if (!empty($form_state['values']['options']['expose']['identifier']) && $form_state['values']['options']['expose']['identifier'] == 'value') {
       form_error($form['expose']['identifier'], t('This identifier is not allowed.'));
     }
-    
+
     if (!$this->view->display_handler->is_identifier_unique($form_state['id'], $form_state['values']['options']['expose']['identifier'])) {
       form_error($form['expose']['identifier'], t('This identifier is used by another handler.'));
     }
Index: includes/admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/admin.inc,v
retrieving revision 1.161.4.2
diff -u -p -r1.161.4.2 admin.inc
--- includes/admin.inc	9 Nov 2009 22:38:07 -0000	1.161.4.2
+++ includes/admin.inc	13 Nov 2009 18:55:27 -0000
@@ -1251,28 +1251,21 @@ function template_preprocess_views_ui_ed
     // Get relationship labels
     $relationships = array();
     // @todo: get_handlers()
-    foreach ($display->handler->get_option('relationships') as $id => $relationship) {
-      $handler = views_get_handler($relationship['table'], $relationship['field'], 'relationship');
-      if (empty($handler)) {
-        continue;
-      }
-      $handler->init($view, $relationship);
+    foreach ($display->handler->get_handlers('relationship') as $id => $handler) {
       $relationships[$id] = $handler->label();
     }
   }
 
-  // @todo: get_handlers()
   foreach ($display->handler->get_option($types[$type]['plural']) as $id => $field) {
     $fields[$id] = array();
 
-    $handler = views_get_handler($field['table'], $field['field'], $type);
+    $handler = $display->handler->get_handler($type, $id);
     if (empty($handler)) {
       $fields[$id]['class'][] = 'broken';
       $fields[$id]['title'] = t("Error: handler for @table > @field doesn't exist!", array('@table' => $field['table'], '@field' => $field['field']));
       $fields[$id]['info'] = '';
       continue;
     }
-    $handler->init($view, $field);
 
     $field_name = $handler->ui_name(TRUE);
     if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
@@ -1286,6 +1279,9 @@ function template_preprocess_views_ui_ed
     }
     $fields[$id]['info'] = $handler->admin_summary();
 
+    if ($display->handler->use_group_by()) {
+      $fields[$id]['links'] = l('<span>' . t('Group settings') . '</span>', "admin/build/views/nojs/config-item-group/$view->name/$display->id/$type/$id", array('attributes' => array('class' => 'views-button-configure views-ajax-link', 'title' => t('Group settings')), 'html' => true));
+    }
     if ($handler->has_extra_options()) {
       $fields[$id]['links'] = l('<span>' . t('Settings') . '</span>', "admin/structure/views/nojs/config-item-extra/$view->name/$display->id/$type/$id", array('attributes' => array('class' => array('views-button-configure', 'views-ajax-link'), 'title' => t('Settings')), 'html' => true));
     }
@@ -1470,6 +1466,10 @@ function views_ui_ajax_forms($key = NULL
       'form_id' => 'views_ui_config_item_form',
       'args' => array('type', 'id'),
     ),
+    'config-item-group' => array(
+      'form_id' => 'views_ui_config_item_group_form',
+      'args' => array('type', 'id'),
+    ),
     'config-item-extra' => array(
       'form_id' => 'views_ui_config_item_extra_form',
       'args' => array('type', 'id'),
@@ -2040,17 +2040,10 @@ function views_ui_rearrange_form($form, 
 
   // Get relationship labels
   $relationships = array();
-  // @todo: get_handlers()
-  foreach ($display->handler->get_option('relationships') as $id => $relationship) {
-    $handler = views_get_handler($relationship['table'], $relationship['field'], 'relationship');
-    if (empty($handler)) {
-      continue;
-    }
-    $handler->init($view, $relationship);
+  foreach ($display->handler->get_handlers('relationship') as $id => $handler) {
     $relationships[$id] = $handler->label();
   }
 
-  // @todo: get_handlers()
   foreach ($display->handler->get_option($types[$type]['plural']) as $id => $field) {
     $form[$id] = array('#tree' => TRUE);
     $form[$id]['weight'] = array(
@@ -2058,9 +2051,8 @@ function views_ui_rearrange_form($form, 
       '#delta' => 25,
       '#default_value' => ++$count,
     );
-    $handler = views_get_handler($field['table'], $field['field'], $type);
+    $handler = $display->handler->get_handler($type, $id);
     if ($handler) {
-      $handler->init($view, $field);
       $name = $handler->ui_name() . ' ' . $handler->admin_summary();
       if (!empty($field['relationship']) && !empty($relationships[$field['relationship']])) {
         $name = '(' . $relationships[$field['relationship']] . ') ' . $name;
@@ -2182,7 +2174,7 @@ function views_ui_add_item_form($form, &
 
   // Figure out all the base tables allowed based upon what the relationships provide.
   $base_tables = $view->get_base_tables();
-  $options = views_fetch_fields(array_keys($base_tables), $type);
+  $options = views_fetch_fields(array_keys($base_tables), $type, $display->handler->use_group_by());
 
   if (!empty($options)) {
     $groups = array('all' => t('<All>'));
@@ -2248,6 +2240,11 @@ function views_ui_add_item_form_submit($
       list($table, $field) = explode('.', $field, 2);
       $id = $form_state['view']->add_item($form_state['display_id'], $type, $table, $field);
 
+      // check to see if we have group by settings
+      if ($form_state['view']->display_handler->use_group_by()) {
+        views_ui_add_form_to_stack('config-item-group', $form_state['view'], $form_state['display_id'], array($type, $id));
+      }
+
       // check to see if this type has settings, if so add the settings form first
       $handler = views_get_handler($table, $field, $type);
       if ($handler && $handler->has_extra_options()) {
@@ -2279,12 +2276,11 @@ function views_ui_config_item_form($form
   $item = $view->get_item($display_id, $type, $id);
 
   if ($item) {
-    $handler = views_get_handler($item['table'], $item['field'], $type);
+    $handler = $view->display_handler->get_handler($type, $id);
     if (empty($handler)) {
       $form['markup'] = array('#value' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
     }
     else {
-      $handler->init($view, $item);
       $types = views_object_types();
 
       if ($view->display_handler->defaultable_sections($types[$type]['plural'])) {
@@ -2312,7 +2308,7 @@ function views_ui_config_item_form($form
         // If this relationship is valid for this type, add it to the list.
         $data = views_fetch_data($relationship['table']);
         $base = $data[$relationship['field']]['relationship']['base'];
-        $base_fields = views_fetch_fields($base, $form_state['type']);
+        $base_fields = views_fetch_fields($base, $form_state['type'], $view->display_handler->use_group_by());
         if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
           $relationship_handler->init($view, $relationship);
           $relationship_options[$relationship['id']] = $relationship_handler->label();
@@ -2322,7 +2318,7 @@ function views_ui_config_item_form($form
       if (!empty($relationship_options)) {
         // Make sure the existing relationship is even valid. If not, force
         // it to none.
-        $base_fields = views_fetch_fields($view->base_table, $form_state['type']);
+        $base_fields = views_fetch_fields($view->base_table, $form_state['type'], $view->display_handler->use_group_by());
         if (isset($base_fields[$item['table'] . '.' . $item['field']])) {
           $relationship_options = array_merge(array('none' => t('Do not use a relationship')), $relationship_options);
         }
@@ -2397,9 +2393,7 @@ function views_ui_config_item_form_submi
   // Store the item back on the view
   $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
 
-  $handler = views_get_handler($item['table'], $item['field'], $form_state['type']);
-  $handler->init($form_state['view'], $item);
-  if ($handler && $handler->needs_style_plugin()) {
+  if ($form_state['handler'] && $form_state['handler']->needs_style_plugin()) {
     views_ui_add_form_to_stack('change-style', $form_state['view'], $form_state['display_id'], array($form_state['type'], $form_state['id']), TRUE);
   }
 
@@ -2408,6 +2402,73 @@ function views_ui_config_item_form_submi
 }
 
 /**
+ * Form to config_item items in the views UI.
+ */
+function views_ui_config_item_group_form(&$form_state) {
+  $view = &$form_state['view'];
+  $display_id = $form_state['display_id'];
+  $type = $form_state['type'];
+  $id = $form_state['id'];
+
+  $view->init_query();
+
+  $form = array('options' => array('#tree' => TRUE));
+  if (!$view->set_display($display_id)) {
+    views_ajax_render(t('Invalid display id @display', array('@display' => $display_id)));
+  }
+
+  $item = $view->get_item($display_id, $type, $id);
+
+  if ($item) {
+    $handler = $view->display_handler->get_handler($type, $id);
+    if (empty($handler)) {
+      $form['markup'] = array('#value' => t("Error: handler for @table > @field doesn't exist!", array('@table' => $item['table'], '@field' => $item['field'])));
+    }
+    else {
+      $handler->init($view, $item);
+      $types = views_object_types();
+
+      $form['#title'] = check_plain($view->display[$display_id]->display_title) . ': ';
+      $form['#title'] .= t('Configure group settings for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $handler->ui_name()));
+
+      $form['#section'] = $display_id . '-' . $type . '-' . $id;
+
+      $info = $view->query->get_aggregation_info();
+      foreach ($info as $id => $aggregate) {
+        $group_types[$id] = $aggregate['title'];
+      }
+
+      $form['group_type'] = array(
+        '#type' => 'select',
+        '#title' => t('Group type'),
+        '#default_value' => $handler->options['group_type'],
+        '#description' => t('Grouping is enabled for this display. You must select what function to use on this field.'),
+        '#options' => $group_types,
+      );
+      $form_state['handler'] = &$handler;
+    }
+
+    views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_item_group_form');
+  }
+  return $form;
+}
+
+/**
+ * Submit handler for configing group settings on a view.
+ */
+function views_ui_config_item_group_form_submit($form, &$form_state) {
+  $item = $form_state['handler']->options;
+
+  $item['group_type'] = $form_state['values']['group_type'];
+
+  // Store the item back on the view
+  $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item);
+
+  // Write to cache
+  views_ui_cache_set($form_state['view']);
+}
+
+/**
  * Submit handler for removing an item from a view
  */
 function views_ui_config_item_form_remove($form, &$form_state) {
@@ -2471,8 +2532,7 @@ function views_ui_config_item_extra_form
 
       // Get form from the handler.
       $handler->extra_options_form($form['options'], $form_state);
-        $form_state['handler'] = &$handler;
-
+      $form_state['handler'] = &$handler;
     }
 
     views_ui_standard_form_buttons($form, $form_state, 'views_ui_config_item_extra_form');
@@ -2916,7 +2976,7 @@ function _views_sort_types($a, $b) {
  * @return
  *   A keyed array of in the form of 'base_table' => 'Description'.
  */
-function views_fetch_fields($base, $type) {
+function views_fetch_fields($base, $type, $grouping = FALSE) {
   static $fields = array();
   if (empty($fields)) {
     $data = views_fetch_data();
@@ -2948,6 +3008,9 @@ function views_fetch_fields($base, $type
         }
         foreach (array('field', 'sort', 'filter', 'argument', 'relationship') as $key) {
           if (!empty($info[$key])) {
+            if ($grouping && !empty($info[$key]['no group by'])) {
+              continue;
+            }
             if (!empty($info[$key]['skip base'])) {
               foreach ((array) $info[$key]['skip base'] as $base_name) {
                 $skip_bases[$field][$key][$base_name] = TRUE;
Index: includes/handlers.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/handlers.inc,v
retrieving revision 1.119.4.1
diff -u -p -r1.119.4.1 handlers.inc
--- includes/handlers.inc	2 Nov 2009 22:01:26 -0000	1.119.4.1
+++ includes/handlers.inc	13 Nov 2009 18:55:29 -0000
@@ -15,11 +15,22 @@ function _views_create_handler($definiti
   }
 
   // class_exists will automatically load the code file.
+  if (!empty($definition['override handler']) &&
+      !class_exists($definition['override handler'])) {
+    return;
+  }
+  
   if (!class_exists($definition['handler'])) {
     return;
   }
+  
+   if (!empty($definition['override handler'])) {
+     $handler = new $definition['override handler'];
+   }
+   else {
+     $handler = new $definition['handler'];
+   }
 
-  $handler = new $definition['handler'];
   $handler->set_definition($definition);
   // let the handler have something like a constructor.
   $handler->construct();
@@ -194,6 +205,14 @@ class views_handler extends views_object
     $this->query = &$view->query;
   }
 
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['group_type'] = array('default' => 'group');
+
+    return $options;
+  }
+
   /**
    * Return a string representing this handler's name in the UI.
    */
@@ -203,15 +222,39 @@ class views_handler extends views_object
   }
 
   /**
-   * Provide a form for setting options.
-   */
-  function options_form(&$form, &$form_state) { }
+   * Shortcut to get a handler's raw field value.
+   *
+   * This should be overridden for handlers with formulae or other
+   * non-standard fields. Because this takes an argument, fields
+   * overriding this can just call return parent::get_field($formula)
+   */
+  function get_field($field = NULL) {
+    if (!isset($field)) {
+      if (!empty($this->formula)) {
+        $field = $this->get_formula();
+      }
+      else {
+        $field = $this->table_alias . '.' . $this->real_field;
+      }
+    }
+
+    // If grouping, check to see if the aggregation method needs to modify the field.
+    if ($this->view->display_handler->use_group_by()) {
+      $info = $this->query->get_aggregation_info();
+      if (!empty($info[$this->options['group_type']]['method']) && function_exists($info[$this->options['group_type']]['method'])) {
+        return $info[$this->options['group_type']]['method']($this->options['group_type'], $field);
+      }
+    }
+
+    return $field;
+  }
 
   /**
    * Validate the options form.
    */
   function options_validate($form, &$form_state) { }
 
+  function options_form(&$form, &$form_state) { }
   /**
    * Perform any necessary changes to the form values prior to storage.
    * There is no need for this function to actually store the data.
@@ -437,7 +480,7 @@ class views_handler extends views_object
    *
    * If we were using PHP5, this would be abstract.
    */
-  function query() { }
+  function query($group_by = FALSE) { }
 
   /**
    * Ensure the main table for this handler is in the query. This is used
@@ -1096,6 +1139,20 @@ function views_views_handlers() {
     'views_handler_sort_date',
     'views_handler_sort_menu_hierarchy',
     'views_handler_sort_random',
+  
+    // group by handlers
+    'views_handler_argument_group_by_numeric' => array(
+      'parent' => 'views_handler_argument',
+    ),
+    'views_handler_field_group_by_numeric' => array(
+      'parent' => 'views_handler_field',
+    ),
+    'views_handler_filter_group_by_numeric' => array(
+      'parent' => 'views_handler_filter_numeric',
+    ),
+    'views_handler_sort_group_by_numeric' => array(
+      'parent' => 'views_handler_sort',
+    ),
   );
 }
 
@@ -1304,7 +1361,8 @@ class views_join {
  */
 function views_views_api() {
   return array(
-    'api' => 2,
+    // in your modules do *not* use views_api_version()!!!
+    'api' => views_api_version(),
     'path' => drupal_get_path('module', 'views') . '/modules',
   );
 }
Index: includes/view.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/includes/view.inc,v
retrieving revision 1.167.4.2
diff -u -p -r1.167.4.2 view.inc
--- includes/view.inc	9 Nov 2009 22:38:07 -0000	1.167.4.2
+++ includes/view.inc	13 Nov 2009 18:55:32 -0000
@@ -51,17 +51,18 @@ class view extends views_db_object {
 
   // Used to store views that were previously running if we recurse.
   var $old_view = array();
+
+  // Where the $query object will reside:
+  var $query = NULL;
   /**
    * Constructor
    */
-  function view() {
+  function __construct() {
     parent::init();
     // Make sure all of our sub objects are arrays.
     foreach ($this->db_objects() as $object) {
       $this->$object = array();
     }
-
-    $this->query = new stdClass();
   }
 
   /**
@@ -477,7 +478,7 @@ class view extends views_db_object {
         }
         else {
           $arg_title = $argument->get_title();
-          $argument->query();
+          $argument->query($this->display_handler->use_group_by());
         }
 
         // Add this argument's substitution
@@ -528,6 +529,14 @@ class view extends views_db_object {
    * Do some common building initialization.
    */
   function init_query() {
+    if (!empty($this->query)) {
+      $class = get_class($this->query);
+      if ($class && $class != 'stdClass') {
+        // return if query is already initialized.
+        return;
+      }
+    }
+
     // Create and initialize the query object.
     $views_data = views_fetch_data($this->base_table);
     $this->base_field = $views_data['table']['base']['field'];
@@ -632,10 +641,10 @@ class view extends views_db_object {
     }
 
     // Allow display handler to affect the query:
-    $this->display_handler->query();
+    $this->display_handler->query($this->display_handler->use_group_by());
 
     // Allow style handler to affect the query:
-    $this->style_plugin->query();
+    $this->style_plugin->query($this->display_handler->use_group_by());
 
     if (variable_get('views_sql_signature', FALSE)) {
       $this->query->add_signature($this);
@@ -671,7 +680,7 @@ class view extends views_db_object {
           }
         }
         $handlers[$id]->set_relationship();
-        $handlers[$id]->query();
+        $handlers[$id]->query($this->display_handler->use_group_by());
       }
     }
   }
Index: modules/comment.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/comment.views.inc,v
retrieving revision 1.33.4.3
diff -u -p -r1.33.4.3 comment.views.inc
--- modules/comment.views.inc	9 Nov 2009 22:49:56 -0000	1.33.4.3
+++ modules/comment.views.inc	13 Nov 2009 18:55:32 -0000
@@ -405,6 +405,7 @@ function comment_views_data_alter(&$data
     'help' => t('The number of new comments on the node.'),
     'field' => array(
       'handler' => 'views_handler_field_node_new_comments',
+      'no group by' => TRUE,
     ),
   );
 
Index: modules/node.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/node.views.inc,v
retrieving revision 1.97.4.2
diff -u -p -r1.97.4.2 node.views.inc
--- modules/node.views.inc	9 Nov 2009 22:49:56 -0000	1.97.4.2
+++ modules/node.views.inc	13 Nov 2009 18:55:33 -0000
@@ -662,6 +662,7 @@ function template_preprocess_views_view_
   // Make sure the variables are defined.
   $vars['node'] = '';
   $vars['comments'] = '';
+  dsm($vars);
 
   $nid = $vars['row']->{$vars['field_alias']};
   if (!is_numeric($nid)) {
Index: modules/profile.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/profile.views.inc,v
retrieving revision 1.10.4.1
diff -u -p -r1.10.4.1 profile.views.inc
--- modules/profile.views.inc	2 Nov 2009 22:01:26 -0000	1.10.4.1
+++ modules/profile.views.inc	13 Nov 2009 18:55:34 -0000
@@ -195,6 +195,7 @@ function profile_views_fetch_field($fiel
         'help' => t('Profile freeform list %field-name.', array('%field-name' => $field->title)),
         'field' => array(
           'handler' => 'views_handler_field_profile_list',
+          'no group by' => TRUE,
         ),
         'filter' => array(
           'handler' => 'views_handler_filter_string',
Index: modules/taxonomy.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/taxonomy.views.inc,v
retrieving revision 1.57.4.1
diff -u -p -r1.57.4.1 taxonomy.views.inc
--- modules/taxonomy.views.inc	2 Nov 2009 22:01:26 -0000	1.57.4.1
+++ modules/taxonomy.views.inc	13 Nov 2009 18:55:34 -0000
@@ -211,6 +211,7 @@ function taxonomy_views_data() {
       'help' => t('Display all taxonomy terms associated with a node from specified vocabularies.'),
       'handler' => 'views_handler_field_term_node_tid',
       'skip base' => 'term_data',
+      'no group by' => TRUE,
     ),
     'argument' => array(
       'handler' => 'views_handler_argument_term_node_tid',
Index: modules/upload.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/upload.views.inc,v
retrieving revision 1.16.4.2
diff -u -p -r1.16.4.2 upload.views.inc
--- modules/upload.views.inc	9 Nov 2009 22:49:56 -0000	1.16.4.2
+++ modules/upload.views.inc	13 Nov 2009 18:55:34 -0000
@@ -116,6 +116,7 @@ function upload_views_data_alter(&$data)
     'real field' => 'vid',
     'field' => array(
       'handler' => 'views_handler_field_upload_fid',
+      'no group by' => TRUE,
     ),
     'filter' => array(
       'handler' => 'views_handler_filter_upload_fid',
Index: modules/user.views.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/user.views.inc,v
retrieving revision 1.59.4.1
diff -u -p -r1.59.4.1 user.views.inc
--- modules/user.views.inc	2 Nov 2009 22:01:26 -0000	1.59.4.1
+++ modules/user.views.inc	13 Nov 2009 18:55:35 -0000
@@ -275,6 +275,7 @@ function user_views_data() {
     'help' => t('Roles that a user belongs to.'),
     'field' => array(
       'handler' => 'views_handler_field_user_roles',
+      'no group by' => TRUE,
     ),
     'filter' => array(
       'handler' => 'views_handler_filter_user_roles',
Index: modules/node/views_handler_field_node.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/modules/node/views_handler_field_node.inc,v
retrieving revision 1.7.4.1
diff -u -p -r1.7.4.1 views_handler_field_node.inc
--- modules/node/views_handler_field_node.inc	2 Nov 2009 22:01:26 -0000	1.7.4.1
+++ modules/node/views_handler_field_node.inc	13 Nov 2009 18:55:35 -0000
@@ -9,14 +9,14 @@
  * Field handler to provide simple renderer that allows linking to a node.
  */
 class views_handler_field_node extends views_handler_field {
-  /**
-   * Constructor to provide additional field to add.
-   */
-  function construct() {
-    parent::construct();
-    $this->additional_fields['nid'] = 'nid';
-    if (module_exists('translation')) {
-      $this->additional_fields['language'] = array('table' => 'node', 'field' => 'language');
+
+  function init(&$view, $options) {
+    parent::init($view, $options);
+    if (!empty($this->options['link_to_node'])) {
+      $this->additional_fields['nid'] = 'nid';
+      if (module_exists('translation')) {
+        $this->additional_fields['language'] = array('table' => 'node', 'field' => 'language');
+      }
     }
   }
 
Index: plugins/views_plugin_display.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/views/plugins/views_plugin_display.inc,v
retrieving revision 1.27.4.4
diff -u -p -r1.27.4.4 views_plugin_display.inc
--- plugins/views_plugin_display.inc	2 Nov 2009 23:57:54 -0000	1.27.4.4
+++ plugins/views_plugin_display.inc	13 Nov 2009 18:55:39 -0000
@@ -130,6 +130,13 @@ class views_plugin_display extends views
   }
 
   /**
+   * Does the display have a more link enabled?
+   */
+  function use_group_by() {
+    return $this->get_option('group_by');
+  }
+
+  /**
    * Should the enabled display more link be shown when no more items?
    */
   function use_more_always() {
@@ -256,6 +263,7 @@ class views_plugin_display extends views
           'exposed_block' => TRUE,
 
           'link_display' => TRUE,
+          'group_by' => TRUE,
 
           'style_plugin' => TRUE,
           'style_options' => TRUE,
@@ -361,6 +369,9 @@ class views_plugin_display extends views
       'distinct' => array(
         'default' => FALSE,
       ),
+      'group_by' => array(
+        'default' => FALSE,
+      ),
 
       'style_plugin' => array(
         'default' => 'default',
@@ -559,7 +570,21 @@ class views_plugin_display extends views
       $types = views_object_types();
       $plural = $types[$type]['plural'];
       foreach ($this->get_option($plural) as $id => $info) {
-        $handler = views_get_handler($info['table'], $info['field'], $type);
+        // If aggregation is on, the group type might override the actual
+        // handler that is in use. This piece of code checks that and,
+        // if necessary, sets the override handler.
+        $override = NULL;
+        if ($this->use_group_by() && !empty($info['group_type'])) {
+          if (empty($this->view->query)) {
+            $this->view->init_query();
+          }
+          $aggregate = $this->view->query->get_aggregation_info();
+          if (!empty($aggregate[$info['group_type']]['handler'][$type])) {
+            $override = $aggregate[$info['group_type']]['handler'][$type];
+          }
+        }
+
+        $handler = views_get_handler($info['table'], $info['field'], $type, $override);
         if ($handler) {
           $handler->init($this->view, $info);
           $this->handlers[$type][$id] = &$handler;
@@ -732,6 +757,16 @@ class views_plugin_display extends views
       'desc' => t('Display only distinct items, without duplicates.'),
     );
 
+    $this->view->init_query();
+    if ($this->view->query->get_aggregation_info()) {
+      $options['group_by'] = array(
+        'category' => 'advanced',
+        'title' => t('Use grouping'),
+        'value' => $this->get_option('group_by') ? t('Yes') : t('No'),
+        'desc' => t('Allow grouping and aggregation (calculation) of fields.'),
+      );
+    }
+
     $access_plugin = $this->get_access_plugin();
     if (!$access_plugin) {
       // default to the no access control plugin.
@@ -944,6 +979,15 @@ class views_plugin_display extends views
           '#default_value' => $this->get_option('distinct'),
         );
         break;
+      case 'group_by':
+        $form['#title'] .= t('Allow grouping and aggregation (calculation) of fields.');
+        $form['group_by'] = array(
+          '#type' => 'checkbox',
+          '#title' => t('Group by'),
+          '#description' => t('If enabled, some fields may become unavailable. All fields that are selected for grouping will be collapsed to one record per distinct value. Other fields which are selected for aggregation will have the function run on them. For example, you can group nodes on title and count the number of nids in order to get a list of duplicate titles.'),
+          '#default_value' => $this->get_option('distinct'),
+        );
+        break;
       case 'access':
         $form['#title'] .= t('Access restrictions');
         $form['access'] = array(
@@ -1504,6 +1548,9 @@ class views_plugin_display extends views
       case 'distinct':
         $this->set_option($section, $form_state['values'][$section]);
         break;
+      case 'group_by':
+        $this->set_option($section, $form_state['values'][$section]);
+        break;
       case 'row_plugin':
         // This if prevents resetting options to default if they don't change
         // the plugin.
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.4.2
diff -u -p -r1.1.4.2 views_plugin_query.inc
--- plugins/views_plugin_query.inc	2 Nov 2009 22:01:27 -0000	1.1.4.2
+++ plugins/views_plugin_query.inc	13 Nov 2009 18:55:39 -0000
@@ -50,4 +50,11 @@ class views_plugin_query extends views_p
    * discern where particular queries might be coming from.
    */
   function add_signature(&$view) { }
+
+  /**
+   * Get aggregation info for group by queries.
+   *
+   * If NULL, aggregation is not allowed.
+   */
+  function get_aggregation_info() { }
 }
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.4.4
diff -u -p -r1.1.4.4 views_plugin_query_default.inc
--- plugins/views_plugin_query_default.inc	10 Nov 2009 22:44:17 -0000	1.1.4.4
+++ plugins/views_plugin_query_default.inc	13 Nov 2009 18:55:41 -0000
@@ -95,6 +95,8 @@ class views_plugin_query_default extends
       'alias' => $base_table,
     );
 
+/**
+ * -- we no longer want the base field to appear automatigically.
     if ($base_field) {
       $this->fields[$base_field] = array(
         'table' => $base_table,
@@ -102,6 +104,7 @@ class views_plugin_query_default extends
         'alias' => $base_field,
       );
     }
+ */
 
     $this->count_field = array(
       'table' => $base_table,
@@ -614,11 +617,14 @@ class views_plugin_query_default extends
    *   The alias to create. If not specified, the alias will be $table_$field
    *   unless $table is NULL. When adding formulae, it is recommended that an
    *   alias be used.
+   * @param $params
+   *   An array of parameters additional to the field that will control items
+   *   such as aggregation functions and DISTINCT.
    *
    * @return $name
    *   The name that this field can be referred to as. Usually this is the alias.
    */
-  function add_field($table, $field, $alias = '', $params = NULL) {
+  function add_field($table, $field, $alias = '', $params = array()) {
     // We check for this specifically because it gets a special alias.
     if ($table == $this->base_table && $field == $this->base_field && empty($alias)) {
       $alias = $this->base_field;
@@ -632,22 +638,30 @@ class views_plugin_query_default extends
       $alias = $table . '_' . $field;
     }
 
-    $name = $alias ? $alias : $field;
+    // Make sure an alias is assigned
+    $alias = $alias ? $alias : $field;
 
-    // @todo FIXME -- $alias, then $name is inconsistent
-    if (empty($this->fields[$alias])) {
-      $this->fields[$name] = array(
-        'field' => $field,
-        'table' => $table,
-        'alias' => $alias,
-      );
+    // Create a field info array.
+    $field_info = array(
+      'field' => $field,
+      'table' => $table,
+      'alias' => $alias,
+    ) + $params;
+
+    // Test to see if the field is actually the same or not. Due to
+    // differing parameters changing the aggregation function, we need
+    // to do some automatic alias collision detection:
+    $base = $alias;
+    $counter = 0;
+    while (!empty($this->fields[$alias]) && $this->fields[$alias] != $field_info) {
+      $field_info['alias'] = $alias = $base . '_' . ++$counter;
     }
 
-    foreach ((array)$params as $key => $value) {
-      $this->fields[$name][$key] = $value;
+    if (empty($this->fields[$alias])) {
+      $this->fields[$alias] = $field_info;
     }
 
-    return $name;
+    return $alias;
   }
 
   /**
@@ -783,8 +797,10 @@ class views_plugin_query_default extends
    *   must also be in the SELECT portion. If an $alias isn't specified
    *   one will be generated for from the $field; however, if the
    *   $field is a formula, this alias will likely fail.
+   * @param $params
+   *   Any params that should be passed through to the add_field.
    */
-  function add_orderby($table, $field, $order, $alias = '') {
+  function add_orderby($table, $field, $order, $alias = '', $params = array()) {
     if ($table) {
       $this->ensure_table($table);
     }
@@ -799,7 +815,7 @@ class views_plugin_query_default extends
     }
 
     if ($field) {
-      $this->add_field($table, $field, $as);
+      $this->add_field($table, $field, $as, $params);
     }
 
     $this->orderby[] = array(
@@ -807,12 +823,16 @@ class views_plugin_query_default extends
       'direction' => strtoupper($order)
     );
 
-    // If grouping, all items in the order by must also be in the
-    // group by clause. Check $table to ensure that this is not a
-    // formula.
-    if ($this->groupby && $table) {
-      $this->add_groupby($as);
-    }
+	/**
+ 	 * -- removing, this should be taken care of by field adding now.
+     * -- leaving commented because I am unsure.
+      // If grouping, all items in the order by must also be in the
+      // group by clause. Check $table to ensure that this is not a
+      // formula.
+      if ($this->groupby && $table) {
+        $this->add_groupby($as);
+      }
+    */
   }
 
   /**
@@ -855,10 +875,8 @@ class views_plugin_query_default extends
   function query($get_count = FALSE) {
     // Check query distinct value.
     if (empty($this->no_distinct) && $this->distinct && !empty($this->fields)) {
-      if (!empty($this->fields[$this->base_field])) {
-        $this->fields[$this->base_field]['distinct'] = TRUE;
-        $this->add_groupby($this->base_field);
-      }
+      $base_field_alias = $this->add_field($this->base_table, $this->base_field, NULL, array('distinct' => TRUE));
+      $this->add_groupby($base_field_alias);
     }
 
     /**
@@ -868,7 +886,7 @@ class views_plugin_query_default extends
     $fields_array = $this->fields;
     if ($get_count && !$this->groupby) {
       foreach ($fields_array as $field) {
-        if (!empty($field['distinct'])) {
+        if (!empty($field['distinct']) || !empty($field['function'])) {
           $get_count_optimized = FALSE;
           break;
         }
@@ -884,7 +902,8 @@ class views_plugin_query_default extends
     // Go ahead and build the query.
     $query = db_select($this->base_table, $this->base_table);
 
-    $joins = $fields = $where = $having = $orderby = $groupby = '';
+    $joins = $where = $having = $orderby = $groupby = '';
+    $fields = $distinct = array();
 
     // Add all the tables to the query via joins. We assume all LEFT joins.
     foreach ($this->table_queue as $table) {
@@ -1064,7 +1083,7 @@ class views_plugin_query_default extends
         // If we already know how many items we have even if we did not run the
         // count query, go ahead and set that value:
         if (empty($this->pager['items_per_page'])) {
-          $this->total_rows = count($this->result);
+          $this->total_rows = count($view->result);
         }
       }
       catch (Exception $e) {
@@ -1085,4 +1104,68 @@ class views_plugin_query_default extends
     $view->query->add_field(NULL, "'" . $view->name . ':' . $view->current_display . "'", 'view_name');
   }
 
+  function get_aggregation_info() {
+    // @todo -- need a way to get database specific and customized aggregation
+    // functions into here.
+    return array(
+      'group' => array(
+        'title' => t('Group results together'),
+        'is aggregate' => FALSE,
+      ),
+      'count' => array(
+        'title' => t('Count'),
+        'method' => 'views_query_default_aggregation_method_simple',
+        'handler' => array(
+          'argument' => 'views_handler_argument_group_by_numeric',
+          'field' => 'views_handler_field_group_by_numeric',
+          'filter' => 'views_handler_filter_group_by_numeric',
+          'sort' => 'views_handler_sort_group_by_numeric',
+        ),
+      ),
+      'sum' => array(
+        'title' => t('Sum'),
+        'method' => 'views_query_default_aggregation_method_simple',
+        'handler' => array(
+          'argument' => 'views_handler_argument_group_by_numeric',
+          'field' => 'views_handler_field_group_by_numeric',
+          'filter' => 'views_handler_filter_group_by_numeric',
+          'sort' => 'views_handler_sort_group_by_numeric',
+        ),
+      ),
+      'avg' => array(
+        'title' => t('Average'),
+        'method' => 'views_query_default_aggregation_method_simple',
+        'handler' => array(
+          'argument' => 'views_handler_argument_group_by_numeric',
+          'field' => 'views_handler_field_group_by_numeric',
+          'filter' => 'views_handler_filter_group_by_numeric',
+          'sort' => 'views_handler_sort_group_by_numeric',
+        ),
+      ),
+      'min' => array(
+        'title' => t('Minimum'),
+        'method' => 'views_query_default_aggregation_method_simple',
+        'handler' => array(
+          'argument' => 'views_handler_argument_group_by_numeric',
+          'field' => 'views_handler_field_group_by_numeric',
+          'filter' => 'views_handler_filter_group_by_numeric',
+          'sort' => 'views_handler_sort_group_by_numeric',
+        ),
+      ),
+      'max' => array(
+        'title' => t('Maximum'),
+        'method' => 'views_query_default_aggregation_method_simple',
+        'handler' => array(
+          'argument' => 'views_handler_argument_group_by_numeric',
+          'field' => 'views_handler_field_group_by_numeric',
+          'filter' => 'views_handler_filter_group_by_numeric',
+          'sort' => 'views_handler_sort_group_by_numeric',
+        ),
+      ),
+    );
+  }
+}
+
+function views_query_default_aggregation_method_simple($group_type, $field) {
+  return strtoupper($group_type) . '(' . $field . ')';
 }
