Browse Source

Merged 6.x-views_join_chado_aggregator_handler into dev -mostly views related changes with a few to the BLAST analysis module

Lacey Sanderson 13 năm trước cách đây
mục cha
commit
9c6e1277f9
31 tập tin đã thay đổi với 2413 bổ sung176 xóa
  1. 71 30
      base/tripal_analysis/views/analysis.views.inc
  2. 83 6
      base/tripal_core/tripal_core.views.inc
  3. 126 0
      base/tripal_core/views/handlers/chado_views_handler_field.inc
  4. 94 0
      base/tripal_core/views/handlers/chado_views_handler_field_aggregate.inc
  5. 112 0
      base/tripal_core/views/handlers/chado_views_handler_field_boolean.inc
  6. 112 0
      base/tripal_core/views/handlers/chado_views_handler_field_counter.inc
  7. 112 0
      base/tripal_core/views/handlers/chado_views_handler_field_custom.inc
  8. 112 0
      base/tripal_core/views/handlers/chado_views_handler_field_date.inc
  9. 112 0
      base/tripal_core/views/handlers/chado_views_handler_field_markup.inc
  10. 112 0
      base/tripal_core/views/handlers/chado_views_handler_field_math.inc
  11. 112 0
      base/tripal_core/views/handlers/chado_views_handler_field_numeric.inc
  12. 75 0
      base/tripal_core/views/handlers/chado_views_handler_filter_boolean_operator.inc
  13. 75 0
      base/tripal_core/views/handlers/chado_views_handler_filter_boolean_operator_string.inc
  14. 75 0
      base/tripal_core/views/handlers/chado_views_handler_filter_date.inc
  15. 75 0
      base/tripal_core/views/handlers/chado_views_handler_filter_equality.inc
  16. 75 0
      base/tripal_core/views/handlers/chado_views_handler_filter_float.inc
  17. 75 0
      base/tripal_core/views/handlers/chado_views_handler_filter_numeric.inc
  18. 75 0
      base/tripal_core/views/handlers/chado_views_handler_filter_string.inc
  19. 49 0
      base/tripal_core/views/handlers/chado_views_handler_sort.inc
  20. 49 0
      base/tripal_core/views/handlers/chado_views_handler_sort_date.inc
  21. 49 0
      base/tripal_core/views/handlers/chado_views_handler_sort_formula.inc
  22. 49 0
      base/tripal_core/views/handlers/chado_views_handler_sort_menu_hierarchy.inc
  23. 49 0
      base/tripal_core/views/handlers/chado_views_handler_sort_random.inc
  24. 96 35
      base/tripal_core/views/handlers/views_handler_filter_chado_select_cvterm_name.inc
  25. 92 31
      base/tripal_core/views/handlers/views_handler_filter_chado_select_string.inc
  26. 168 0
      base/tripal_core/views/handlers/views_handler_join_chado_aggregator.inc
  27. 112 0
      base/tripal_core/views/handlers/views_handler_join_chado_through_linking.inc
  28. 0 64
      base/tripal_organism/views/handlers/views_handler_filter_organism_common_name.inc
  29. 1 1
      base/tripal_organism/views/organism.views.inc
  30. 9 2
      extensions/tripal_analysis_blast/includes/parse_blast_XML.inc
  31. 7 7
      extensions/tripal_analysis_blast/theme/tripal_feature/tripal_feature_blast_results.tpl.php

+ 71 - 30
base/tripal_analysis/views/analysis.views.inc

@@ -51,26 +51,67 @@ function retrieve_analysis_views_data() {
   }
 
   // Define relationships between this table and others
-  $data['analysis']['table']['join'] = array(
-    'analysisfeature' => array(
-      'left_field' => 'analysis_id',
-      'field' => 'analysis_id',
-    ),
-    'feature' => array(
-      'left_table' => 'analysisfeature',
-      'left_field' => 'analysis_id',
+  $data['analysis']['table']['join']['feature'] = array(
+    'linking' => array(
+      'table' => 'analysisfeature',
+      'left_field' => 'feature_id',
       'field' => 'analysis_id',
     ),
+    'left_field' => 'feature_id',
+    'field' => 'analysis_id',
+    'handler' => 'views_handler_join_chado_through_linking'
   );
 
-  // Describe the joins with the analysis_feature table
-  $data['analysisfeature']['table']['join'] = array(
-    'feature' => array(
-      'left_field' => 'feature_id',
-      'field' => 'feature_id',
+  // Analysis properties?
+ 	$data['analysisprop']['table'] = array(
+'group' => 'Chado analysis Properties',
+ 		'field' => 'analysisprop_id',
+ 		'title' => t('Chado analysis Properties'),
+ 		'help' => '  ',
+ 	);
+ 	
+ 	$data['analysisprop']['table']['join']['analysis'] = array(
+    'left_field' => 'analysis_id',
+    'field' => 'analysis_id',
+    'handler' => 'views_handler_join_chado_aggregator'
+ 	);
+ 	
+ 	$data['analysisprop']['value'] = array(
+    'title' => t('Value'),
+    'help' => t(' '),
+    'field' => array(
+      'handler' => 'chado_views_handler_field',
+      'click sortable' => TRUE,
     ),
-  );
-	
+    'sort' => array(
+      'handler' => 'chado_views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'chado_views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+ 	);
+ 	
+ 	$data['analysisprop']['all'] = array(
+    'title' => t('All'),
+    'help' => t('An aggregate field that contains all fields for a row.'),
+    'field' => array(
+      'handler' => 'chado_views_handler_field_aggregate',
+      'click sortable' => TRUE,
+    ),
+    'sort' => array(
+      'handler' => 'chado_views_handler_sort',
+    ),
+    'filter' => array(
+      'handler' => 'chado_views_handler_filter_string',
+    ),
+    'argument' => array(
+      'handler' => 'views_handler_argument_string',
+    ),
+ 	);
+ 	
 	// Table Field Definitions----------------------
 	// Field: analysis_id (primary key)
 	$data['analysis']['analysis_id'] = array(
@@ -84,7 +125,7 @@ function retrieve_analysis_views_data() {
 	    'handler' => 'views_handler_filter_numeric',
 	  ),
 	  'sort' => array(
-	    'handler' => 'views_handler_sort',
+	    'handler' => 'chado_views_handler_sort',
 	  ),
 	);
 	
@@ -127,10 +168,10 @@ function retrieve_analysis_views_data() {
       'click sortable' => TRUE,
     ),
     'sort' => array(
-      'handler' => 'views_handler_sort',
+      'handler' => 'chado_views_handler_sort',
     ),
     'filter' => array(
-      'handler' => 'views_handler_filter_string',
+      'handler' => 'views_handler_filter_chado_select_string',
     ),
     'argument' => array(
       'handler' => 'views_handler_argument_string',
@@ -150,10 +191,10 @@ function retrieve_analysis_views_data() {
       'click sortable' => TRUE,
     ),
     'sort' => array(
-      'handler' => 'views_handler_sort',
+      'handler' => 'chado_views_handler_sort',
     ),
     'filter' => array(
-      'handler' => 'views_handler_filter_string',
+      'handler' => 'chado_views_handler_filter_string',
     ),
     'argument' => array(
       'handler' => 'views_handler_argument_string',
@@ -169,7 +210,7 @@ function retrieve_analysis_views_data() {
       'click sortable' => TRUE,
     ),
     'sort' => array(
-      'handler' => 'views_handler_sort',
+      'handler' => 'chado_views_handler_sort',
     ),
     'filter' => array(
       'handler' => 'views_handler_filter_chado_select_string',
@@ -188,7 +229,7 @@ function retrieve_analysis_views_data() {
       'click sortable' => TRUE,
     ),
     'sort' => array(
-      'handler' => 'views_handler_sort',
+      'handler' => 'chado_views_handler_sort',
     ),
     'filter' => array(
       'handler' => 'views_handler_filter_chado_select_string',
@@ -207,7 +248,7 @@ function retrieve_analysis_views_data() {
       'click sortable' => TRUE,
     ),
     'sort' => array(
-      'handler' => 'views_handler_sort',
+      'handler' => 'chado_views_handler_sort',
     ),
     'filter' => array(
       'handler' => 'views_handler_filter_chado_select_string',
@@ -225,10 +266,10 @@ function retrieve_analysis_views_data() {
       'click sortable' => TRUE,
     ),
     'sort' => array(
-      'handler' => 'views_handler_sort',
+      'handler' => 'chado_views_handler_sort',
     ),
     'filter' => array(
-      'handler' => 'views_handler_filter_string',
+      'handler' => 'chado_views_handler_filter_string',
     ),
     'argument' => array(
       'handler' => 'views_handler_argument_string',
@@ -244,10 +285,10 @@ function retrieve_analysis_views_data() {
       'click sortable' => TRUE,
     ),
     'sort' => array(
-      'handler' => 'views_handler_sort',
+      'handler' => 'chado_views_handler_sort',
     ),
     'filter' => array(
-      'handler' => 'views_handler_filter_string',
+      'handler' => 'chado_views_handler_filter_string',
     ),
     'argument' => array(
       'handler' => 'views_handler_argument_string',
@@ -263,10 +304,10 @@ function retrieve_analysis_views_data() {
       'click sortable' => TRUE,
     ),
     'sort' => array(
-      'handler' => 'views_handler_sort',
+      'handler' => 'chado_views_handler_sort',
     ),
     'filter' => array(
-      'handler' => 'views_handler_filter_string',
+      'handler' => 'chado_views_handler_filter_string',
     ),
     'argument' => array(
       'handler' => 'views_handler_argument_string',
@@ -282,7 +323,7 @@ function retrieve_analysis_views_data() {
       'click sortable' => TRUE,
     ),
     'sort' => array(
-      'handler' => 'views_handler_sort_date',
+      'handler' => 'chado_views_handler_sort_date',
     ),
   );
 

+ 83 - 6
base/tripal_core/tripal_core.views.inc

@@ -1,5 +1,8 @@
 <?php
 
+include('views/handlers/views_handler_join_chado_through_linking.inc');
+include('views/handlers/views_handler_join_chado_aggregator.inc');
+
 /**
  * @defgroup views Views Integration
  * @{
@@ -52,11 +55,25 @@
  * @ingroup tripal_core
  */
 function tripal_core_views_handlers() {
+
  return array(
    'info' => array(
      'path' => drupal_get_path('module', 'tripal_core') . '/views/handlers',
    ),
    'handlers' => array(
+   
+     // Custom Chado Handlers
+     'chado_views_handler_field_aggregate' => array(
+      'parent' => 'chado_views_handler_field',
+     ),
+     'views_handler_filter_chado_select_string' => array(
+      'parent' => 'views_handler_filter_string',
+     ),
+     'views_handler_filter_chado_select_cvterm_name' => array(
+      'parent' => 'views_handler_filter_string',
+     ),
+     
+     // Old Handlers
      'views_handler_field_node_optional' => array(
        'parent' => 'views_handler_field_node',
      ),
@@ -69,18 +86,78 @@ function tripal_core_views_handlers() {
      'views_handler_field_chado_count' => array(
       'parent' => 'views_handler_field',
      ),
-     'views_handler_filter_chado_select_string' => array(
-      'parent' => 'views_handler_filter_string',
-     ),
-     'views_handler_filter_chado_select_cvterm_name' => array(
-      'parent' => 'views_handler_filter_string',
-     ),
      'views_handler_filter_chado_boolean' => array(
       'parent' => 'views_handler_filter_boolean_operator',
      ),
      'views_handler_field_chado_rel_by_type' => array(
       'parent' => 'views_handler_field_prerender_list',
      ),
+
+     
+     // Wrappers for Default Views Handlers-----
+     // Field Handlers
+     'chado_views_handler_field' => array(
+      'parent' => 'views_handler_field'
+     ),
+     'chado_views_handler_field_boolean' => array(
+      'parent' => 'views_handler_field_boolean'
+     ),
+     'chado_views_handler_field_counter' => array(
+      'parent' => 'views_handler_field_counter'
+     ),
+     'chado_views_handler_field_custom' => array(
+      'parent' => 'views_handler_field_custom'
+     ),
+     'chado_views_handler_field_date' => array(
+      'parent' => 'views_handler_field_date'
+     ),
+     'chado_views_handler_field_markup' => array(
+      'parent' => 'views_handler_field_markup'
+     ),
+     'chado_views_handler_field_math' => array(
+      'parent' => 'views_handler_field_math'
+     ),
+     'chado_views_handler_field_numeric' => array(
+      'parent' => 'views_handler_field_numeric'
+     ),
+     // Filter Handlers
+     'chado_views_handler_filter_string' => array(
+      'parent' => 'views_handler_filter_string',
+     ), 
+     'chado_views_handler_filter_boolean_operator_string' => array(
+      'parent' => 'views_handler_filter_boolean_operator_string',
+     ),
+     'chado_views_handler_filter_boolean_operator' => array(
+      'parent' => 'views_handler_filter_boolean_operator',
+     ),
+     'chado_views_handler_filter_date' => array(
+      'parent' => 'views_handler_filter_date',
+     ),
+     'chado_views_handler_filter_equality' => array(
+      'parent' => 'views_handler_filter_equality',
+     ),
+     'chado_views_handler_filter_float' => array(
+      'parent' => 'views_handler_filter_float',
+     ),
+     'chado_views_handler_filter_numeric' => array(
+      'parent' => 'views_handler_filter_numeric',
+     ),
+     // Sort Handlers
+     'chado_views_handler_sort' => array(
+      'parent' => 'views_handler_sort'
+     ),     
+     'chado_views_handler_sort_date' => array(
+      'parent' => 'views_handler_sort_date'
+     ), 
+     'chado_views_handler_sort_formula' => array(
+      'parent' => 'views_handler_sort_formula'
+     ), 
+     'chado_views_handler_sort_menu_hierarchy' => array(
+      'parent' => 'views_handler_sort_menu_hierarchy'
+     ), 
+     'chado_views_handler_sort_random' => array(
+      'parent' => 'views_handler_sort_random'
+     ), 
    ),
  );
 }

+ 126 - 0
base/tripal_core/views/handlers/chado_views_handler_field.inc

@@ -0,0 +1,126 @@
+<?php
+
+class chado_views_handler_field extends views_handler_field {
+
+  /**
+   * Defines the defaults for the options form
+   */
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['type'] = array('default' => 'separator');
+    $options['separator'] = array('default' => ', ');
+
+    return $options;
+  }
+  
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    
+    $form['type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Display type'),
+      '#options' => array(
+        'ul' => t('Unordered list'),
+        'ol' => t('Ordered list'),
+        'separator' => t('Simple separator'),
+      ),
+      '#default_value' => $this->options['type'],
+    );
+
+    $form['separator'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Separator'),
+      '#default_value' => $this->options['separator'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('radio:options[type]' => array('separator')),
+    );
+  }
+  
+  /**
+   * Determines whether the current field is aggregated or not
+   * Note: The parent::query() takes care of adding the field to the query, etc.
+   */
+  function query () {
+    parent::query();
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+  }
+  
+  /**
+   * Splits the aggregated values up for use in rendering
+   */
+  function pre_render (&$values) {
+    
+    if ($this->aggregated) {
+      foreach($values as $k => $v) {
+        $values[$k]->{$this->field_alias} = $this->split_array_agg_results($v->{$this->field_alias});
+      }
+    }
+    
+  }
+  
+  /**
+   * Render the field.
+   *
+   * Note: Checks to see if we have an array or simple field. If we have an array, then
+   *   split it up and render each part using the parent render functionality.
+   *
+   * @param $values
+   *   The values retrieved from the database.
+   */
+  function render($values) {
+
+    // If it's aggregated (an array), then render each part 
+    // using the parent render functionality
+    if ($this->aggregated) {
+      $items = array();
+      
+      $parts = $values->{$this->field_alias};
+      foreach ($parts as $p) {
+        $v[ $this->field_alias ] = $p;
+        $val = (object) $v;
+        $items[] = parent::render($val);
+        unset($v, $val);
+      }
+      
+      if ($this->options['type'] == 'separator') {
+        return implode(check_plain($this->options['separator']), $items);
+      }
+      else {
+        return theme('item_list', $items, NULL, $this->options['type']);
+      }
+    
+    // Otherwise it is not aggragated
+    // Just render like the default handler would
+    } else {
+      return parent::render($values);
+    }
+    
+  }
+  
+  /**
+   * Splits an SQL array of results in a single field
+   * into a php array
+   *
+   * @param $field
+   *   An SQL array (ie: {"",^(.*)$,646,tripal_analysis_blast} )
+   * @return
+   *   A PHP version of the SQL array (ie: array('','^(.*)$','646','tripal_analysis_blast') )
+   */
+  function split_array_agg_results($field) {
+    if(preg_match('/^{(.*)}$/',$field, $matches)) {
+      return str_getcsv($matches[1]);
+    } else {
+      return array();
+    }
+  }
+}

+ 94 - 0
base/tripal_core/views/handlers/chado_views_handler_field_aggregate.inc

@@ -0,0 +1,94 @@
+<?php
+
+class chado_views_handler_field_aggregate extends chado_views_handler_field {
+  
+  function init(&$view, $options) {
+    parent::init($view, $options);
+    
+    if (!isset($this->chado_table_description)) {
+      $this->chado_table_description = module_invoke_all('chado_'.$this->table.'_schema');
+      foreach($this->chado_table_description['foreign keys'] as $defn) {
+        if ($defn['table'] != $this->view->base_table) {
+          $join_table = module_invoke_all('chado_'.$defn['table'].'_schema');
+          foreach ($join_table['fields'] as $fname => $f) {
+            $this->chado_table_description['fields'][$defn['table'] .'_'. $fname] = $f;
+          }
+        }
+      }
+    }
+    
+  }
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+
+    $form['format'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Format Output',
+      '#description' => t('The following fields specify how a single result of this field will be 
+        displayed. When there are multiple results of this field due to aggregation, each result
+        will be rendered according to the following rules and then all results will be joined
+        together based on the "Display Type" indicated.')
+    );
+
+    $this->tokens = array();
+    $value = array();
+    foreach( array_keys($this->chado_table_description['fields']) as $field ) {
+      $t = '[' . $this->options['id'] . '-' . $field . ']';
+      $this->tokens[$t] = t($field);
+      $value[] = $t . ' == ' . $field;
+    }
+    
+    $form['format']['format_string'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Format String'),
+      '#description' => 'Use any of the format tokens below to indicate what fields you want displayed.',
+      '#default_value' => ($this->options['format']['format_string']) ? $this->options['format']['format_string'] : implode(', ', array_keys($this->tokens)),
+    );
+    
+    $form['format']['tokens'] = array(
+      '#type' => 'item',
+      '#title' => 'Format Tokens',
+      '#value' => implode("<br />",$value),
+    );
+    
+  }
+  
+  function query() {
+    parent::query();
+    
+    $this->table_definition = $this->query->get_table_info($this->table);
+  }
+
+
+  function pre_render (&$values) {
+    
+    if ($this->aggregated) {
+      foreach($values as $k => $v) {
+        $values[$k]->{$this->field_alias} = $this->split_array_agg_results($v->{$this->field_alias});
+        
+        foreach($values[$k]->{$this->field_alias} as &$val) {
+          
+          // First, get the token values
+          $subparts = explode(',',$val);
+          $token_values = array();
+          foreach($subparts as $ssk => $ssv) {
+            if(preg_match('/(.*)::(.*)/',$ssv,$matches)) {
+              $token_values[ '[all-'.$matches[1].']' ] = $matches[2];
+            }
+          }
+          
+          // Now manually sub them in
+          $val = str_replace(array_keys($token_values),$token_values,$this->options['format']['format_string']);
+          
+        }
+      }
+
+    }
+    
+  }
+	
+}

+ 112 - 0
base/tripal_core/views/handlers/chado_views_handler_field_boolean.inc

@@ -0,0 +1,112 @@
+<?php
+
+class chado_views_handler_field_boolean extends views_handler_field_boolean {
+
+  /**
+   * Defines the defaults for the options form
+   */
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['type'] = array('default' => 'separator');
+    $options['separator'] = array('default' => ', ');
+
+    return $options;
+  }
+  
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $form['type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Display type'),
+      '#options' => array(
+        'ul' => t('Unordered list'),
+        'ol' => t('Ordered list'),
+        'separator' => t('Simple separator'),
+      ),
+      '#default_value' => $this->options['type'],
+    );
+
+    $form['separator'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Separator'),
+      '#default_value' => $this->options['separator'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('radio:options[type]' => array('separator')),
+    );
+  }
+  
+  /**
+   * Determines whether the current field is aggregated or not
+   * Note: The parent::query() takes care of adding the field to the query, etc.
+   */
+  function query () {
+    parent::query();
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+  }
+  
+  /**
+   * Render the field.
+   *
+   * Note: Checks to see if we have an array or simple field. If we have an array, then
+   *   split it up and render each part using the parent render functionality.
+   *
+   * @param $values
+   *   The values retrieved from the database.
+   */
+  function render($values) {
+    
+    // If it's aggregated (an array), then render each part 
+    // using the parent render functionality
+    if ($this->aggregated) {
+      $items = array();
+      
+      $parts = $this->split_array_agg_results($values->{$this->field_alias});
+      foreach ($parts as $p) {
+        $v[ $this->field_alias ] = $p;
+        $val = (object) $v;
+        $items[] = parent::render($val);
+        unset($v, $val);
+      }
+      
+      if ($this->options['type'] == 'separator') {
+        return implode(check_plain($this->options['separator']), $items);
+      }
+      else {
+        return theme('item_list', $items, NULL, $this->options['type']);
+      }
+    
+    // Otherwise it is not aggragated
+    // Just render like the default handler would
+    } else {
+      return parent::render($values);
+    }
+    
+  }
+  
+  /**
+   * Splits an SQL array of results in a single field
+   * into a php array
+   *
+   * @param $field
+   *   An SQL array (ie: {"",^(.*)$,646,tripal_analysis_blast} )
+   * @return
+   *   A PHP version of the SQL array (ie: array('','^(.*)$','646','tripal_analysis_blast') )
+   */
+  function split_array_agg_results($field) {
+    if(preg_match('/^{(.*)}$/',$field, $matches)) {
+      return str_getcsv($matches[1]);
+    } else {
+      return array();
+    }
+  }
+}

+ 112 - 0
base/tripal_core/views/handlers/chado_views_handler_field_counter.inc

@@ -0,0 +1,112 @@
+<?php
+
+class chado_views_handler_field_counter extends views_handler_field_counter {
+
+  /**
+   * Defines the defaults for the options form
+   */
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['type'] = array('default' => 'separator');
+    $options['separator'] = array('default' => ', ');
+
+    return $options;
+  }
+  
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $form['type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Display type'),
+      '#options' => array(
+        'ul' => t('Unordered list'),
+        'ol' => t('Ordered list'),
+        'separator' => t('Simple separator'),
+      ),
+      '#default_value' => $this->options['type'],
+    );
+
+    $form['separator'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Separator'),
+      '#default_value' => $this->options['separator'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('radio:options[type]' => array('separator')),
+    );
+  }
+  
+  /**
+   * Determines whether the current field is aggregated or not
+   * Note: The parent::query() takes care of adding the field to the query, etc.
+   */
+  function query () {
+    parent::query();
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+  }
+  
+  /**
+   * Render the field.
+   *
+   * Note: Checks to see if we have an array or simple field. If we have an array, then
+   *   split it up and render each part using the parent render functionality.
+   *
+   * @param $values
+   *   The values retrieved from the database.
+   */
+  function render($values) {
+    
+    // If it's aggregated (an array), then render each part 
+    // using the parent render functionality
+    if ($this->aggregated) {
+      $items = array();
+      
+      $parts = $this->split_array_agg_results($values->{$this->field_alias});
+      foreach ($parts as $p) {
+        $v[ $this->field_alias ] = $p;
+        $val = (object) $v;
+        $items[] = parent::render($val);
+        unset($v, $val);
+      }
+      
+      if ($this->options['type'] == 'separator') {
+        return implode(check_plain($this->options['separator']), $items);
+      }
+      else {
+        return theme('item_list', $items, NULL, $this->options['type']);
+      }
+    
+    // Otherwise it is not aggragated
+    // Just render like the default handler would
+    } else {
+      return parent::render($values);
+    }
+    
+  }
+  
+  /**
+   * Splits an SQL array of results in a single field
+   * into a php array
+   *
+   * @param $field
+   *   An SQL array (ie: {"",^(.*)$,646,tripal_analysis_blast} )
+   * @return
+   *   A PHP version of the SQL array (ie: array('','^(.*)$','646','tripal_analysis_blast') )
+   */
+  function split_array_agg_results($field) {
+    if(preg_match('/^{(.*)}$/',$field, $matches)) {
+      return str_getcsv($matches[1]);
+    } else {
+      return array();
+    }
+  }
+}

+ 112 - 0
base/tripal_core/views/handlers/chado_views_handler_field_custom.inc

@@ -0,0 +1,112 @@
+<?php
+
+class chado_views_handler_field_custom extends views_handler_field_custom {
+
+  /**
+   * Defines the defaults for the options form
+   */
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['type'] = array('default' => 'separator');
+    $options['separator'] = array('default' => ', ');
+
+    return $options;
+  }
+  
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $form['type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Display type'),
+      '#options' => array(
+        'ul' => t('Unordered list'),
+        'ol' => t('Ordered list'),
+        'separator' => t('Simple separator'),
+      ),
+      '#default_value' => $this->options['type'],
+    );
+
+    $form['separator'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Separator'),
+      '#default_value' => $this->options['separator'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('radio:options[type]' => array('separator')),
+    );
+  }
+  
+  /**
+   * Determines whether the current field is aggregated or not
+   * Note: The parent::query() takes care of adding the field to the query, etc.
+   */
+  function query () {
+    parent::query();
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+  }
+  
+  /**
+   * Render the field.
+   *
+   * Note: Checks to see if we have an array or simple field. If we have an array, then
+   *   split it up and render each part using the parent render functionality.
+   *
+   * @param $values
+   *   The values retrieved from the database.
+   */
+  function render($values) {
+    
+    // If it's aggregated (an array), then render each part 
+    // using the parent render functionality
+    if ($this->aggregated) {
+      $items = array();
+      
+      $parts = $this->split_array_agg_results($values->{$this->field_alias});
+      foreach ($parts as $p) {
+        $v[ $this->field_alias ] = $p;
+        $val = (object) $v;
+        $items[] = parent::render($val);
+        unset($v, $val);
+      }
+      
+      if ($this->options['type'] == 'separator') {
+        return implode(check_plain($this->options['separator']), $items);
+      }
+      else {
+        return theme('item_list', $items, NULL, $this->options['type']);
+      }
+    
+    // Otherwise it is not aggragated
+    // Just render like the default handler would
+    } else {
+      return parent::render($values);
+    }
+    
+  }
+  
+  /**
+   * Splits an SQL array of results in a single field
+   * into a php array
+   *
+   * @param $field
+   *   An SQL array (ie: {"",^(.*)$,646,tripal_analysis_blast} )
+   * @return
+   *   A PHP version of the SQL array (ie: array('','^(.*)$','646','tripal_analysis_blast') )
+   */
+  function split_array_agg_results($field) {
+    if(preg_match('/^{(.*)}$/',$field, $matches)) {
+      return str_getcsv($matches[1]);
+    } else {
+      return array();
+    }
+  }
+}

+ 112 - 0
base/tripal_core/views/handlers/chado_views_handler_field_date.inc

@@ -0,0 +1,112 @@
+<?php
+
+class chado_views_handler_field_date extends views_handler_field_date {
+
+  /**
+   * Defines the defaults for the options form
+   */
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['type'] = array('default' => 'separator');
+    $options['separator'] = array('default' => ', ');
+
+    return $options;
+  }
+  
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $form['type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Display type'),
+      '#options' => array(
+        'ul' => t('Unordered list'),
+        'ol' => t('Ordered list'),
+        'separator' => t('Simple separator'),
+      ),
+      '#default_value' => $this->options['type'],
+    );
+
+    $form['separator'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Separator'),
+      '#default_value' => $this->options['separator'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('radio:options[type]' => array('separator')),
+    );
+  }
+  
+  /**
+   * Determines whether the current field is aggregated or not
+   * Note: The parent::query() takes care of adding the field to the query, etc.
+   */
+  function query () {
+    parent::query();
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+  }
+  
+  /**
+   * Render the field.
+   *
+   * Note: Checks to see if we have an array or simple field. If we have an array, then
+   *   split it up and render each part using the parent render functionality.
+   *
+   * @param $values
+   *   The values retrieved from the database.
+   */
+  function render($values) {
+    
+    // If it's aggregated (an array), then render each part 
+    // using the parent render functionality
+    if ($this->aggregated) {
+      $items = array();
+      
+      $parts = $this->split_array_agg_results($values->{$this->field_alias});
+      foreach ($parts as $p) {
+        $v[ $this->field_alias ] = $p;
+        $val = (object) $v;
+        $items[] = parent::render($val);
+        unset($v, $val);
+      }
+      
+      if ($this->options['type'] == 'separator') {
+        return implode(check_plain($this->options['separator']), $items);
+      }
+      else {
+        return theme('item_list', $items, NULL, $this->options['type']);
+      }
+    
+    // Otherwise it is not aggragated
+    // Just render like the default handler would
+    } else {
+      return parent::render($values);
+    }
+    
+  }
+  
+  /**
+   * Splits an SQL array of results in a single field
+   * into a php array
+   *
+   * @param $field
+   *   An SQL array (ie: {"",^(.*)$,646,tripal_analysis_blast} )
+   * @return
+   *   A PHP version of the SQL array (ie: array('','^(.*)$','646','tripal_analysis_blast') )
+   */
+  function split_array_agg_results($field) {
+    if(preg_match('/^{(.*)}$/',$field, $matches)) {
+      return str_getcsv($matches[1]);
+    } else {
+      return array();
+    }
+  }
+}

+ 112 - 0
base/tripal_core/views/handlers/chado_views_handler_field_markup.inc

@@ -0,0 +1,112 @@
+<?php
+
+class chado_views_handler_field_markup extends views_handler_field_markup {
+
+  /**
+   * Defines the defaults for the options form
+   */
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['type'] = array('default' => 'separator');
+    $options['separator'] = array('default' => ', ');
+
+    return $options;
+  }
+  
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $form['type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Display type'),
+      '#options' => array(
+        'ul' => t('Unordered list'),
+        'ol' => t('Ordered list'),
+        'separator' => t('Simple separator'),
+      ),
+      '#default_value' => $this->options['type'],
+    );
+
+    $form['separator'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Separator'),
+      '#default_value' => $this->options['separator'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('radio:options[type]' => array('separator')),
+    );
+  }
+  
+  /**
+   * Determines whether the current field is aggregated or not
+   * Note: The parent::query() takes care of adding the field to the query, etc.
+   */
+  function query () {
+    parent::query();
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+  }
+  
+  /**
+   * Render the field.
+   *
+   * Note: Checks to see if we have an array or simple field. If we have an array, then
+   *   split it up and render each part using the parent render functionality.
+   *
+   * @param $values
+   *   The values retrieved from the database.
+   */
+  function render($values) {
+    
+    // If it's aggregated (an array), then render each part 
+    // using the parent render functionality
+    if ($this->aggregated) {
+      $items = array();
+      
+      $parts = $this->split_array_agg_results($values->{$this->field_alias});
+      foreach ($parts as $p) {
+        $v[ $this->field_alias ] = $p;
+        $val = (object) $v;
+        $items[] = parent::render($val);
+        unset($v, $val);
+      }
+      
+      if ($this->options['type'] == 'separator') {
+        return implode(check_plain($this->options['separator']), $items);
+      }
+      else {
+        return theme('item_list', $items, NULL, $this->options['type']);
+      }
+    
+    // Otherwise it is not aggragated
+    // Just render like the default handler would
+    } else {
+      return parent::render($values);
+    }
+    
+  }
+  
+  /**
+   * Splits an SQL array of results in a single field
+   * into a php array
+   *
+   * @param $field
+   *   An SQL array (ie: {"",^(.*)$,646,tripal_analysis_blast} )
+   * @return
+   *   A PHP version of the SQL array (ie: array('','^(.*)$','646','tripal_analysis_blast') )
+   */
+  function split_array_agg_results($field) {
+    if(preg_match('/^{(.*)}$/',$field, $matches)) {
+      return str_getcsv($matches[1]);
+    } else {
+      return array();
+    }
+  }
+}

+ 112 - 0
base/tripal_core/views/handlers/chado_views_handler_field_math.inc

@@ -0,0 +1,112 @@
+<?php
+
+class chado_views_handler_field_math extends views_handler_field_math {
+
+  /**
+   * Defines the defaults for the options form
+   */
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['type'] = array('default' => 'separator');
+    $options['separator'] = array('default' => ', ');
+
+    return $options;
+  }
+  
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $form['type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Display type'),
+      '#options' => array(
+        'ul' => t('Unordered list'),
+        'ol' => t('Ordered list'),
+        'separator' => t('Simple separator'),
+      ),
+      '#default_value' => $this->options['type'],
+    );
+
+    $form['separator'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Separator'),
+      '#default_value' => $this->options['separator'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('radio:options[type]' => array('separator')),
+    );
+  }
+  
+  /**
+   * Determines whether the current field is aggregated or not
+   * Note: The parent::query() takes care of adding the field to the query, etc.
+   */
+  function query () {
+    parent::query();
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+  }
+  
+  /**
+   * Render the field.
+   *
+   * Note: Checks to see if we have an array or simple field. If we have an array, then
+   *   split it up and render each part using the parent render functionality.
+   *
+   * @param $values
+   *   The values retrieved from the database.
+   */
+  function render($values) {
+    
+    // If it's aggregated (an array), then render each part 
+    // using the parent render functionality
+    if ($this->aggregated) {
+      $items = array();
+      
+      $parts = $this->split_array_agg_results($values->{$this->field_alias});
+      foreach ($parts as $p) {
+        $v[ $this->field_alias ] = $p;
+        $val = (object) $v;
+        $items[] = parent::render($val);
+        unset($v, $val);
+      }
+      
+      if ($this->options['type'] == 'separator') {
+        return implode(check_plain($this->options['separator']), $items);
+      }
+      else {
+        return theme('item_list', $items, NULL, $this->options['type']);
+      }
+    
+    // Otherwise it is not aggragated
+    // Just render like the default handler would
+    } else {
+      return parent::render($values);
+    }
+    
+  }
+  
+  /**
+   * Splits an SQL array of results in a single field
+   * into a php array
+   *
+   * @param $field
+   *   An SQL array (ie: {"",^(.*)$,646,tripal_analysis_blast} )
+   * @return
+   *   A PHP version of the SQL array (ie: array('','^(.*)$','646','tripal_analysis_blast') )
+   */
+  function split_array_agg_results($field) {
+    if(preg_match('/^{(.*)}$/',$field, $matches)) {
+      return str_getcsv($matches[1]);
+    } else {
+      return array();
+    }
+  }
+}

+ 112 - 0
base/tripal_core/views/handlers/chado_views_handler_field_numeric.inc

@@ -0,0 +1,112 @@
+<?php
+
+class chado_views_handler_field_numeric extends views_handler_field_numeric {
+
+  /**
+   * Defines the defaults for the options form
+   */
+  function option_definition() {
+    $options = parent::option_definition();
+
+    $options['type'] = array('default' => 'separator');
+    $options['separator'] = array('default' => ', ');
+
+    return $options;
+  }
+  
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+    $form['type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Display type'),
+      '#options' => array(
+        'ul' => t('Unordered list'),
+        'ol' => t('Ordered list'),
+        'separator' => t('Simple separator'),
+      ),
+      '#default_value' => $this->options['type'],
+    );
+
+    $form['separator'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Separator'),
+      '#default_value' => $this->options['separator'],
+      '#process' => array('views_process_dependency'),
+      '#dependency' => array('radio:options[type]' => array('separator')),
+    );
+  }
+  
+  /**
+   * Determines whether the current field is aggregated or not
+   * Note: The parent::query() takes care of adding the field to the query, etc.
+   */
+  function query () {
+    parent::query();
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+  }
+  
+  /**
+   * Render the field.
+   *
+   * Note: Checks to see if we have an array or simple field. If we have an array, then
+   *   split it up and render each part using the parent render functionality.
+   *
+   * @param $values
+   *   The values retrieved from the database.
+   */
+  function render($values) {
+    
+    // If it's aggregated (an array), then render each part 
+    // using the parent render functionality
+    if ($this->aggregated) {
+      $items = array();
+      
+      $parts = $this->split_array_agg_results($values->{$this->field_alias});
+      foreach ($parts as $p) {
+        $v[ $this->field_alias ] = $p;
+        $val = (object) $v;
+        $items[] = parent::render($val);
+        unset($v, $val);
+      }
+      
+      if ($this->options['type'] == 'separator') {
+        return implode(check_plain($this->options['separator']), $items);
+      }
+      else {
+        return theme('item_list', $items, NULL, $this->options['type']);
+      }
+    
+    // Otherwise it is not aggragated
+    // Just render like the default handler would
+    } else {
+      return parent::render($values);
+    }
+    
+  }
+  
+  /**
+   * Splits an SQL array of results in a single field
+   * into a php array
+   *
+   * @param $field
+   *   An SQL array (ie: {"",^(.*)$,646,tripal_analysis_blast} )
+   * @return
+   *   A PHP version of the SQL array (ie: array('','^(.*)$','646','tripal_analysis_blast') )
+   */
+  function split_array_agg_results($field) {
+    if(preg_match('/^{(.*)}$/',$field, $matches)) {
+      return str_getcsv($matches[1]);
+    } else {
+      return array();
+    }
+  }
+}

+ 75 - 0
base/tripal_core/views/handlers/chado_views_handler_filter_boolean_operator.inc

@@ -0,0 +1,75 @@
+<?php
+
+class chado_views_handler_filter_boolean_operator extends views_handler_filter_boolean_operator {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>If this filter applies to a table that is aggregated, additionally options may be ignored.</b>'
+    );
+    
+    parent::options_form($form, $form_state);
+    
+    $form['agg'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Apply to fields that are aggregated'
+    );
+    
+    $form['agg']['records_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter base table records'),
+      '#description' => t('Filters '.$this->view->base_table.' to only those with the value in the aggregate array.'),
+      '#default_value' => (isset($this->options['records_with'])) ? $this->options['records_with'] : TRUE,
+    );
+    
+    $form['agg']['aggregates_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter aggregates displayed'),
+      '#description' => t('Filters the aggregates shown based on the value. Doesn\'t affect the number of '.$this->view->base_table.' records.'),
+      '#default_value' => (isset($this->options['aggregates_with'])) ? $this->options['aggregates_with'] : TRUE,    
+    );
+    
+  }
+  
+  /**
+   * If the table to be filtered is not aggregated uses the parent::query()
+   * However, if it is uses postgresql any() function to compare
+   */
+  function query () {
+    $this->ensure_my_table();
+    
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+
+    if (!$this->aggregated) {
+      parent::query();
+    } else {
+
+      // Only base records with value in the aggregated field
+      // This doesn't restrict the items in the aggregate field
+      $this->ensure_my_table();
+      $field = "$this->table_alias.$this->real_field";
+      if ($this->options['agg']['records_with']) {
+        $where = "'%s' = ANY($field)";
+        $this->query->add_where($this->options['group'], $where, $this->value);
+      }
+
+      // To restrict the items in the aggregate...
+      // Tell the join handler about the filter 
+      // so it can be done in the join query
+      if ($this->options['agg']['aggregates_with']) {
+        $table['join']->filter[] = $field ." = '". $this->value."'";
+      }    
+    }
+    
+  }
+  
+}

+ 75 - 0
base/tripal_core/views/handlers/chado_views_handler_filter_boolean_operator_string.inc

@@ -0,0 +1,75 @@
+<?php
+
+class chado_views_handler_filter_boolean_operator_string extends views_handler_filter_boolean_operator_string {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>If this filter applies to a table that is aggregated, additionally options may be ignored.</b>'
+    );
+    
+    parent::options_form($form, $form_state);
+    
+    $form['agg'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Apply to fields that are aggregated'
+    );
+    
+    $form['agg']['records_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter base table records'),
+      '#description' => t('Filters '.$this->view->base_table.' to only those with the value in the aggregate array.'),
+      '#default_value' => (isset($this->options['records_with'])) ? $this->options['records_with'] : TRUE,
+    );
+    
+    $form['agg']['aggregates_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter aggregates displayed'),
+      '#description' => t('Filters the aggregates shown based on the value. Doesn\'t affect the number of '.$this->view->base_table.' records.'),
+      '#default_value' => (isset($this->options['aggregates_with'])) ? $this->options['aggregates_with'] : TRUE,    
+    );
+    
+  }
+  
+  /**
+   * If the table to be filtered is not aggregated uses the parent::query()
+   * However, if it is uses postgresql any() function to compare
+   */
+  function query () {
+    $this->ensure_my_table();
+    
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+
+    if (!$this->aggregated) {
+      parent::query();
+    } else {
+
+      // Only base records with value in the aggregated field
+      // This doesn't restrict the items in the aggregate field
+      $this->ensure_my_table();
+      $field = "$this->table_alias.$this->real_field";
+      if ($this->options['agg']['records_with']) {
+        $where = "'%s' = ANY($field)";
+        $this->query->add_where($this->options['group'], $where, $this->value);
+      }
+
+      // To restrict the items in the aggregate...
+      // Tell the join handler about the filter 
+      // so it can be done in the join query
+      if ($this->options['agg']['aggregates_with']) {
+        $table['join']->filter[] = $field ." = '". $this->value."'";
+      }    
+    }
+    
+  }
+  
+}

+ 75 - 0
base/tripal_core/views/handlers/chado_views_handler_filter_date.inc

@@ -0,0 +1,75 @@
+<?php
+
+class chado_views_handler_filter_date extends views_handler_filter_date {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>If this filter applies to a table that is aggregated, additionally options may be ignored.</b>'
+    );
+    
+    parent::options_form($form, $form_state);
+    
+    $form['agg'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Apply to fields that are aggregated'
+    );
+    
+    $form['agg']['records_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter base table records'),
+      '#description' => t('Filters '.$this->view->base_table.' to only those with the value in the aggregate array.'),
+      '#default_value' => (isset($this->options['records_with'])) ? $this->options['records_with'] : TRUE,
+    );
+    
+    $form['agg']['aggregates_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter aggregates displayed'),
+      '#description' => t('Filters the aggregates shown based on the value. Doesn\'t affect the number of '.$this->view->base_table.' records.'),
+      '#default_value' => (isset($this->options['aggregates_with'])) ? $this->options['aggregates_with'] : TRUE,    
+    );
+    
+  }
+  
+  /**
+   * If the table to be filtered is not aggregated uses the parent::query()
+   * However, if it is uses postgresql any() function to compare
+   */
+  function query () {
+    $this->ensure_my_table();
+    
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+
+    if (!$this->aggregated) {
+      parent::query();
+    } else {
+
+      // Only base records with value in the aggregated field
+      // This doesn't restrict the items in the aggregate field
+      $this->ensure_my_table();
+      $field = "$this->table_alias.$this->real_field";
+      if ($this->options['agg']['records_with']) {
+        $where = "'%s' = ANY($field)";
+        $this->query->add_where($this->options['group'], $where, $this->value);
+      }
+
+      // To restrict the items in the aggregate...
+      // Tell the join handler about the filter 
+      // so it can be done in the join query
+      if ($this->options['agg']['aggregates_with']) {
+        $table['join']->filter[] = $field ." = '". $this->value."'";
+      }    
+    }
+    
+  }
+  
+}

+ 75 - 0
base/tripal_core/views/handlers/chado_views_handler_filter_equality.inc

@@ -0,0 +1,75 @@
+<?php
+
+class chado_views_handler_filter_equality extends views_handler_filter_equality {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>If this filter applies to a table that is aggregated, additionally options may be ignored.</b>'
+    );
+    
+    parent::options_form($form, $form_state);
+    
+    $form['agg'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Apply to fields that are aggregated'
+    );
+    
+    $form['agg']['records_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter base table records'),
+      '#description' => t('Filters '.$this->view->base_table.' to only those with the value in the aggregate array.'),
+      '#default_value' => (isset($this->options['records_with'])) ? $this->options['records_with'] : TRUE,
+    );
+    
+    $form['agg']['aggregates_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter aggregates displayed'),
+      '#description' => t('Filters the aggregates shown based on the value. Doesn\'t affect the number of '.$this->view->base_table.' records.'),
+      '#default_value' => (isset($this->options['aggregates_with'])) ? $this->options['aggregates_with'] : TRUE,    
+    );
+    
+  }
+  
+  /**
+   * If the table to be filtered is not aggregated uses the parent::query()
+   * However, if it is uses postgresql any() function to compare
+   */
+  function query () {
+    $this->ensure_my_table();
+    
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+
+    if (!$this->aggregated) {
+      parent::query();
+    } else {
+
+      // Only base records with value in the aggregated field
+      // This doesn't restrict the items in the aggregate field
+      $this->ensure_my_table();
+      $field = "$this->table_alias.$this->real_field";
+      if ($this->options['agg']['records_with']) {
+        $where = "'%s' = ANY($field)";
+        $this->query->add_where($this->options['group'], $where, $this->value);
+      }
+
+      // To restrict the items in the aggregate...
+      // Tell the join handler about the filter 
+      // so it can be done in the join query
+      if ($this->options['agg']['aggregates_with']) {
+        $table['join']->filter[] = $field ." = '". $this->value."'";
+      }    
+    }
+    
+  }
+  
+}

+ 75 - 0
base/tripal_core/views/handlers/chado_views_handler_filter_float.inc

@@ -0,0 +1,75 @@
+<?php
+
+class chado_views_handler_filter_float extends views_handler_filter_float {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>If this filter applies to a table that is aggregated, additionally options may be ignored.</b>'
+    );
+    
+    parent::options_form($form, $form_state);
+    
+    $form['agg'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Apply to fields that are aggregated'
+    );
+    
+    $form['agg']['records_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter base table records'),
+      '#description' => t('Filters '.$this->view->base_table.' to only those with the value in the aggregate array.'),
+      '#default_value' => (isset($this->options['records_with'])) ? $this->options['records_with'] : TRUE,
+    );
+    
+    $form['agg']['aggregates_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter aggregates displayed'),
+      '#description' => t('Filters the aggregates shown based on the value. Doesn\'t affect the number of '.$this->view->base_table.' records.'),
+      '#default_value' => (isset($this->options['aggregates_with'])) ? $this->options['aggregates_with'] : TRUE,    
+    );
+    
+  }
+  
+  /**
+   * If the table to be filtered is not aggregated uses the parent::query()
+   * However, if it is uses postgresql any() function to compare
+   */
+  function query () {
+    $this->ensure_my_table();
+    
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+
+    if (!$this->aggregated) {
+      parent::query();
+    } else {
+
+      // Only base records with value in the aggregated field
+      // This doesn't restrict the items in the aggregate field
+      $this->ensure_my_table();
+      $field = "$this->table_alias.$this->real_field";
+      if ($this->options['agg']['records_with']) {
+        $where = "'%s' = ANY($field)";
+        $this->query->add_where($this->options['group'], $where, $this->value);
+      }
+
+      // To restrict the items in the aggregate...
+      // Tell the join handler about the filter 
+      // so it can be done in the join query
+      if ($this->options['agg']['aggregates_with']) {
+        $table['join']->filter[] = $field ." = '". $this->value."'";
+      }    
+    }
+    
+  }
+  
+}

+ 75 - 0
base/tripal_core/views/handlers/chado_views_handler_filter_numeric.inc

@@ -0,0 +1,75 @@
+<?php
+
+class chado_views_handler_filter_numeric extends views_handler_filter_numeric {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>If this filter applies to a table that is aggregated, additionally options may be ignored.</b>'
+    );
+    
+    parent::options_form($form, $form_state);
+    
+    $form['agg'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Apply to fields that are aggregated'
+    );
+    
+    $form['agg']['records_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter base table records'),
+      '#description' => t('Filters '.$this->view->base_table.' to only those with the value in the aggregate array.'),
+      '#default_value' => (isset($this->options['records_with'])) ? $this->options['records_with'] : TRUE,
+    );
+    
+    $form['agg']['aggregates_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter aggregates displayed'),
+      '#description' => t('Filters the aggregates shown based on the value. Doesn\'t affect the number of '.$this->view->base_table.' records.'),
+      '#default_value' => (isset($this->options['aggregates_with'])) ? $this->options['aggregates_with'] : TRUE,    
+    );
+    
+  }
+  
+  /**
+   * If the table to be filtered is not aggregated uses the parent::query()
+   * However, if it is uses postgresql any() function to compare
+   */
+  function query () {
+    $this->ensure_my_table();
+    
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+
+    if (!$this->aggregated) {
+      parent::query();
+    } else {
+
+      // Only base records with value in the aggregated field
+      // This doesn't restrict the items in the aggregate field
+      $this->ensure_my_table();
+      $field = "$this->table_alias.$this->real_field";
+      if ($this->options['agg']['records_with']) {
+        $where = "'%s' = ANY($field)";
+        $this->query->add_where($this->options['group'], $where, $this->value);
+      }
+
+      // To restrict the items in the aggregate...
+      // Tell the join handler about the filter 
+      // so it can be done in the join query
+      if ($this->options['agg']['aggregates_with']) {
+        $table['join']->filter[] = $field ." = '". $this->value."'";
+      }    
+    }
+    
+  }
+  
+}

+ 75 - 0
base/tripal_core/views/handlers/chado_views_handler_filter_string.inc

@@ -0,0 +1,75 @@
+<?php
+
+class chado_views_handler_filter_string extends views_handler_filter_string {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>If this filter applies to a table that is aggregated, additionally options may be ignored.</b>'
+    );
+    
+    parent::options_form($form, $form_state);
+    
+    $form['agg'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Apply to fields that are aggregated'
+    );
+    
+    $form['agg']['records_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter base table records'),
+      '#description' => t('Filters '.$this->view->base_table.' to only those with the value in the aggregate array.'),
+      '#default_value' => (isset($this->options['records_with'])) ? $this->options['records_with'] : TRUE,
+    );
+    
+    $form['agg']['aggregates_with'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Filter aggregates displayed'),
+      '#description' => t('Filters the aggregates shown based on the value. Doesn\'t affect the number of '.$this->view->base_table.' records.'),
+      '#default_value' => (isset($this->options['aggregates_with'])) ? $this->options['aggregates_with'] : TRUE,    
+    );
+    
+  }
+  
+  /**
+   * If the table to be filtered is not aggregated uses the parent::query()
+   * However, if it is uses postgresql any() function to compare
+   */
+  function query () {
+    $this->ensure_my_table();
+    
+    
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+
+    if (!$this->aggregated) {
+      parent::query();
+    } else {
+
+      // Only base records with value in the aggregated field
+      // This doesn't restrict the items in the aggregate field
+      $this->ensure_my_table();
+      $field = "$this->table_alias.$this->real_field";
+      if ($this->options['agg']['records_with']) {
+        $where = "'%s' = ANY($field)";
+        $this->query->add_where($this->options['group'], $where, $this->value);
+      }
+
+      // To restrict the items in the aggregate...
+      // Tell the join handler about the filter 
+      // so it can be done in the join query
+      if ($this->options['agg']['aggregates_with']) {
+        $table['join']->filter[] = $field ." = '". $this->value."'";
+      }    
+    }
+    
+  }
+  
+}

+ 49 - 0
base/tripal_core/views/handlers/chado_views_handler_sort.inc

@@ -0,0 +1,49 @@
+<?php
+
+class chado_views_handler_sort extends views_handler_sort {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>Sorting of aggregated fields only works for PostgreSQL 9.0+. This is due to lack of support at the database level. With lower postgreSQL versions, no sorting is applied.</b>'
+    );
+
+    parent::options_form($form, $form_state);
+  }
+  
+  /**
+   * Adds the sort to the query only if the field isn't aggregated
+   * If the field is aggregated then the sort has to be applied at the join handler level
+   */
+  function query () {
+    
+    // Determine if the current field is part of an aggregated table
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+    
+    // One day when the aggregated sort will work (ie: Postgresql 9.0+)
+    // it will need to be applied in join handler
+    // thus tell join handler about the sort
+    $table['join']->sort[] = array(
+      'table' => $table['alias'],
+      'field' => $this->options['field'],
+      'order' => $this->options['order']
+    );
+    
+    
+    // if not then add the sort
+    if (!$this->aggregated) {
+      parent::query();
+    }
+    
+  }
+  
+}

+ 49 - 0
base/tripal_core/views/handlers/chado_views_handler_sort_date.inc

@@ -0,0 +1,49 @@
+<?php
+
+class chado_views_handler_sort extends views_handler_sort_date {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>Sorting of aggregated fields only works for PostgreSQL 9.0+. This is due to lack of support at the database level. With lower postgreSQL versions, no sorting is applied.</b>'
+    );
+
+    parent::options_form($form, $form_state);
+  }
+  
+  /**
+   * Adds the sort to the query only if the field isn't aggregated
+   * If the field is aggregated then the sort has to be applied at the join handler level
+   */
+  function query () {
+    
+    // Determine if the current field is part of an aggregated table
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+    
+    // One day when the aggregated sort will work (ie: Postgresql 9.0+)
+    // it will need to be applied in join handler
+    // thus tell join handler about the sort
+    $table['join']->sort[] = array(
+      'table' => $table['alias'],
+      'field' => $this->options['field'],
+      'order' => $this->options['order']
+    );
+    
+    
+    // if not then add the sort
+    if (!$this->aggregated) {
+      parent::query();
+    }
+    
+  }
+  
+}

+ 49 - 0
base/tripal_core/views/handlers/chado_views_handler_sort_formula.inc

@@ -0,0 +1,49 @@
+<?php
+
+class chado_views_handler_sort extends views_handler_sort_formula {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>Sorting of aggregated fields only works for PostgreSQL 9.0+. This is due to lack of support at the database level. With lower postgreSQL versions, no sorting is applied.</b>'
+    );
+
+    parent::options_form($form, $form_state);
+  }
+  
+  /**
+   * Adds the sort to the query only if the field isn't aggregated
+   * If the field is aggregated then the sort has to be applied at the join handler level
+   */
+  function query () {
+    
+    // Determine if the current field is part of an aggregated table
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+    
+    // One day when the aggregated sort will work (ie: Postgresql 9.0+)
+    // it will need to be applied in join handler
+    // thus tell join handler about the sort
+    $table['join']->sort[] = array(
+      'table' => $table['alias'],
+      'field' => $this->options['field'],
+      'order' => $this->options['order']
+    );
+    
+    
+    // if not then add the sort
+    if (!$this->aggregated) {
+      parent::query();
+    }
+    
+  }
+  
+}

+ 49 - 0
base/tripal_core/views/handlers/chado_views_handler_sort_menu_hierarchy.inc

@@ -0,0 +1,49 @@
+<?php
+
+class chado_views_handler_sort extends views_handler_sort_menu_hierarchy {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>Sorting of aggregated fields only works for PostgreSQL 9.0+. This is due to lack of support at the database level. With lower postgreSQL versions, no sorting is applied.</b>'
+    );
+
+    parent::options_form($form, $form_state);
+  }
+  
+  /**
+   * Adds the sort to the query only if the field isn't aggregated
+   * If the field is aggregated then the sort has to be applied at the join handler level
+   */
+  function query () {
+    
+    // Determine if the current field is part of an aggregated table
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+    
+    // One day when the aggregated sort will work (ie: Postgresql 9.0+)
+    // it will need to be applied in join handler
+    // thus tell join handler about the sort
+    $table['join']->sort[] = array(
+      'table' => $table['alias'],
+      'field' => $this->options['field'],
+      'order' => $this->options['order']
+    );
+    
+    
+    // if not then add the sort
+    if (!$this->aggregated) {
+      parent::query();
+    }
+    
+  }
+  
+}

+ 49 - 0
base/tripal_core/views/handlers/chado_views_handler_sort_random.inc

@@ -0,0 +1,49 @@
+<?php
+
+class chado_views_handler_sort extends views_handler_sort_random {
+
+  /**
+   * Defines the options form (form available to admin when they add a field to a view)
+   */
+  function options_form(&$form, &$form_state) {
+    
+    $form['msg'] = array(
+      '#type' => 'item',
+      '#value' => '<b>Sorting of aggregated fields only works for PostgreSQL 9.0+. This is due to lack of support at the database level. With lower postgreSQL versions, no sorting is applied.</b>'
+    );
+
+    parent::options_form($form, $form_state);
+  }
+  
+  /**
+   * Adds the sort to the query only if the field isn't aggregated
+   * If the field is aggregated then the sort has to be applied at the join handler level
+   */
+  function query () {
+    
+    // Determine if the current field is part of an aggregated table
+    $table = $this->query->get_table_info($this->table);
+    if (preg_match('/aggregator/',$table['join']->definition['handler'])) {
+      $this->aggregated = TRUE;
+    } else {
+      $this->aggregated = FALSE;
+    }
+    
+    // One day when the aggregated sort will work (ie: Postgresql 9.0+)
+    // it will need to be applied in join handler
+    // thus tell join handler about the sort
+    $table['join']->sort[] = array(
+      'table' => $table['alias'],
+      'field' => $this->options['field'],
+      'order' => $this->options['order']
+    );
+    
+    
+    // if not then add the sort
+    if (!$this->aggregated) {
+      parent::query();
+    }
+    
+  }
+  
+}

+ 96 - 35
base/tripal_core/views/handlers/views_handler_filter_chado_select_cvterm_name.inc

@@ -18,34 +18,47 @@ class views_handler_filter_chado_select_cvterm_name extends views_handler_filter
   function init(&$view, $options) {
     parent::init($view, $options);
   	
-  	$cv_id = variable_get('chado_'.$this->view->base_table.'_cv', null);
-    if ($cv_id) {
-			$results = tripal_core_chado_select('cvterm',array('cvterm_id','name'), array('cv_id'=>$cv_id));				
-			foreach ($results as $c) {
-				$cvterms[$c->cvterm_id] = $c->name;
-			}    	
-    } else {
-    	//get a list of cvs currently used
-    	if ($this->view->base_table == 'cvterm') {
-      	$sql = 'SELECT distinct(cv.cv_id) FROM '.$this->view->base_table
-      		.' LEFT JOIN cv cv ON cv.cv_id=cvterm.cv_id';	
-    	} else {
-      	$sql = 'SELECT distinct(cv.cv_id) FROM '.$this->view->base_table
-      		.' LEFT JOIN cvterm cvterm ON cvterm.cvterm_id='.$this->view->base_table.'.type_id '
-      		.'LEFT JOIN cv cv ON cv.cv_id=cvterm.cv_id';
-      }
-    	$previous_db = tripal_db_set_active('chado');
-    	$resource = db_query($sql);
-    	tripal_db_set_active($previous_db);
-    	$cvterms = array();
-			while ( $r = db_fetch_object($resource) ) {
-				$results = tripal_core_chado_select('cvterm',array('cvterm_id','name'), array('cv_id'=>$r->cv_id));				
-				foreach ($results as $c) {
-					$cvterms[$c->cvterm_id] = $c->name;
-				}
-			}
-    }// end of if variable not defined
+  	if ($this->options['show_all']) {
+        $cv_id = variable_get('chado_'.$this->view->base_table.'_cv', null);
+        if ($cv_id) {
+          $results = tripal_core_chado_select('cvterm',array('cvterm_id','name'), array('cv_id'=>$cv_id));	
+          if (empty($results)) { $results = array(); }
+          foreach ($results as $c) {
+            $cvterms[$c->cvterm_id] = $c->name;
+          }    	
+        } else {
+          //get a list of cvs currently used
+          if ($this->view->base_table == 'cvterm') {
+            $sql = 'SELECT distinct(cv.cv_id) FROM '.$this->view->base_table
+              .' LEFT JOIN cv cv ON cv.cv_id=cvterm.cv_id';	
+          } else {
+            $sql = 'SELECT distinct(cv.cv_id) FROM '.$this->view->base_table
+              .' LEFT JOIN cvterm cvterm ON cvterm.cvterm_id='.$this->view->base_table.'.type_id '
+              .'LEFT JOIN cv cv ON cv.cv_id=cvterm.cv_id';
+          }
+          $previous_db = tripal_db_set_active('chado');
+          $resource = db_query($sql);
+          tripal_db_set_active($previous_db);
+          $cvterms = array();
+          while ( $r = db_fetch_object($resource) ) {
+            $results = tripal_core_chado_select('cvterm',array('cvterm_id','name'), array('cv_id'=>$r->cv_id));	
+            if (empty($results)) { $results = array(); }
+            foreach ($results as $c) {
+              $cvterms[$c->cvterm_id] = $c->name;
+            }
+          }
+        }// end of if variable not defined
     
+    } else {
+        $sql = "SELECT cvterm_id, name FROM cvterm WHERE cvterm_id IN (SELECT distinct(type_id) FROM %s)";
+        $previous_db = tripal_db_set_active('chado');
+        $resource = db_query($sql, $this->view->base_table);
+        tripal_db_set_active($previous_db);
+        $cvterms = array();
+        while ( $r = db_fetch_object($resource) ) {
+          $cvterms[$r->cvterm_id] = $r->name;
+        }        
+    }
     //sort cvterms by name (case insensitive)
     natcasesort($cvterms);
     
@@ -68,6 +81,26 @@ class views_handler_filter_chado_select_cvterm_name extends views_handler_filter
       ),
       '#default_value' => $this->options['values_form_type'],
     );
+
+    $form['multiple'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Select Multiple'),
+      '#description' => t('Allows more then one option to be selected.'),
+      '#default_value' => (isset($this->options['multiple'])) ? $this->options['multiple'] : FALSE,
+    );
+
+    $form['optional'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Optional'),
+      '#description' => t('Adds --Any-- to the available options.'),
+      '#default_value' => (isset($this->options['optional'])) ? $this->options['optional'] : TRUE,
+    );
+    
+    $form['show_all'] = array(
+      '#type' => 'checkbox',
+      '#title' => 'Show All Terms',
+      '#description' => 'Otherwise only cvterms used in the base table will be used'
+    );
   }
   
  /**
@@ -76,13 +109,32 @@ class views_handler_filter_chado_select_cvterm_name extends views_handler_filter
   function query() {
     $this->ensure_table;
     
-    if (preg_match('/^\d+$/', $this->value)) {
-      $where = 'cvterm.cvterm_id=%d';
+    if ($this->options['multiple']) {
+      // Remove any if it's there
+      unset($this->value['All']);
+      
+      if (sizeof($this->value)) {
+        $holders = array();
+        foreach ($this->value as $v) {
+          if (preg_match('/^[\d\.]+$/',$v)) {
+            $holders[] = '%d';
+          } else {
+            $holders[] = "'%s'";
+          }
+        }
+        $where = "cvterm.cvterm_id IN (".implode(", ",$holders).")";
+      }
     } else {
-      $where = "cvterm.name" . $this->operator . "'%s'";
+      if (preg_match('/^\d+$/', $this->value)) {
+        $where = 'cvterm.cvterm_id=%d';
+      } else {
+        $where = "cvterm.name" . $this->operator . "'%s'";
+      }
+    }
+    
+    if ($where) {
+      $this->query->add_where($this->options['group'], $where, $this->value);
     }
-
-    $this->query->add_where($this->options['group'], $where, $this->value);
   }
   
  /**
@@ -94,10 +146,10 @@ class views_handler_filter_chado_select_cvterm_name extends views_handler_filter
     
     if (preg_match('/select/', $this->options['values_form_type'])) {
       // Get Options
-      if ($this->options['exposed']) {    
-        $options['All'] = '<Any>';
+      if ($this->options['optional']) {
+        $options['<select '.$this->table.'>'] = '--None--';
+        $options['All'] = '--Any--';
       }
-      $options['<select '.$this->table.'>'] = '<None>';
       $max_length = 40;
       foreach ($this->cvterm_options as $cvterm_id => $cvterm_name) {
         if (strlen($cvterm_name) > $max_length) {
@@ -107,6 +159,10 @@ class views_handler_filter_chado_select_cvterm_name extends views_handler_filter
         }
       }
       
+      if (empty($options)) {
+        $options[0] = '';
+      }
+      
       //Select List
       $form['value'] = array(
           '#type' => 'select',
@@ -114,6 +170,11 @@ class views_handler_filter_chado_select_cvterm_name extends views_handler_filter
           '#options' => $options,
           '#default_value' => $this->value,
       );
+      
+      if ($this->options['multiple']) {
+        $form['value']['#multiple'] = TRUE;
+      }
+    
     } else {
       $form['value'] = array(
         '#type' => 'textfield',

+ 92 - 31
base/tripal_core/views/handlers/views_handler_filter_chado_select_string.inc

@@ -9,6 +9,35 @@
  */
 class views_handler_filter_chado_select_string extends views_handler_filter_string {
 
+  function options_form(&$form, &$form_state) {
+    parent::options_form($form, $form_state);
+
+    $form['values_form_type'] = array(
+      '#type' => 'radios',
+      '#title' => 'Filter Type',
+      '#options' => array(
+        'textfield' => 'Text Field',
+        'select' => 'Drop-Down Box',
+      ),
+      '#default_value' => ($this->options['values_form_type']) ? $this->options['values_form_type'] : 'select',
+    );
+    
+    $form['multiple'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Select Multiple'),
+      '#description' => t('Allows more then one option to be selected.'),
+      '#default_value' => (isset($this->options['multiple'])) ? $this->options['multiple'] : FALSE,
+    );
+
+    $form['optional'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Optional'),
+      '#description' => t('Adds --Any-- to the available options.'),
+      '#default_value' => (isset($this->options['optional'])) ? $this->options['optional'] : TRUE,
+    );
+    
+  }
+  
  /**
   * Defines the value field in both the views filter options form
   *   and the exposed form
@@ -16,32 +45,44 @@ class views_handler_filter_chado_select_string extends views_handler_filter_stri
   function value_form(&$form, &$form_state) {
     parent::value_form($form, $form_state);
     
-    // Get Options
-    if ($this->options['exposed']) {    
-      $options['All'] = '<Any>';
-    }
-    $options['<select '.$this->table.'>'] = '<None>';
-    $results = tripal_core_chado_select(
-      $this->table,
-      array($this->field),
-      array()
-    );
-    $max_length = 40;
-    foreach ($results as $r) {
-      if (strlen($r->{$this->field}) > $max_length) {
-        $options[$r->{$this->field}] = substr($r->{$this->field},0,$max_length) . '...';
-      } else {
-        $options[$r->{$this->field}] = $r->{$this->field};    
-      }
-    }
-    
-    //Select List
-    $form['value'] = array(
-        '#type' => 'select',
+    if (preg_match('/textfield/', $this->options['values_form_type'])) {
+      $form['value'] = array(
+        '#type' => 'textfield',
         '#title' => $this->options['label'],
-        '#options' => $options,
         '#default_value' => $this->value,
-    );
+      );    
+    } else {
+      // Get Options
+      if ($this->options['optional']) {
+        $options['<select '.$this->table.'>'] = '--None--';
+        $options['All'] = '--Any--';
+      }
+      $results = tripal_core_chado_select(
+        $this->table,
+        array($this->field),
+        array()
+      );
+      $max_length = 40;
+      foreach ($results as $r) {
+        if (strlen($r->{$this->field}) > $max_length) {
+          $options[$r->{$this->field}] = substr($r->{$this->field},0,$max_length) . '...';
+        } else {
+          $options[$r->{$this->field}] = $r->{$this->field};    
+        }
+      }
+      
+      //Select List
+      $form['value'] = array(
+          '#type' => 'select',
+          '#title' => $this->options['label'],
+          '#options' => $options,
+          '#default_value' => $this->value,
+      );
+      
+      if ($this->options['multiple']) {
+        $form['value']['#multiple'] = TRUE;
+      }
+    }
   }
 
  /**
@@ -80,17 +121,37 @@ class views_handler_filter_chado_select_string extends views_handler_filter_stri
   *
   */
   function query() {
+  
     $this->ensure_my_table();
     $field = "$this->table_alias.$this->real_field";
     $upper = $this->case_transform();
-
-    // Deal with All/Any as value
-    if (preg_match('/All/', $this->value)) {
-      // Don't do anything    
+      
+    if ($this->options['multiple']) {
+      // Remove any if it's there
+      unset($this->value['All']);
+      
+      if (sizeof($this->value)) {
+        $holders = array();
+        foreach ($this->value as $v) {
+          if (preg_match('/^[\d\.]+$/',$v)) {
+            $holders[] = '%d';
+          } else {
+            $holders[] = "'%s'";
+          }
+        }
+        $where = "$field IN (".implode(", ",$holders).")";
+        $this->query->add_where($this->options['group'], $where, $this->value);
+      }
     } else {
-      $info = $this->operators();
-      if (!empty($info[$this->operator]['method'])) {
-        $this->{$info[$this->operator]['method']}($field, $upper);
+  
+      // Deal with All/Any as value
+      if (preg_match('/All/', $this->value)) {
+        // Don't do anything    
+      } else {
+        $info = $this->operators();
+        if (!empty($info[$this->operator]['method'])) {
+          $this->{$info[$this->operator]['method']}($field, $upper);
+        }
       }
     }
   }

+ 168 - 0
base/tripal_core/views/handlers/views_handler_join_chado_aggregator.inc

@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * Handler to allow joins between records via a linking table
+ *
+ * Example Usage:
+ *   To join the analysisprop table to the analysis table,
+ *   Use the following code in the analysisprop hook_views_data:
+ *   @code
+        $data['analysisprop']['table']['join']['analysis'] = array(
+          'left_field' => 'analysis_id',
+          'field' => 'analysis_id',
+          'handler' => 'views_handler_join_chado_aggregator'
+        );
+ *   @endcode
+ */
+class views_handler_join_chado_aggregator extends views_join {
+
+  // PHP 4 doesn't call constructors of the base class automatically from a
+  // constructor of a derived class. It is your responsibility to propagate
+  // the call to constructors upstream where appropriate.
+  function construct($table = NULL, $left_table = NULL, $left_field = NULL, $field = NULL, $extra = array(), $type = 'LEFT', $added = NULL) {
+    parent::construct($table, $left_table, $left_field, $field, $extra, $type);
+    
+    // Determine the postgresql version
+    $postgresql_version = pg_version();
+    $this->postgresql_version = $postgresql_version['client'];
+
+    // If version is 9.0+ then indicate
+    // Needed to apply sorting for aggregated fields
+    if (intval($postgresql_version['client']) >= 9) {
+      $this->postgresql_9up = TRUE;
+    }
+
+  }
+
+  /**
+   * Creates SQL including aggregation query used in join
+   */
+  function join($table, &$query) {
+    $output = array();
+
+    // Create the table SQL (used in join) -------
+    // query creating one-to-one table using array_agg
+    $table_desc = module_invoke_all('chado_'.$this->definition['table'].'_schema');
+    $select_fields[ $this->definition['table'] ] = $table_desc['fields'];
+    
+    // Add joins to tables with a foreign key in this table
+    // (ie: add join to cvterm if this table has a type_id
+    $joins = array();
+    foreach($table_desc['foreign keys'] as $defn) {
+      if ($defn['table'] != $this->left_table) {
+        foreach( $defn['columns'] as $left => $right) {
+          $left = $this->definition['table'] .'.'. $left;
+          $right = $defn[table] .'.'. $right;
+          $joins[] = "LEFT JOIN $defn[table] $defn[table] ON $left=$right";
+        }
+        
+        // Fields to be selected from joined table
+        $join_table = module_invoke_all('chado_'.$defn['table'].'_schema');
+        $select_fields[ $defn['table'] ] = $join_table['fields'];
+      } 
+    }
+    
+    // Determine Order BY's for aggregates
+    $order_by = array();
+    foreach ($this->sort as $s) {
+      $order_by[] = $s['table'].'.'.$s['field'].' '.$s['order'];
+    }
+    
+    
+    // Fields to be selected
+    foreach ($select_fields as $table => $table_fields) {
+      foreach ($table_fields as $fname => $f) {
+        $alias = '';
+        if ($table != $this->definition['table']) {
+          $alias = $table .'_';
+        }
+        
+        if ($fname != $this->definition['field']) {
+          // Add sort to aggregate field if postgreSQL 9.0+
+          if ($this->postgresql_9up && !empty($order_by)) {
+            $fields[] = 'array_agg('.$table.'.'.$fname.' ORDER BY '.implode(',',$order_by).') as '.$alias.$fname;
+          } else {
+            $fields[] = 'array_agg('.$table.'.'.$fname.') as '.$alias.$fname;
+          }
+          $composite_field_parts[] = "'".$alias.$fname."::' ||".$table.'.'.$fname;
+        } else {
+          $fields[] = $fname;
+          $composite_field_parts[] = "'".$alias.$fname."::' ||".$table.'.'.$fname;
+        }
+      }
+    }
+    
+    // composite field 
+    // (combines all other fields before aggregating)
+    // Add sort to aggregate field if postgreSQL 9.0+
+    if ($this->postgresql_9up && !empty($order_by)) {
+      $composite_field = "array_agg('{'||".implode(" || ',' || ",$composite_field_parts)."||'}' ORDER BY ".implode(',',$order_by).") as all";
+    } else {
+      $composite_field = "array_agg('{'||".implode(" || ',' || ",$composite_field_parts)."||'}') as all";
+    }
+    $fields[] = $composite_field;
+    
+    // SQL to use in the join
+    $sql = 'SELECT '.implode(', ',$fields)
+      .' FROM '.$this->definition['table']
+      .' '.implode(' ',$joins);
+ 
+    if (!empty($this->filter)) {
+      $sql .= ' WHERE '.implode(', ', $this->filter);
+    }
+    
+    $sql .= ' GROUP BY '.$this->definition['field'];
+    
+    // Create the join (full SQL) ----------------
+    $output[] = $this->create_single_join(
+      $query,
+      array(
+        'table' => $this->definition['table'],
+        'field' => $this->definition['field'],
+        'table_sql' => $sql,
+        'is_drupal' => FALSE,
+      ),
+      array(
+        'table' => $this->definition['left_table'],
+        'field' => $this->definition['left_field'],
+      ),
+      'LEFT'
+    );
+    
+    return implode("\n",$output);
+  }
+  
+  /**
+   * Creates SQL for a single join based on parameters
+   * Join will be: <type> JOIN (<query creating one-to-one table using array_agg>) <table alias> 
+   *                  ON <qualified left field>=<qualified right field>
+   */
+  function create_single_join (&$query, $right_spec, $left_spec, $join_type) {
+    
+    if ($right_spec['table']) {
+      $right = $query->get_table_info($right_spec['table']);
+      if (!$right['alias']) { $right['alias'] = $right_spec['table']; }
+      $right_field = "$right[alias].$right_spec[field]";
+
+      if ($right_spec['is_drupal']) {
+        $right_table = '{'.$right_spec['table'].'}';
+      } else {
+        $right_table = $right_spec['table'];
+      }
+    }   
+
+    if ($left_spec['table']) {
+      $left = $query->get_table_info($left_spec['table']);
+      if (!$left['alias']) { $left['alias'] = $left_spec['table']; }
+      $left_field = "$left[alias].$left_spec[field]";
+    } else {
+      // This can be used if left_field is a formula or something. It should be used only *very* rarely.
+      $left_field = $this->left_spec['field'];
+    }
+    
+    $output = " $join_type JOIN ($right_spec[table_sql]) $right[alias] ON $left_field = $right_field";
+     
+    return $output;
+  }
+ 
+}

+ 112 - 0
base/tripal_core/views/handlers/views_handler_join_chado_through_linking.inc

@@ -0,0 +1,112 @@
+<?php
+
+/**
+ * Handler to allow joins between records via a linking table
+ *
+ * Example Usage:
+ *   To join the analysis table to the feature table through the analysisfeature table,
+ *   (ie: get analysis fields to show up in a feature view)
+ *   Use the following code in the analysis hook_views_data:
+ *   @code
+        $data['analysis']['table']['join']['feature'] = array(
+          'linking' => array(
+            'table' => 'analysisfeature',
+            'left_field' => 'feature_id',
+            'field' => 'analysis_id',
+          ),
+          'left_field' => 'feature_id',
+          'field' => 'analysis_id',
+          'handler' => 'views_handler_join_chado_through_linking'
+        ); 
+ *   @endcode
+ *
+ * NOTE: If the right table is in the drupal schema rather then the chado schema 
+ *  (ie: node, chado_feature) then add the following to the above join description:
+ *  @code
+    'table_is_drupal' => TRUE
+ *  @endcode
+ *  This will ensure the drupal table is surrounded by { } and as such any database
+ *  prefixes are added correctly. If the left table is in the drupal schema it should already
+ *  be defined by a previous join (or the From clause).
+ */
+class views_handler_join_chado_through_linking extends views_join {
+
+  // PHP 4 doesn't call constructors of the base class automatically from a
+  // constructor of a derived class. It is your responsibility to propagate
+  // the call to constructors upstream where appropriate.
+  function construct($table = NULL, $left_table = NULL, $left_field = NULL, $field = NULL, $extra = array(), $type = 'LEFT', $added = NULL) {
+    parent::construct($table, $left_table, $left_field, $field, $extra, $type);
+  }
+  
+  /**
+   * Creates SQL for both joins table => linking_table and linking_table => left_table
+   * NOTE: Uses fields in definition as passed in from hook_views_data join definition
+   */
+  function join($table, &$query) {
+    $output = '';
+    $joins = array();
+
+    $joins[] = $this->create_single_join(
+      $query,
+      array(
+        'table' => $this->definition['linking']['table'],
+        'field' => $this->definition['linking']['left_field'],
+        'is_drupal' => FALSE,
+      ),
+      array(
+        'table' => $this->definition['left_table'],
+        'field' => $this->definition['left_field'],
+      ),
+      'LEFT'
+    );
+    
+    $joins[] = $this->create_single_join(
+      $query,
+      array(
+        'table' => $this->definition['table'],
+        'field' => $this->definition['field'],
+        'is_drupal' => $this->definition['table_is_drupal'],
+      ),
+      array(
+        'table' => $this->definition['linking']['table'],
+        'field' => $this->definition['linking']['field'],
+      ),
+      'LEFT'
+    );
+    
+    $output .= implode("\n",$joins);
+    return $output;
+  }
+  
+  /**
+   * Creates SQL for a single join based on parameters
+   */
+  function create_single_join (&$query, $right_spec, $left_spec, $join_type) {
+    
+    if ($right_spec['table']) {
+      $right = $query->get_table_info($right_spec['table']);
+      if (!$right['alias']) { $right['alias'] = $right_spec['table']; }
+      $right_field = "$right[alias].$right_spec[field]";
+
+      if ($right_spec['is_drupal']) {
+        $right_table = '{'.$right_spec['table'].'}';
+      } else {
+        $right_table = $right_spec['table'];
+      }
+    }   
+
+    if ($left_spec['table']) {
+      $left = $query->get_table_info($left_spec['table']);
+      if (!$left['alias']) { $left['alias'] = $left_spec['table']; }
+      $left_field = "$left[alias].$left_spec[field]";
+    } else {
+      // This can be used if left_field is a formula or something. It should be used only *very* rarely.
+      $left_field = $this->left_spec['field'];
+    }
+    
+    $output = " $join_type JOIN $right_table $right[alias] ON $left_field = $right_field";
+     
+    return $output;
+  }
+ 
+}

+ 0 - 64
base/tripal_organism/views/handlers/views_handler_filter_organism_common_name.inc

@@ -1,64 +0,0 @@
-<?php
-
-class views_handler_filter_organism_common_name extends views_handler_filter_string {
-
-  function value_form(&$form, &$form_state) {
-    parent::value_form($form, $form_state);
-
-    // Get Options
-    $options['<select organism>'] = '<None>';
-    if ($this->options['exposed']) {
-      $options['All'] = '<Any>';
-    }
-    $results = tripal_core_chado_select(
-      'organism',
-      array('common_name','abbreviation'),
-      array()
-    );
-    foreach ($results as $r) {
-      $options[$r->common_name] = $r->common_name . ' ('.$r->abbreviation.')';    
-    }
-    
-    //Select List
-    $form['value'] = array(
-        '#type' => 'select',
-        '#title' => $this->options['label'],
-        '#options' => $options,
-        '#default_value' => $this->value,
-    );
-    
-
-  }
-
- /**
-  * Render our chunk of the exposed filter form when selecting
-  */
-  function exposed_form(&$form, &$form_state) {
-    if (empty($this->options['exposed'])) {
-      return;
-    }
-
-    $value = $this->options['expose']['identifier'];
-    $this->value_form($form, $form_state);
-    $form[$value] = $form['value'];
-
-    if (isset($form[$value]['#title']) && !empty($form[$value]['#type']) && $form[$value]['#type'] != 'checkbox') {
-      unset($form[$value]['#title']);
-    }
-
-    $this->exposed_translate($form[$value], 'value');
-
-    if (!empty($form['#type']) && ($form['#type'] == 'checkboxes' || ($form['#type'] == 'select' && !empty($form['#multiple'])))) {
-      unset($form[$value]['#default_value']);
-    }
-
-    if (!empty($form['#type']) && $form['#type'] == 'select' && empty($form['#multiple'])) {
-      $form[$value]['#default_value'] = 'All';
-    }
-
-    if ($value != 'value') {
-      unset($form['value']);
-    }
-
-  }
-}

+ 1 - 1
base/tripal_organism/views/organism.views.inc

@@ -186,7 +186,7 @@ function retrieve_organism_views_data() {
        'handler' => 'views_handler_sort',
      ),
      'filter' => array(
-       'handler' => 'views_handler_filter_organism_common_name',
+       'handler' => 'views_handler_filter_chado_select_string',
      ),
      'argument' => array(
        'handler' => 'views_handler_argument_string',

+ 9 - 2
extensions/tripal_analysis_blast/includes/parse_blast_XML.inc

@@ -603,9 +603,16 @@ function tripal_analysis_blast_get_result_object($xml_string,$db,$max,$feature_i
 			$percent_identity = number_format($best_identity/$best_len*100, 2);
 			$hits_array[$hit_count]['percent_identity'] = $percent_identity;
 		}
-		
+
 		$hits_array[$hit_count]['description'] = $description;
-		$hits_array[$hit_count]['hsp'] = $hsp_array;
+		
+		// if there is at least one HSP
+		if (!empty($hsp_array[0]['query_frame'])) {
+		  $hits_array[$hit_count]['hsp'] = $hsp_array;
+		} else {
+		  $hits_array[$hit_count]['hsp'] = array();
+		}
+		
 		$hit_count ++;
 		
 		// if we've hit the maximum number of hits then return

+ 7 - 7
extensions/tripal_analysis_blast/theme/tripal_feature/tripal_feature_blast_results.tpl.php

@@ -84,7 +84,7 @@ if(count($blast_results_list) > 0){
 			   <td nowrap><?php  if (!empty($hit['percent_identity'])) { print $hit['percent_identity']; } ?></td>
 			   <td><?php print $hit['description']?></td>
 		   </tr>
-		   
+   
 		   <!-- If there is alignment information for at least one HSP -->
 		   <?php if (!empty($hit['hsp'][0]['query_frame'])) { ?>
 	      <tr class="<?php print $class ?>">
@@ -102,14 +102,14 @@ if(count($blast_results_list) > 0){
 				      &nbsp;HSP <?php  print $hsp['hsp_num'] ?>
 				      <pre>Score: <?php print $hsp['bit_score'] ?> bits (<?php print $hsp['score'] ?>), Expect = <?php print $hsp['evalue'] ?><br>Identity = <?php print sprintf("%d/%d (%.2f%%)", $hsp['identity'], $hsp['align_len'], $hsp['identity']/$hsp['align_len']*100) ?>, Postives = <?php print sprintf("%d/%d (%.2f%%)", $hsp['positive'], $hsp['align_len'], $hsp['positive']/$hsp['align_len']*100)?>, Query Frame = <?php print $hsp['query_frame']?></a><br><br></a>Query: <?php print sprintf("%4d", $hsp['query_from'])?> <?php print $hsp['qseq'] ?> <?php print sprintf("%d", $hsp['query_to']); ?><br>            <?php print $hsp['midline'] ?><br>Sbjct: <?php print sprintf("%4d", $hsp['hit_from']) ?> <?php print $hsp['hseq']?> <?php print sprintf("%d",$hsp['hit_to']) ?></pre><br>
 			      </div>
-		      <?php } ?>
+		      <?php } //end of foreach hsp ?>
 		      </td>
 	      </tr>		
-         <?php }
-         $i++;
-	   } ?>
+         <?php } //end of if there is query sequence for at least one hsp
+         $i++;  
+     } //end of foreach hit ?>
 	</table>
 </div>
 </div>
-  <?php } ?>
-<?php } ?>
+  <?php } // end of foreach blast result ?>
+<?php } //end of if there are blast results ?>