소스 검색

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 년 전
부모
커밋
9c6e1277f9
31개의 변경된 파일2413개의 추가작업 그리고 176개의 파일을 삭제
  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 ?>