Răsfoiți Sursa

Merge branch '7.x-2.x' into entities

Stephen Ficklin 9 ani în urmă
părinte
comite
981c0ca6f4
100 a modificat fișierele cu 3154 adăugiri și 1746 ștergeri
  1. 43 1
      tripal_analysis/api/tripal_analysis.api.inc
  2. 18 6
      tripal_analysis/includes/tripal_analysis.chado_node.inc
  3. 1 1
      tripal_analysis/tripal_analysis.info
  4. 1 4
      tripal_analysis/tripal_analysis.install
  5. 9 1
      tripal_analysis/tripal_analysis.views_default.inc
  6. 163 0
      tripal_bulk_loader/api/tripal_bulk_loader.api.templates.inc
  7. 47 17
      tripal_bulk_loader/includes/tripal_bulk_loader.admin.templates.inc
  8. 15 5
      tripal_bulk_loader/includes/tripal_bulk_loader.loader.inc
  9. 1 1
      tripal_bulk_loader/tripal_bulk_loader.info
  10. 32 0
      tripal_contact/api/tripal_contact.api.inc
  11. 5 7
      tripal_contact/includes/tripal_contact.chado_node.inc
  12. 1 1
      tripal_contact/tripal_contact.info
  13. 1 4
      tripal_contact/tripal_contact.install
  14. 7 0
      tripal_contact/tripal_contact.module
  15. 9 1
      tripal_contact/tripal_contact.views_default.inc
  16. 8 3
      tripal_core/api/tripal_core.chado_general.api.inc
  17. 386 169
      tripal_core/api/tripal_core.chado_nodes.api.inc
  18. 28 14
      tripal_core/api/tripal_core.chado_nodes.relationships.api.inc
  19. 5 10
      tripal_core/api/tripal_core.chado_nodes.title_and_path.inc
  20. 6 5
      tripal_core/api/tripal_core.chado_query.api.inc
  21. 53 0
      tripal_core/api/tripal_core.d3js.api.inc
  22. 25 8
      tripal_core/api/tripal_core.files.api.inc
  23. 6 1
      tripal_core/api/tripal_core.jobs.api.inc
  24. 2 1
      tripal_core/api/tripal_core.mviews.api.inc
  25. 19 13
      tripal_core/includes/tripal_core.extensions.inc
  26. 11 4
      tripal_core/includes/tripal_core.jobs.inc
  27. 1 0
      tripal_core/includes/tripal_core.mviews.inc
  28. 60 23
      tripal_core/tripal_core.drush.inc
  29. 2 2
      tripal_core/tripal_core.info
  30. 2 12
      tripal_core/tripal_core.install
  31. 19 1
      tripal_core/tripal_core.module
  32. 3 7
      tripal_cv/includes/tripal_cv.obo_loader.inc
  33. 1 1
      tripal_cv/tripal_cv.info
  34. 0 3
      tripal_cv/tripal_cv.install
  35. 354 348
      tripal_cv/tripal_cv.views_default.inc
  36. 1 1
      tripal_db/tripal_db.info
  37. 0 3
      tripal_db/tripal_db.install
  38. 8 2
      tripal_db/tripal_db.views_default.inc
  39. 3 4
      tripal_example/includes/tripal_example.chado_node.inc
  40. 1 1
      tripal_example/tripal_example.info
  41. 0 9
      tripal_example/tripal_example.install
  42. 11 5
      tripal_example/tripal_example.module
  43. 0 53
      tripal_feature/api/tripal_feature.schema.api.inc
  44. 20 1
      tripal_feature/includes/tripal_feature.admin.inc
  45. 2 4
      tripal_feature/includes/tripal_feature.chado_node.inc
  46. 67 109
      tripal_feature/includes/tripal_feature.delete.inc
  47. 71 72
      tripal_feature/includes/tripal_feature.fasta_loader.inc
  48. 267 138
      tripal_feature/includes/tripal_feature.gff_loader.inc
  49. 26 5
      tripal_feature/theme/css/tripal_feature.css
  50. 243 0
      tripal_feature/theme/js/tripalFeature.adminChart.js
  51. 28 0
      tripal_feature/theme/templates/tripal_feature_bar_chart_type_organism_summary.tpl.php
  52. 93 26
      tripal_feature/theme/templates/tripal_feature_relationships.tpl.php
  53. 106 121
      tripal_feature/theme/templates/tripal_organism_feature_browser.tpl.php
  54. 121 8
      tripal_feature/theme/tripal_feature.theme.inc
  55. 1 1
      tripal_feature/tripal_feature.info
  56. 140 35
      tripal_feature/tripal_feature.install
  57. 42 33
      tripal_feature/tripal_feature.module
  58. 10 14
      tripal_feature/tripal_feature.views_default.inc
  59. 8 6
      tripal_featuremap/includes/tripal_featuremap.chado_node.inc
  60. 1 1
      tripal_featuremap/tripal_featuremap.info
  61. 0 2
      tripal_featuremap/tripal_featuremap.install
  62. 9 6
      tripal_featuremap/tripal_featuremap.views_default.inc
  63. 1 1
      tripal_genetic/tripal_genetic.info
  64. 9 2
      tripal_genetic/tripal_genetic.views_default.inc
  65. 3 4
      tripal_library/includes/tripal_library.chado_node.inc
  66. 1 1
      tripal_library/tripal_library.info
  67. 0 3
      tripal_library/tripal_library.install
  68. 9 6
      tripal_library/tripal_library.views_default.inc
  69. 1 1
      tripal_natural_diversity/tripal_natural_diversity.info
  70. 0 3
      tripal_natural_diversity/tripal_natural_diversity.install
  71. 36 16
      tripal_natural_diversity/tripal_natural_diversity.views_default.inc
  72. 38 2
      tripal_organism/api/tripal_organism.api.inc
  73. 3 4
      tripal_organism/includes/tripal_organism.chado_node.inc
  74. 1 1
      tripal_organism/tripal_organism.info
  75. 0 7
      tripal_organism/tripal_organism.install
  76. 27 22
      tripal_organism/tripal_organism.module
  77. 4 2
      tripal_organism/tripal_organism.views_default.inc
  78. 1 1
      tripal_phenotype/tripal_phenotype.info
  79. 9 2
      tripal_phenotype/tripal_phenotype.views_default.inc
  80. 3 4
      tripal_project/includes/tripal_project.chado_node.inc
  81. 1 1
      tripal_project/tripal_project.info
  82. 0 3
      tripal_project/tripal_project.install
  83. 9 1
      tripal_project/tripal_project.views_default.inc
  84. 14 13
      tripal_pub/api/tripal_pub.api.inc
  85. 31 24
      tripal_pub/includes/tripal_pub.chado_node.inc
  86. 48 29
      tripal_pub/includes/tripal_pub.pub_citation.inc
  87. 153 0
      tripal_pub/theme/templates/pub_types/default.inc
  88. 27 187
      tripal_pub/theme/templates/tripal_pub_base.tpl.php
  89. 8 3
      tripal_pub/tripal_pub.drush.inc
  90. 1 1
      tripal_pub/tripal_pub.info
  91. 0 3
      tripal_pub/tripal_pub.install
  92. 5 7
      tripal_pub/tripal_pub.module
  93. 9 13
      tripal_pub/tripal_pub.views_default.inc
  94. 21 10
      tripal_stock/includes/tripal_stock.chado_node.inc
  95. 1 1
      tripal_stock/tripal_stock.info
  96. 0 3
      tripal_stock/tripal_stock.install
  97. 9 9
      tripal_stock/tripal_stock.views_default.inc
  98. 0 1
      tripal_views/api/tripal_views.api.inc
  99. 25 1
      tripal_views/includes/tripal_views_integration.inc
  100. 23 20
      tripal_views/includes/tripal_views_integration_UI.inc

+ 43 - 1
tripal_analysis/api/tripal_analysis.api.inc

@@ -108,7 +108,7 @@ function tripal_get_analysis($identifier, $options) {
     $property = $identifiers['property'];
     unset($identifiers['property']);
     $analysis = chado_get_record_with_property(
-      array('table' => 'analysis', 'base_records' => $identifiers), 
+      array('table' => 'analysis', 'base_records' => $identifiers),
       array('type_name' => $property)
     );
   }
@@ -154,4 +154,46 @@ function tripal_get_analysis($identifier, $options) {
   else {
     return $analysis;
   }
+}
+/**
+ * Returns a list of analyses that are currently synced with Drupal to use in select lists
+ *
+ * @param $syncd_only
+ *   Whether or not to return all chado analyses or just those sync'd with drupal. Defaults
+ *   to TRUE (only sync'd analyses)
+ * @return
+ *   An array of analyses sync'd with Drupal where each value is the analysis scientific
+ *   name and the keys are analysis_id's
+ *
+ * @ingroup tripal_analysis_api
+ */
+function tripal_get_analysis_select_options($syncd_only = TRUE) {
+  $analysis_list = array();
+  $analysis_list[] = 'Select an analysis';
+
+  if ($syncd_only) {
+    $sql = "
+      SELECT *
+      FROM public.chado_analysis CA
+        INNER JOIN {analysis} A ON A.analysis_id = CO.analysis_id
+      ORDER BY A.name
+    ";
+    $orgs = chado_query($sql);
+
+    // iterate through the analyses and build an array of those that are synced
+    foreach ($analyses as $analysis) {
+      $analysis_list[$analysis->analysis_id] = $analysis->name;
+    }
+  }
+  else {
+    // use this SQL statement for getting the analyses
+    $csql =  "SELECT * FROM {analysis} ORDER BY name";
+    $analyses = chado_query($csql);
+
+    // iterate through the analyses and build an array of those that are synced
+    foreach ($analyses as $analysis) {
+      $analysis_list[$analysis->analysis_id] = $analysis->name;
+    }
+  }
+  return $analysis_list;
 }

+ 18 - 6
tripal_analysis/includes/tripal_analysis.chado_node.inc

@@ -8,6 +8,7 @@
 
 /**
  * Implements hook_node_info().
+ *
  * Provide information to drupal about the node types that we're creating
  * in this module
  *
@@ -38,6 +39,18 @@ function tripal_analysis_node_info() {
   return $nodes;
 }
 
+/**
+ * Implements hook_chado_node_sync_form()
+ */
+function chado_analysis_chado_node_sync_form($form, &$form_state) {
+  if (array_key_exists('sync', $form) and array_key_exists('ids', $form['sync'])) {
+    $form['sync']['ids']['#prefix'] .= t('Please note that if analyses exist
+      that were created by other Tripal modules (e.g. Tripal Analysis Blast)
+      then you should sync those using that module\'s sync interface. Otherwise
+      they may not have all of the proper functionality.');
+  }
+  return $form;
+}
 /**
  * Implements hook_form().
  * When editing or creating a new node of type 'chado_analysis' we need
@@ -218,7 +231,7 @@ function chado_analysis_form($node, &$form_state) {
     ),
   );
   $form['description']= array(
-    '#type' => 'textarea',
+    '#type' => 'text_format',
     '#rows' => 15,
     '#title' => t('Materials & Methods (Description and/or Program Settings)'),
     '#required' => FALSE,
@@ -306,7 +319,6 @@ function tripal_analysis_validate($node, $form, &$form_state) {
 
   // remove surrounding white-space on submitted values
   $node->analysisname = trim($node->analysisname);
-  $node->description = trim($node->description);
   $node->program = trim($node->program);
   $node->programversion = trim($node->programversion);
   $node->algorithm = trim($node->algorithm);
@@ -395,13 +407,13 @@ function tripal_analysis_validate($node, $form, &$form_state) {
 function chado_analysis_insert($node) {
 
   $node->analysisname = trim($node->analysisname);
-  $node->description = trim($node->description);
   $node->program = trim($node->program);
   $node->programversion = trim($node->programversion);
   $node->algorithm = trim($node->algorithm);
   $node->sourcename = trim($node->sourcename);
   $node->sourceversion = trim($node->sourceversion);
   $node->sourceuri = trim($node->sourceuri);
+  $node->description  = trim($node->description['value']);
 
   // if there is an analysis_id in the $node object then this must be a sync so
   // we can skip adding the analysis as it is already there, although
@@ -415,7 +427,7 @@ function chado_analysis_insert($node) {
     $year  = $time['year'];
     $timestamp = $month . '/' . $day . '/' . $year;
 
-    // insert and then get the newly inserted analysis record
+    // Insert and then get the newly inserted analysis record
     $values = array(
       'name'           => $node->analysisname,
       'description'    => $node->description,
@@ -501,13 +513,13 @@ function chado_analysis_delete($node) {
  */
 function chado_analysis_update($node) {
   $node->analysisname = trim($node->analysisname);
-  $node->description = trim($node->description);
   $node->program = trim($node->program);
   $node->programversion = trim($node->programversion);
   $node->algorithm = trim($node->algorithm);
   $node->sourcename = trim($node->sourcename);
   $node->sourceversion = trim($node->sourceversion);
   $node->sourceuri = trim($node->sourceuri);
+  $node->description  = trim($node->description['value']);
 
   // Create a timestamp so we can insert it into the chado database
   $time = $node->timeexecuted;
@@ -793,5 +805,5 @@ function chado_analysis_chado_node_default_title_format() {
  * Designates a default URL format for analysis nodes.
  */
 function chado_analysis_chado_node_default_url_format() {
-  return '/analysis/[analysis.program]/[analysis.programversion]/[analysis.sourcename]';
+  return '/analysis/[analysis.analysis_id]';
 }

+ 1 - 1
tripal_analysis/tripal_analysis.info

@@ -3,7 +3,7 @@ description = Supports the companalyses tables of Chado by providing pages for v
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 configure = admin/tripal/chado/tripal_analysis/configuration
 
 dependencies[] = tripal_core

+ 1 - 4
tripal_analysis/tripal_analysis.install

@@ -50,9 +50,6 @@ function tripal_analysis_requirements($phase) {
  */
 function tripal_analysis_install() {
 
-  // create the module's data directory
-  tripal_create_files_dir('tripal_analysis');
-
   // we may need the analysisfeatureprop table if it doesn't already exist
   tripal_analysis_create_analysisfeatureprop();
 
@@ -125,7 +122,7 @@ function tripal_analysis_add_cvs() {
 function tripal_analysis_add_cvterms() {
 
   tripal_insert_cv(
-    'tripal_analysis', 
+    'tripal_analysis',
     'Terms used for managing analyses in Tripal'
   );
 

+ 9 - 1
tripal_analysis/tripal_analysis.views_default.inc

@@ -93,7 +93,10 @@ function tripal_analysis_defaultvalue_admin_analysis() {
   $handler->display->display_options['access']['perm'] = 'access chado_analysis content';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of analyses that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of analyses or to find a specific analysis.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -214,6 +217,11 @@ function tripal_analysis_defaultvalue_admin_analysis() {
   $handler->display->display_options['fields']['nothing']['label'] = '';
   $handler->display->display_options['fields']['nothing']['alter']['text'] = '[edit_node]   [delete_node]';
   $handler->display->display_options['fields']['nothing']['element_label_colon'] = FALSE;
+  /* Sort criterion: Chado Analysis: Analysis Id */
+  $handler->display->display_options['sorts']['analysis_id']['id'] = 'analysis_id';
+  $handler->display->display_options['sorts']['analysis_id']['table'] = 'analysis';
+  $handler->display->display_options['sorts']['analysis_id']['field'] = 'analysis_id';
+  $handler->display->display_options['sorts']['analysis_id']['order'] = 'DESC';
   /* Filter criterion: Chado Analysis: Name */
   $handler->display->display_options['filters']['name']['id'] = 'name';
   $handler->display->display_options['filters']['name']['table'] = 'analysis';

+ 163 - 0
tripal_bulk_loader/api/tripal_bulk_loader.api.templates.inc

@@ -14,6 +14,169 @@
  * @}
  */
 
+/**
+ * Validates an $options array for insert or update of a bulk loader record.
+ *
+ * @param $val_type
+ *   The type of validation. Can be either 'insert' or 'update'.
+ * @param $options
+ *   An array of key/value pairs containing the following keys:
+ *     'template_name':   The name of the template.
+ *     'template_array':  The JSON array representing the template.
+ *   Optional:
+ *     'strict':          If set then only JSON formatted templates are allowed.
+ * @param $errors
+ *   An empty array where validation error messages will be set. The keys
+ *   of the array will be name of the field from the options array and the
+ *   value is the error message.
+ * @param $warnings
+ *   An empty array where validation warning messagges will be set. The
+ *   warnings should not stop an insert or an update but should be provided
+ *   to the user as information by a drupal_set_message() if appropriate. The
+ *   keys of the array will be name of the field from the options array and the
+ *   value is the error message.
+ *
+ * @return
+ *   If validation failes then FALSE is returned.  Any options that do not pass
+ *   validation checks will be added in the $errors array with the key being
+ *   the option and the value being the error message.  If validation
+ *   is successful then TRUE is returned.
+ *
+ */
+function tripal_validate_bulk_loader_template($val_type, &$options, &$errors, &$warnings = array()) {
+  $template_array = trim($options['template_array']);
+  $template_name = trim($options['template_name']);
+  $strict = array_key_exists('strict', $options)  ? $options['strict'] : FALSE;
+
+  // Make sure the template array is one of the supported types
+  // DEPRECATED: A stringified version of the array (causes security issues)
+  if (preg_match('/^array/', $template_array)) {
+    if ($strict) {
+      $errors['template_array'] = t('Invalid template array. Please provide
+        a JSON formatted array');
+      return FALSE;
+    }
+    else {
+      $warnings['template_array'] = t('Please note that import of
+        bulk loader templates as PHP arrays as a stringified array is deprecated
+        and will be removed in future versions of Tripal. Export and import
+        format will be JSON.');
+    }
+  }
+  // DEPRECATED: A serialized PHP array
+  elseif (preg_match('/^a:/', $template_array)) {
+    if ($strict) {
+      $errors['template_array'] = t('Invalid template array. Please provide
+        a JSON formatted array');
+      return FALSE;
+    }
+    else {
+      $warnings['template_array'] = t('Please note that import of
+        bulk loader templates as PHP serialized arrays is deprecated and will
+        be removed in future versions of Tripal. Export and import format will
+        be JSON.');
+    }
+  }
+  // JSON FORMAT
+  elseif (json_decode($template_array)) {
+    // This is correct!
+  }
+  else {
+    $errors['template_array'] = t('The template array must be in
+      JSON format (although PHP strigified arrays and PHP serialized
+      arrays are temporarily supported for backwards compatibility).');
+    return FALSE;
+  }
+
+  // Make sure the template name is unique
+  $name_exists = db_select('tripal_bulk_loader_template', 'tblt')
+    ->fields('tblt', array('template_id'))
+    ->condition('name', $template_name)
+    ->execute()
+    ->fetchField();
+  if ($name_exists) {
+    $errors['template_name'] = t('The template name already exists. Please
+      choose another name.');
+    return FALSE;
+  }
+  return TRUE;
+}
+
+/**
+ * Inserts a bulk loader template record.
+ *
+ * This function validates the options passed prior to insertion of the record,
+ *
+ * @param $options
+ *   An array of key/value pairs containing the following keys:
+ *     'template_name':   The name of the template.
+ *     'template_array':  The JSON array representing the template.
+ *   Optional:
+ *     'strict':          If set then only JSON formatted templates are allowed.
+ * @param $errors
+ *   An empty array where validation error messages will be set. The keys
+ *   of the array will be name of the field from the options array and the
+ *   value is the error message.
+ * @param $warnings
+ *   An empty array where validation warning messagges will be set. The
+ *   warnings should not stop an insert or an update but should be provided
+ *   to the user as information by a drupal_set_message() if appropriate. The
+ *   keys of the array will be name of the field from the options array and the
+ *   value is the error message.
+ * @return
+ *   TRUE for success and FALSE for failure.
+ */
+function tripal_insert_bulk_loader_template($options, &$errors, &$warnings) {
+
+  $success = tripal_validate_bulk_loader_template('insert', $options, $errors, $warnings);
+  if (!$success) {
+    foreach ($errors as $field => $message) {
+      tripal_report_error('tripal_bulkldr', TRIPAL_ERROR, $message);
+    }
+    return FALSE;
+  }
+
+  // Insert the bulk loader template.
+  $template_array = trim($options['template_array']);
+  $template_name = trim($options['template_name']);
+
+  // Previous version of Tripal would export the template as a PHP array.
+  // This has security implications and is deprecated. This support should
+  // be reomved in future versions of Tripal, but to support transfers of
+  // templates between v1.1 and v2.x sites we support it.
+  if (preg_match('/^array/', $template_array)) {
+    $tarray = array();
+    eval("\$tarray = $template_array;");
+    $template_array = serialize($tarray);
+  }
+  // For a brief period, the bulk loader templates were exported as a PHP
+  // serialized array. We have moved to exporting in JSON as JSON is more
+  // user friendly. But we must support the serialized PHP array for
+  // backwards compatibility of v2.0-rc1 sites and v2.x sites.
+  elseif (preg_match('/^a:/', $template_array)) {
+    // do nothing it's in PHP serialized format
+  }
+  // The typical format is JSON
+  elseif (json_decode($template_array)) {
+    $template_array = serialize(json_decode($template_array, TRUE));
+  }
+  else {
+    $errors['template_array'] = t('Unrecognized array type.');
+    return FALSE;
+  }
+
+  $record = array(
+    'name' => $template_name,
+    'template_array' => $template_array,
+    'created' => time(),
+    'changed' => time()
+  );
+  if (!drupal_write_record('tripal_bulk_loader_template', $record)) {
+    return FALSE;
+  }
+  return TRUE;
+}
+
 /**
  * Meant to be called from a form_validate function to ensure a newly added bulk loader record
  * name is unique and not empty.

+ 47 - 17
tripal_bulk_loader/includes/tripal_bulk_loader.admin.templates.inc

@@ -692,7 +692,7 @@ function tripal_bulk_loader_import_template_form($form, &$form_state) {
   $breadcrumb[] = l('Templates', 'admin/tripal/loaders/bulk/templates');
   drupal_set_breadcrumb($breadcrumb);
 
-  $form['new_template_name'] = array(
+  $form['template_name'] = array(
     '#type' => 'textfield',
     '#title' => 'Template Name',
     '#weight' => 1,
@@ -716,6 +716,35 @@ function tripal_bulk_loader_import_template_form($form, &$form_state) {
   return $form;
 }
 
+/**
+ * Validates the import template form
+ */
+function tripal_bulk_loader_import_template_form_validate($form, &$form_state) {
+
+  $options = array(
+    'template_name' => trim($form_state['values']['template_name']),
+    'template_array' => trim($form_state['values']['template_array'])
+  );
+  $errors = array();
+  $warnings = array();
+
+  // Use the API function to Validate the submission.
+  tripal_validate_bulk_loader_template('insert', $options, $errors, $warnings);
+
+  // Now set form errors if any errors were detected.
+  if (count($errors) > 0) {
+    foreach($errors as $field => $message) {
+      form_set_error($field, $message);
+    }
+  }
+  // Add any warnings if any were detected
+  if (count($warnings) > 0) {
+    foreach($warnings as $field => $message) {
+      drupal_set_message($message, 'warning');
+    }
+  }
+}
+
 /**
  * Import Template Form Submit
  *
@@ -727,21 +756,22 @@ function tripal_bulk_loader_import_template_form($form, &$form_state) {
  * @ingroup tripal_bulk_loader
  */
 function tripal_bulk_loader_import_template_form_submit($form, &$form_state) {
-
-  $record = array(
-    'name' => $form_state['values']['new_template_name'],
-    'template_array' => $form_state['values']['template_array'],
-    'created' => time(),
-    'changed' => time()
+  $options = array(
+    'template_name' => trim($form_state['values']['template_name']),
+    'template_array' => trim($form_state['values']['template_array'])
   );
-  $result = drupal_write_record('tripal_bulk_loader_template', $record);
-  if ($result) {
-    drupal_set_message(t('Successfully imported Tripal Bulk Loader Template.'));
-  }
-
-  $form_state['rebuild'] = FALSE;
-  $form_state['redirect'] = 'admin/tripal/loaders/bulk/templates';
+  $errors = array();
+  $warnings = array();
 
+  // Use the API function to insert a bulk loader.
+  if (tripal_insert_bulk_loader_template($options, $errors, $warnings)) {
+    drupal_set_message(t('Successfully imported Tripal bulk loader template.'));
+    $form_state['rebuild'] = FALSE;
+    $form_state['redirect'] = 'admin/tripal/loaders/bulk/templates';
+  }
+  else {
+    drupal_set_message(t('Unable to import Tripal bulk loader template.'), 'error');
+  }
 }
 
 /**
@@ -777,7 +807,7 @@ function tripal_bulk_loader_export_template_form($form, &$form_state) {
   if ($template_id > 0) {
     $result = db_select('tripal_bulk_loader_template','t')
       ->fields('t')
-      ->condition('t.template_id',$template_id)
+      ->condition('t.template_id', $template_id)
       ->execute();
     $template = $result->fetchObject();
     $form_state['storage']['template_array'] = $template->template_array;
@@ -792,8 +822,8 @@ function tripal_bulk_loader_export_template_form($form, &$form_state) {
   $form['template_array'] = array(
     '#type' => 'textarea',
     '#title' => 'Export',
-    '#default_value' => $form_state['storage']['template_array'],
-    '#description' => t('Use this serialized array for import.'),
+    '#default_value' => json_encode(unserialize($template->template_array)),
+    '#description' => t('Use this JSON array for import of this bulk loader template into another Tripal site.'),
     '#rows' => 30,
     '#weight' => 5,
 

+ 15 - 5
tripal_bulk_loader/includes/tripal_bulk_loader.loader.inc

@@ -150,7 +150,17 @@ function tripal_bulk_loader_load_data($nid, $job_id) {
   $node = node_load($nid);
   print "Template: " . $node->template->name . " (" . $node->template_id . ")\n";
 
-  $total_lines = trim(`wc --lines < $node->file`);
+  // Determine the total number of lines in the file.
+  $total_lines = 0;
+  $handle = fopen($node->file, "r");
+  while(!feof($handle)){
+    $line = fgets($handle);
+    $total_lines++;
+  }
+  fclose($handle);
+
+  // Correct for files with a single line and no enter character.
+  $total_lines = ($total_lines == 0) ? 1 : $total_lines;
   print "File: " . $node->file . " (" . $total_lines . " lines)\n";
 
   //print "\nClearing all prepared statements from previous runs of this loader...\n";
@@ -577,7 +587,7 @@ function process_data_array_for_line($priority, &$data, &$default_data, $addt) {
   // skip optional fields
   if ($skip_optional) {
     // SPF -- Commented out the following line.  This state is intentional due
-    // to the loader setup and and is not an error.  If informational it 
+    // to the loader setup and and is not an error.  If informational it
     // prints too much to the terminal.
     // tripal_bulk_loader_throw_error('Skipping an optional record (%record)',array('%record'=>$table_data['record_id']),TRIPAL_NOTICE);
     return $no_errors;
@@ -586,7 +596,7 @@ function process_data_array_for_line($priority, &$data, &$default_data, $addt) {
   // check if it is already inserted
   if (array_key_exists('inserted', $table_data) and $table_data['inserted']) {
     // SPF -- Commented out the following line.  This state is intentional due
-    // to the loader setup and and is not an error.  If informational it 
+    // to the loader setup and and is not an error.  If informational it
     // prints too much to the terminal.
     // tripal_bulk_loader_throw_error('Skipping %record since it is already inserted',array('%record'=>$table_data['record_id']),TRIPAL_NOTICE);
     return $no_errors;
@@ -597,7 +607,7 @@ function process_data_array_for_line($priority, &$data, &$default_data, $addt) {
   if (array_key_exists('selected', $table_data) and $table_data['selected']) {
     $data[$priority]['values_array'] = $default_data[$priority]['values_array'];
     // SPF -- Commented out the following line.  This state is intentional due
-    // to the loader setup and and is not an error.  If informational it 
+    // to the loader setup and and is not an error.  If informational it
     // prints too much to the terminal.
     // tripal_bulk_loader_throw_error('%record was already selected thus we are just returning the values previously selected.',array('%record'=>$table_data['record_id']),TRIPAL_NOTICE);
     return $no_errors;
@@ -651,7 +661,7 @@ function process_data_array_for_line($priority, &$data, &$default_data, $addt) {
       // return if this is a select_if_duplicate
       if ($table_data['select_if_duplicate'] == 1) {
         // SPF -- Commented out the following line.  This state is intentional due
-        // to the loader setup and and is not an error.  If informational it 
+        // to the loader setup and and is not an error.  If informational it
         // prints too much to the terminal.
         // tripal_bulk_loader_throw_error('Simply returning values for %record since it was already inserted',array('%record'=>$table_data['record_id']),TRIPAL_NOTICE);
         return $no_errors;

+ 1 - 1
tripal_bulk_loader/tripal_bulk_loader.info

@@ -3,7 +3,7 @@ description = Supports the construction of templates for customizable uploading
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 
 dependencies[] = tripal_core
 dependencies[] = tripal_views

+ 32 - 0
tripal_contact/api/tripal_contact.api.inc

@@ -110,3 +110,35 @@ function tripal_insert_contact($values) {
   }
   return $contact;
 }
+
+
+/**
+ * This function is intended to be used in autocomplete forms for contacts.
+ *
+ * @param $text
+ *   The string to search for
+ *
+ * @return
+ *   A json array of terms that begin with the provided string
+ *
+ * @ingroup tripal_organism_api
+ */
+function tripal_autocomplete_contact($text) {
+  $matches = array();
+
+  $sql = "SELECT * FROM {contact} WHERE lower(name) like lower(:name) ";
+  $args = array();
+  $args[':name'] = $text . '%';
+  $sql .= "ORDER BY name ";
+  $sql .= "LIMIT 25 OFFSET 0 ";
+  $results = chado_query($sql, $args);
+  $items = array();
+  foreach ($results as $contact) {
+    // Don't include the null contact
+    if ($contact->name == 'null') {
+      continue;
+    }
+    $items[$contact->name] = $contact->name;
+  }
+  drupal_json_output($items);
+}

+ 5 - 7
tripal_contact/includes/tripal_contact.chado_node.inc

@@ -171,7 +171,7 @@ function chado_contact_form(&$node, $form_state) {
   );
 
   $form['description']= array(
-    '#type'          => 'textarea',
+    '#type'          => 'text_format',
     '#title'         => t('Contact Description'),
     '#description'   => t('A brief description of the contact'),
     '#required'      => TRUE,
@@ -262,8 +262,6 @@ function chado_contact_validate($node, $form, &$form_state) {
 
   // remove surrounding white-space on submitted values
   $node->contactname = property_exists($node, 'contactname') ? trim($node->contactname) : '';
-  $node->description = property_exists($node, 'description') ? trim($node->description) : '';
-
 
   // Validating for an update
   if (!is_null($node->nid)) {
@@ -374,8 +372,8 @@ function chado_contact_insert($node) {
   // we do need to proceed with insertion into the chado/drupal linking table.
   if (!property_exists($node, 'contact_id')) {
     // remove surrounding white-space on submitted values
-    $node->contactname    = trim($node->contactname);
-    $node->description    = trim($node->description);
+    $node->contactname = trim($node->contactname);
+    $node->description = trim($node->description['value']);
 
 
     // insert and then get the newly inserted contact record
@@ -451,8 +449,8 @@ function chado_contact_insert($node) {
  */
 function chado_contact_update($node) {
   // remove surrounding white-space on submitted values
-  $node->contactname          = trim($node->contactname);
-  $node->description    = trim($node->description);
+  $node->contactname = trim($node->contactname);
+  $node->description = trim($node->description['value']);
 
   $contact_id = chado_get_id_from_nid('contact', $node->nid) ;
 

+ 1 - 1
tripal_contact/tripal_contact.info

@@ -3,7 +3,7 @@ description = Supports the contact tables of Chado by providing pages for viewin
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 
 dependencies[] = tripal_core
 dependencies[] = tripal_views

+ 1 - 4
tripal_contact/tripal_contact.install

@@ -51,9 +51,6 @@ function tripal_contact_requirements($phase) {
  */
 function tripal_contact_install() {
 
-  // Create the module's data directory.
-  tripal_create_files_dir('tripal_contact');
-
   // Add the contactprop table to Chado.
   tripal_contact_add_custom_tables();
 
@@ -406,7 +403,7 @@ function tripal_contact_update_7202() {
     db_delete('tripal_cv_obo')
       ->condition('name', 'Tripal Contacts')
       ->execute();
-    
+
     // Add in the updated path.
     $obo_path = '{tripal_contact}/files/tcontact.obo';
     $obo_id = tripal_insert_obo('Tripal Contacts', $obo_path);

+ 7 - 0
tripal_contact/tripal_contact.module

@@ -101,6 +101,13 @@ function tripal_contact_menu() {
     'file path' => drupal_get_path('module', 'tripal_core'),
     'weight' => 3
   );
+
+  $items['admin/tripal/chado/tripal_contact/contact/auto_name/%'] = array(
+    'page callback' => 'tripal_autocomplete_contact',
+    'page arguments' => array(6),
+    'access arguments' => array('administer tripal contact'),
+    'type' => MENU_CALLBACK,
+  );
   return $items;
 }
 

+ 9 - 1
tripal_contact/tripal_contact.views_default.inc

@@ -96,7 +96,10 @@ function tripal_contact_defaultview_admin_contacts() {
   $handler->display->display_options['access']['type'] = 'perm';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of contacts that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of contacts or to find a specific contact.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -205,6 +208,11 @@ function tripal_contact_defaultview_admin_contacts() {
   $handler->display->display_options['fields']['nothing']['label'] = '';
   $handler->display->display_options['fields']['nothing']['alter']['text'] = '[edit_node]  [delete_node]';
   $handler->display->display_options['fields']['nothing']['element_label_colon'] = FALSE;
+  /* Sort criterion: Chado Analysis: Contact Id */
+  $handler->display->display_options['sorts']['contact_id']['id'] = 'contact_id';
+  $handler->display->display_options['sorts']['contact_id']['table'] = 'contact';
+  $handler->display->display_options['sorts']['contact_id']['field'] = 'contact_id';
+  $handler->display->display_options['sorts']['contact_id']['order'] = 'DESC';
   /* Filter criterion: Chado Contact: Type Id */
   $handler->display->display_options['filters']['type_id']['id'] = 'type_id';
   $handler->display->display_options['filters']['type_id']['table'] = 'contact';

+ 8 - 3
tripal_core/api/tripal_core.chado_general.api.inc

@@ -283,8 +283,13 @@ function chado_insert_property($record, $property, $options = array()) {
 
   // First see if the property is already assigned to the record. I
   $props = chado_get_property($record, $property);
-  if (!is_array($props) and $props) {
-    $props = array($props);
+  if (!is_array($props)) {
+    if ($props) {
+      $props = array($props);
+    }
+    else {
+      $props = array();
+    }
   }
   if (count($props) > 0) {
     // The property is already assigned, so, see if we should update it.
@@ -401,7 +406,7 @@ function chado_insert_property($record, $property, $options = array()) {
  *
  * @ingroup tripal_chado_api
  */
-function chado_update_property($record, $property, $options) {
+function chado_update_property($record, $property, $options = array()) {
 
   $base_table  = array_key_exists('table', $record) ? $record['table'] : '';
   $base_id     = array_key_exists('id', $record) ? $record['id'] : '';

+ 386 - 169
tripal_core/api/tripal_core.chado_nodes.api.inc

@@ -133,36 +133,36 @@ function chado_node_get_base_table($content_type, $module = FALSE) {
 
 }
 
-/** 
+/**
  * @section
  * Common Functionality for Properties, Dbxrefs and relationships chado node API
  */
 
 /**
  * Validate the Triggering element from a node form.
- * 
+ *
  * We are going to inspect the post to determine what PHP knows is the triggering
  * element and if it doesn't agree with Drupal then we are actually going to
  * change it in Drupal.
- * 
+ *
  * This fixes an obscure bug triggered when a property is added and then
  * a relationship removed, Drupal thinks the first property remove button was
  * clicked and instead removes a property (not a relationship) and renders the new
  * property table in the relationship table page space.
- * 
+ *
  * NOTE: Many Drupal issues state that this problem is solved if the #name
  * of the button is unique (which it is in our case) but we are still experiencing
  * incorrectly determined triggering elements so we need to handle it ourselves.
  */
 function chado_validate_node_form_triggering_element($form, &$form_state) {
-  
+
   // We are going to inspect the post to determine what PHP knows is the triggering
   // element and if it doesn't agree with Drupal then we are actually going to
   // change it in Drupal.
   if ($_POST['_triggering_element_name'] != $form_state['triggering_element']['#name']) {
     $form_state['triggering_element']['#name'] = $_POST['_triggering_element_name'];
   }
-  
+
 }
 
 /**
@@ -221,7 +221,7 @@ function chado_add_node_form_subtables_add_button_submit($form, &$form_state) {
         break;
     }
   }
-  
+
   // This is needed to ensure the form builder function is called for the node
   // form in order for any of these changes to be seen.
   $form_state['rebuild'] = TRUE;
@@ -230,28 +230,28 @@ function chado_add_node_form_subtables_add_button_submit($form, &$form_state) {
 /**
  * Validate Removing Subtables entries from the node forms.
  * Supported subtables: Properties, Relationships, Additional DBxrefs.
- * 
+ *
  * Since Removing isn't associated with any user input the only thing we
  * need to validate is that Drupal has determined the triggering element correctly.
  * That said, we will call each subtables associated validate function just incase
  * there is some case-specific validation we do not know of or have not anticipated.
- * 
+ *
  * @param array $form
  * @param array $form_state
  */
 function chado_add_node_form_subtables_remove_button_validate($form, &$form_state) {
- 
+
   // We need to validate the trigerring element since Drupal has known
   // issues determining this correctly when there are multiple buttons
   // with the same label.
   chado_validate_node_form_triggering_element($form, $form_state);
-  
+
   // Based on triggering element call the correct validation function
   // ASUMPTION #1: each of the buttons must have property, dbxref or relationship
   // as the first part of the #name to uniquely identify the subsection.
   if (preg_match('/^([a-z]+).*/', $form_state['triggering_element']['#name'], $matches)) {
     $subsection = $matches[1];
-    
+
       switch($subsection) {
       case 'properties':
         chado_add_node_form_properties_remove_button_validate($form, $form_state);
@@ -306,18 +306,18 @@ function chado_add_node_form_subtables_remove_button_submit($form, &$form_state)
  * @ingroup tripal_core
  */
 function chado_add_node_form_subtable_ajax_update($form, &$form_state) {
-  
+
   // We need to validate the trigerring element since Drupal has known
   // issues determining this correctly when there are multiple buttons
   // with the same label.
   chado_validate_node_form_triggering_element($form, $form_state);
-  
+
   // Based on triggering element render the correct part of the form.
   // ASUMPTION: each of the buttons must have property, dbxref or relationship
   // as the first part of the #name to uniquely identify the subsection.
   if (preg_match('/^([a-z]+).*/', $form_state['triggering_element']['#name'], $matches)) {
     $subsection = $matches[1];
-    
+
     switch($subsection) {
       case 'properties':
         return $form['properties']['property_table'];
@@ -352,7 +352,7 @@ function chado_add_node_form_subtable_ajax_update($form, &$form_state) {
     $module_name = 'tripal_example';
 
     // the base specified in hook_node_info
-    $linking_table = 'chado_example';
+    $node_type = 'chado_example';
 
     // This menu item will be a tab on the admin/tripal/chado/tripal_example page
     // that is not selected by default
@@ -360,7 +360,7 @@ function chado_add_node_form_subtable_ajax_update($form, &$form_state) {
       'title' => ' Sync',
       'description' => 'Sync examples from Chado with Drupal',
       'page callback' => 'drupal_get_form',
-      'page arguments' => array('chado_node_sync_form', $module_name, $linking_table),
+      'page arguments' => array('chado_node_sync_form', $module_name, $node_type),
       'access arguments' => array('administer tripal examples'),
       'type' => MENU_LOCAL_TASK,
       'weight' => 0
@@ -380,8 +380,12 @@ function chado_add_node_form_subtable_ajax_update($form, &$form_state) {
 
         // this is what differs from the regular Drupal-documented hook_node_info()
         'chado_node_api' => array(
-          'base_table' => 'example',            // the name of the chado base table
-          'hook_prefix' => 'chado_example',     // usually the name of the node type
+          'base_table' => 'example',            // The name of the chado base table
+          'hook_prefix' => 'chado_example',     // Usually the name of the node type
+          'linking_table' => 'chado_example',   // Specifies the linking table used
+                                                // to map records to Drupal nodes.
+                                                // if 'linking_table' is not specified
+                                                // it defaults to the node_type name.
           'record_type_title' => array(
             'singular' => t('Example'),         // Singular human-readable title
             'plural' => t('Examples')           // Plural human-readable title
@@ -414,9 +418,18 @@ function chado_node_sync_form($form, &$form_state) {
 
   if (isset($form_state['build_info']['args'][0])) {
     $module = $form_state['build_info']['args'][0];
-    $linking_table = $form_state['build_info']['args'][1];
+    $node_type = $form_state['build_info']['args'][1];
     $node_info = call_user_func($module . '_node_info');
-    $args = $node_info[$linking_table]['chado_node_api'];
+
+    // If a linking table is set in the node_info array then use that,
+    // otherwise ues the node_type as the linking table.
+    if (array_key_exists('linking_table', $node_info[$node_type]['chado_node_api'])) {
+      $linking_table = $node_info[$node_type]['chado_node_api']['linking_table'];
+    }
+    else {
+      $linking_table = 'chado_' . $node_info[$node_type]['chado_node_api']['base_table'];
+    }
+    $args = $node_info[$node_type]['chado_node_api'];
     $form_state['chado_node_api'] = $args;
   }
 
@@ -425,6 +438,11 @@ function chado_node_sync_form($form, &$form_state) {
     '#value' => $linking_table
   );
 
+  $form['node_type'] = array(
+    '#type' => 'hidden',
+    '#value' => $node_type
+  );
+
   // define the fieldsets
   $form['sync'] = array(
     '#type' => 'fieldset',
@@ -498,20 +516,67 @@ function chado_node_sync_form($form, &$form_state) {
     if (array_key_exists('primary key', $table_info) and count($table_info['primary key']) == 1) {
       $pkey = $table_info['primary key'][0];
       $columns  = $args['sync_filters']['checkboxes'];
-      $select_cols = implode("|| ' ' ||", $columns);
-
-      // get non-synced records
-      $sql = "
-        SELECT BT.$pkey as id, $select_cols as value
-        FROM {" . $base_table . "} BT
-          LEFT JOIN public.$linking_table LT ON LT.$pkey = BT.$pkey
-        WHERE LT.$pkey IS NULL
-        ORDER BY value ASC
-      ";
-      $results = chado_query($sql);
+      $select_cols = '';
+      foreach ($columns as $column) {
+        $select_cols .= $base_table . '.' . $column . "|| ' ' ||";
+      }
+      // Remove trailing || ' ' ||
+      $select_cols = substr($select_cols, 0, -9);
+      $base_table_id  = $base_table . '_id';
+
+      $select = array($base_table . '.' . $pkey, $select_cols . ' as value');
+      $joins = array();
+      $where_clauses = array();
+      $where_args = array();
+
+      // Allow module to update the query.
+      $hook_query_alter = $node_type . '_chado_node_sync_select_query';
+      if (function_exists($hook_query_alter)) {
+        $update = call_user_func($hook_query_alter, array(
+          'select' => $select,
+          'joins' => $joins,
+          'where_clauses' => $where_clauses,
+          'where_args' => $where_args,
+        ));
+        // Now add in any new changes
+        if ($update and is_array($update)) {
+          $select = $update['select'];
+          $joins = $update['joins'];
+          $where_clauses = $update['where_clauses'];
+          $where_args = $update['where_args'];
+        }
+      }
+
+      // Build Query, we do a left join on the chado_xxxx table in the Drupal schema
+      // so that if no criteria are specified we only get those items that have not
+      // yet been synced.
+      $query = "SELECT " . implode(', ', $select) . ' ' .
+               'FROM {' . $base_table . '} ' . $base_table . ' ' . implode(' ', $joins) . ' '.
+               "  LEFT JOIN public.$linking_table CT ON CT.$base_table_id = $base_table.$base_table_id " .
+               "WHERE CT.$base_table_id IS NULL";
+
+      // extend the where clause if needed
+      $where = '';
+      $sql_args = array();
+      foreach ($where_clauses as $category => $items) {
+        $where .= ' AND (';
+        foreach ($items as $item) {
+          $where .= $item . ' OR ';
+        }
+        $where = substr($where, 0, -4); // remove the trailing 'OR'
+        $where .= ') ';
+        $sql_args = array_merge($sql_args, $where_args[$category]);
+      }
+
+      if ($where) {
+        $query .= $where;
+      }
+      $query .= " ORDER BY $base_table." . implode(", $base_table.", $columns);
+      $results = chado_query($query, $sql_args);
+
       $values = array();
       foreach ($results as $result) {
-        $values[$result->id] = $result->value;
+        $values[$result->$pkey] = $result->value;
       }
       if (count($values) > 0) {
         $form['sync']['ids'] = array(
@@ -520,7 +585,7 @@ function chado_node_sync_form($form, &$form_state) {
           '#options'       => $values,
           '#default_value' => (isset($form_state['values']['ids'])) ? $form_state['values']['ids'] : array(),
           '#suffix'        => '</div><br>',
-          '#prefix'       => t("The following  %title_plural have not been synced. Check those to be synced or leave all unchecked to sync them all.",
+          '#prefix'        => t("The following  %title_plural have not been synced. Check those to be synced or leave all unchecked to sync them all.",
               array(
                 '%title_singular' => strtolower($args['record_type_title']['singular']),
                 '%title_plural'   => strtolower($args['record_type_title']['plural'])
@@ -561,7 +626,13 @@ function chado_node_sync_form($form, &$form_state) {
         "\"orphaned\".  This can occur if a node in Drupal is " .
         "deleted but the corresponding chado records is not and/or vice " .
         "versa. Click the button below to resolve these discrepancies.</p>"),
-    '#weight' => 1,
+    '#weight' => -10,
+  );
+  $form['cleanup']['cleanup_batch_size'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Batch Size'),
+      '#description' => t('The number of records to analyze together in a batch. If you are having memory issues you might want to decrease this number.'),
+      '#default_value' => variable_get('chado_node_api_cleanup_batch_size', 25000),
   );
   $form['cleanup']['button'] = array(
     '#type' => 'submit',
@@ -578,6 +649,26 @@ function chado_node_sync_form($form, &$form_state) {
   return $form;
 }
 
+/**
+ * Generic Sync Form Validate
+ *
+ * @ingroup tripal_core
+ */
+function chado_node_sync_form_validate($form, &$form_state) {
+
+  if (empty($form_state['values']['cleanup_batch_size'])) {
+    $form_state['values']['cleanup_batch_size'] = 25000;
+    drupal_set_message('You entered a Batch Size of 0 for Cleaning-up orphaned nodes. Since this is not valid, we reset it to the default of 25,000.', 'warning');
+  }
+  elseif (!is_numeric($form_state['values']['cleanup_batch_size'])) {
+    form_set_error('cleanup_batch_size', 'The batch size must be a postitive whole number.');
+  }
+  else {
+    // Round the value just to make sure.
+    $form_state['values']['cleanup_batch_size'] = abs(round($form_state['values']['cleanup_batch_size']));
+  }
+}
+
 /**
  * Generic Sync Form Submit
  *
@@ -593,6 +684,7 @@ function chado_node_sync_form_submit($form, $form_state) {
     $module = $form_state['chado_node_api']['hook_prefix'];
     $base_table = $form_state['chado_node_api']['base_table'];
     $linking_table = $form_state['values']['linking_table'];
+    $node_type = $form_state['values']['node_type'];
 
     // Allow each module to hijack the submit if needed
     $hook_form_hijack_submit = $args['hook_prefix'] . '_chado_node_sync_form_submit';
@@ -639,7 +731,8 @@ function chado_node_sync_form_submit($form, $form_state) {
       'organism_id' => $organism_id,
       'types' => $types,
       'ids' => $ids,
-      'inking_table' => $linking_table
+      'linking_table' => $linking_table,
+      'node_type' => $node_type
     );
 
     $title = "Sync " . $args['record_type_title']['plural'];
@@ -648,7 +741,10 @@ function chado_node_sync_form_submit($form, $form_state) {
   if (preg_match('/^Clean up orphaned/', $form_state['values']['op'])) {
     $module = $form_state['chado_node_api']['hook_prefix'];
     $base_table = $form_state['chado_node_api']['base_table'];
-    $job_args = array($base_table);
+    $linking_table = $form_state['values']['linking_table'];
+    $node_type = $form_state['values']['node_type'];
+    $job_args = array($base_table, $form_state['values']['cleanup_batch_size'], $linking_table, $node_type);
+    variable_set('chado_node_api_cleanup_batch_size', $form_state['values']['cleanup_batch_size']);
     tripal_add_job($form_state['values']['op'], $module, 'chado_cleanup_orphaned_nodes', $job_args, $user->uid);
   }
 }
@@ -676,14 +772,20 @@ function chado_node_sync_form_submit($form, $form_state) {
  *   are named as 'chado_' . $base_table.  But if for some reason the
  *   linking table is not named in this way then it can be provided by this
  *   argument.
+ * @param $node_type
+ *   Optional: Tripal maintains "linking" tables in the Drupal schema
+ *   to link Drupal nodes with Chado records. By default, Tripal expects that
+ *   the node_type and linking table are named the same. However, if this
+ *   is not the case, you can provide the node type name here.
  * @param $job_id
  *   Optional. Used by the Trpial Jobs system when running this function
  *   as a job. It is not needed othewise.
  *
  * @ingroup tripal_chado_node_api
  */
-function chado_node_sync_records($base_table, $max_sync = FALSE, $organism_id = FALSE,
-    $types = array(), $ids = array(), $linking_table = FALSE, $job_id = NULL) {
+function chado_node_sync_records($base_table, $max_sync = FALSE,
+    $organism_id = FALSE, $types = array(), $ids = array(),
+    $linking_table = FALSE, $node_type = FALSE, $job_id = NULL) {
 
   global $user;
   $base_table_id = $base_table . '_id';
@@ -691,6 +793,9 @@ function chado_node_sync_records($base_table, $max_sync = FALSE, $organism_id =
   if (!$linking_table) {
     $linking_table = 'chado_' . $base_table;
   }
+  if (!$node_type) {
+    $node_type = 'chado_' . $base_table;
+  }
 
   print "\nSync'ing $base_table records.  ";
 
@@ -735,7 +840,7 @@ function chado_node_sync_records($base_table, $max_sync = FALSE, $organism_id =
   }
 
   // Allow module to add to query
-  $hook_query_alter = $linking_table . '_chado_node_sync_select_query';
+  $hook_query_alter = $node_type . '_chado_node_sync_select_query';
   if (function_exists($hook_query_alter)) {
     $update = call_user_func($hook_query_alter, array(
       'select' => $select,
@@ -811,31 +916,25 @@ function chado_node_sync_records($base_table, $max_sync = FALSE, $organism_id =
   try {
     $percent = 0;
     foreach ($results as $record) {
-
-      //print "\nLoading $base_table " . ($i + 1) . " of $count ($base_table_id=" . $record->{$base_table_id} . ")...";
-
-      // update the job status every 1% features
+      // Update the job status every 1% features.
       if ($job_id and $i % $interval == 0) {
         $percent = sprintf("%.2f", (($i + 1) / $count) * 100);
         print "Syncing $base_table " . ($i + 1) . " of $count (" . $percent . "%). Memory: " . number_format(memory_get_usage()) . " bytes.\r";
         tripal_set_job_progress($job_id, intval(($i/$count)*100));
       }
 
-      // Check if it is in the chado linking table (ie: check to see if it is already linked to a node)
+      // Check if the record is already in the chado linking table
+      // (ie: check to see if it is already linked to a node).
       $result = db_select($linking_table, 'lnk')
         ->fields('lnk',array('nid'))
         ->condition($base_table_id, $record->{$base_table_id}, '=')
         ->execute()
         ->fetchObject();
 
-      if (!empty($result)) {
-        //print " Previously Sync'd";
-      }
-      else {
-
-        // Create generic new node
+      if (empty($result)) {
+        // Create generic new node.
         $new_node = new stdClass();
-        $new_node->type = $linking_table;
+        $new_node->type = $node_type;
         $new_node->uid = $user->uid;
         $new_node->{$base_table_id} = $record->{$base_table_id};
         $new_node->$base_table = $record;
@@ -843,7 +942,7 @@ function chado_node_sync_records($base_table, $max_sync = FALSE, $organism_id =
 
         // TODO: should we get rid of this hook and use hook_node_presave() instead?
         // allow base module to set additional fields as needed
-        $hook_create_new_node = $linking_table . '_chado_node_sync_create_new_node';
+        $hook_create_new_node = $node_type . '_chado_node_sync_create_new_node';
         if (function_exists($hook_create_new_node)) {
           $new_node = call_user_func($hook_create_new_node, $new_node, $record);
         }
@@ -855,11 +954,12 @@ function chado_node_sync_records($base_table, $max_sync = FALSE, $organism_id =
 
         if (!form_get_errors()) {
           $node = node_submit($new_node);
+          // If there are memory leaks on the node_save it is probably
+          // caused by the hook_node_insert() function.
           node_save($node);
-          //print " Node Created (nid=".$node->nid.")";
         }
         else {
-          watchdog('trp-fsync', "Failed to insert $base_table: %title", array('%title' => $new_node->title), WATCHDOG_ERROR);
+          throw new Exception(t("Failed to insert $base_table: %title", array('%title' => $new_node->title)));
         }
       }
       $i++;
@@ -875,6 +975,81 @@ function chado_node_sync_records($base_table, $max_sync = FALSE, $organism_id =
   }
 }
 
+/**
+ * This function is a wrapper for the chado_cleanup_orphaned_nodes function.
+ * It breaks up the work of chado_cleanup_orphaned_nodes into smaller pieces
+ * that are more managable for servers that may  have low php memory settings.
+ *
+ * @param $table
+ *   The name of the table that corresonds to the node type we want to clean up.
+ * @param $nentries
+ *   Optional. The number of entries to parse at one time (ie: the batch size).
+ *   Set to zero if no limit is needed.
+ * @param $linking_table
+ *   Optional. The name of the linking table that maps Drupal nodes to Chado
+ *   records. This is only required if the linking table name is not of the
+ *   form: chado_[table] where [table] is the value provided to the $table
+ *   argument.
+ * @param $node_type
+ *   Optional. The name of the node type for the records.  This is only
+ *   required if the node type is not of the form: chado_[table] where
+ *   [table] is the value provided to the $table.
+ * @param $job_id
+ *   Optional. This should be the job id from the Tripal jobs system. Typically,
+ *   only the Tripal jobs system will use the argument.
+ *
+ * @ingroup tripal_chado_node_api
+ */
+function chado_cleanup_orphaned_nodes($table, $nentries = 25000, $linking_table = NULL, $node_type = NULL, $job_id = NULL) {
+  // The max number of records either as nodes or linked records.
+  $count = 0;
+  // Will hold the number of nodes of this type.
+  $ncount = 0;
+  // Will hold the number of linked records.
+  $clcount = 0;
+
+  if (!$node_type) {
+    $node_type = 'chado_' . $table;
+  }
+  if (!$linking_table) {
+    $linking_table = 'chado_' . $table;
+  }
+  // Find the number nodes of type chado_$table and find the number of entries
+  // in chado_$table; keep the larger of the two numbers.
+  $dsql = "SELECT COUNT(*) FROM {node} WHERE type = :node_type";
+  $ndat = db_query($dsql, array(':node_type' => $node_type));
+  $temp = $ndat->fetchObject();
+  $ncount = $temp->count;
+  $clsql= "SELECT COUNT(*) FROM {" . $linking_table . "}";
+  $cdat = db_query($clsql);
+  $clcount = $cdat->fetchObject();
+  if ($ncount < $clcount) {
+    $count = $clcount;
+  }
+  else {
+    $count = $ncount;
+  }
+
+  $transaction = db_transaction();
+  print "\nNOTE: This operation is performed using a database transaction. \n" .
+    "If it fails or is terminated prematurely then the entire set of \n" .
+    "changes is rolled back and will not be found in the database\n\n";
+  try {
+    $m = ceil($count / $nentries);
+    for ($i = 0; $i < $m; $i++) {
+      $offset = ($nentries * $i);
+      chado_cleanup_orphaned_nodes_part($table, $job_id, $nentries, $offset, $linking_table, $node_type);
+    }
+  }
+  catch (Exception $e) {
+    $transaction->rollback();
+    print "\n"; // make sure we start errors on new line
+    watchdog_exception('trp-fsync', $e);
+    print "FAILED: Rolling back database changes...\n";
+  }
+  return '';
+}
+
 /**
  * This function will delete Drupal nodes for any sync'ed table (e.g.
  * feature, organism, analysis, stock, library) if the chado record has been
@@ -888,103 +1063,133 @@ function chado_node_sync_records($base_table, $max_sync = FALSE, $organism_id =
  *
  * @ingroup tripal_chado_node_api
  */
-function chado_cleanup_orphaned_nodes($table, $job_id = NULL) {
-  $count = 0;
+function chado_cleanup_orphaned_nodes_part($table, $job_id = NULL, $nentries,
+    $offset, $linking_table, $node_type) {
 
-  // build the SQL statments needed to check if nodes point to valid analyses
-  $dsql = "SELECT * FROM {node} WHERE type = 'chado_" . $table . "' order by nid";
-  $nsql = "SELECT * FROM {node} WHERE nid = :nid";
-  $csql = "SELECT * FROM {chado_" . $table . "} WHERE nid = :nid ";
-  $clsql= "SELECT * FROM {chado_" . $table . "}";
-  $lsql = "SELECT * FROM {" . $table . "} where " . $table . "_id = :" . $table . "_id ";
-
-  // load into nodes array
-  print "Getting nodes\n";
-  $nodes = array();
-  $res = db_query($dsql);
-  foreach ($res as $node) {
-    $nodes[$count] = $node;
-    $count++;
-  }
+  $count = 0;
 
-  // load the chado_$table into an array
-  print "Getting chado_$table\n";
+  // Retrieve all of the entries in the linker table for a given node type
+  // and place into an array.
+  print "Verifying $linking_table records...\n";
   $cnodes = array();
-  $res = db_query($clsql);
+  $clsql= "
+    SELECT *
+    FROM {" . $linking_table . "} LT
+      INNER JOIN {node} N ON N.nid = LT.nid
+    WHERE N.type = :node_type
+    ORDER BY LT.nid LIMIT $nentries OFFSET $offset";
+  $res = db_query($clsql, array(':node_type' => $node_type));
   foreach ($res as $node) {
     $cnodes[$count] = $node;
     $count++;
   }
-  $interval = intval($count * 0.01);
-  if ($interval < 1) {
-    $interval = 1;
-  }
 
-  // iterate through all of the chado_$table entries and remove those
-  // that don't have a node or don't have a $table record in chado.libary
-  print "Verifying all chado_$table Entries\n";
+  // Iterate through all of the $linking_table entries and remove those
+  // that don't have a node or don't have a $table record.
   $deleted = 0;
-  foreach ($cnodes as $nid) {
-
-    // update the job status every 1% analyses
-    if ($job_id and $i % $interval == 0) {
-      tripal_set_job_progress($job_id, intval(($i / $count) * 100));
+  if ($count > 0) {
+    $i = 0;
+    $interval = intval($count * 0.01);
+    if ($interval < 1) {
+      $interval = 1;
     }
+    foreach ($cnodes as $nid) {
+      // Update the job status every 1% analyses
+      if ($job_id and $i % $interval == 0) {
+        $percent = sprintf("%.2f", ($i / $count) * 100);
+        tripal_set_job_progress($job_id, intval($percent));
+        print "Percent complete: $percent%. Memory: " . number_format(memory_get_usage()) . " bytes.\r";
+      }
 
-    // see if the node exits, if not remove the entry from the chado_$table table
-    $results = db_query($nsql, array(':nid' => $nid->nid));
-    $node = $results->fetchObject();
-    if (!$node) {
-      $deleted++;
-      db_query("DELETE FROM {chado_" . $table . "} WHERE nid = :nid", array(':nid' => $nid->nid));
-      $message = "chado_$table missing node.... DELETING: $nid->nid";
-      watchdog('tripal_core', $message, array(), WATCHDOG_WARNING);
-    }
+      // See if the node exits, if not remove the entry from linking table table.
+      $nsql = "SELECT * FROM {node} WHERE nid = :nid";
+      $results = db_query($nsql, array(':nid' => $nid->nid));
+      $node = $results->fetchObject();
+      if (!$node) {
+        $deleted++;
+        db_query("DELETE FROM {" . $linking_table . "} WHERE nid = :nid", array(':nid' => $nid->nid));
+        //print "$linking_table missing node.... DELETING: $nid->nid\n";
+      }
 
-    // see if the record in chado exist, if not remove the entry from the chado_$table
-    $table_id = $table . "_id";
-    $results = chado_query($lsql, array(":" . $table . "_id" => $nid->$table_id));
-    $record = $results->fetchObject();
-    if (!$record) {
-      $deleted++;
-      $sql = "DELETE FROM {chado_" . $table . "} WHERE " . $table . "_id = :" . $table . "_id";
-      db_query($sql, array(":" . $table . "_id" => $nid->$table_id));
-      $message = "chado_$table missing $table.... DELETING entry.";
-      watchdog('tripal_core', $message, array(), WATCHDOG_NOTICE);
+      // Does record in chado exists, if not remove entry from $linking_table.
+      $table_id = $table . "_id";
+      $lsql = "SELECT * FROM {" . $table . "} where " . $table . "_id = :" . $table . "_id";
+      $results = chado_query($lsql, array(":" . $table . "_id" => $nid->$table_id));
+      $record = $results->fetchObject();
+      if (!$record) {
+        $deleted++;
+        $sql = "DELETE FROM {" . $linking_table . "} WHERE " . $table . "_id = :" . $table . "_id";
+        db_query($sql, array(":" . $table . "_id" => $nid->$table_id));
+        //print "$linking_table missing $table.... DELETING entry.\n";
+      }
+      $i++;
     }
-    $i++;
+    $percent = sprintf("%.2f", ($i / $count) * 100);
+    tripal_set_job_progress($job_id, intval($percent));
+    print "Percent complete: $percent%. Memory: " . number_format(memory_get_usage()) . " bytes.\r";
+  }
+  print "\nDeleted $deleted record(s) from $linking_table missing either a node or chado entry.\n";
+
+  // Build the SQL statements needed to check if nodes point to valid record.
+  print "Verifying nodes...\n";
+  $dsql = "
+    SELECT *
+    FROM {node}
+    WHERE type = :node_type
+    ORDER BY nid
+    LIMIT $nentries OFFSET $offset
+  ";
+
+  $dsql_args = array(':node_type' => $node_type);
+  $nodes = array();
+  $res = db_query($dsql, $dsql_args);
+  $count = 0;
+  foreach ($res as $node) {
+    $nodes[$count] = $node;
+    $count++;
   }
-  print "\t$deleted chado_$table entries missing either a node or chado entry.\n";
 
-  // iterate through all of the nodes and delete those that don't
-  // have a corresponding entry in chado_$table
+  // Iterate through all of the nodes and delete those that don't
+  // have a corresponding entry in the linking table.
   $deleted = 0;
-  foreach ($nodes as $node) {
-
-    // update the job status every 1% libraries
-    if ($job_id and $i % $interval == 0) {
-      tripal_set_job_progress($job_id, intval(($i / $count) * 100));
+  if ($count > 0) {
+    $i = 0;
+    $interval = intval($count * 0.01);
+    if ($interval < 1) {
+      $interval = 1;
     }
-
-    // check to see if the node has a corresponding entry
-    // in the chado_$table table. If not then delete the node.
-    $results = db_query($csql, array(":nid" => $node->nid));
-    $link = $results->fetchObject();
-    if (!$link) {
-      if (node_access('delete', $node)) {
-        $deleted++;
-        $message = "Node missing in chado_$table table.... DELETING node $node->nid";
-        watchdog("tripal_core", $message, array(), WATCHDOG_NOTICE);
-        node_delete($node->nid);
-      }
-      else {
-        $message = "Node missing in chado_$table table.... but cannot delete due to improper permissions (node $node->nid)";
-        watchdog("tripal_core", $message, array(), WATCHDOG_WARNING);
+    foreach ($nodes as $node) {
+      // update the job status every 1%
+      if ($job_id and $i % $interval == 0) {
+        $percent = sprintf("%.2f", ($i / $count) * 100);
+        tripal_set_job_progress($job_id, intval($percent));
+        print "Percent complete: $percent%. Memory: " . number_format(memory_get_usage()) . " bytes.\r";
       }
+
+      // check to see if the node has a corresponding entry
+      // in the $linking_table table. If not then delete the node.
+       $csql = "SELECT * FROM {" . $linking_table . "} WHERE nid = :nid ";
+       $results = db_query($csql, array(':nid' => $node->nid));
+       $link = $results->fetchObject();
+       if (!$link) {
+         // Checking node_access creates a memory leak. Commenting out for now
+         // assuming that this code can only be run by a site administrator
+         // anyway.
+//         if (node_access('delete', $node)) {
+           $deleted++;
+           node_delete($node->nid);
+//         }
+//         else {
+//           print "\nNode missing in $linking_table table.... but cannot delete due to improper permissions (node $node->nid)\n";
+//         }
+       }
+      $i++;
     }
-    $i++;
+    $percent = sprintf("%.2f", ($i / $count) * 100);
+    tripal_set_job_progress($job_id, intval($percent));
+    print "Percent complete: $percent%. Memory: " . number_format(memory_get_usage()) . " bytes.\r";
+    print "\nDeleted $deleted node(s) that did not have corresponding $linking_table entries.\n";
   }
-  print "\t$deleted nodes did not have corresponding chado_$table entries.\n";
 
   return '';
 }
@@ -992,8 +1197,8 @@ function chado_cleanup_orphaned_nodes($table, $job_id = NULL) {
 /**
  * Create New Node
  *
- * Note: For your own module, replace hook in the function name with the machine-name of
- * your chado node type (ie: chado_feature).
+ * Note: For your own module, replace hook in the function name with the
+ * machine-name of your chado node type (ie: chado_feature).
  *
  * @param $new_node:
  *   a basic new node object
@@ -1001,51 +1206,53 @@ function chado_cleanup_orphaned_nodes($table, $job_id = NULL) {
  *   the record object from chado specifying the biological data for this node
  *
  * @return
- *   A node object containing all the fields necessary to create a new node during sync
+ *   A node object containing all the fields necessary to create a new node
+ *   during sync
  *
  * @ingroup tripal_chado_node_api
  */
 function hook_chado_node_sync_create_new_node($new_node, $record) {
 
-  // Add relevant chado details to the new node object
-  // This really only needs to be the fields from the node used during node creation
-  // including values used to generate the title, etc.
-  // All additional chado data will be added via nodetype_load when the node is later used
+  // Add relevant chado details to the new node object. This really only
+  // needs to be the fields from the node used during node creation
+  // including values used to generate the title, etc. All additional chado
+  // data will be added via nodetype_load when the node is later used
   $new_node->uniquename = $record->uniquename;
 
   return $new_node;
 }
 
 /**
- * Alter the sync form (optional)
+ * Alter the Chado node sync form.
  *
- * This might be necessary if you need additional filtering options for choosing which
- * chado records to sync or even if you just want to further customize the help text
- * provided by the form.
+ * This might be necessary if you need additional filtering options for
+ * choosing which chado records to sync or even if you just want to further
+ * customize the help text provided by the form.
  *
- * Note: For your own module, replace hook in the function name with the machine-name of
- * your chado node type (ie: chado_feature).
+ * Note: For your own module, replace hook in the function name with the
+ * machine-name of your chado node type (ie: chado_feature).
  *
  * @ingroup tripal_chado_node_api
  */
 function hook_chado_node_sync_form($form, &$form_state) {
 
-  // Change or add to the form array as needed
-  // Any changes should be made in accordance with the Drupal Form API
+  // Change or add to the form array as needed.
+  // Any changes should be made in accordance with the Drupal Form API.
 
   return $form;
 }
 
 /**
- * Bypass chado node api sync form submit (optional). Allows you to use this function
- * as your own submit.
+ * Bypass chado node api sync form submit.
+ *
+ * Allows you to use this function as your own submit.
  *
- * This might be necessary if you want to add additional arguements to the tripal job or
- * to call your own sync'ing function if the generic chado_node_sync_records() is not
- * sufficient.
+ * This might be necessary if you want to add additional arguements to the
+ * tripal job or to call your own sync'ing function if the generic
+ * chado_node_sync_records() is not sufficient.
  *
- * Note: For your own module, replace hook in the function name with the machine-name of
- * your chado node type (ie: chado_feature).
+ * Note: For your own module, replace hook in the function name with the
+ * machine-name of your chado node type (ie: chado_feature).
  *
  * @ingroup tripal_chado_node_api
  */
@@ -1054,19 +1261,28 @@ function hook_chado_node_sync_form_submit ($form, $form_state) {
   global $user;
 
   $job_args = array(
-    $base_table,      // the base chado table (ie: feature)
-    $max_sync,        // the maximum number of records to sync or FALSE for sync all that match
-    $organism_id,     // the organism_id to restrict records to or FALSE if not to restrict by organism_id
-    $types            // A string with the cvterm.name of the types to restrict to separated by |||
+    // The base chado table (ie: feature).
+    $base_table,
+    // The maximum number of records to sync or FALSE for sync all that match.
+    $max_sync,
+    // The organism_id to restrict records to or FALSE if not to restrict by organism_id.
+    $organism_id,
+    // A string with the cvterm.name of the types to restrict to separated by |||
+    $types
   );
 
   // You should register a tripal job
   tripal_add_job(
-    $title,                                   // the title of the job -be descriptive
-    $module,                                  // the name of your module
-    'chado_node_sync_records',    // the chado node api sync function
-    $job_args,                                // an array with the arguments to pass to the above function
-    $user->uid                                // the user who submitted the job
+    // The title of the job -be descriptive.
+    $title,
+    // The name of your module.
+    $module,
+    // The chado node api sync function.
+    'chado_node_sync_records',
+    // An array with the arguments to pass to the above function.
+    $job_args,
+    // The user who submitted the job.
+    $user->uid
   );
 
 }
@@ -1075,13 +1291,14 @@ function hook_chado_node_sync_form_submit ($form, $form_state) {
 /**
  * Alter the query that retrieves records to be sync'd (optional)
  *
- * This might be necessary if you need fields from other chado tables to create your node
- * or if your chado node type only supports a subset of a given table (ie: a germplasm node
- * type might only support node creation for cerain types of stock records in which case
- * you would need to filter the results to only those types).
+ * This might be necessary if you need fields from other chado tables to
+ * create your node or if your chado node type only supports a subset of a
+ * given table (ie: a germplasm node type might only support node creation for
+ * cerain types of stock records in which case you would need to filter the
+ * results to only those types).
  *
- * Note: For your own module, replace hook in the function name with the machine-name of
- * your chado node type (ie: chado_feature).
+ * Note: For your own module, replace hook in the function name with the
+ * machine-name of your chado node type (ie: chado_feature).
  *
  * @param $query
  *   An array containing the following:

+ 28 - 14
tripal_core/api/tripal_core.chado_nodes.relationships.api.inc

@@ -300,14 +300,14 @@ function chado_add_node_form_relationships(&$form, &$form_state, $details) {
     ),
   );
 
-  $instructions = 'Relationships should be read like a sentence ([subject] [type] 
-    [object]) in order to determine their direction and thus their meaning. When 
-    adding a relationship, it is easiest to first select the type of relationship you would 
+  $instructions = 'Relationships should be read like a sentence ([subject] [type]
+    [object]) in order to determine their direction and thus their meaning. When
+    adding a relationship, it is easiest to first select the type of relationship you would
     like to enter and then select whether the current %nodetype is the subject
-    or object (based on which "sentence" makes sense). Finally enter the other 
-    %nodetype in the remaining text box (making sure to select from the 
-    autocomplete drop-down) before clicking "Add". To remove incorrect relationships, click the 
-    "Remove" button. Note: you cannot edit previously added relationships 
+    or object (based on which "sentence" makes sense). Finally enter the other
+    %nodetype in the remaining text box (making sure to select from the
+    autocomplete drop-down) before clicking "Add". To remove incorrect relationships, click the
+    "Remove" button. Note: you cannot edit previously added relationships
     but instead need to remove and re-add them.';
   $form['relationships']['descrip'] = array(
     '#type' => 'item',
@@ -321,7 +321,8 @@ function chado_add_node_form_relationships(&$form, &$form_state, $details) {
     '#tree' => TRUE,
     '#prefix' => '<div id="tripal-generic-edit-relationships-table">',
     '#suffix' => '</div>',
-    '#theme' => 'chado_node_relationships_form_table'
+    '#theme' => 'chado_node_relationships_form_table',
+    '#weight' => 5
   );
 
   // Add defaults into form_state to be used elsewhere
@@ -420,13 +421,24 @@ function chado_add_node_form_relationships(&$form, &$form_state, $details) {
     if (array_key_exists($relationship->type_id, $type_options)) {
       $num_relationships++;
 
+      $type_class = str_replace(array(' ','_'), '-', $relationship->type_name);
+      $current_class = 'current-unknown';
+      if ($details['base_key_value']) {
+        if ($relationship->object_id == $details['base_key_value']) {
+          $current_class = 'current-object';
+        }
+        elseif ($relationship->subject_id == $details['base_key_value']) {
+          $current_class = 'current-subject';
+        }
+      }
+
       $form['relationships']['relationship_table'][$relationship->type_id]['#type'] = 'markup';
       $form['relationships']['relationship_table'][$relationship->type_id]['#type'] = '';
 
       $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['#type'] = 'markup';
       $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['#value'] = '';
       $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['#attributes'] = array(
-        'class' => array('relationship', 'saved')
+        'class' => array('relationship', 'saved', $type_class, $current_class)
       );
 
       // Determine whether this relationship is unsaved or not.
@@ -434,7 +446,7 @@ function chado_add_node_form_relationships(&$form, &$form_state, $details) {
       // saved yet we will have entered a TEMP###.
       if (preg_match('/^TEMP/', $relationship->relationship_id)) {
         $form['relationships']['relationship_table'][$relationship->type_id][$relationship->relationship_id]['#attributes'] = array(
-          'class' => array('relationship', 'unsaved')
+          'class' => array('relationship', 'unsaved', $type_class, $current_class)
         );
       }
 
@@ -577,7 +589,8 @@ function chado_add_node_form_relationships(&$form, &$form_state, $details) {
 
   $form['relationships']['admin_message'] = array(
     '#type' => 'markup',
-    '#markup' => $tripal_message
+    '#markup' => $tripal_message,
+    '#weight' => 10
   );
 }
 
@@ -590,7 +603,7 @@ function chado_add_node_form_relationships(&$form, &$form_state, $details) {
 function chado_add_node_form_relationships_add_button_validate($form, &$form_state) {
 
   $details = unserialize($form_state['values']['relationship_table']['details']);
-  
+
   // First deal with autocomplete fields.
   // Extract the base_id assuming '(###) NAME FIELD'.
   if (!empty($form_state['values']['relationship_table']['new']['subject_name'])) {
@@ -630,8 +643,9 @@ function chado_add_node_form_relationships_add_button_validate($form, &$form_sta
     AND $form_state['values']['relationship_table']['new']['object_is_current']) {
 
     form_set_error('relationship_table][new][object_is_current', 'Only one member of the relationship may be
-      the current '.$details['nodetype'].'. This is specified by checking the "Current '.$details['nodetype'].'"
-      checkbox for either the subject or object.');
+      the current '.$details['nodetype'].' in order to avoid a circular relationship. If you really meant to
+      add a circular relationship then check the "Current '.$details['nodetype'].'" for one side of the
+      relationship and enter the current stock name via the autocomplete for the other side of the relationship.');
   }
   // If it was determined current via checkbox, we need to ensure the name
   // provided actually matches the current node.

+ 5 - 10
tripal_core/api/tripal_core.chado_nodes.title_and_path.inc

@@ -546,14 +546,9 @@ function chado_get_node_url($node) {
           $url = str_replace($token, $value, $url);
         }
         else {
-          tripal_report_error(
-            'chado_node_api',
-            TRIPAL_ERROR,
-            'Unable to replace %token. The value in the node should be a string but is instead: %value',
-            array(
-              '%token' => $token,
-              '%value' => print_r($value, TRUE)
-            )
+          tripal_report_error('chado_node_api',TRIPAL_ERROR,
+            'Unable to replace %token. The value in the node should be a string but is instead: \'%value\'',
+            array('%token' => $token, '%value' => print_r($value, TRUE))
           );
         }
       }
@@ -1332,7 +1327,7 @@ function chado_get_token_value($token_info, $node) {
       else {
         tripal_report_error('chado_node_api', TRIPAL_WARNING,
           'Tokens: Unable to determine the value of %token. Things went awry when trying ' .
-          'to access %index for the following: %var.',
+          'to access \'%index\' for the following: \'%var\'.',
           array('%token' => $token, '%index' => $index, '%var' => print_r($var,TRUE))
         );
         return '';
@@ -1346,7 +1341,7 @@ function chado_get_token_value($token_info, $node) {
     else {
       tripal_report_error('chado_node_api', TRIPAL_WARNING,
         'Tokens: Unable to determine the value of %token. Things went awry when trying ' .
-        'to access %index for the following: %var.',
+        'to access \'%index\' for the following: \'%var\'.',
         array('%token' => $token, '%index' => $index, '%var' => print_r($var,TRUE))
       );
       return '';

+ 6 - 5
tripal_core/api/tripal_core.chado_query.api.inc

@@ -1153,8 +1153,9 @@ function chado_select_record($table, $columns, $values, $options = NULL) {
         else {
           tripal_report_error('tripal_core', TRIPAL_ERROR,
             'chado_select_record: There is no value for %field thus we cannot ' .
-            ' check if this record for table, %table, is unique.',
-            array('%field' => $field, '%table' => $table), array('print' => $print_errors));
+            'check if this record for table, %table, is unique. %values',
+            array('%field' => $field, '%table' => $table, '%values' => print_r($values, TRUE)),
+            array('print' => $print_errors));
           return FALSE;
         }
       }
@@ -1263,8 +1264,8 @@ function chado_select_record($table, $columns, $values, $options = NULL) {
           // Ensure that there were results returned.
           elseif (count($results)==0) {
             tripal_report_error('tripal_core', TRIPAL_ERROR,
-              'chado_select_record: the foreign key definition for %field
-              returned no results where the definition supplied was %value',
+              'chado_select_record: the foreign key definition for \'%field\' '.
+              'returned no results where the definition supplied was %value',
               array('%field' => $field, '%value' => print_r($value, TRUE))
             );
             return array();
@@ -1499,7 +1500,7 @@ function chado_query($sql, $args = array()) {
     // the featureloc table has some indexes that use function that call other functions
     // and those calls do not reference a schema, therefore, any tables with featureloc
     // must automaticaly have the chado schema set as active to find
-    if (preg_match('/chado.featureloc/i', $sql)) {
+    if (preg_match('/chado.featureloc/i', $sql) or preg_match('/chado.feature/i', $sql)) {
       $previous_db = chado_set_active('chado') ;
       $results = db_query($sql, $args);
       chado_set_active($previous_db);

+ 53 - 0
tripal_core/api/tripal_core.d3js.api.inc

@@ -0,0 +1,53 @@
+<?php
+/**
+ *
+ */
+
+/**
+ * Implements hook_libraries_info().
+ */
+function tripal_core_libraries_info() {
+  $libraries = array();
+  $libraries['d3'] = array(
+    'name' => 'D3.js',
+    'vendor url' => 'http://d3js.org/',
+    'download url' => 'https://github.com/mbostock/d3',
+    'version arguments' => array(
+      'file' => 'd3.js',
+      'pattern' => '/\s*version: "(\d+\.\d+\.\d+)"/',
+    ),
+    'files' => array(
+      'js' => array(
+        'd3.min.js',
+      ),
+    ),
+  );
+
+  return $libraries;
+}
+
+/**
+ * Load D3.js releated javascripts for the current page.
+ */
+function tripal_add_d3js() {
+  $library = array('loaded' => FALSE);
+
+  // First try to load d3.js using the libraries API.
+  // This will work if the site admin has saved d3.js in their libraries folder.
+  if (module_exists('libraries_api')) {
+    $library = libraries_load('d3');
+  }
+
+  // If we were not able to load d3.js using the libraries API
+  // then revert to loading the remote files manually.
+  if (!isset($library['loaded']) OR !$library['loaded']) {
+
+    // If SSL is being used then use a secure CDN for d3.js
+    if (isset($_SERVER['HTTPS'])) {
+      drupal_add_js('https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js');
+    }
+    else {
+      drupal_add_js('http://d3js.org/d3.v3.min.js');
+    }
+  }
+}

+ 25 - 8
tripal_core/api/tripal_core.files.api.inc

@@ -16,10 +16,16 @@
  */
 
 /**
- * This function is typically used in the '.install' file for a Tripal module
- * Each module should call this function during installation to create
- * the module data directory which is sites/default/files/tripal/[module_name].
- * This directory can then be used by the module for storing files.
+ * Creates a directory for a module in the Drupal's public files directory.
+ *
+ * Previously it was recommended that this function be called during
+ * installation of the module in the .install file.  However this causes
+ * permission problems if the module is installed via drush with a
+ * user account that is not the same as the web user.  Therefore, this
+ * function should not be called in a location accessiblve via a drush
+ * command.  The tripal_get_files_dir() and tripal_get_files_stream()
+ * will automatically create the directory if it doesn't exist so there is
+ * little need to call this function directly.
  *
  * @param $module_name
  *   the name of the module being installed
@@ -50,7 +56,6 @@ function tripal_create_files_dir($module_name, $path = FALSE) {
 
     // now make sure the sub dir exists
     $sub_dir = tripal_get_files_dir() . '/' . $module_name . '/' . $path;
-    print "$sub_dir\n";
     if (!file_prepare_directory($sub_dir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS)) {
       $message = "Can not create directory $sub_dir. ";
       drupal_set_message(check_plain(t($message)), 'error');
@@ -61,7 +66,7 @@ function tripal_create_files_dir($module_name, $path = FALSE) {
 
 /**
  * Retreives the Drupal relative directory for a Tripal module.
- * 
+ *
  * Each Tripal module has a unique data directory which was created using the
  * tripal_create_files_dir function during installation.  This function
  * retrieves the directory path.
@@ -76,10 +81,16 @@ function tripal_create_files_dir($module_name, $path = FALSE) {
  */
 function tripal_get_files_dir($module_name = FALSE) {
 
+  // Build the directory path.
   $data_dir = variable_get('file_public_path', conf_path() . '/files/tripal');
 
+  // If a module name is provided then append the module directory.
   if ($module_name) {
     $data_dir .= "/$module_name";
+
+    // Make sure the directory exists.
+    tripal_create_files_dir($module_name, "/$module_name");
+
   }
 
   return $data_dir;
@@ -87,7 +98,7 @@ function tripal_get_files_dir($module_name = FALSE) {
 
 /**
  * Retreives the Drupal stream (e.g. public://...) for a Tripal module.
- * 
+ *
  * Each Tripal module has a unique data directory which was created using the
  * tripal_create_files_dir function during installation.  This function
  * retrieves the directory path.
@@ -101,10 +112,16 @@ function tripal_get_files_dir($module_name = FALSE) {
  * @ingroup tripal_files_api
  */
 function tripal_get_files_stream($module_name = FALSE) {
+  // Build the directory path.
   $stream =  'public://tripal';
+
+  // If a module name is provided then append the module directory.
   if ($module_name) {
     $stream .= "/$module_name";
+
+    // Make sure the directory exists.
+    tripal_create_files_dir($module_name, "/$module_name");
   }
-  
+
   return $stream;
 }

+ 6 - 1
tripal_core/api/tripal_core.jobs.api.inc

@@ -65,6 +65,7 @@
  * @ingroup tripal_jobs_api
  */
 function tripal_add_job($job_name, $modulename, $callback, $arguments, $uid, $priority = 10) {
+  global $user;
 
   // convert the arguments into a string for storage in the database
   $args = array();
@@ -86,7 +87,11 @@ function tripal_add_job($job_name, $modulename, $callback, $arguments, $uid, $pr
     drupal_set_message(t("Job '%job_name' submitted.", array('%job_name' => $job_name)));
     if (user_access('administer tripal')) {
       $jobs_url = url("admin/tripal/tripal_jobs");
-      drupal_set_message(t("Check the <a href='!jobs_url'>jobs page</a> for status.", array('!jobs_url' => $jobs_url)));
+      drupal_set_message(t("Check the <a href='!jobs_url'>jobs page</a> for status.",
+        array('!jobs_url' => $jobs_url)));
+      drupal_set_message(t("You can execute the job queue manually on the command line " .
+        "using the following Drush command: <br>drush trp-run-jobs --username=%uname --root=%base_path",
+        array('%base_path' => DRUPAL_ROOT, '%uname' => $user->name)));
     }
   }
   else {

+ 2 - 1
tripal_core/api/tripal_core.mviews.api.inc

@@ -41,7 +41,7 @@
  * @ingroup tripal_mviews_api
  *
 function tripal_add_legacy_mview($name, $modulename, $mv_table, $mv_specs, $indexed,
-  $query, $special_index, $comment = NULL) {
+  $query, $special_index, $comment =OR NULL) {
 
   // Create a new record
   $record = new stdClass();
@@ -314,6 +314,7 @@ function chado_get_mview_table_names() {
   $sql = "SELECT name FROM {tripal_mviews}";
   $resource = db_query($sql);
 
+  $tables = array();
   foreach ($resource as $r) {
     $tables[$r->name] = $r->name;
   }

+ 19 - 13
tripal_core/includes/tripal_core.extensions.inc

@@ -319,6 +319,8 @@ function tripal_core_extension_form_add_extensions(&$form, $form_state, $extensi
         continue;
       }
 
+      $extension['title'] = trim($extension['title']);
+
       // If this is an extension module then there will be a home page for it
       $home_page = '';
       if (array_key_exists('home_page', $extension[$namespace])) {
@@ -347,7 +349,7 @@ function tripal_core_extension_form_add_extensions(&$form, $form_state, $extensi
             ->execute()
             ->fetchField();
           if ($blk_id) {
-            $is_installed = '<li>A bulk loader tempalte with this name is already installed.</li>';
+            $is_installed = '<li>A bulk loader template with this name is already installed.</li>';
           }
           break;
         case 'Materialized View':
@@ -524,20 +526,24 @@ function tripal_core_extensions_form_submit($form, &$form_state) {
     $type = $extension['category'];
     switch ($type) {
       case 'Bulk Loader Template':
-        // TODO: we need an API function for adding a bulk loader
-        $id = db_insert('tripal_bulk_loader_template')
-          ->fields(array(
-            'name' => $extension['title'],
-            'template_array' => $extension[$namespace]['bulkldr_export'],
-            'created' => time(),
-            'changed' => time()
-          ))
-          ->execute();
-        if (!$id) {
-          drupal_set_message("Cannot import this bulk loader. Please view the 'Recent log messages' report for more details.",  'error');
+        $options = array(
+          'template_name' => $extension['title'],
+          'template_array' => $extension[$namespace]['bulkldr_export'],
+          'strict' => TRUE,
+        );
+        $errors = array();
+        $warnings = array();
+        $success = tripal_insert_bulk_loader_template($options, $errors, $warnings);
+        if ($success) {
+          drupal_set_message("Bulk loader succesfully added.");
         }
         else {
-          drupal_set_message("Bulk loader succesfully added.");
+          drupal_set_message("Error importing this bulk loader.",  'error');
+          if (count($errors) > 0) {
+            foreach($errors as $field => $message) {
+              drupal_set_message($message, 'error');
+            }
+          }
         }
         break;
       case 'Materialized View':

+ 11 - 4
tripal_core/includes/tripal_core.jobs.inc

@@ -252,10 +252,17 @@ function tripal_jobs_view($job_id) {
 
   // We do not know what the arguments are for and we want to provide a
   // meaningful description to the end-user. So we use a callback function
-  // deinfed in the module that created the job to describe in an array
+  // defined in the module that created the job to describe in an array
   // the arguments provided.  If the callback fails then just use the
-  // arguments as they are
-  $args = preg_split("/::/", $job->arguments);
+  // arguments as they are.  Historically, job arguments were separated with
+  // two colon. We now store them as a serialized array. So, we need to handle
+  // both cases.
+  if (preg_match("/::/", $job->arguments)) {
+    $args = preg_split("/::/", $job->arguments);
+  }
+  else {
+    $args = unserialize($job->arguments);
+  }
   $arg_hook = $job->modulename . "_job_describe_args";
   if (is_callable($arg_hook)) {
     $new_args = call_user_func_array($arg_hook, array($job->callback, $args));
@@ -276,7 +283,7 @@ function tripal_jobs_view($job_id) {
   }
 
   // build the links
-  $links  = l('Return to jobs list', "admin/tripal/tripal_jobs/") . ' | ' .
+  $links  = l('Return to jobs list', "admin/tripal/tripal_jobs/") . ' | ';
   $links .= l('Re-run this job', "admin/tripal/tripal_jobs/rerun/" . $job->job_id) . ' | ';
   if ($job->start_time == 0 and $job->end_time == 0) {
     $links .= l('Cancel this job', "admin/tripal/tripal_jobs/cancel/" . $job->job_id) . ' | ';

+ 1 - 0
tripal_core/includes/tripal_core.mviews.inc

@@ -76,6 +76,7 @@ function tripal_mview_report($mview_id) {
   $update_url = url("admin/tripal/schema/mviews/action/update/$mview->mview_id");
   $delete_url = url("admin/tripal/schema/mviews/action/delete/$mview->mview_id");
   $edit_url = url("admin/tripal/schema/mviews/edit/$mview->mview_id");
+  $export_url = url("admin/tripal/schema/mviews/export/$mview->mview_id");
   $rows[] = array('Actions', "<a href='$update_url'>Populate</a>, <a href='$edit_url'>Edit</a>,  <a href='$delete_url'>Delete</a>");
 
   if ($mview->last_update > 0) {

+ 60 - 23
tripal_core/tripal_core.drush.inc

@@ -21,7 +21,7 @@
  */
 function tripal_core_drush_help($command) {
   switch ($command) {
-    
+
     // TRIPAL JOBS
     case 'trp-run-jobs':
       return dt('Launches pending jobs waiting in the queue.');
@@ -101,14 +101,16 @@ function tripal_core_drush_command() {
   $items['trp-run-jobs'] = array(
     'description' => dt('Launches jobs waiting in the queue. Only one job can execute at a time unless the --parllel=1 option is provided.'),
     'examples' => array(
-      'Single Job' => 'drush trp-run-jobs --user=administrator',
-      'Parallel Job' => 'drush trp-run-jobs --user=administrator --parallel=1'
+      'Single Job' => 'drush trp-run-jobs --username=administrator',
+      'Parallel Job' => 'drush trp-run-jobs --username=administrator --parallel=1'
     ),
     'arguments' => array(),
     'options' => array(
       'user' => array(
+        'description' => dt('DEPRECATED. Conflicts with Drush 7.x --user argument. Please use the --username argument.'),
+      ),
+      'username' => array(
         'description' => dt('The Drupal user name for which the job should be run.  The permissions for this user will be used.'),
-        'required' => TRUE,
       ),
       'parallel' => dt('Normally jobs are executed one at a time. But if you are certain no conflicts will occur with other currently running jobs you may set this argument to a value of 1 to make the job run in parallel with other running jobs.'),
       'job_id' => dt('Provide a job_id to run a specific job. Only jobs that have not been run already can be used'),
@@ -117,14 +119,16 @@ function tripal_core_drush_command() {
   $items['trp-rerun-job'] = array(
     'description' => dt('Re-run a specific job from the queue.'),
     'examples' => array(
-      'Single Job' => 'drush trp-rerun-job --user=administrator --job_id=2',
-      'Parallel Job' => 'drush trp-rerun-job --user=administrator  --job_id=2 --parallel=1'
+      'Single Job' => 'drush trp-rerun-job --username=administrator --job_id=2',
+      'Parallel Job' => 'drush trp-rerun-job --username=administrator  --job_id=2 --parallel=1'
     ),
     'arguments' => array(),
     'options' => array(
       'user' => array(
+        'description' => dt('DEPRECATED. Conflicts with Drush 7.x --user argument. Please use the --username argument.'),
+      ),
+      'username' => array(
         'description' => dt('The Drupal user name for which the job should be run.  The permissions for this user will be used.'),
-        'required' => TRUE,
       ),
       'job_id' => array(
         'description' => dt('The job ID to run.'),
@@ -199,7 +203,7 @@ function tripal_core_drush_command() {
       'Parallel Job' => 'drush tripal-jobs-launch admin --parallel=1'
     ),
     'arguments' => array(
-      'user' => dt('The Drupal username under which the job should be run.  The permissions for this user will be used.'),
+      'username' => dt('The Drupal username under which the job should be run.  The permissions for this user will be used.'),
     ),
     'options' => array(
       'parallel' => dt('Normally jobs are executed one at a time. But if you are certain no conflicts will occur with other currently running jobs you may set this argument to a value of 1 to make the job run in parallel with other running jobs.'),
@@ -214,7 +218,7 @@ function tripal_core_drush_command() {
       'Parallel Job' => 'drush tripal-jobs-rerun admin  2 --parallel=1'
     ),
     'arguments' => array(
-      'user' => dt('The Drupal username under which the job should be run.  The permissions for this user will be used.'),
+      'username' => dt('The Drupal username under which the job should be run.  The permissions for this user will be used.'),
       'job_id' => dt('The job ID to run.'),
     ),
     'options' => array(
@@ -269,7 +273,7 @@ function drush_tripal_core_set_user($username) {
     $results = db_query($sql, array(':name' => $username));
     $u = $results->fetchObject();
     if (!$u) {
-      drush_print('ERROR: Please provide a valid username for running this job.');
+      drush_print('ERROR: Please provide a valid username (--username argument) for running this job.');
       exit;
     }
     global $user;
@@ -277,7 +281,7 @@ function drush_tripal_core_set_user($username) {
     return $u->uid;
   }
   else {
-    drush_print('ERROR: Please provide a username for running this job.');
+    drush_print('ERROR: Please provide a username (--username argument) for running this job.');
     exit;
   }
 }
@@ -290,10 +294,26 @@ function drush_tripal_core_set_user($username) {
  * @ingroup tripal_drush
  */
 function drush_tripal_core_trp_run_jobs() {
-  $username = drush_get_option('user');
   $parallel = drush_get_option('parallel');
   $job_id   = drush_get_option('job_id');
 
+  // Unfortunately later versions of Drush use the '--user' argument which
+  // makes it incompatible with how Tripal was using it.  For backwards
+  // compatabiliy we will accept --user with a non numeric value only. The
+  // numeric value should be for Drush. Tripal will instead use the
+  // --username argument for the fture.
+  $user = drush_get_option('user');
+  $uname = drush_get_option('username');
+  if ($user and is_numeric($user)) {
+  }
+  elseif ($user) {
+    print "\nNOTE: Use of the --user argument is deprecated as it conflicts with the --user argument of Drush 7.x. Please now use --username instead.\n\n";
+    $username = $user;
+  }
+  if ($uname) {
+    $username = $uname;
+  }
+
   drush_tripal_core_set_user($username);
 
   if ($parallel) {
@@ -321,7 +341,7 @@ function drush_tripal_core_tripal_jobs_launch($username) {
   $job_id = drush_get_option('job_id');
 
   drush_tripal_core_set_user($username);
-  
+
   drush_print("\n\nDEPRECATED: This drush command is outdated.\nIt will ".
     "continue to work but please consider using the 'trp-run-jobs' command.\n\n");
 
@@ -347,10 +367,27 @@ function drush_tripal_core_tripal_jobs_launch($username) {
  * @ingroup tripal_drush
  */
 function drush_tripal_core_trp_rerun_job() {
-  $username = drush_get_option('user');
+  // Unfortunately later versions of Drush use the '--user' argument which
+  // makes it incompatible with how Tripal was using it.  For backwards
+  // compatabiliy we will accept --user with a non numeric value only. The
+  // numeric value should be for Drush. Tripal will instead use the
+  // --username argument for the fture.
+  $user = drush_get_option('user');
+  $uname = drush_get_option('username');
+  print "USER: '$user', UNAME: '$uname'\n";
+  if ($user and is_numeric($user)) {
+  }
+  elseif ($user) {
+    print "\nNOTE: Use of the --user argument is deprecated as it conflicts with the --user argument of Drush 7.x. Please now use --username instead.\n\n";
+    $username = $user;
+  }
+  if ($uname) {
+    $username = $uname;
+  }
+
   $parallel = drush_get_option('parallel');
   $job_id   = drush_get_option('job_id');
-  
+
   drush_tripal_core_set_user($username);
   $new_job_id = tripal_rerun_job($job_id, FALSE);
 
@@ -377,10 +414,10 @@ function drush_tripal_core_trp_rerun_job() {
  * @ingroup tripal_drush
  */
 function drush_tripal_core_tripal_jobs_rerun($username, $job_id) {
-  
+
   drush_print("\n\nDEPRECATED: This drush command is outdated.\nIt will ".
       "continue to work but please consider using the 'trp-rerun-job' command.\n\n");
-  
+
   drush_tripal_core_set_user($username);
   $new_job_id = tripal_rerun_job($job_id, FALSE);
   drush_tripal_core_tripal_jobs_launch($username, $new_job_id);
@@ -430,7 +467,7 @@ function drush_tripal_core_trp_get_currjob() {
 function drush_tripal_core_tripal_jobs_current() {
   drush_print("\n\nDEPRECATED: This drush command is outdated.\nIt will ".
       "continue to work but please consider using the 'trp-get-currjob' command.\n\n");
-  
+
   drush_tripal_core_trp_get_currjob();
 }
 
@@ -476,10 +513,10 @@ function drush_tripal_core_trp_refresh_mview() {
  * @ingroup tripal_drush
  */
 function drush_tripal_core_tripal_update_mview() {
-  
+
   $mview_id = drush_get_option('mview_id');
   $table_name = drush_get_option('table_name');
-  
+
   drush_print("\n\nDEPRECATED: This drush command is outdated.\nIt will ".
       "continue to work but please consider using the 'trp-refresh-mview' command.\n\n");
 
@@ -518,7 +555,7 @@ function drush_tripal_core_tripal_update_mview() {
 function drush_tripal_core_tripal_chado_version() {
   drush_print("\n\nDEPRECATED: This drush command is outdated.\nIt will ".
       "continue to work but please consider using the 'trp-get-cversion' command.\n\n");
-  
+
   drush_tripal_core_trp_get_cversion();
 }
 
@@ -562,7 +599,7 @@ function drush_tripal_core_trp_get_table() {
  */
 function drush_tripal_core_tripal_chadotable_desc($table_name) {
   $section = drush_get_option('section');
-  
+
   drush_print("\n\nDEPRECATED: This drush command is outdated.\nIt will ".
       "continue to work but please consider using the 'trp-get-table' command.\n\n");
 
@@ -584,7 +621,7 @@ function drush_tripal_core_tripal_chadotable_desc($table_name) {
  */
 function drush_tripal_core_trp_clean_nodes() {
   $table = drush_get_option('table');
-  
+
   chado_cleanup_orphaned_nodes($table, 0);
 }
 

+ 2 - 2
tripal_core/tripal_core.info

@@ -3,7 +3,7 @@ description = Provides support for all Tripal modules and includes the Tripal AP
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 configure = admin/tripal
 
 stylesheets[all][] = theme/css/tripal.css
@@ -12,4 +12,4 @@ scripts[]          = theme/js/tripal.js
 dependencies[] = views
 dependencies[] = path
 dependencies[] = search
-dependencies[] = php
+dependencies[] = php

+ 2 - 12
tripal_core/tripal_core.install

@@ -11,18 +11,8 @@
  */
 function tripal_core_install() {
 
-  // make the data directory for this module
-  $data_dir = tripal_get_files_dir();
-  if (!file_prepare_directory($data_dir, FILE_CREATE_DIRECTORY)) {
-    $message = "Cannot create directory $data_dir. This module may not " .
-               "behave correctly without this directory.  Please  create " .
-               "the directory manually or fix the problem and reinstall.";
-    drupal_set_message(check_plain($message), 'error');
-    tripal_report_error('tripal_core', TRIPAL_ERROR, $message, array());
-  }
-
-  // the foreign key specification doesn't really add one to the
-  // Drupal schema, it is just used internally, but we want one
+  // The foreign key specification doesn't really add one to the
+  // Drupal schema, it is just used internally, but we want one.
   db_query('
       ALTER TABLE {tripal_custom_tables}
       ADD CONSTRAINT tripal_custom_tables_fk1

+ 19 - 1
tripal_core/tripal_core.module

@@ -34,7 +34,7 @@ require_once 'api/tripal_core.files.api.inc';
 require_once 'api/tripal_core.jobs.api.inc';
 require_once 'api/tripal_core.tripal.api.inc';
 require_once 'api/tripal_core.tripal_variables.api.inc';
-
+require_once 'api/tripal_core.d3js.api.inc';
 require_once 'api/tripal_core.DEPRECATED.inc';
 
 // INCLUDES
@@ -313,6 +313,24 @@ function tripal_core_menu() {
     'access arguments' => array('administer tripal'),
     'type' => MENU_CALLBACK,
   );
+  // TODO: complete the code for exporting and importing of MViews.
+  // Need to address security issues of sharing SQL.
+  $items['admin/tripal/schema/mviews/import'] = array(
+    'title' => 'Import MView',
+    'description' => 'Import a materialized view from another Tripal instance.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('tripal_mviews_import_form'),
+    'access arguments' => array('administer tripal'),
+    'type' => MENU_CALLBACK,
+  );
+  $items['admin/tripal/schema/mviews/%tblid/export'] = array(
+    'title' => 'Export MView',
+    'description' => 'Export a materialized view for use by another Tripal instance.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('tripal_mviews_export_form', 5),
+    'access arguments' => array('administer tripal'),
+    'type' => MENU_CALLBACK,
+  );
 
   // Custom Tables
   $items['admin/tripal/schema/custom_tables'] = array(

+ 3 - 7
tripal_cv/includes/tripal_cv.obo_loader.inc

@@ -151,7 +151,7 @@ function tripal_cv_load_obo_v1_2_id($obo_id, $jobid = NULL) {
     $module = $matches[1];
     $path = drupal_realpath(drupal_get_path('module', $module));
     $obo->path = preg_replace("/\{.*?\}/", $path, $obo->path);
-  } 
+  }
 
   // if the reference is for a remote URL then run the URL processing function
   if (preg_match("/^http:\/\//", $obo->path) or preg_match("/^ftp:\/\//", $obo->path)) {
@@ -871,11 +871,10 @@ function tripal_cv_obo_add_synonyms($term, $cvterm) {
           'name' => 'synonym_type',
         ),
       );
-      $options = array('is_duplicate' => 1);
-      $results = chado_select_record('cvterm', array('*'), $values, $options);
+      $syntype = tripal_get_cvterm($values);
 
       // if it doesn't exist then add it
-      if (!$results) {
+      if (!$syntype) {
         // build a 'term' object so we can add the missing term
         $term = array(
            'name' => $scope,
@@ -890,9 +889,6 @@ function tripal_cv_obo_add_synonyms($term, $cvterm) {
           tripal_cv_obo_quiterror("Cannot add synonym type: internal:$scope");
         }
       }
-      else {
-        $syntype = $results[0];
-      }
 
       // make sure the synonym doesn't already exists
       $values = array(

+ 1 - 1
tripal_cv/tripal_cv.info

@@ -3,7 +3,7 @@ description = Supports the Controlled Vocabulary (CV) tables of Chado by providi
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 configure = admin/tripal/chado/tripal_cv
 
 dependencies[] = tripal_core

+ 0 - 3
tripal_cv/tripal_cv.install

@@ -49,9 +49,6 @@ function tripal_cv_requirements($phase) {
  */
 function tripal_cv_install() {
 
-  // create the module's data directory
-  tripal_create_files_dir('tripal_cv');
-
   // add the cv_root_mview
   tripal_cv_add_cv_root_mview();
 

+ 354 - 348
tripal_cv/tripal_cv.views_default.inc

@@ -32,51 +32,54 @@ function tripal_cv_views_default_views() {
  */
 function tripal_cv_defaultview_admin_cvs_listing() {
 
-$view = new view();
-$view->name = 'tripal_cv_admin_cvs';
-$view->description = 'DO NOT DISABLE';
-$view->tag = 'tripal admin';
-$view->base_table = 'cv';
-$view->human_name = 'CVs Admin';
-$view->core = 6;
-$view->api_version = '3.0';
-$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+  $view = new view();
+  $view->name = 'tripal_cv_admin_cvs';
+  $view->description = 'DO NOT DISABLE';
+  $view->tag = 'tripal admin';
+  $view->base_table = 'cv';
+  $view->human_name = 'CVs Admin';
+  $view->core = 6;
+  $view->api_version = '3.0';
+  $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
 
-/* Display: Defaults */
-$handler = $view->new_display('default', 'Defaults', 'default');
-$handler->display->display_options['title'] = 'Controlled Vocabularies';
-$handler->display->display_options['use_more_always'] = FALSE;
-$handler->display->display_options['access']['type'] = 'perm';
-$handler->display->display_options['access']['perm'] = 'administer controlled vocabularies';
-$handler->display->display_options['cache']['type'] = 'none';
-$handler->display->display_options['query']['type'] = 'views_query';
-$handler->display->display_options['exposed_form']['type'] = 'basic';
-$handler->display->display_options['pager']['type'] = 'full';
-$handler->display->display_options['pager']['options']['items_per_page'] = '25';
-$handler->display->display_options['pager']['options']['offset'] = '0';
-$handler->display->display_options['pager']['options']['id'] = '0';
-$handler->display->display_options['pager']['options']['quantity'] = '9';
-$handler->display->display_options['style_plugin'] = 'table';
-$handler->display->display_options['style_options']['grouping'] = '';
-$handler->display->display_options['style_options']['columns'] = array(
-  'name' => 'name',
-  'definition' => 'definition',
-  'nothing' => 'nothing',
-);
-$handler->display->display_options['style_options']['default'] = 'name';
-$handler->display->display_options['style_options']['info'] = array(
-  'name' => array(
-    'sortable' => 1,
-    'separator' => '',
-  ),
-  'definition' => array(
-    'sortable' => 0,
-    'separator' => '',
-  ),
-  'nothing' => array(
-    'separator' => '',
-  ),
-);
+  /* Display: Defaults */
+  $handler = $view->new_display('default', 'Defaults', 'default');
+  $handler->display->display_options['title'] = 'Controlled Vocabularies';
+  $handler->display->display_options['use_more_always'] = FALSE;
+  $handler->display->display_options['access']['type'] = 'perm';
+  $handler->display->display_options['access']['perm'] = 'administer controlled vocabularies';
+  $handler->display->display_options['cache']['type'] = 'none';
+  $handler->display->display_options['query']['type'] = 'views_query';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of controlled vocabularies that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of vocabularies or to find a specific vocabulary.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
+  $handler->display->display_options['pager']['type'] = 'full';
+  $handler->display->display_options['pager']['options']['items_per_page'] = '25';
+  $handler->display->display_options['pager']['options']['offset'] = '0';
+  $handler->display->display_options['pager']['options']['id'] = '0';
+  $handler->display->display_options['pager']['options']['quantity'] = '9';
+  $handler->display->display_options['style_plugin'] = 'table';
+  $handler->display->display_options['style_options']['grouping'] = '';
+  $handler->display->display_options['style_options']['columns'] = array(
+    'name' => 'name',
+    'definition' => 'definition',
+    'nothing' => 'nothing',
+  );
+  $handler->display->display_options['style_options']['default'] = 'name';
+  $handler->display->display_options['style_options']['info'] = array(
+    'name' => array(
+      'sortable' => 1,
+      'separator' => '',
+    ),
+    'definition' => array(
+      'sortable' => 0,
+      'separator' => '',
+    ),
+    'nothing' => array(
+      'separator' => '',
+    ),
+  );
 /* Header: Global: Action Links */
   $handler->display->display_options['header']['action_links_area']['id'] = 'action_links_area';
   $handler->display->display_options['header']['action_links_area']['table'] = 'views';
@@ -91,118 +94,118 @@ $handler->display->display_options['style_options']['info'] = array(
     'label-2' => 'Load Ontology',
     'path-2' => 'admin/tripal/chado/tripal_cv/obo_loader',
   );
-/* No results behavior: Global: Text area */
-$handler->display->display_options['empty']['text']['id'] = 'area';
-$handler->display->display_options['empty']['text']['table'] = 'views';
-$handler->display->display_options['empty']['text']['field'] = 'area';
-$handler->display->display_options['empty']['text']['content'] = 'No controlled vocabularies match the supplied criteria.';
-$handler->display->display_options['empty']['text']['format'] = '2';
-/* Field: Chado Cv: Cv Id */
-$handler->display->display_options['fields']['cv_id']['id'] = 'cv_id';
-$handler->display->display_options['fields']['cv_id']['table'] = 'cv';
-$handler->display->display_options['fields']['cv_id']['field'] = 'cv_id';
-$handler->display->display_options['fields']['cv_id']['exclude'] = TRUE;
-$handler->display->display_options['fields']['cv_id']['separator'] = '';
-/* Field: Chado Cv: Name */
-$handler->display->display_options['fields']['name']['id'] = 'name';
-$handler->display->display_options['fields']['name']['table'] = 'cv';
-$handler->display->display_options['fields']['name']['field'] = 'name';
-/* Field: Chado Cv: Definition */
-$handler->display->display_options['fields']['definition']['id'] = 'definition';
-$handler->display->display_options['fields']['definition']['table'] = 'cv';
-$handler->display->display_options['fields']['definition']['field'] = 'definition';
-/* Field: Global: Custom text */
-$handler->display->display_options['fields']['nothing_1']['id'] = 'nothing_1';
-$handler->display->display_options['fields']['nothing_1']['table'] = 'views';
-$handler->display->display_options['fields']['nothing_1']['field'] = 'nothing';
-$handler->display->display_options['fields']['nothing_1']['label'] = 'Edit Link';
-$handler->display->display_options['fields']['nothing_1']['exclude'] = TRUE;
-$handler->display->display_options['fields']['nothing_1']['alter']['text'] = 'edit';
-$handler->display->display_options['fields']['nothing_1']['alter']['make_link'] = TRUE;
-$handler->display->display_options['fields']['nothing_1']['alter']['path'] = 'admin/tripal/chado/tripal_cv/cv/edit/[cv_id]';
-/* Field: Global: Custom text */
-$handler->display->display_options['fields']['nothing']['id'] = 'nothing';
-$handler->display->display_options['fields']['nothing']['table'] = 'views';
-$handler->display->display_options['fields']['nothing']['field'] = 'nothing';
-$handler->display->display_options['fields']['nothing']['label'] = 'View Terms link';
-$handler->display->display_options['fields']['nothing']['exclude'] = TRUE;
-$handler->display->display_options['fields']['nothing']['alter']['text'] = 'View Terms';
-$handler->display->display_options['fields']['nothing']['alter']['make_link'] = TRUE;
-$handler->display->display_options['fields']['nothing']['alter']['path'] = 'admin/tripal/chado/tripal_cv/cvterms?cv=[name]';
-$handler->display->display_options['fields']['nothing']['hide_alter_empty'] = TRUE;
-/* Field: Global: Custom text */
-$handler->display->display_options['fields']['nothing_3']['id'] = 'nothing_3';
-$handler->display->display_options['fields']['nothing_3']['table'] = 'views';
-$handler->display->display_options['fields']['nothing_3']['field'] = 'nothing';
-$handler->display->display_options['fields']['nothing_3']['label'] = 'Add term';
-$handler->display->display_options['fields']['nothing_3']['exclude'] = TRUE;
-$handler->display->display_options['fields']['nothing_3']['alter']['text'] = 'Add Term';
-$handler->display->display_options['fields']['nothing_3']['alter']['make_link'] = TRUE;
-$handler->display->display_options['fields']['nothing_3']['alter']['path'] = 'admin/tripal/chado/tripal_cv/cv/[cv_id]/cvterm/add';
-/* Field: Global: Custom text */
-$handler->display->display_options['fields']['nothing_2']['id'] = 'nothing_2';
-$handler->display->display_options['fields']['nothing_2']['table'] = 'views';
-$handler->display->display_options['fields']['nothing_2']['field'] = 'nothing';
-$handler->display->display_options['fields']['nothing_2']['label'] = '';
-$handler->display->display_options['fields']['nothing_2']['alter']['text'] = '[nothing_1]<br />
-  [nothing]   [nothing_3]';
-$handler->display->display_options['fields']['nothing_2']['element_class'] = 'short-column';
-$handler->display->display_options['fields']['nothing_2']['element_label_class'] = 'short-column';
-$handler->display->display_options['fields']['nothing_2']['element_label_colon'] = FALSE;
-/* Sort criterion: Chado Cv: Name */
-$handler->display->display_options['sorts']['name']['id'] = 'name';
-$handler->display->display_options['sorts']['name']['table'] = 'cv';
-$handler->display->display_options['sorts']['name']['field'] = 'name';
-/* Filter criterion: Chado Cv: Name */
-$handler->display->display_options['filters']['name']['id'] = 'name';
-$handler->display->display_options['filters']['name']['table'] = 'cv';
-$handler->display->display_options['filters']['name']['field'] = 'name';
-$handler->display->display_options['filters']['name']['operator'] = 'contains';
-$handler->display->display_options['filters']['name']['group'] = '0';
-$handler->display->display_options['filters']['name']['exposed'] = TRUE;
-$handler->display->display_options['filters']['name']['expose']['operator_id'] = 'name_op';
-$handler->display->display_options['filters']['name']['expose']['label'] = 'Name Contains';
-$handler->display->display_options['filters']['name']['expose']['operator'] = 'name_op';
-$handler->display->display_options['filters']['name']['expose']['identifier'] = 'name';
-$handler->display->display_options['filters']['name']['expose']['remember_roles'] = array(
-  2 => '2',
-  1 => 0,
-  3 => 0,
-);
-/* Filter criterion: Chado Cv: Definition */
-$handler->display->display_options['filters']['definition']['id'] = 'definition';
-$handler->display->display_options['filters']['definition']['table'] = 'cv';
-$handler->display->display_options['filters']['definition']['field'] = 'definition';
-$handler->display->display_options['filters']['definition']['operator'] = 'contains';
-$handler->display->display_options['filters']['definition']['group'] = '0';
-$handler->display->display_options['filters']['definition']['exposed'] = TRUE;
-$handler->display->display_options['filters']['definition']['expose']['operator_id'] = 'definition_op';
-$handler->display->display_options['filters']['definition']['expose']['label'] = 'Definition Contains';
-$handler->display->display_options['filters']['definition']['expose']['operator'] = 'definition_op';
-$handler->display->display_options['filters']['definition']['expose']['identifier'] = 'definition';
-$handler->display->display_options['filters']['definition']['expose']['remember_roles'] = array(
-  2 => '2',
-  1 => 0,
-  3 => 0,
-);
+  /* No results behavior: Global: Text area */
+  $handler->display->display_options['empty']['text']['id'] = 'area';
+  $handler->display->display_options['empty']['text']['table'] = 'views';
+  $handler->display->display_options['empty']['text']['field'] = 'area';
+  $handler->display->display_options['empty']['text']['content'] = 'No controlled vocabularies match the supplied criteria.';
+  $handler->display->display_options['empty']['text']['format'] = '2';
+  /* Field: Chado Cv: Cv Id */
+  $handler->display->display_options['fields']['cv_id']['id'] = 'cv_id';
+  $handler->display->display_options['fields']['cv_id']['table'] = 'cv';
+  $handler->display->display_options['fields']['cv_id']['field'] = 'cv_id';
+  $handler->display->display_options['fields']['cv_id']['exclude'] = TRUE;
+  $handler->display->display_options['fields']['cv_id']['separator'] = '';
+  /* Field: Chado Cv: Name */
+  $handler->display->display_options['fields']['name']['id'] = 'name';
+  $handler->display->display_options['fields']['name']['table'] = 'cv';
+  $handler->display->display_options['fields']['name']['field'] = 'name';
+  /* Field: Chado Cv: Definition */
+  $handler->display->display_options['fields']['definition']['id'] = 'definition';
+  $handler->display->display_options['fields']['definition']['table'] = 'cv';
+  $handler->display->display_options['fields']['definition']['field'] = 'definition';
+  /* Field: Global: Custom text */
+  $handler->display->display_options['fields']['nothing_1']['id'] = 'nothing_1';
+  $handler->display->display_options['fields']['nothing_1']['table'] = 'views';
+  $handler->display->display_options['fields']['nothing_1']['field'] = 'nothing';
+  $handler->display->display_options['fields']['nothing_1']['label'] = 'Edit Link';
+  $handler->display->display_options['fields']['nothing_1']['exclude'] = TRUE;
+  $handler->display->display_options['fields']['nothing_1']['alter']['text'] = 'edit';
+  $handler->display->display_options['fields']['nothing_1']['alter']['make_link'] = TRUE;
+  $handler->display->display_options['fields']['nothing_1']['alter']['path'] = 'admin/tripal/chado/tripal_cv/cv/edit/[cv_id]';
+  /* Field: Global: Custom text */
+  $handler->display->display_options['fields']['nothing']['id'] = 'nothing';
+  $handler->display->display_options['fields']['nothing']['table'] = 'views';
+  $handler->display->display_options['fields']['nothing']['field'] = 'nothing';
+  $handler->display->display_options['fields']['nothing']['label'] = 'View Terms link';
+  $handler->display->display_options['fields']['nothing']['exclude'] = TRUE;
+  $handler->display->display_options['fields']['nothing']['alter']['text'] = 'View Terms';
+  $handler->display->display_options['fields']['nothing']['alter']['make_link'] = TRUE;
+  $handler->display->display_options['fields']['nothing']['alter']['path'] = 'admin/tripal/chado/tripal_cv/cvterms?cv=[name]';
+  $handler->display->display_options['fields']['nothing']['hide_alter_empty'] = TRUE;
+  /* Field: Global: Custom text */
+  $handler->display->display_options['fields']['nothing_3']['id'] = 'nothing_3';
+  $handler->display->display_options['fields']['nothing_3']['table'] = 'views';
+  $handler->display->display_options['fields']['nothing_3']['field'] = 'nothing';
+  $handler->display->display_options['fields']['nothing_3']['label'] = 'Add term';
+  $handler->display->display_options['fields']['nothing_3']['exclude'] = TRUE;
+  $handler->display->display_options['fields']['nothing_3']['alter']['text'] = 'Add Term';
+  $handler->display->display_options['fields']['nothing_3']['alter']['make_link'] = TRUE;
+  $handler->display->display_options['fields']['nothing_3']['alter']['path'] = 'admin/tripal/chado/tripal_cv/cv/[cv_id]/cvterm/add';
+  /* Field: Global: Custom text */
+  $handler->display->display_options['fields']['nothing_2']['id'] = 'nothing_2';
+  $handler->display->display_options['fields']['nothing_2']['table'] = 'views';
+  $handler->display->display_options['fields']['nothing_2']['field'] = 'nothing';
+  $handler->display->display_options['fields']['nothing_2']['label'] = '';
+  $handler->display->display_options['fields']['nothing_2']['alter']['text'] = '[nothing_1]<br />
+    [nothing]   [nothing_3]';
+  $handler->display->display_options['fields']['nothing_2']['element_class'] = 'short-column';
+  $handler->display->display_options['fields']['nothing_2']['element_label_class'] = 'short-column';
+  $handler->display->display_options['fields']['nothing_2']['element_label_colon'] = FALSE;
+  /* Sort criterion: Chado Cv: Name */
+  $handler->display->display_options['sorts']['name']['id'] = 'name';
+  $handler->display->display_options['sorts']['name']['table'] = 'cv';
+  $handler->display->display_options['sorts']['name']['field'] = 'name';
+  /* Filter criterion: Chado Cv: Name */
+  $handler->display->display_options['filters']['name']['id'] = 'name';
+  $handler->display->display_options['filters']['name']['table'] = 'cv';
+  $handler->display->display_options['filters']['name']['field'] = 'name';
+  $handler->display->display_options['filters']['name']['operator'] = 'contains';
+  $handler->display->display_options['filters']['name']['group'] = '0';
+  $handler->display->display_options['filters']['name']['exposed'] = TRUE;
+  $handler->display->display_options['filters']['name']['expose']['operator_id'] = 'name_op';
+  $handler->display->display_options['filters']['name']['expose']['label'] = 'Name Contains';
+  $handler->display->display_options['filters']['name']['expose']['operator'] = 'name_op';
+  $handler->display->display_options['filters']['name']['expose']['identifier'] = 'name';
+  $handler->display->display_options['filters']['name']['expose']['remember_roles'] = array(
+    2 => '2',
+    1 => 0,
+    3 => 0,
+  );
+  /* Filter criterion: Chado Cv: Definition */
+  $handler->display->display_options['filters']['definition']['id'] = 'definition';
+  $handler->display->display_options['filters']['definition']['table'] = 'cv';
+  $handler->display->display_options['filters']['definition']['field'] = 'definition';
+  $handler->display->display_options['filters']['definition']['operator'] = 'contains';
+  $handler->display->display_options['filters']['definition']['group'] = '0';
+  $handler->display->display_options['filters']['definition']['exposed'] = TRUE;
+  $handler->display->display_options['filters']['definition']['expose']['operator_id'] = 'definition_op';
+  $handler->display->display_options['filters']['definition']['expose']['label'] = 'Definition Contains';
+  $handler->display->display_options['filters']['definition']['expose']['operator'] = 'definition_op';
+  $handler->display->display_options['filters']['definition']['expose']['identifier'] = 'definition';
+  $handler->display->display_options['filters']['definition']['expose']['remember_roles'] = array(
+    2 => '2',
+    1 => 0,
+    3 => 0,
+  );
 
-  /** MANUALLY ADD since filter handler no exporting correctly */
+  /** MANUALLY ADD since filter handler not exporting correctly */
   $handler->display->display_options['filters']['name']['expose']['values_form_type'] = 'textfield';
   $handler->display->display_options['filters']['name']['expose']['select_multiple'] = FALSE;
   $handler->display->display_options['filters']['name']['expose']['select_optional'] = FALSE;
   $handler->display->display_options['filters']['name']['expose']['max_length'] = 40;
 
-/* Display: Page */
-$handler = $view->new_display('page', 'Page', 'page_1');
-$handler->display->display_options['path'] = 'admin/tripal/chado/tripal_cv/cvs';
-$handler->display->display_options['menu']['type'] = 'default tab';
-$handler->display->display_options['menu']['title'] = 'Vocabularies';
-$handler->display->display_options['menu']['description'] = 'A listing of all controlled vocabularies';
-$handler->display->display_options['menu']['weight'] = '-10';
-$handler->display->display_options['menu']['name'] = 'management';
-$handler->display->display_options['menu']['context'] = 0;
-$handler->display->display_options['menu']['context_only_inline'] = 0;
-$handler->display->display_options['tab_options']['weight'] = '0';
+  /* Display: Page */
+  $handler = $view->new_display('page', 'Page', 'page_1');
+  $handler->display->display_options['path'] = 'admin/tripal/chado/tripal_cv/cvs';
+  $handler->display->display_options['menu']['type'] = 'default tab';
+  $handler->display->display_options['menu']['title'] = 'Vocabularies';
+  $handler->display->display_options['menu']['description'] = 'A listing of all controlled vocabularies';
+  $handler->display->display_options['menu']['weight'] = '-10';
+  $handler->display->display_options['menu']['name'] = 'management';
+  $handler->display->display_options['menu']['context'] = 0;
+  $handler->display->display_options['menu']['context_only_inline'] = 0;
+  $handler->display->display_options['tab_options']['weight'] = '0';
 
 
   /** MANUALLY ADD since filter handler no exporting correctly
@@ -222,62 +225,65 @@ $handler->display->display_options['tab_options']['weight'] = '0';
  */
 function tripal_cv_defaultview_admin_cvterms_listing() {
 
-$view = new view();
-$view->name = 'tripal_cv_admin_cvterms';
-$view->description = 'DO NOT DISABLE';
-$view->tag = 'tripal admin';
-$view->base_table = 'cvterm';
-$view->human_name = 'CV Terms Admin';
-$view->core = 0;
-$view->api_version = '3.0';
-$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
+  $view = new view();
+  $view->name = 'tripal_cv_admin_cvterms';
+  $view->description = 'DO NOT DISABLE';
+  $view->tag = 'tripal admin';
+  $view->base_table = 'cvterm';
+  $view->human_name = 'CV Terms Admin';
+  $view->core = 0;
+  $view->api_version = '3.0';
+  $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
 
-/* Display: Defaults */
-$handler = $view->new_display('default', 'Defaults', 'default');
-$handler->display->display_options['title'] = 'Controlled Vocabulary Terms';
-$handler->display->display_options['use_more_always'] = FALSE;
-$handler->display->display_options['access']['type'] = 'perm';
-$handler->display->display_options['access']['perm'] = 'administer controlled vocabularies';
-$handler->display->display_options['cache']['type'] = 'none';
-$handler->display->display_options['query']['type'] = 'views_query';
-$handler->display->display_options['exposed_form']['type'] = 'basic';
-$handler->display->display_options['pager']['type'] = 'full';
-$handler->display->display_options['pager']['options']['items_per_page'] = '25';
-$handler->display->display_options['pager']['options']['offset'] = '0';
-$handler->display->display_options['pager']['options']['id'] = '0';
-$handler->display->display_options['pager']['options']['quantity'] = '9';
-$handler->display->display_options['style_plugin'] = 'table';
-$handler->display->display_options['style_options']['grouping'] = '';
-$handler->display->display_options['style_options']['columns'] = array(
-  'name_1' => 'name_1',
-  'name' => 'name',
-  'definition' => 'definition',
-  'is_obsolete' => 'is_obsolete',
-  'is_relationshiptype' => 'is_relationshiptype',
-);
-$handler->display->display_options['style_options']['default'] = '-1';
-$handler->display->display_options['style_options']['info'] = array(
-  'name_1' => array(
-    'sortable' => 1,
-    'separator' => '',
-  ),
-  'name' => array(
-    'sortable' => 1,
-    'separator' => '',
-  ),
-  'definition' => array(
-    'sortable' => 0,
-    'separator' => '',
-  ),
-  'is_obsolete' => array(
-    'sortable' => 1,
-    'separator' => '',
-  ),
-  'is_relationshiptype' => array(
-    'sortable' => 1,
-    'separator' => '',
-  ),
-);
+  /* Display: Defaults */
+  $handler = $view->new_display('default', 'Defaults', 'default');
+  $handler->display->display_options['title'] = 'Controlled Vocabulary Terms';
+  $handler->display->display_options['use_more_always'] = FALSE;
+  $handler->display->display_options['access']['type'] = 'perm';
+  $handler->display->display_options['access']['perm'] = 'administer controlled vocabularies';
+  $handler->display->display_options['cache']['type'] = 'none';
+  $handler->display->display_options['query']['type'] = 'views_query';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of controlled vocabulary terms that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of terms or to find a specific term.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
+  $handler->display->display_options['pager']['type'] = 'full';
+  $handler->display->display_options['pager']['options']['items_per_page'] = '25';
+  $handler->display->display_options['pager']['options']['offset'] = '0';
+  $handler->display->display_options['pager']['options']['id'] = '0';
+  $handler->display->display_options['pager']['options']['quantity'] = '9';
+  $handler->display->display_options['style_plugin'] = 'table';
+  $handler->display->display_options['style_options']['grouping'] = '';
+  $handler->display->display_options['style_options']['columns'] = array(
+    'name_1' => 'name_1',
+    'name' => 'name',
+    'definition' => 'definition',
+    'is_obsolete' => 'is_obsolete',
+    'is_relationshiptype' => 'is_relationshiptype',
+  );
+  $handler->display->display_options['style_options']['default'] = '-1';
+  $handler->display->display_options['style_options']['info'] = array(
+    'name_1' => array(
+      'sortable' => 1,
+      'separator' => '',
+    ),
+    'name' => array(
+      'sortable' => 1,
+      'separator' => '',
+    ),
+    'definition' => array(
+      'sortable' => 0,
+      'separator' => '',
+    ),
+    'is_obsolete' => array(
+      'sortable' => 1,
+      'separator' => '',
+    ),
+    'is_relationshiptype' => array(
+      'sortable' => 1,
+      'separator' => '',
+    ),
+  );
 /* Header: Global: Action Links */
   $handler->display->display_options['header']['action_links_area']['id'] = 'action_links_area';
   $handler->display->display_options['header']['action_links_area']['table'] = 'views';
@@ -288,140 +294,140 @@ $handler->display->display_options['style_options']['info'] = array(
     'label-1' => 'Add Term',
     'path-1' => 'admin/tripal/chado/tripal_cv/cvterm/add',
   );
-/* No results behavior: Global: Text area */
-$handler->display->display_options['empty']['text']['id'] = 'area';
-$handler->display->display_options['empty']['text']['table'] = 'views';
-$handler->display->display_options['empty']['text']['field'] = 'area';
-$handler->display->display_options['empty']['text']['content'] = 'There are no terms associated with the selected controlled vocabulary. Please select a different vocabulary from the list above.';
-$handler->display->display_options['empty']['text']['format'] = '1';
-/* Field: Chado Cv: Cv Id */
-$handler->display->display_options['fields']['cv_id']['id'] = 'cv_id';
-$handler->display->display_options['fields']['cv_id']['table'] = 'cv';
-$handler->display->display_options['fields']['cv_id']['field'] = 'cv_id';
-$handler->display->display_options['fields']['cv_id']['exclude'] = TRUE;
-$handler->display->display_options['fields']['cv_id']['separator'] = '';
-/* Field: Chado Cv: Name */
-$handler->display->display_options['fields']['name_1']['id'] = 'name_1';
-$handler->display->display_options['fields']['name_1']['table'] = 'cv';
-$handler->display->display_options['fields']['name_1']['field'] = 'name';
-$handler->display->display_options['fields']['name_1']['label'] = 'Vocabulary';
-$handler->display->display_options['fields']['name_1']['alter']['make_link'] = TRUE;
-$handler->display->display_options['fields']['name_1']['alter']['path'] = 'admin/tripal/chado/tripal_cv/cvs?name=[name_1]';
-/* Field: Chado Cvterm: Cvterm Id */
-$handler->display->display_options['fields']['cvterm_id']['id'] = 'cvterm_id';
-$handler->display->display_options['fields']['cvterm_id']['table'] = 'cvterm';
-$handler->display->display_options['fields']['cvterm_id']['field'] = 'cvterm_id';
-$handler->display->display_options['fields']['cvterm_id']['exclude'] = TRUE;
-$handler->display->display_options['fields']['cvterm_id']['separator'] = '';
-/* Field: Chado Cvterm: Name */
-$handler->display->display_options['fields']['name']['id'] = 'name';
-$handler->display->display_options['fields']['name']['table'] = 'cvterm';
-$handler->display->display_options['fields']['name']['field'] = 'name';
-/* Field: Chado Cvterm: Definition */
-$handler->display->display_options['fields']['definition']['id'] = 'definition';
-$handler->display->display_options['fields']['definition']['table'] = 'cvterm';
-$handler->display->display_options['fields']['definition']['field'] = 'definition';
-$handler->display->display_options['fields']['definition']['element_class'] = 'wide-column';
-$handler->display->display_options['fields']['definition']['element_label_class'] = 'wide-column';
-/* Field: Chado Cvterm: Is Obsolete */
-$handler->display->display_options['fields']['is_obsolete']['id'] = 'is_obsolete';
-$handler->display->display_options['fields']['is_obsolete']['table'] = 'cvterm';
-$handler->display->display_options['fields']['is_obsolete']['field'] = 'is_obsolete';
-$handler->display->display_options['fields']['is_obsolete']['label'] = 'Obsolete?';
-$handler->display->display_options['fields']['is_obsolete']['alter']['alter_text'] = TRUE;
-$handler->display->display_options['fields']['is_obsolete']['alter']['text'] = 'Yes';
-$handler->display->display_options['fields']['is_obsolete']['element_class'] = 'short-column';
-$handler->display->display_options['fields']['is_obsolete']['element_label_class'] = 'short-column';
-$handler->display->display_options['fields']['is_obsolete']['empty'] = 'No';
-$handler->display->display_options['fields']['is_obsolete']['empty_zero'] = TRUE;
-$handler->display->display_options['fields']['is_obsolete']['separator'] = '';
-/* Field: Chado Cvterm: Is Relationshiptype */
-$handler->display->display_options['fields']['is_relationshiptype']['id'] = 'is_relationshiptype';
-$handler->display->display_options['fields']['is_relationshiptype']['table'] = 'cvterm';
-$handler->display->display_options['fields']['is_relationshiptype']['field'] = 'is_relationshiptype';
-$handler->display->display_options['fields']['is_relationshiptype']['label'] = 'Relation-ship?';
-$handler->display->display_options['fields']['is_relationshiptype']['alter']['alter_text'] = TRUE;
-$handler->display->display_options['fields']['is_relationshiptype']['alter']['text'] = 'Yes';
-$handler->display->display_options['fields']['is_relationshiptype']['element_class'] = 'short-column';
-$handler->display->display_options['fields']['is_relationshiptype']['element_label_class'] = 'short-column';
-$handler->display->display_options['fields']['is_relationshiptype']['empty'] = 'No';
-$handler->display->display_options['fields']['is_relationshiptype']['empty_zero'] = TRUE;
-$handler->display->display_options['fields']['is_relationshiptype']['separator'] = '';
-/* Field: Global: Custom text */
-$handler->display->display_options['fields']['nothing']['id'] = 'nothing';
-$handler->display->display_options['fields']['nothing']['table'] = 'views';
-$handler->display->display_options['fields']['nothing']['field'] = 'nothing';
-$handler->display->display_options['fields']['nothing']['label'] = 'Edit Link';
-$handler->display->display_options['fields']['nothing']['exclude'] = TRUE;
-$handler->display->display_options['fields']['nothing']['alter']['text'] = 'edit';
-$handler->display->display_options['fields']['nothing']['alter']['make_link'] = TRUE;
-$handler->display->display_options['fields']['nothing']['alter']['path'] = 'admin/tripal/chado/tripal_cv/cv/[cv_id]/cvterm/edit/[cvterm_id]';
-/* Field: Global: Custom text */
-$handler->display->display_options['fields']['nothing_1']['id'] = 'nothing_1';
-$handler->display->display_options['fields']['nothing_1']['table'] = 'views';
-$handler->display->display_options['fields']['nothing_1']['field'] = 'nothing';
-$handler->display->display_options['fields']['nothing_1']['label'] = '';
-$handler->display->display_options['fields']['nothing_1']['alter']['text'] = '[nothing]';
-$handler->display->display_options['fields']['nothing_1']['element_class'] = 'short-column';
-$handler->display->display_options['fields']['nothing_1']['element_label_class'] = 'short-column';
-$handler->display->display_options['fields']['nothing_1']['element_label_colon'] = FALSE;
-/* Sort criterion: Chado Cv: Name */
-$handler->display->display_options['sorts']['name']['id'] = 'name';
-$handler->display->display_options['sorts']['name']['table'] = 'cv';
-$handler->display->display_options['sorts']['name']['field'] = 'name';
-/* Sort criterion: Chado Cvterm: Name */
-$handler->display->display_options['sorts']['name_1']['id'] = 'name_1';
-$handler->display->display_options['sorts']['name_1']['table'] = 'cvterm';
-$handler->display->display_options['sorts']['name_1']['field'] = 'name';
-/* Filter criterion: Chado Cv: Name */
-$handler->display->display_options['filters']['name']['id'] = 'name';
-$handler->display->display_options['filters']['name']['table'] = 'cv';
-$handler->display->display_options['filters']['name']['field'] = 'name';
-$handler->display->display_options['filters']['name']['value'] = 'All';
-$handler->display->display_options['filters']['name']['group'] = '0';
-$handler->display->display_options['filters']['name']['exposed'] = TRUE;
-$handler->display->display_options['filters']['name']['expose']['operator_id'] = 'name_op';
-$handler->display->display_options['filters']['name']['expose']['label'] = 'Vocabulary';
-$handler->display->display_options['filters']['name']['expose']['operator'] = 'name_op';
-$handler->display->display_options['filters']['name']['expose']['identifier'] = 'cv';
-$handler->display->display_options['filters']['name']['expose']['remember_roles'] = array(
-  2 => '2',
-  1 => 0,
-  3 => 0,
-);
-/* Filter criterion: Chado Cvterm: Name */
-$handler->display->display_options['filters']['name_1']['id'] = 'name_1';
-$handler->display->display_options['filters']['name_1']['table'] = 'cvterm';
-$handler->display->display_options['filters']['name_1']['field'] = 'name';
-$handler->display->display_options['filters']['name_1']['operator'] = 'contains';
-$handler->display->display_options['filters']['name_1']['group'] = '0';
-$handler->display->display_options['filters']['name_1']['exposed'] = TRUE;
-$handler->display->display_options['filters']['name_1']['expose']['operator_id'] = '';
-$handler->display->display_options['filters']['name_1']['expose']['label'] = 'Name Contains';
-$handler->display->display_options['filters']['name_1']['expose']['identifier'] = 'name';
-$handler->display->display_options['filters']['name_1']['expose']['remember_roles'] = array(
-  2 => '2',
-  1 => 0,
-  3 => 0,
-);
-/* Filter criterion: Chado Cvterm: Definition */
-$handler->display->display_options['filters']['definition']['id'] = 'definition';
-$handler->display->display_options['filters']['definition']['table'] = 'cvterm';
-$handler->display->display_options['filters']['definition']['field'] = 'definition';
-$handler->display->display_options['filters']['definition']['operator'] = 'contains';
-$handler->display->display_options['filters']['definition']['group'] = '0';
-$handler->display->display_options['filters']['definition']['exposed'] = TRUE;
-$handler->display->display_options['filters']['definition']['expose']['operator_id'] = 'definition_op';
-$handler->display->display_options['filters']['definition']['expose']['label'] = 'Definition Contains';
-$handler->display->display_options['filters']['definition']['expose']['operator'] = 'definition_op';
-$handler->display->display_options['filters']['definition']['expose']['identifier'] = 'definition';
-$handler->display->display_options['filters']['definition']['expose']['remember_roles'] = array(
-  2 => '2',
-  1 => 0,
-  3 => 0,
-);
+  /* No results behavior: Global: Text area */
+  $handler->display->display_options['empty']['text']['id'] = 'area';
+  $handler->display->display_options['empty']['text']['table'] = 'views';
+  $handler->display->display_options['empty']['text']['field'] = 'area';
+  $handler->display->display_options['empty']['text']['content'] = 'There are no terms associated with the selected controlled vocabulary. Please select a different vocabulary from the list above.';
+  $handler->display->display_options['empty']['text']['format'] = '1';
+  /* Field: Chado Cv: Cv Id */
+  $handler->display->display_options['fields']['cv_id']['id'] = 'cv_id';
+  $handler->display->display_options['fields']['cv_id']['table'] = 'cv';
+  $handler->display->display_options['fields']['cv_id']['field'] = 'cv_id';
+  $handler->display->display_options['fields']['cv_id']['exclude'] = TRUE;
+  $handler->display->display_options['fields']['cv_id']['separator'] = '';
+  /* Field: Chado Cv: Name */
+  $handler->display->display_options['fields']['name_1']['id'] = 'name_1';
+  $handler->display->display_options['fields']['name_1']['table'] = 'cv';
+  $handler->display->display_options['fields']['name_1']['field'] = 'name';
+  $handler->display->display_options['fields']['name_1']['label'] = 'Vocabulary';
+  $handler->display->display_options['fields']['name_1']['alter']['make_link'] = TRUE;
+  $handler->display->display_options['fields']['name_1']['alter']['path'] = 'admin/tripal/chado/tripal_cv/cvs?name=[name_1]';
+  /* Field: Chado Cvterm: Cvterm Id */
+  $handler->display->display_options['fields']['cvterm_id']['id'] = 'cvterm_id';
+  $handler->display->display_options['fields']['cvterm_id']['table'] = 'cvterm';
+  $handler->display->display_options['fields']['cvterm_id']['field'] = 'cvterm_id';
+  $handler->display->display_options['fields']['cvterm_id']['exclude'] = TRUE;
+  $handler->display->display_options['fields']['cvterm_id']['separator'] = '';
+  /* Field: Chado Cvterm: Name */
+  $handler->display->display_options['fields']['name']['id'] = 'name';
+  $handler->display->display_options['fields']['name']['table'] = 'cvterm';
+  $handler->display->display_options['fields']['name']['field'] = 'name';
+  /* Field: Chado Cvterm: Definition */
+  $handler->display->display_options['fields']['definition']['id'] = 'definition';
+  $handler->display->display_options['fields']['definition']['table'] = 'cvterm';
+  $handler->display->display_options['fields']['definition']['field'] = 'definition';
+  $handler->display->display_options['fields']['definition']['element_class'] = 'wide-column';
+  $handler->display->display_options['fields']['definition']['element_label_class'] = 'wide-column';
+  /* Field: Chado Cvterm: Is Obsolete */
+  $handler->display->display_options['fields']['is_obsolete']['id'] = 'is_obsolete';
+  $handler->display->display_options['fields']['is_obsolete']['table'] = 'cvterm';
+  $handler->display->display_options['fields']['is_obsolete']['field'] = 'is_obsolete';
+  $handler->display->display_options['fields']['is_obsolete']['label'] = 'Obsolete?';
+  $handler->display->display_options['fields']['is_obsolete']['alter']['alter_text'] = TRUE;
+  $handler->display->display_options['fields']['is_obsolete']['alter']['text'] = 'Yes';
+  $handler->display->display_options['fields']['is_obsolete']['element_class'] = 'short-column';
+  $handler->display->display_options['fields']['is_obsolete']['element_label_class'] = 'short-column';
+  $handler->display->display_options['fields']['is_obsolete']['empty'] = 'No';
+  $handler->display->display_options['fields']['is_obsolete']['empty_zero'] = TRUE;
+  $handler->display->display_options['fields']['is_obsolete']['separator'] = '';
+  /* Field: Chado Cvterm: Is Relationshiptype */
+  $handler->display->display_options['fields']['is_relationshiptype']['id'] = 'is_relationshiptype';
+  $handler->display->display_options['fields']['is_relationshiptype']['table'] = 'cvterm';
+  $handler->display->display_options['fields']['is_relationshiptype']['field'] = 'is_relationshiptype';
+  $handler->display->display_options['fields']['is_relationshiptype']['label'] = 'Relation-ship?';
+  $handler->display->display_options['fields']['is_relationshiptype']['alter']['alter_text'] = TRUE;
+  $handler->display->display_options['fields']['is_relationshiptype']['alter']['text'] = 'Yes';
+  $handler->display->display_options['fields']['is_relationshiptype']['element_class'] = 'short-column';
+  $handler->display->display_options['fields']['is_relationshiptype']['element_label_class'] = 'short-column';
+  $handler->display->display_options['fields']['is_relationshiptype']['empty'] = 'No';
+  $handler->display->display_options['fields']['is_relationshiptype']['empty_zero'] = TRUE;
+  $handler->display->display_options['fields']['is_relationshiptype']['separator'] = '';
+  /* Field: Global: Custom text */
+  $handler->display->display_options['fields']['nothing']['id'] = 'nothing';
+  $handler->display->display_options['fields']['nothing']['table'] = 'views';
+  $handler->display->display_options['fields']['nothing']['field'] = 'nothing';
+  $handler->display->display_options['fields']['nothing']['label'] = 'Edit Link';
+  $handler->display->display_options['fields']['nothing']['exclude'] = TRUE;
+  $handler->display->display_options['fields']['nothing']['alter']['text'] = 'edit';
+  $handler->display->display_options['fields']['nothing']['alter']['make_link'] = TRUE;
+  $handler->display->display_options['fields']['nothing']['alter']['path'] = 'admin/tripal/chado/tripal_cv/cv/[cv_id]/cvterm/edit/[cvterm_id]';
+  /* Field: Global: Custom text */
+  $handler->display->display_options['fields']['nothing_1']['id'] = 'nothing_1';
+  $handler->display->display_options['fields']['nothing_1']['table'] = 'views';
+  $handler->display->display_options['fields']['nothing_1']['field'] = 'nothing';
+  $handler->display->display_options['fields']['nothing_1']['label'] = '';
+  $handler->display->display_options['fields']['nothing_1']['alter']['text'] = '[nothing]';
+  $handler->display->display_options['fields']['nothing_1']['element_class'] = 'short-column';
+  $handler->display->display_options['fields']['nothing_1']['element_label_class'] = 'short-column';
+  $handler->display->display_options['fields']['nothing_1']['element_label_colon'] = FALSE;
+  /* Sort criterion: Chado Cv: Name */
+  $handler->display->display_options['sorts']['name']['id'] = 'name';
+  $handler->display->display_options['sorts']['name']['table'] = 'cv';
+  $handler->display->display_options['sorts']['name']['field'] = 'name';
+  /* Sort criterion: Chado Cvterm: Name */
+  $handler->display->display_options['sorts']['name_1']['id'] = 'name_1';
+  $handler->display->display_options['sorts']['name_1']['table'] = 'cvterm';
+  $handler->display->display_options['sorts']['name_1']['field'] = 'name';
+  /* Filter criterion: Chado Cv: Name */
+  $handler->display->display_options['filters']['name']['id'] = 'name';
+  $handler->display->display_options['filters']['name']['table'] = 'cv';
+  $handler->display->display_options['filters']['name']['field'] = 'name';
+  $handler->display->display_options['filters']['name']['value'] = 'All';
+  $handler->display->display_options['filters']['name']['group'] = '0';
+  $handler->display->display_options['filters']['name']['exposed'] = TRUE;
+  $handler->display->display_options['filters']['name']['expose']['operator_id'] = 'name_op';
+  $handler->display->display_options['filters']['name']['expose']['label'] = 'Vocabulary';
+  $handler->display->display_options['filters']['name']['expose']['operator'] = 'name_op';
+  $handler->display->display_options['filters']['name']['expose']['identifier'] = 'cv';
+  $handler->display->display_options['filters']['name']['expose']['remember_roles'] = array(
+    2 => '2',
+    1 => 0,
+    3 => 0,
+  );
+  /* Filter criterion: Chado Cvterm: Name */
+  $handler->display->display_options['filters']['name_1']['id'] = 'name_1';
+  $handler->display->display_options['filters']['name_1']['table'] = 'cvterm';
+  $handler->display->display_options['filters']['name_1']['field'] = 'name';
+  $handler->display->display_options['filters']['name_1']['operator'] = 'contains';
+  $handler->display->display_options['filters']['name_1']['group'] = '0';
+  $handler->display->display_options['filters']['name_1']['exposed'] = TRUE;
+  $handler->display->display_options['filters']['name_1']['expose']['operator_id'] = '';
+  $handler->display->display_options['filters']['name_1']['expose']['label'] = 'Name Contains';
+  $handler->display->display_options['filters']['name_1']['expose']['identifier'] = 'name';
+  $handler->display->display_options['filters']['name_1']['expose']['remember_roles'] = array(
+    2 => '2',
+    1 => 0,
+    3 => 0,
+  );
+  /* Filter criterion: Chado Cvterm: Definition */
+  $handler->display->display_options['filters']['definition']['id'] = 'definition';
+  $handler->display->display_options['filters']['definition']['table'] = 'cvterm';
+  $handler->display->display_options['filters']['definition']['field'] = 'definition';
+  $handler->display->display_options['filters']['definition']['operator'] = 'contains';
+  $handler->display->display_options['filters']['definition']['group'] = '0';
+  $handler->display->display_options['filters']['definition']['exposed'] = TRUE;
+  $handler->display->display_options['filters']['definition']['expose']['operator_id'] = 'definition_op';
+  $handler->display->display_options['filters']['definition']['expose']['label'] = 'Definition Contains';
+  $handler->display->display_options['filters']['definition']['expose']['operator'] = 'definition_op';
+  $handler->display->display_options['filters']['definition']['expose']['identifier'] = 'definition';
+  $handler->display->display_options['filters']['definition']['expose']['remember_roles'] = array(
+    2 => '2',
+    1 => 0,
+    3 => 0,
+  );
 
-/** MANUALLY ADDED since filter handler no exporting correctly */
+  /** MANUALLY ADDED since filter handler no exporting correctly */
   $handler->display->display_options['filters']['name']['expose']['values_form_type'] = 'select';
   $handler->display->display_options['filters']['name']['expose']['select_multiple'] = FALSE;
   $handler->display->display_options['filters']['name']['expose']['select_optional'] = TRUE;
@@ -432,16 +438,16 @@ $handler->display->display_options['filters']['definition']['expose']['remember_
   $handler->display->display_options['filters']['name_1']['expose']['select_optional'] = FALSE;
   $handler->display->display_options['filters']['name_1']['expose']['max_length'] = 40;
 
-/* Display: Page */
-$handler = $view->new_display('page', 'Page', 'page_1');
-$handler->display->display_options['path'] = 'admin/tripal/chado/tripal_cv/cvterms';
-$handler->display->display_options['menu']['type'] = 'tab';
-$handler->display->display_options['menu']['title'] = 'Terms';
-$handler->display->display_options['menu']['description'] = 'A listing of a controlled vocabulary terms for a given vocabulary';
-$handler->display->display_options['menu']['weight'] = '-8';
-$handler->display->display_options['menu']['name'] = 'management';
-$handler->display->display_options['menu']['context'] = 0;
-$handler->display->display_options['menu']['context_only_inline'] = 0;
+  /* Display: Page */
+  $handler = $view->new_display('page', 'Page', 'page_1');
+  $handler->display->display_options['path'] = 'admin/tripal/chado/tripal_cv/cvterms';
+  $handler->display->display_options['menu']['type'] = 'tab';
+  $handler->display->display_options['menu']['title'] = 'Terms';
+  $handler->display->display_options['menu']['description'] = 'A listing of a controlled vocabulary terms for a given vocabulary';
+  $handler->display->display_options['menu']['weight'] = '-8';
+  $handler->display->display_options['menu']['name'] = 'management';
+  $handler->display->display_options['menu']['context'] = 0;
+  $handler->display->display_options['menu']['context_only_inline'] = 0;
 
   /** MANUALLY ADD since filter handler no exporting correctly
   $handler->display->display_options['filters']['name']['expose']['values_form_type'] = 'select';

+ 1 - 1
tripal_db/tripal_db.info

@@ -3,7 +3,7 @@ description = Supports the database cross-reference tables of Chado by providing
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 configure = admin/tripal/chado/tripal_db
 
 dependencies[] = tripal_core

+ 0 - 3
tripal_db/tripal_db.install

@@ -48,9 +48,6 @@ function tripal_db_requirements($phase) {
  */
 function tripal_db_install() {
 
-  // create the module's data directory
-  tripal_create_files_dir('tripal_db');
-
 }
 
 /**

+ 8 - 2
tripal_db/tripal_db.views_default.inc

@@ -50,7 +50,10 @@ function tripal_db_defaultview_admin_db_listing() {
   $handler->display->display_options['access']['perm'] = 'access chado_db content';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of external databases that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of databases or to find a specific database.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '50';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -231,7 +234,10 @@ function tripal_db_defaultview_admin_dbxref_listing() {
   $handler->display->display_options['access']['perm'] = 'administer db cross-references';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of external database references that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of references or to find a specific reference.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '50';
   $handler->display->display_options['style_plugin'] = 'table';

+ 3 - 4
tripal_example/includes/tripal_example.chado_node.inc

@@ -220,7 +220,7 @@ function chado_example_form($node, &$form_state) {
   );
 
   $form['description'] = array(
-    '#type' => 'textarea',
+    '#type' => 'text_format',
     '#title' => t('Description'),
     '#required' => TRUE,
     '#default_value' => $description,
@@ -324,7 +324,6 @@ function chado_example_validate($node, $form, &$form_state) {
 
   // be sure to always trim text fields
   $node->uniquename   = property_exists($node, 'uniquename') ? trim($node->uniquename) : '';
-  $node->description  = property_exists($node, 'description') ? trim($node->description) : '';
 
   // Validating for an update. If the 'nid' property is present in the node then
   // this is an update and validation can be different for updates
@@ -385,7 +384,7 @@ function chado_example_insert($node) {
 
     // be sure to always trim text fields
     $node->uniquename   = trim($node->uniquename);
-    $node->description  = trim($node->description);
+    $node->description  = trim($node->description['value']);
 
     // get the example type record
     $type_cv = tripal_get_default_cv('example', 'type_id');
@@ -484,7 +483,7 @@ function chado_example_insert($node) {
 function chado_example_update($node) {
   // be sure to always trim text fields
   $node->uniquename   = trim($node->uniquename);
-  $node->description  = trim($node->description);
+  $node->description  = trim($node->description['value']);
 
   // use the chado_update_record() function to update the record
   $match = array(

+ 1 - 1
tripal_example/tripal_example.info

@@ -26,7 +26,7 @@ package = Tripal Extensions
 ; Follow these instructions when specifying the version:
 ; https://drupal.org/node/1015226
 ;
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 
 ;
 ; Style-sheets containing CSS that should always be available for the

+ 0 - 9
tripal_example/tripal_example.install

@@ -69,15 +69,6 @@ function tripal_example_requirements($phase) {
  */
 function tripal_example_install() {
 
-  // EXPLANATION: If your module will making data publicly available for
-  // download or use by the site you can create the directory using the
-  // tripal_create_files_dir() function. This will create a directory in the
-  // public access directory which will typically be in
-  // sites/default/files/tripal/[module name]/
-
-  // create the module's data directory
-  tripal_create_files_dir('tripal_example');
-
   // EXPLANATION: Here is a good place to add any materialized views, controlled
   // vocabularies CV, databases or CV terms needed by your module.
   // To keep this module code short, create functions to do each of those tasks

+ 11 - 5
tripal_example/tripal_example.module

@@ -9,10 +9,11 @@
  */
 
 // EXPLANATION: include any files needed for this module. That includes any API
-// file, the theme file, or include files.
+// file, the theme file, or files with functions for new node types.  Try to
+// include other files only when needed so as to reduce the loading time
+// for the module.
 require('api/tripal_example.api.inc');
 require('theme/tripal_example.theme.inc');
-require('includes/tripal_example.admin.inc');
 require('includes/tripal_example.chado_node.inc');
 
 
@@ -90,6 +91,11 @@ function tripal_example_menu() {
     'page callback' => 'tripal_example_admin_examples_listing',
     'access arguments' => array('administer tripal example'),
     'type' => MENU_NORMAL_ITEM,
+    // We include the file where the 'page callback' function
+    // is located.  This removes the need to include all of the
+    // include files at the top of the module, and speeds
+    // module loading time.
+    'file' => '/includes/tripal_example.admin.inc',
   );
 
   // EXPLANATION: all extension modules should provide help documentation to
@@ -107,7 +113,7 @@ function tripal_example_menu() {
     'page arguments' => array('tripal_example_help'),
     'access arguments' => array('administer tripal example'),
     'type' => MENU_LOCAL_TASK,
-    'weight' => 10
+    'weight' => 10,
   );
 
   // EXPLANATION: all extension modules should provide a configuration page.
@@ -126,7 +132,7 @@ function tripal_example_menu() {
     'page arguments' => array('tripal_example_admin'),
     'access arguments' => array('administer tripal example'),
     'type' => MENU_LOCAL_TASK,
-    'weight' => 5
+    'weight' => 5,
   );
 
   // EXPLANATION: If your module defines a new chado node type and that node
@@ -141,7 +147,7 @@ function tripal_example_menu() {
     'page arguments' => array('chado_node_sync_form', 'tripal_example', 'chado_example'),
     'access arguments' => array('administer tripal example'),
     'type' => MENU_LOCAL_TASK,
-    'weight' => 2
+    'weight' => 2,
   );
 
   // EXPLANATION: If your module defines a new node type that uses the default

+ 0 - 53
tripal_feature/api/tripal_feature.schema.api.inc

@@ -1,53 +0,0 @@
-<?php
-/**
- * @file
- * Further describe some of the feature tables to the tripal schema
- */
-
-/**
- * Implements hook_chado_schema_v1_2_tripal_gff_temp()
- * Purpose: To describe the structure of 'tripal_gff_temp' to tripal
- * @see chado_insert_record()
- * @see chado_update_record()
- * @see chado_select_record()
- *
- * @return
- *    An array describing the 'tripal_gff_temp' table
- *
- * @ingroup tripal_chado_v1.2_schema_api
- *
- */
-function tripal_feature_chado_schema_v1_2_tripal_gff_temp() {
-  $schema = array(
-    'table' => 'tripal_gff_temp',
-    'fields' => array(
-      'feature_id' => array(
-        'type' => 'int',
-        'not null' => TRUE,
-      ),
-      'organism_id' => array(
-        'type' => 'int',
-        'not null' => TRUE,
-      ),
-      'uniquename' => array(
-        'type' => 'text',
-        'not null' => TRUE,
-      ),
-      'type_name' => array(
-        'type' => 'varchar',
-        'length' => '1024',
-        'not null' => TRUE,
-      ),
-    ),
-    'indexes' => array(
-      'tripal_gff_temp_idx0' => array('feature_id'),
-      'tripal_gff_temp_idx0' => array('organism_id'),
-      'tripal_gff_temp_idx1' => array('uniquename'),
-    ),
-    'unique keys' => array(
-      'tripal_gff_temp_uq0' => array('feature_id'),
-      'tripal_gff_temp_uq1' => array('uniquename', 'organism_id', 'type_name'),
-    ),
-  );
-  return $schema;
-}

+ 20 - 1
tripal_feature/includes/tripal_feature.admin.inc

@@ -36,6 +36,16 @@ function tripal_feature_admin_feature_view() {
     $output .= '</ul>';
   }
 
+  // Add a summary chart.
+  //-----------------------------------
+  $output .= theme('tripal_feature_bar_chart_type_organism_summary');
+  drupal_add_js('
+    Drupal.behaviors.tripalFeature_moveAdminSummaryChart = {
+      attach: function (context, settings) {
+        jQuery("#tripal-feature-admin-summary").insertBefore( jQuery(".view-filters") );
+    }};
+  ', 'inline');
+
   return $output;
 }
 
@@ -56,7 +66,7 @@ function tripal_feature_admin() {
     'options' => array(
       '[feature.name]' => 'Feature Name Only',
       '[feature.uniquename]' => 'Feature Unique Name Only',
-        // there should always be one options matching the unique constraint.
+      // there should always be one options matching the unique constraint.
       '[feature.name], [feature.uniquename] ([feature.type_id>cvterm.name]) [feature.organism_id>organism.genus] [feature.organism_id>organism.species]' => 'Unique Contraint: Includes the name, uniquename, type and scientific name'
     ),
     // the token indicating the unique constraint in the options array
@@ -159,3 +169,12 @@ function tripal_feature_admin_validate($form, &$form_state) {
   }
 
 }
+
+/**
+ * USort function for the admin summary chart.
+ * Not meant to be called directly.
+ */
+function tripal_feature_admin_summary_sort($a, $b) {
+  if ($a['total_features'] == $b['total_features']) return 0;
+  return $b['total_features'] - $a['total_features'];
+}

+ 2 - 4
tripal_feature/includes/tripal_feature.chado_node.inc

@@ -599,13 +599,13 @@ function chado_feature_delete($node) {
 
   $feature_id  = chado_get_id_from_nid('feature', $node->nid);
 
-  // if we don't have a library id for this node then this isn't a node of
+  // If we don't have a feature id for this node then this isn't a node of
   // type chado_library or the entry in the chado_library table was lost.
   if (!$feature_id) {
     return;
   }
 
-  // remove the drupal content
+  // Remove the drupal content.
   $sql_del = "DELETE FROM {chado_feature} WHERE nid = :nid AND vid = :vid";
   db_query($sql_del, array(':nid' => $node->nid, ':vid' => $node->vid));
   $sql_del = "DELETE FROM {node} WHERE nid = :nid AND vid = :vid";
@@ -793,7 +793,6 @@ function tripal_feature_node_presave($node) {
  * @ingroup tripal_feature
  */
 function tripal_feature_node_insert($node) {
-
   // set the URL path after inserting.  We do it here because we do not
   // know the feature_id in the presave
   switch ($node->type) {
@@ -810,7 +809,6 @@ function tripal_feature_node_insert($node) {
 
       // Now get the title.
       $node->title = chado_get_node_title($node);
-
       break;
   }
 }

+ 67 - 109
tripal_feature/includes/tripal_feature.delete.inc

@@ -10,14 +10,6 @@
  * @ingroup tripal_feature
  */
 function tripal_feature_delete_form() {
-  // get the list of organisms
-  $sql = "SELECT * FROM {organism} ORDER BY genus, species";
-  $org_rset = chado_query($sql);
-  $organisms = array();
-  $organisms[''] = '';
-  while ($organism = $org_rset->fetchObject()) {
-    $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
-  }
   $form['desc'] = array(
     '#markup' => t("Use one or more of the following fields to identify sets of features to be deleted."),
   );
@@ -35,12 +27,15 @@ function tripal_feature_delete_form() {
     '#description' => t('Select this checbox if the names listed in the feature
       names box above are the unique name of the feature rather than the human readable names.'),
   );
+  $cv = tripal_get_cv(array('name' => 'sequence'));
   $form['seq_type']= array(
-    '#type' => 'textfield',
-    '#title' => t('Sequence Type'),
-    '#description' => t('Please enter the Sequence Ontology term that describes the features to be deleted. Use in conjunction with an organism or anaylysis.'),
+   '#title'       => t('Feature Type'),
+   '#type'        => 'textfield',
+   '#description' => t("Choose the feature type."),
+   '#autocomplete_path' => "admin/tripal/chado/tripal_cv/cvterm/auto_name/$cv->cv_id",
   );
 
+  $organisms = tripal_get_organism_select_options(FALSE);
   $form['organism_id'] = array(
    '#title'       => t('Organism'),
    '#type'        => 'select',
@@ -48,23 +43,13 @@ function tripal_feature_delete_form() {
    '#options'     => $organisms,
   );
 
-
-  // get the list of analyses
-  $sql = "SELECT * FROM {analysis} ORDER BY name";
-  $org_rset = chado_query($sql);
-  $analyses = array();
-  $analyses[''] = '';
-  while ($analysis = $org_rset->fetchObject()) {
-    $analyses[$analysis->analysis_id] = "$analysis->name ($analysis->program $analysis->programversion, $analysis->sourcename)";
-  }
-  //  TODO: ADD THIS BACK IN LATER
-  //
-  //   $form['analysis']['analysis_id'] = array (
-  //     '#title'       => t('Analysis'),
-  //     '#type'        => t('select'),
-  //     '#description' => t("Choose the analysis for which associated features will be deleted."),
-  //     '#options'     => $analyses,
-  //   );
+  $analyses = tripal_get_analysis_select_options(FALSE);
+  $form['analysis_id'] = array (
+    '#title'       => t('Analysis'),
+    '#type'        => t('select'),
+    '#description' => t("Choose the analysis for which associated features will be deleted."),
+    '#options'     => $analyses,
+  );
 
   $form['button'] = array(
     '#type' => 'submit',
@@ -81,30 +66,13 @@ function tripal_feature_delete_form() {
 function tripal_feature_delete_form_validate($form, &$form_state) {
   $organism_id   = $form_state['values']['organism_id'];
   $seq_type      = trim($form_state['values']['seq_type']);
-  //$analysis_id   = $form_state['values']['analysis_id'];
+  $analysis_id   = $form_state['values']['analysis_id'];
   $is_unique     = $form_state['values']['is_unique'];
   $feature_names = $form_state['values']['feature_names'];
 
-  if (!$organism_id and !$seq_type and !$feature_names) { // !$anaysis_id and
+  if (!$analysis_id and !$organism_id and !$seq_type and !$feature_names) {
     form_set_error('feature_names', t("Please select at least one option"));
   }
-
-  // check to make sure the types exists
-  if ($seq_type) {
-    $cvtermsql = "
-      SELECT CVT.cvterm_id
-      FROM {cvterm} CVT
-        INNER JOIN {cv} CV on CVT.cv_id = CV.cv_id
-        LEFT JOIN {cvtermsynonym} CVTS on CVTS.cvterm_id = CVT.cvterm_id
-      WHERE cv.name = :cvname and (CVT.name = :name or CVTS.synonym = :synonym)
-    ";
-    $cvterm = chado_query($cvtermsql,
-      array(':cvname' => 'sequence', ':name' => $seq_type, ':synonym' => $seq_type))->fetchObject();
-    if (!$cvterm) {
-      form_set_error('seq_type', t("The Sequence Ontology (SO) term selected for the " .
-      "sequence type is not available in the database. Please check spelling or select another."));
-    }
-  }
 }
 
 /**
@@ -117,13 +85,11 @@ function tripal_feature_delete_form_submit($form, &$form_state) {
 
   $organism_id   = $form_state['values']['organism_id'];
   $seq_type      = trim($form_state['values']['seq_type']);
-  //$analysis_id   = $form_state['values']['analysis_id'];
+  $analysis_id   = $form_state['values']['analysis_id'];
   $is_unique     = $form_state['values']['is_unique'];
   $feature_names = $form_state['values']['feature_names'];
-  $analysis_id = NULL;
 
   $args = array($organism_id, $analysis_id, $seq_type, $is_unique, $feature_names);
-
   tripal_add_job("Delete features", 'tripal_feature',
     'tripal_feature_delete_features', $args, $user->uid);
 }
@@ -153,21 +119,14 @@ function tripal_feature_delete_features($organism_id, $analysis_id, $seq_type,
   global $user;
   $match = array();
 
-  // Deleting of features will cause a cascade delete on the
-  // featureloc table which in turn will wind up calling create_point
-  // function which is not prefix with the schema, and an error occurs.
-  // Therefore, we set the active database to chado to get around that
-  // problem.
-  $previous_db = chado_set_active('chado');
-  
-  // begin the transaction
+  // Begin the transaction.
   $transaction = db_transaction();
   print "\nNOTE: Deleting features is performed using a database transaction. \n" .
       "If the load fails or is terminated prematurely then the entire set of \n" .
       "deletions is rolled back and will not be found in the database\n\n";
   try {
 
-    // if feature names have been provided then handle that separately
+    // If feature names have been provided then handle those
     if ($feature_names) {
       $names = preg_split('/\s+/', $feature_names);
       if (sizeof($names) == 1) {
@@ -181,33 +140,59 @@ function tripal_feature_delete_features($organism_id, $analysis_id, $seq_type,
       }
       $num_deletes = chado_select_record('feature', array('count(*) as cnt'), $match);
       print "Deleting " . $num_deletes[0]->cnt . " features\n";
-      
+
       chado_delete_record('feature', $match);
+      return;
+    }
+
+    // Now handle the combintation of all other inputs.
+    $args = array();
+    $sql = "";
+    $join = '';
+    $where = '';
+    if ($analysis_id) {
+      $join .= 'INNER JOIN {analysisfeature} AF on F.feature_id = AF.feature_id ';
+      $join .= 'INNER JOIN {analysis} A on A.analysis_id = AF.analysis_id ';
+      $where .= 'AND A.analysis_id = :analysis_id ';
+      $args[':analysis_id'] = $analysis_id;
     }
-  
-    // if the user has provided an analysis_id then handle that separately
-    elseif ($analysis_id) {
-      tripal_feature_delete_by_analysis();
+    if ($organism_id) {
+      $where .= 'AND F.organism_id = :organism_id ';
+      $args[':organism_id'] = $organism_id;
     }
-    else {
-  
-      if ($organism_id) {
-        $match['organism_id'] = $organism_id;
-      }
-      if ($seq_type) {
-        $match['type_id'] = array(
-          'name' => $seq_type,
-          'cv_id' => array(
-            'name' => 'sequence'
-          ),
-        );
-      }
-      $num_deletes = chado_select_record('feature', array('count(*) as cnt'), $match);
-      print "Deleting " . $num_deletes[0]->cnt . " features\n";
-      chado_delete_record('feature', $match);
+    if ($seq_type) {
+      $join .= 'INNER JOIN {cvterm} CVT ON CVT.cvterm_id = F.type_id';
+      $where .= 'AND CVT.name = :type_name';
+      $args[':type_name'] = $seq_type;
     }
-  
-    print "Removing orphaned feature pages\n";
+
+    // Do not perform a delete if we have no additions to the where clause
+    // otherwise all features will be deleted and this is probably not what
+    // is wanted.
+    if (!$where) {
+      throw new Exception('Cannot delete features as no filters are available');
+    }
+    // First, count the number of records to be deleted
+    $sql = "
+      SELECT count(F.feature_id)
+      FROM {feature} F
+        $join
+      WHERE 1=1 $where
+    ";
+    $num_deleted = chado_query($sql, $args)->fetchField();
+    // Second, delete the records.
+    $sql = "
+      DELETE FROM {feature} WHERE feature_id IN (
+        SELECT F.feature_id
+        FROM {feature} F
+          $join
+        WHERE 1=1 $where
+      )
+    ";
+    chado_query($sql, $args);
+    print "Deletiong completed successfully. Deleted $num_deleted feature(s).\n";
+
+    print "Now removing orphaned feature pages\n";
     chado_cleanup_orphaned_nodes('feature');
   }
   catch (Exception $e) {
@@ -217,33 +202,6 @@ function tripal_feature_delete_features($organism_id, $analysis_id, $seq_type,
     watchdog_exception('tripal_feature', $e);
     return 0;
   }
-  chado_set_active($previous_db);
   print "\nDone\n";
 }
 
-/**
- * Function to delete features based on an analysis passed in. This has not yet been
- * implemented in the form
- *
- * @todo: Implement this functionality and then add back in the form field
- *
- * @param $organism_id
- *   (Optional) The organism_id of the features to delete
- * @param $analysis_id
- *   (Optional) The analysis_id of the features to delete
- * @param $seq_type
- *   (Optional) The cvterm.name of the feature types to delete
- * @param $is_unique
- *   (Optional) A Boolean stating whether the names are unique (ie: feature.uniquename)
- *   or not (ie: feature.name)
- * @param $feature_names
- *   (Optional) A space separated list of the names of features to delete
- * @param $job
- *   The tripal_job id
- *
- * @ingroup tripal_feature
- */
-function tripal_feature_delete_by_analysis($organism_id, $analysis_id, $seq_type,
-  $is_unique, $feature_names, $job = NULL) {
-
-}

+ 71 - 72
tripal_feature/includes/tripal_feature.fasta_loader.inc

@@ -41,10 +41,8 @@ function tripal_feature_fasta_load_form() {
   );
 
   // get the sequence ontology CV ID
-  $values = array('name' => 'sequence'
-  );
-  $cv = chado_select_record('cv', array('cv_id'
-  ), $values);
+  $values = array('name' => 'sequence');
+  $cv = chado_select_record('cv', array('cv_id'), $values);
   $cv_id = $cv[0]->cv_id;
 
   $form['seqtype'] = array('#type' => 'textfield','#title' => t('Sequence Type'),
@@ -123,14 +121,18 @@ function tripal_feature_fasta_load_form() {
     '#description' => t('Enter the regular expression that will extract the
        feature name from the FASTA definition line. For example, for a
        defintion line with a name and unique name separated by a bar \'|\' (>seqname|uniquename),
-       the regular expression for the name would be, "^(.*?)\|.*$".')
+       the regular expression for the name would be, "^(.*?)\|.*$".  All FASTA
+       definition lines begin with the ">" symbol.  You do not need to incldue
+       this symbol in your regular expression.')
   );
   $form['advanced']['re_uname'] = array('#type' => 'textfield',
     '#title' => t('Regular expression for the unique name'),'#required' => FALSE,
     '#description' => t('Enter the regular expression that will extract the
        feature name from the FASTA definition line. For example, for a
        defintion line with a name and unique name separated by a bar \'|\' (>seqname|uniquename),
-       the regular expression for the unique name would be "^.*?\|(.*)$").')
+       the regular expression for the unique name would be "^.*?\|(.*)$").  All FASTA
+       definition lines begin with the ">" symbol.  You do not need to incldue
+       this symbol in your regular expression.')
   );
 
   // Advanced database cross-reference optoins
@@ -163,7 +165,7 @@ function tripal_feature_fasta_load_form() {
   $rels = array();
   $rels[''] = '';
   $rels['part_of'] = 'part of';
-  $rels['derives_from'] = 'produced by';
+  $rels['derives_from'] = 'produced by (derives from)';
 
   // Advanced references options
   $form['advanced']['relationship']['rel_type'] = array('#title' => t('Relationship Type'),
@@ -400,21 +402,16 @@ function tripal_feature_load_fasta($dfile, $organism_id, $type, $re_name, $re_un
         LEFT JOIN {cvtermsynonym} CVTS on CVTS.cvterm_id = CVT.cvterm_id
       WHERE cv.name = :cvname and (CVT.name = :name or CVTS.synonym = :synonym)
     ";
-    $cvterm = chado_query($cvtermsql, array(':cvname' => 'sequence',':name' => $type,
-      ':synonym' => $type
-    ))->fetchObject();
+    $cvterm = chado_query($cvtermsql, array(':cvname' => 'sequence',':name' => $type,':synonym' => $type))->fetchObject();
     if (!$cvterm) {
-      tripal_report_error("T_fasta_loader", TRIPAL_ERROR, "Cannot find the term type: '%type'", array(
-        '%type' => $type
-      ));
+      tripal_report_error("T_fasta_loader", TRIPAL_ERROR,
+        "Cannot find the term type: '%type'", array('%type' => $type));
       return 0;
     }
 
     // Second, if there is a parent type then get that.
     if ($parent_type) {
-      $parentcvterm = chado_query($cvtermsql, array(':cvname' => 'sequence',
-        ':name' => $parent_type,':synonym' => $parent_type
-      ))->fetchObject();
+      $parentcvterm = chado_query($cvtermsql, array(':cvname' => 'sequence', ':name' => $parent_type,':synonym' => $parent_type))->fetchObject();
       if (!$parentcvterm) {
         tripal_report_error("T_fasta_loader", TRIPAL_ERROR, "Cannot find the paretne term type: '%type'", array(
           '%type' => $parentcvterm
@@ -425,9 +422,7 @@ function tripal_feature_load_fasta($dfile, $organism_id, $type, $re_name, $re_un
 
     // Third, if there is a relationship type then get that.
     if ($rel_type) {
-      $relcvterm = chado_query($cvtermsql, array(':cvname' => 'sequence',':name' => $rel_type,
-        ':synonym' => $rel_type
-      ))->fetchObject();
+      $relcvterm = chado_query($cvtermsql, array(':cvname' => 'sequence',':name' => $rel_type,':synonym' => $rel_type))->fetchObject();
       if (!$relcvterm) {
         tripal_report_error("T_fasta_loader", TRIPAL_ERROR, "Cannot find the relationship term type: '%type'", array(
           '%type' => $relcvterm
@@ -498,8 +493,7 @@ function tripal_feature_load_fasta($dfile, $organism_id, $type, $re_name, $re_un
           if (preg_match("/^\s*(.*?)[\s\|].*$/", $defline, $matches)) {
             if (strlen($matches[1]) > $feature_tbl['fields']['name']['length']) {
               tripal_report_error('trp-fasta', "WARNING: Regular expression retrieves a feature name too long for the feature name. Line %line.", array(
-                '%line' => $i
-              ), 'error');
+                '%line' => $i), 'error');
             }
             else {
               $name = trim($matches[1]);
@@ -507,8 +501,7 @@ function tripal_feature_load_fasta($dfile, $organism_id, $type, $re_name, $re_un
           }
           else {
             tripal_report_error('trp-fasta', "ERROR: Cannot find a feature name. Line %line.", array(
-              '%line' => $i
-            ), 'error');
+              '%line' => $i), 'error');
           }
         }
 
@@ -516,8 +509,7 @@ function tripal_feature_load_fasta($dfile, $organism_id, $type, $re_name, $re_un
         if ($re_uname) {
           if (!preg_match("/$re_uname/", $defline, $matches)) {
             tripal_report_error('trp-fasta', "ERROR: Regular expression for the feature unique name finds nothing. Line %line.", array(
-              '%line' => $i
-            ), 'error');
+              '%line' => $i), 'error');
           }
           $uname = trim($matches[1]);
         }
@@ -530,8 +522,7 @@ function tripal_feature_load_fasta($dfile, $organism_id, $type, $re_name, $re_un
           }
           else {
             tripal_report_error('trp-fasta', "ERROR: Cannot find a feature unique name. Line %line.", array(
-              '%line' => $i
-            ), 'error');
+              '%line' => $i), 'error');
           }
         }
 
@@ -551,8 +542,12 @@ function tripal_feature_load_fasta($dfile, $organism_id, $type, $re_name, $re_un
         $subject = trim($matches[1]);
 
         // Add the details to the sequence.
-        $seqs[$num_seqs] = array('name' => $name,'uname' => $uname,'accession' => $accession,
-          'subject' => $subject,'seq_start' => ftell($fh)
+        $seqs[$num_seqs] = array(
+          'name' => $name,
+          'uname' => $uname,
+          'accession' => $accession,
+          'subject' => $subject,
+          'seq_start' => ftell($fh)
         );
         $set_start = TRUE;
         // If this isn't the first sequence, then we want to specify where
@@ -634,9 +629,7 @@ function tripal_feature_load_fasta_feature($fh, $name, $uname, $db_id, $accessio
     ), $values);
     if (count($results) > 1) {
       tripal_report_error('T_fasta_loader', "Multiple features exist with the name '%name' of type
-               '%type' for the organism.  skipping", array(
-        '%name' => $name,'%type' => $type
-      ));
+               '%type' for the organism.  skipping", array('%name' => $name,'%type' => $type));
       return 0;
     }
     if (count($results) == 1) {
@@ -646,16 +639,16 @@ function tripal_feature_load_fasta_feature($fh, $name, $uname, $db_id, $accessio
 
   // Check if this feature already exists if the match_type is 'Unique Name'.
   if (strcmp($match_type, 'Unique name') == 0) {
-    $values = array('organism_id' => $organism_id,'uniquename' => $uname,
+    $values = array(
+      'organism_id' => $organism_id,
+      'uniquename' => $uname,
       'type_id' => $cvterm->cvterm_id
     );
 
-    $results = chado_select_record('feature', array('feature_id'
-    ), $values);
+    $results = chado_select_record('feature', array('feature_id'), $values);
     if (count($results) > 1) {
       tripal_report_error('T_fasta_loader', TRIPAL_WARNING, "Multiple features exist with the name '%name' of type '%type' for the organism.  skipping", array(
-        '%name' => $name,'%type' => $type
-      ));
+        '%name' => $name,'%type' => $type));
       return 0;
     }
     if (count($results) == 1) {
@@ -683,31 +676,33 @@ function tripal_feature_load_fasta_feature($fh, $name, $uname, $db_id, $accessio
     }
 
     // Insert the feature record.
-    $values = array('organism_id' => $organism_id,'name' => $name,'uniquename' => $uname,
+    $values = array(
+      'organism_id' => $organism_id,
+      'name' => $name,
+      'uniquename' => $uname,
       'type_id' => $cvterm->cvterm_id
     );
     $success = chado_insert_record('feature', $values);
     if (!$success) {
       tripal_report_error('T_fasta_loader', TRIPAL_ERROR, "Failed to insert feature '%name (%uname)'", array(
-        '%name' => $name,'%uname' => $numane
-      ));
+        '%name' => $name,'%uname' => $numane));
       return 0;
     }
 
     // now get the feature we just inserted
-    $values = array('organism_id' => $organism_id,'uniquename' => $uname,
+    $values = array(
+      'organism_id' => $organism_id,
+      'uniquename' => $uname,
       'type_id' => $cvterm->cvterm_id
     );
-    $results = chado_select_record('feature', array('feature_id'
-    ), $values);
+    $results = chado_select_record('feature', array('feature_id'), $values);
     if (count($results) == 1) {
       $inserted = 1;
       $feature = $results[0];
     }
     else {
       tripal_report_error('T_fasta_loader', TRIPAL_ERROR, "Failed to retreive newly inserted feature '%name (%uname)'", array(
-        '%name' => $name,'%uname' => $numane
-      ));
+        '%name' => $name,'%uname' => $numane));
       return 0;
     }
 
@@ -719,8 +714,7 @@ function tripal_feature_load_fasta_feature($fh, $name, $uname, $db_id, $accessio
   if (!$feature and (strcmp($method, 'Update only') == 0 or
      drupal_strcmp($method, 'Insert and update') == 0)) {
     tripal_report_error('T_fasta_loader', TRIPAL_ERROR, "Failed to find feature '%name' ('%uname') while matching on " .
-     drupal_strtolower($match_type), array('%name' => $name,'%uname' => $uname
-    ));
+      drupal_strtolower($match_type), array('%name' => $name,'%uname' => $uname));
     return 0;
   }
 
@@ -739,7 +733,9 @@ function tripal_feature_load_fasta_feature($fh, $name, $uname, $db_id, $accessio
         // First check to make sure that by changing the unique name of this
         // feature that we won't conflict with another existing feature of
         // the same name
-        $values = array('organism_id' => $organism_id,'uniquename' => $uname,
+        $values = array(
+          'organism_id' => $organism_id,
+          'uniquename' => $uname,
           'type_id' => $cvterm->cvterm_id
         );
         $results = chado_select_record('feature', array('feature_id'
@@ -753,9 +749,10 @@ function tripal_feature_load_fasta_feature($fh, $name, $uname, $db_id, $accessio
         }
 
         // the changes to the uniquename don't conflict so proceed with the update
-        $values = array('uniquename' => $uname
-        );
-        $match = array('name' => $name,'organism_id' => $organism_id,
+        $values = array('uniquename' => $uname);
+        $match = array(
+          'name' => $name,
+          'organism_id' => $organism_id,
           'type_id' => $cvterm->cvterm_id
         );
 
@@ -776,9 +773,10 @@ function tripal_feature_load_fasta_feature($fh, $name, $uname, $db_id, $accessio
       // we want to update the name.
       $values = array();
       if ($name) {
-        $values = array('name' => $name
-        );
-        $match = array('uniquename' => $uname,'organism_id' => $organism_id,
+        $values = array('name' => $name);
+        $match = array(
+          'uniquename' => $uname,
+          'organism_id' => $organism_id,
           'type_id' => $cvterm->cvterm_id
         );
         $success = chado_update_record('feature', $match, $values);
@@ -798,10 +796,11 @@ function tripal_feature_load_fasta_feature($fh, $name, $uname, $db_id, $accessio
   // add in the analysis link
   if ($analysis_id) {
     // if the association doens't alredy exist then add one
-    $values = array('analysis_id' => $analysis_id,'feature_id' => $feature->feature_id
+    $values = array(
+      'analysis_id' => $analysis_id,
+      'feature_id' => $feature->feature_id
     );
-    $results = chado_select_record('analysisfeature', array('analysisfeature_id'
-    ), $values);
+    $results = chado_select_record('analysisfeature', array('analysisfeature_id'), $values);
     if (count($results) == 0) {
       $success = chado_insert_record('analysisfeature', $values);
       if (!$success) {
@@ -816,28 +815,26 @@ function tripal_feature_load_fasta_feature($fh, $name, $uname, $db_id, $accessio
   // now add the database cross reference
   if ($db_id) {
     // check to see if this accession reference exists, if not add it
-    $values = array('db_id' => $db_id,'accession' => $accession
+    $values = array(
+      'db_id' => $db_id,
+      'accession' => $accession
     );
-    $results = chado_select_record('dbxref', array('dbxref_id'
-    ), $values);
+    $results = chado_select_record('dbxref', array('dbxref_id'), $values);
     // if the accession doesn't exist then add it
     if (count($results) == 0) {
       $results = chado_insert_record('dbxref', $values);
       if (!$results) {
         tripal_report_error('T_fasta_loader', TRIPAL_ERROR, "Failed to add database accession '%accession'", array(
-          '%accession' => $accession
-        ));
+          '%accession' => $accession));
         return 0;
       }
-      $results = chado_select_record('dbxref', array('dbxref_id'
-      ), $values);
+      $results = chado_select_record('dbxref', array('dbxref_id'), $values);
       if (count($results) == 1) {
         $dbxref = $results[0];
       }
       else {
         tripal_report_error('T_fasta_loader', TRIPAL_ERROR, "Failed to retreive newly inserted dbxref '%name (%uname)'", array(
-          '%name' => $name,'%uname' => $numane
-        ));
+          '%name' => $name,'%uname' => $numane));
         return 0;
       }
     }
@@ -846,10 +843,11 @@ function tripal_feature_load_fasta_feature($fh, $name, $uname, $db_id, $accessio
     }
 
     // check to see if the feature dbxref record exists if not, then add it
-    $values = array('feature_id' => $feature->feature_id,'dbxref_id' => $dbxref->dbxref_id
+    $values = array(
+      'feature_id' => $feature->feature_id,
+      'dbxref_id' => $dbxref->dbxref_id
     );
-    $results = chado_select_record('feature_dbxref', array('feature_dbxref_id'
-    ), $values);
+    $results = chado_select_record('feature_dbxref', array('feature_dbxref_id'), $values);
     if (count($results) == 0) {
       $success = chado_insert_record('feature_dbxref', $values);
       if (!$success) {
@@ -878,11 +876,12 @@ function tripal_feature_load_fasta_feature($fh, $name, $uname, $db_id, $accessio
     $parent_feature = $results[0];
 
     // check to see if the relationship already exists if not then add it
-    $values = array('subject_id' => $feature->feature_id,'object_id' => $parent_feature->feature_id,
+    $values = array(
+      'subject_id' => $feature->feature_id,
+      'object_id' => $parent_feature->feature_id,
       'type_id' => $relcvterm->cvterm_id
     );
-    $results = chado_select_record('feature_relationship', array('feature_relationship_id'
-    ), $values);
+    $results = chado_select_record('feature_relationship', array('feature_relationship_id'), $values);
     if (count($results) == 0) {
       $success = chado_insert_record('feature_relationship', $values);
       if (!$success) {

+ 267 - 138
tripal_feature/includes/tripal_feature.gff_loader.inc

@@ -125,20 +125,26 @@ function tripal_feature_gff3_load_form() {
                          database will not be altered.'),
     '#default_value' => 1,
   );
-  $form['import_options']['refresh']= array(
-    '#type' => 'checkbox',
-    '#title' => t('Import all and replace'),
-    '#required' => FALSE,
-    '#description' => t('Existing features will be updated and feature properties not
-                         present in the GFF file will be removed.'),
-  );
-  $form['import_options']['remove']= array(
-    '#type' => 'checkbox',
-    '#title' => t('Delete features'),
-    '#required' => FALSE,
-    '#description' => t('Features present in the GFF file that exist in the database
-                         will be removed rather than imported'),
-  );
+// SPF: there are bugs in refreshing and removing features.  The bugs arise
+//      if a feature in the GFF does not have a uniquename. GenSAS will auto
+//      generate this uniquename and it will not be the same as a previous
+//      load because it uses the date.  This causes orphaned CDS/exons, UTRs
+//      to be left behind during a delete or refresh.  So, the short term
+//      fix is to remove these options.
+//   $form['import_options']['refresh']= array(
+//     '#type' => 'checkbox',
+//     '#title' => t('Import all and replace'),
+//     '#required' => FALSE,
+//     '#description' => t('Existing features will be updated and feature properties not
+//                          present in the GFF file will be removed.'),
+//   );
+//   $form['import_options']['remove']= array(
+//     '#type' => 'checkbox',
+//     '#title' => t('Delete features'),
+//     '#required' => FALSE,
+//     '#description' => t('Features present in the GFF file that exist in the database
+//                          will be removed rather than imported'),
+//   );
   $form['import_options']['create_organism']= array(
     '#type' => 'checkbox',
     '#title' => t('Create organism'),
@@ -218,8 +224,8 @@ function tripal_feature_gff3_load_form_validate($form, &$form_state) {
   $create_organism = $form_state['values']['create_organism'];
   $add_only = $form_state['values']['add_only'];
   $update   = $form_state['values']['update'];
-  $refresh  = $form_state['values']['refresh'];
-  $remove   = $form_state['values']['remove'];
+  $refresh  = 0; //$form_state['values']['refresh'];
+  $remove   = 0; //$form_state['values']['remove'];
   $use_transaction   = $form_state['values']['use_transaction'];
   $line_number   = trim($form_state['values']['line_number']);
   $landmark_type   = trim($form_state['values']['landmark_type']);
@@ -264,8 +270,8 @@ function tripal_feature_gff3_load_form_submit($form, &$form_state) {
   $organism_id = $form_state['values']['organism_id'];
   $add_only = $form_state['values']['add_only'];
   $update   = $form_state['values']['update'];
-  $refresh  = $form_state['values']['refresh'];
-  $remove   = $form_state['values']['remove'];
+  $refresh  = 0; //$form_state['values']['refresh'];
+  $remove   = 0; //$form_state['values']['remove'];
   $analysis_id = $form_state['values']['analysis_id'];
   $use_transaction   = $form_state['values']['use_transaction'];
   $target_organism_id = $form_state['values']['target_organism_id'];
@@ -381,10 +387,19 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
   $job = NULL) {
 
   $ret = array();
+  $date = getdate();
 
-  // empty the temp table
+  // An array that stores CVterms that have been looked up so we don't have
+  // to do the database query every time.
+  $cvterm_lookup = array();
+
+  // empty the temp tables
   $sql = "DELETE FROM {tripal_gff_temp}";
   chado_query($sql);
+  $sql = "DELETE FROM {tripal_gffcds_temp}";
+  chado_query($sql);
+  $sql = "DELETE FROM {tripal_gffprotein_temp}";
+  chado_query($sql);
 
   // begin the transaction
   $transaction = null;
@@ -429,7 +444,6 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
         "Cannot find the 'sequence' ontology", array());
       return '';
     }
-
     // get the organism for which this GFF3 file belongs
     $sql = "SELECT * FROM {organism} WHERE organism_id = :organism_id";
     $organism = chado_query($sql, array(':organism_id' => $organism_id))->fetchObject();
@@ -454,6 +468,23 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
        (lower(CVT.name) = lower(:name) or lower(CVTS.synonym) = lower(:synonym))
      ";
 
+    // If a landmark type was provided then pre-retrieve that.
+    if ($landmark_type) {
+      $query = array(
+        ':cv_id' => $cv->cv_id,
+        ':name' => $landmark_type,
+        ':synonym' => $landmark_type
+      );
+      $result = chado_query($sel_cvterm_sql, $query);
+      $landmark_cvterm = $result->fetchObject();
+      if (!$landmark_cvterm) {
+        tripal_report_error('tripal_feature', TRIPAL_ERROR,
+          'cannot find landmark feature type \'%landmark_type\'.',
+          array('%landmark_type' => $landmark_type));
+        return '';
+      }
+    }
+
     // iterate through each line of the GFF file
     print "Parsing Line $line_num (0.00%). Memory: " . number_format(memory_get_usage()) . " bytes\r";
     while ($line = fgets($fh)) {
@@ -491,20 +522,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
         $rstart = $region_matches[2];
         $rend = $region_matches[3];
         if ($landmark_type) {
-          $query = array(
-            ':cv_id' => $cv->cv_id,
-            ':name' => $landmark_type,
-            ':synonym' => $landmark_type
-          );
-          $result = chado_query($sel_cvterm_sql, $query);
-          $cvterm = $result->fetchObject();
-          if (!$cvterm) {
-            tripal_report_error('tripal_feature', TRIPAL_ERROR,
-             'cannot find feature type \'%landmark_type\' on line %line_num of the GFF file',
-              array('%landmark_type' => $landmark_type, '%line_num' => $line_num));
-            return '';
-          }
-          tripal_feature_load_gff3_feature($organism, $analysis_id, $cvterm, $rid,
+          tripal_feature_load_gff3_feature($organism, $analysis_id, $landmark_cvterm, $rid,
             $rid, '', 'f', 'f', 1, 0);
         }
         continue;
@@ -561,13 +579,18 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
       if (strcmp($phase, '.') == 0) {
         $phase = '';
       }
-      $result = chado_query($sel_cvterm_sql, array(':cv_id' => $cv->cv_id, ':name' => $type, ':synonym' => $type));
-
-      $cvterm = $result->fetchObject();
-      if (!$cvterm) {
-        tripal_report_error('tripal_feature', TRIPAL_ERROR, 'cannot find feature term \'%type\' on line %line_num of the GFF file',
-          array('%type' => $type, '%line_num' => $line_num));
-        return '';
+      if (array_key_exists($type, $cvterm_lookup)) {
+        $cvterm = $cvterm_lookup[$type];
+      }
+      else {
+        $result = chado_query($sel_cvterm_sql, array(':cv_id' => $cv->cv_id, ':name' => $type, ':synonym' => $type));
+        $cvterm = $result->fetchObject();
+        $cvterm_lookup[$type] = $cvterm;
+        if (!$cvterm) {
+          tripal_report_error('tripal_feature', TRIPAL_ERROR, 'cannot find feature term \'%type\' on line %line_num of the GFF file',
+            array('%type' => $type, '%line_num' => $line_num));
+          return '';
+        }
       }
 
       // break apart each of the attributes
@@ -650,7 +673,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
               }
             }
             else {
-              // we found the organism in the database so use it
+              // We found the organism in the database so use it.
               $feature_organism = $org[0];
             }
           }
@@ -661,7 +684,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
             $skip_feature = 1;
           }
         }
-        // get the list of non-reserved attributes
+        // Get the list of non-reserved attributes.
         elseif (strcmp($tag_name, 'Alias') != 0        and strcmp($tag_name, 'Parent') != 0 and
                 strcmp($tag_name, 'Target') != 0       and strcmp($tag_name, 'Gap') != 0 and
                 strcmp($tag_name, 'Derives_from') != 0 and strcmp($tag_name, 'Note') != 0 and
@@ -673,49 +696,46 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
           }
         }
       }
-      // if neither name nor uniquename are provided then generate one
+      // If neither name nor uniquename are provided then generate one.
       if (!$attr_uniquename and !$attr_name) {
-        // check if an alternate ID field is suggested, if so, then use
-        // that for the name
+        // Check if an alternate ID field is suggested, if so, then use
+        // that for the name.
         if (array_key_exists($alt_id_attr, $tags)) {
           $attr_uniquename = $tags[$alt_id_attr][0];
           $attr_name = $attr_uniquename;
         }
-        // if the row has a parent then generate a uniquename using the parent name
+        // If the row has a parent then generate a uniquename using the parent name
         // add the date to the name in the event there are more than one child with
         // the same parent.
         elseif (array_key_exists('Parent', $tags)) {
-          $date = getdate();
           $attr_uniquename = $tags['Parent'][0] . "-$type-$landmark-" . $date[0] . ":" . ($fmin + 1) . ".." . $fmax;
           $attr_name = $attr_uniquename;
         }
-        // generate a unique name based on the date, type and location
-        // and set the name to simply be the type
+        // Generate a unique name based on the date, type and location
+        // and set the name to simply be the type.
         else {
-          $date = getdate();
           $attr_uniquename = $date[0] . "-$type-$landmark:" . ($fmin + 1) . ".." . $fmax;
           $attr_name = $type;
         }
       }
 
-      // if a name is not specified then use the unique name as the name
+      // If a name is not specified then use the unique name as the name
       if (strcmp($attr_name, '') == 0) {
         $attr_name = $attr_uniquename;
       }
 
-      // if an ID attribute is not specified then we must generate a
+      // If an ID attribute is not specified then we must generate a
       // unique ID. Do this by combining the attribute name with the date
       // and line number.
       if (!$attr_uniquename) {
-        $date = getdate();
         $attr_uniquename = $attr_name . '-' . $date[0] . '-' . $line_num;
       }
 
-      // make sure the landmark sequence exists in the database.  If the user
-      // has not specified a landmark type (and it's not requiredin the GFF foramt)
-      // then We don't know the type of the landmark so we'll hope that it's unique across
-      // all types for the orgnaism. Only do this test if the landmark and the feature are
-      // different.
+      // Make sure the landmark sequence exists in the database.  If the user
+      // has not specified a landmark type (and it's not required in the GFF
+      // format) then we don't know the type of the landmark so we'll hope
+      // that it's unique across all types for the orgnaism. Only do this
+      // test if the landmark and the feature are different.
       if (!$remove and !(strcmp($landmark, $attr_uniquename) == 0 or strcmp($landmark, $attr_name) == 0)) {
         $select = array(
           'organism_id' => $organism->organism_id,
@@ -760,16 +780,17 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
           return '';
         }
       }
-
-      // if the option is to remove or refresh then we want to remove
+/*
+      // If the option is to remove or refresh then we want to remove
       // the feature from the database.
       if ($remove or $refresh) {
+        // Next remove the feature itself.
         $sql = "DELETE FROM {feature}
                 WHERE organism_id = %d and uniquename = '%s' and type_id = %d";
         $match = array(
-           'organism_id' => $feature_organism->organism_id,
-           'uniquename'  => $attr_uniquename,
-           'type_id'     => $cvterm->cvterm_id
+          'organism_id' => $feature_organism->organism_id,
+          'uniquename'  => $attr_uniquename,
+          'type_id'     => $cvterm->cvterm_id
         );
         $result = chado_delete_record('feature', $match);
         if (!$result) {
@@ -779,19 +800,19 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
         $feature = 0;
         unset($result);
       }
-
-      // add or update the feature and all properties
+ */
+      // Add or update the feature and all properties.
       if ($update or $refresh or $add_only) {
 
-        // add/update the feature
+        // Add/update the feature.
         $feature = tripal_feature_load_gff3_feature($feature_organism, $analysis_id, $cvterm,
           $attr_uniquename, $attr_name, $residues, $attr_is_analysis,
           $attr_is_obsolete, $add_only, $score);
 
         if ($feature) {
 
-          // add a record for this feature to the tripal_gff_temp table for
-          // later lookup
+          // Add a record for this feature to the tripal_gff_temp table for
+          // later lookup.
           $values = array(
             'feature_id' => $feature->feature_id,
             'organism_id' => $feature->organism_id,
@@ -830,7 +851,8 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
           }
           // add parent relationships
           if (array_key_exists('Parent', $tags)) {
-            tripal_feature_load_gff3_parents($feature, $cvterm, $tags['Parent'], $feature_organism->organism_id, $fmin);
+            tripal_feature_load_gff3_parents($feature, $cvterm, $tags['Parent'],
+              $feature_organism->organism_id, $strand, $phase, $fmin, $fmax);
           }
 
           // add target relationships
@@ -851,7 +873,8 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
           }
           // add the Derives_from relationship (e.g. polycistronic genes).
           if (array_key_exists('Derives_from', $tags)) {
-            tripal_feature_load_gff3_derives_from($feature, $tags['Derives_from'][0], $feature_organism);
+            tripal_feature_load_gff3_derives_from($feature, $cvterm, $tags['Derives_from'][0],
+              $feature_organism, $fmin, $fmax);
           }
           // add in the GFF3_source dbxref so that GBrowse can find the feature using the source column
           $source_ref = array('GFF_source:' . $source);
@@ -869,10 +892,79 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
       }
     }
 
+    // Do some last bit of processing.
     if (!$remove) {
+
+      // First, add any protein sequences if needed.
+      $sql = "SELECT feature_id FROM {tripal_gffcds_temp} LIMIT 1 OFFSET 1";
+      $has_cds = chado_query($sql)->fetchField();
+      if ($has_cds) {
+        print "\nAdding protein sequences if CDS exist and no proteins in GFF...\n";
+        $sql = "
+          SELECT F.feature_id, F.name, F.uniquename, TGCT.strand,
+            CVT.cvterm_id, CVT.name as feature_type,
+            min(TGCT.fmin) as fmin, max(TGCT.fmax) as fmax,
+            TGPT.feature_id as protein_id, TGPT.fmin as protein_fmin,
+            TGPT.fmax as protein_fmax
+          FROM {tripal_gffcds_temp} TGCT
+            INNER JOIN {feature} F on F.feature_id = TGCT.parent_id
+            INNER JOIN {cvterm} CVT on CVT.cvterm_id = F.type_id
+            LEFT JOIN {tripal_gffprotein_temp} TGPT on TGPT.parent_id = F.feature_id
+          GROUP BY F.feature_id, F.name, F.uniquename, CVT.cvterm_id, CVT.name,
+            TGPT.feature_id, TGPT.fmin, TGPT.fmax, TGCT.strand
+        ";
+        $results = chado_query($sql);
+        $protein_cvterm = tripal_get_cvterm(array(
+          'name' => 'polypeptide',
+          'cv_id' => array(
+            'name' => 'sequence'
+          )
+        ));
+        while ($result = $results->fetchObject()) {
+          // If a protein exists with this same parent then don't add a new
+          // protein.
+          if (!$result->protein_id) {
+            // Get details about this protein
+            $uname = $result->uniquename . '-protein';
+            $name =  $result->name;
+            $values = array(
+              'parent_id' => $result->feature_id,
+              'fmin' => $result->fmin
+            );
+            $min_phase = chado_select_record('tripal_gffcds_temp', array('phase'), $values);
+            $values = array(
+              'parent_id' => $result->feature_id,
+              'fmax' => $result->fmax
+            );
+            $max_phase = chado_select_record('tripal_gffcds_temp', array('phase'), $values);
+
+            $pfmin = $result->fmin;
+            $pfmax = $result->fmax;
+            if ($result->strand == '-1') {
+              $pfmax -= $max_phase[0]->phase;
+            }
+            else {
+              $pfmin += $min_phase[0]->phase;
+            }
+
+            // Add the new protein record.
+            $feature = tripal_feature_load_gff3_feature($organism, $analysis_id,
+              $protein_cvterm, $uname, $name, '', 'f', 'f', 1, 0);
+            // Add the derives_from relationship.
+            $cvterm = tripal_get_cvterm(array('cvterm_id' => $result->cvterm_id));
+            tripal_feature_load_gff3_derives_from($feature, $cvterm,
+              $result->uniquename, $organism, $pfmin, $pfmax);
+            // Add the featureloc record. Set the start of the protein to
+            // be the start of the coding sequence minus the phase.
+            tripal_feature_load_gff3_featureloc($feature, $organism, $landmark,
+              $pfmin, $pfmax, $strand, '', 'f', 'f', '', 0);
+          }
+        }
+      }
+
       print "\nSetting ranks of children...\n";
 
-      // get features in a relationship that are also children of an alignment
+      // Get features in a relationship that are also children of an alignment.
       $sql = "
         SELECT DISTINCT F.feature_id, F.organism_id, F.type_id,
           F.uniquename, FL.strand
@@ -885,15 +977,17 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
       ";
       $parents = chado_query($sql);
 
-      // build and prepare the SQL for selecting the children relationship
+      // Build and prepare the SQL for selecting the children relationship.
       $sel_gffchildren_sql = "
         SELECT DISTINCT FR.feature_relationship_id, FL.fmin, FR.rank
         FROM {feature_relationship} FR
           INNER JOIN {featureloc} FL on FL.feature_id = FR.subject_id
-        WHERE FR.object_id = :feature_id ORDER BY FL.fmin ASC
+          INNER JOIN {cvterm} CVT on CVT.cvterm_id = FR.type_id
+        WHERE FR.object_id = :feature_id AND CVT.name = 'part_of'
+        ORDER BY FL.fmin ASC
       ";
 
-      // now set the rank of any parent/child relationships.  The order is based
+      // Now set the rank of any parent/child relationships.  The order is based
       // on the fmin.  The start rank is 1.  This allows features with other
       // relationships to be '0' (the default), and doesn't interfer with the
       // ordering defined here.
@@ -977,37 +1071,42 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
  *
  * @ingroup gff3_loader
  */
-function tripal_feature_load_gff3_derives_from($feature, $subject, $organism) {
+function tripal_feature_load_gff3_derives_from($feature, $cvterm, $object,
+  $organism, $fmin, $fmax) {
+
+  $type = $cvterm->name;
 
-  // get the subject. If the subject is not in the tripal_gff_temp table
-  // then look for the subject in the feature table using the unique name.
-  // if it is not unique then we can provide an error
+  // First look for the object feature in the temp table to get it's type.
   $values = array(
     'organism_id' => $organism->organism_id,
-    'uniquename' => $subject,
+    'uniquename' => $object,
   );
   $result = chado_select_record('tripal_gff_temp', array('type_name'), $values);
-  $type_id = array();
+  $type_id = NULL;
   if (count($result) > 0) {
-    $type_id = array(
+    $otype = tripal_get_cvterm(array(
       'name' => $result[0]->type_name,
       'cv_id' => array(
         'name' => 'sequence'
-      ),
-    );
+      )
+    ));
+    if ($otype) {
+      $type_id = $otype->cvterm_id;
+    }
   }
 
-  // if we don't have a subject type then look for the feature in the feature table
-  if (empty($type_id)) {
+  // If the object wasn't in the temp table then look for it in the
+  // feature table and get it's type.
+  if (!$type_id) {
     $result = chado_select_record('feature', array('type_id'), $values);
     if (count($result) > 1) {
-      watchdog("tripal_feature", "Cannot find subject type for feature,'%subject' , in 'derives_from' relationship. Multiple matching features exist with this uniquename.",
-        array('%subject' => $subject), WATCHDOG_WARNING);
+      watchdog("tripal_feature", "Cannot find feature type for, '%subject' , in 'derives_from' relationship. Multiple matching features exist with this uniquename.",
+        array('%subject' => $object), WATCHDOG_WARNING);
       return '';
     }
     else if (count($result) == 0) {
-      watchdog("tripal_feature", "Cannot find subject type for feature,'%subject' , in 'derives_from' relationship.",
-        array('%subject' => $subject), WATCHDOG_WARNING);
+      watchdog("tripal_feature", "Cannot find feature type for, '%subject' , in 'derives_from' relationship.",
+        array('%subject' => $object), WATCHDOG_WARNING);
       return '';
     }
     else {
@@ -1015,32 +1114,43 @@ function tripal_feature_load_gff3_derives_from($feature, $subject, $organism) {
     }
   }
 
-  // get the subject feature
+  // Get the object feature.
   $match = array(
     'organism_id' => $organism->organism_id,
-    'uniquename' => $subject,
-    'type_id' => array(
-      'name' => $subject_type,
-      'cv_id' => array(
-        'name' => 'sequence'
-      ),
-    ),
+    'uniquename' => $object,
+    'type_id' => $type_id,
   );
-  $sfeature = chado_select_record('feature', array('feature_id'), $match);
-  if (count($sfeature)==0) {
+  $ofeature = chado_select_record('feature', array('feature_id'), $match);
+  if (count($ofeature) == 0) {
     tripal_report_error('tripal_feature', TRIPAL_ERROR, "Could not add 'Derives_from' relationship " .
       "for %uniquename and %subject.  Subject feature, '%subject', " .
       "cannot be found", array('%uniquename' => $feature->uniquename, '%subject' => $subject));
     return;
   }
 
-   // now check to see if the relationship already exists
+  // If this feature is a protein then add it to the tripal_gffprotein_temp.
+  if ($type == 'protein' or $type == 'polypeptide') {
+    $values = array(
+      'feature_id' => $feature->feature_id,
+      'parent_id' => $ofeature[0]->feature_id,
+      'fmin' => $fmin,
+      'fmax' => $fmax
+    );
+    $result = chado_insert_record('tripal_gffprotein_temp', $values);
+    if (!$result) {
+      tripal_report_error('tripal_feature', TRIPAL_ERROR, "Cound not save record in temporary protein table, Cannot continue.", array());
+      exit;
+    }
+  }
+
+   // Now check to see if the relationship already exists. If it does
+   // then just return.
   $values = array(
-    'object_id' => $sfeature[0]->feature_id,
+    'object_id' => $ofeature[0]->feature_id,
     'subject_id' => $feature->feature_id,
     'type_id' => array(
        'cv_id' => array(
-          'name' => 'relationship'
+          'name' => 'sequence'
         ),
        'name' => 'derives_from',
     ),
@@ -1070,13 +1180,14 @@ function tripal_feature_load_gff3_derives_from($feature, $subject, $organism) {
  *
  * @ingroup gff3_loader
  */
-function tripal_feature_load_gff3_parents($feature, $cvterm, $parents, $organism_id, $fmin) {
+function tripal_feature_load_gff3_parents($feature, $cvterm, $parents,
+  $organism_id, $strand, $phase, $fmin, $fmax) {
 
   $uname = $feature->uniquename;
   $type = $cvterm->name;
   $rel_type = 'part_of';
 
-  // prepare these SQL statements that will be used repeatedly.
+  // Prepare these SQL statements that will be used repeatedly.
   $cvterm_sql = "
     SELECT CVT.cvterm_id
     FROM {cvterm} CVT
@@ -1085,9 +1196,9 @@ function tripal_feature_load_gff3_parents($feature, $cvterm, $parents, $organism
     WHERE cv.name = :cvname and (CVT.name = :name or CVTS.synonym = :synonym)
   ";
 
-  // iterate through the parents in the list
+  // Iterate through the parents in the list.
   foreach ($parents as $parent) {
-    // get the parent cvterm
+    // Get the parent cvterm.
     $values = array(
       'organism_id' => $organism_id,
       'uniquename' => $parent,
@@ -1101,7 +1212,7 @@ function tripal_feature_load_gff3_parents($feature, $cvterm, $parents, $organism
 
     // try to find the parent
     $parentcvterm = chado_query($cvterm_sql, array(':cvname' => 'sequence', ':name' => $parent_type, ':synonym' => $parent_type))->fetchObject();
-    $relcvterm = chado_query($cvterm_sql, array(':cvname' => 'relationship', ':name' => $rel_type, ':synonym' => $rel_type))->fetchObject();
+    $relcvterm = chado_query($cvterm_sql, array(':cvname' => 'sequence', ':name' => $rel_type, ':synonym' => $rel_type))->fetchObject();
     $values = array(
         'organism_id' => $organism_id,
         'uniquename' => $parent,
@@ -1136,6 +1247,24 @@ function tripal_feature_load_gff3_parents($feature, $cvterm, $parents, $organism
             array());
         }
       }
+
+      // If this feature is a CDS and now that we know the parent we can
+      // add it to the tripal_gffcds_temp table for later lookup.
+      if ($type == 'CDS') {
+        $values = array(
+          'feature_id' => $feature->feature_id,
+          'parent_id' => $parent_feature->feature_id,
+          'fmin' => $fmin,
+          'fmax' => $fmax,
+          'strand' => $strand,
+          'phase' => $phase,
+        );
+        $result = chado_insert_record('tripal_gffcds_temp', $values);
+        if (!$result) {
+          tripal_report_error('tripal_feature', TRIPAL_ERROR, "Cound not save record in temporary CDS table, Cannot continue.", array());
+          exit;
+        }
+      }
     }
     else {
       tripal_report_error("tripal_feature", TRIPAL_WARNING, "Cannot establish relationship '$uname' ($type) $rel_type '$parent' ($parent_type): Cannot find the parent",
@@ -1482,12 +1611,12 @@ function tripal_feature_load_gff3_alias($feature, $aliases) {
 function tripal_feature_load_gff3_feature($organism, $analysis_id, $cvterm, $uniquename,
   $name, $residues, $is_analysis = 'f', $is_obsolete = 'f', $add_only, $score) {
 
-  // check to see if the feature already exists
+  // Check to see if the feature already exists.
   $feature = NULL;
   $fselect = array(
-     'organism_id' => $organism->organism_id,
-     'uniquename' => $uniquename,
-     'type_id' => $cvterm->cvterm_id
+    'organism_id' => $organism->organism_id,
+    'uniquename' => $uniquename,
+    'type_id' => $cvterm->cvterm_id
   );
   $columns = array('feature_id', 'name', 'uniquename', 'seqlen', 'organism_id', 'type_id');
   $result = chado_select_record('feature', $columns, $fselect);
@@ -1508,21 +1637,19 @@ function tripal_feature_load_gff3_feature($organism, $analysis_id, $cvterm, $uni
     $is_analysis = 'TRUE';
   }
 
-  // insert the feature if it does not exist otherwise perform an update
+  // Insert the feature if it does not exist otherwise perform an update.
   if (!$feature) {
     $values = array(
-       'organism_id' => $organism->organism_id,
-       'name' => $name,
-       'uniquename' => $uniquename,
-//       'residues' => $residues,
-//       'seqlen' => drupal_strlen($residues),
-       'md5checksum' => md5($residues),
-       'type_id' => $cvterm->cvterm_id,
-       'is_analysis' => $is_analysis,
-       'is_obsolete' => $is_obsolete,
+      'organism_id' => $organism->organism_id,
+      'name' => $name,
+      'uniquename' => $uniquename,
+      'md5checksum' => md5($residues),
+      'type_id' => $cvterm->cvterm_id,
+      'is_analysis' => $is_analysis,
+      'is_obsolete' => $is_obsolete,
     );
-    $result = chado_insert_record('feature', $values);
-    if (!$result) {
+    $feature = (object) chado_insert_record('feature', $values);
+    if (!$feature) {
       tripal_report_error("tripal_feature", TRIPAL_WARNING, "Failed to insert feature '$uniquename' ($cvterm->name)", array());
       return 0;
     }
@@ -1530,8 +1657,6 @@ function tripal_feature_load_gff3_feature($organism, $analysis_id, $cvterm, $uni
   elseif (!$add_only) {
     $values = array(
       'name' => $name,
-//      'residues' => $residues,
-//      'seqlen' => drupal_strlen($residues),
       'md5checksum' => md5($residues),
       'is_analysis' => $is_analysis,
       'is_obsolete' => $is_obsolete,
@@ -1548,17 +1673,13 @@ function tripal_feature_load_gff3_feature($organism, $analysis_id, $cvterm, $uni
     }
   }
   else {
-    // the feature exists and we don't want to update it so return
+    // The feature exists and we don't want to update it so return
     // a value of 0.  This will stop all downstream property additions
-    return 0;
+    return $feature;
   }
 
-  // get the newly added feature
-  $columns = array('feature_id', 'name', 'uniquename', 'seqlen', 'organism_id', 'type_id');
-  $result = chado_select_record('feature', $columns, $fselect);
-  $feature = $result[0];
-
-  // add the analysisfeature entry to the analysisfeature table if it doesn't already exist
+  // Add the analysisfeature entry to the analysisfeature table if
+  // it doesn't already exist.
   $af_values = array(
     'analysis_id' => $analysis_id,
     'feature_id' => $feature->feature_id
@@ -1589,6 +1710,7 @@ function tripal_feature_load_gff3_feature($organism, $analysis_id, $cvterm, $uni
       }
     }
   }
+
   return $feature;
 }
 
@@ -1916,14 +2038,18 @@ function tripal_feature_load_gff3_fasta($fh, $interval, &$num_read, &$intv_read,
         else {
           // if we have a feature then add the residues
           $feature = $result[0];
-          $values = array('residues' => $residues);
+          $values = array(
+            'residues' => $residues,
+            'seqlen' => strlen($residues)
+          );
           $match = array('feature_id' => $feature->feature_id);
           chado_update_record('feature', $match, $values);
         }
       }
 
-      // get the feature ID for this ID from the tripal_gff_temp table
-      $id = preg_replace('/^>(.*)$/', '\1', $line);
+      // get the feature ID for this ID from the tripal_gff_temp table. It
+      // should be the name up to the first space
+      $id = preg_replace('/^>([^\s]+).*$/', '\1', $line);
       $residues = '';
     }
     else {
@@ -1941,7 +2067,10 @@ function tripal_feature_load_gff3_fasta($fh, $interval, &$num_read, &$intv_read,
   else {
     // if we have a feature then add the residues
     $feature = $result[0];
-    $values = array('residues' => $residues);
+    $values = array(
+      'residues' => $residues,
+      'seqlen' => strlen($residues)
+    );
     $match = array('feature_id' => $feature->feature_id);
     chado_update_record('feature', $match, $values);
   }

+ 26 - 5
tripal_feature/theme/css/tripal_feature.css

@@ -11,7 +11,7 @@
 pre.tripal_feature-sequence {
   color: #000000;
   height: 300px;
-  overflow: scroll; 
+  overflow: scroll;
   border: 1px solid #DDDDDD;
   max-width: 500px;
   white-space: normal;
@@ -52,23 +52,23 @@ span.tripal_feature-featureloc_sequence-exon {
 
 span.tripal_feature-featureloc_sequence-intron {
    background-image: url('../images/type_color6.png');
- 
+
 }
 
-/* 
+/*
  * Sequence Retrieval Form
  */
 #tripal-feature-seq-extract-form-table {
   border-collapse: collapse;
   border: 0px solid #DDDDDD;
-  border-spacing: 0;  
+  border-spacing: 0;
   margin: 1em 0;
   width: auto;
 }
 #tripal-feature-seq-extract-form-table tr {
   background-color: transparent;
   border: 0px solid #CCCCCC;
-  padding: 0.1em 0.6em;  
+  padding: 0.1em 0.6em;
 }
 #tripal-feature-seq-extract-form-table td {
   border: 0px solid #DDDDDD;
@@ -77,4 +77,25 @@ span.tripal_feature-featureloc_sequence-intron {
 }
 #tripal-feature-seq-extract-form-table .form-item {
   white-space: normal;
+}
+
+/*
+ * Admin Summary Charts
+ */
+.tripal-admin-summary {
+  border: 1px solid #ccc;
+  padding: 15px;
+  margin-bottom: 15px;
+}
+.tripal-admin-summary .axis {
+  font: 10px sans-serif;
+}
+.tripal-admin-summary .axis path,
+.tripal-admin-summary .axis line {
+  fill: none;
+  stroke: #000;
+  shape-rendering: crispEdges;
+}
+.tripal-admin-figure-desc .figure-title {
+  font-weight: bold;
 }

+ 243 - 0
tripal_feature/theme/js/tripalFeature.adminChart.js

@@ -0,0 +1,243 @@
+Drupal.behaviors.tripalFeature_adminSummaryChart = {
+  attach: function (context, settings) {
+
+    // Set-up the dimensions for our chart canvas.
+    // Note: these are adjusted below so think of these are your minimum size.
+    var margin = {top: 20, right: 50, bottom: 20, left: 100},
+        fullWidth = document.getElementById('tripal-feature-admin-summary').offsetWidth,
+        width = fullWidth - margin.left - margin.right,
+        height = 300 - margin.top - margin.bottom;
+
+    var color = d3.scale.ordinal()
+        .range(["#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6","#6a3d9a","#ffff99","#b15928"]);
+
+    var formatNum = d3.format("0,000");
+
+    // The data was parsed and saved into tripalFeature.admin.summary
+    // in the preprocess function for this template.
+    if (Drupal.settings.tripalFeature.admin.summary) {
+
+      // Determine the max number of characters in both the type name
+      // and the total number of features per bar for use in width/magin adjustments.
+      var maxTypeLength = 0;
+      var maxTotalLength = 0;
+      var numBars = Drupal.settings.tripalFeature.admin.summary.length;
+      for(var i=0; i < numBars; i++){
+        var element = Drupal.settings.tripalFeature.admin.summary[i];
+
+        if(element.name.length > maxTypeLength){
+          maxTypeLength = element.name.length;
+        }
+
+        if(element.total_features.length > maxTotalLength){
+          maxTotalLength = element.total_features.length;
+        }
+      }
+      // Ensure a minimum in case something goes wrong...
+      if (maxTotalLength < 4) { maxTotalLength = 4; }
+      if (maxTypeLength < 10) { maxTypeLength = 10; }
+
+      // Adjust our bottom margin based on the length of type names in the data.
+      // Assume 4px/character based on the slope of the label.
+      xAxisHeight = maxTypeLength * 3;
+      margin.bottom = xAxisHeight + 25;
+
+      // Adjust the width of the chart based on the number or bars (types)
+      // and the length of the bar totals which need to fit on the top of the bar.
+      // Assume 9px/character since it's not rotated.
+      if ((width + margin.left + margin.right) < (numBars * (maxTotalLength * 9))) {
+        width = numBars * (maxTotalLength * 9);
+      }
+
+      // Determine the best place for the legend. Default to top since that
+      // will for sure not cause conflict... even though it looks better
+      // on the right ;).
+      // Logic: If the difference between the max & min bar heights is greater
+      // than 1/2 the chart height (max bar height) then there "should"
+      // be room for the chart nested on the right.
+      minBarHeight = d3.min(Drupal.settings.tripalFeature.admin.summary, function(d,i) { return d.total_features; });
+      barHeightDifference = Drupal.settings.tripalFeature.admin.maxBarHeight - minBarHeight;
+      if (barHeightDifference >= Drupal.settings.tripalFeature.admin.maxBarHeight/2) {
+        Drupal.settings.tripalFeature.admin.legendPosition = 'right';
+      }
+
+      // Also if we need to put the legend along the top we need to
+      // increase the top margin.
+      if (Drupal.settings.tripalFeature.admin.legendPosition == 'top') {
+        // Draw a top legend in the margin.
+        var columnWidth = d3.max(Drupal.settings.tripalFeature.admin.organisms, function(d,i) {return d.length;}) * 10;
+        var maxNumColumns = Math.round(width / columnWidth);
+        var numRows = Math.ceil(Drupal.settings.tripalFeature.admin.organisms.length / maxNumColumns);
+        var idealNumColumns = Math.round(Drupal.settings.tripalFeature.admin.organisms.length / numRows);
+        var legendMargin = {
+          left: (width - (idealNumColumns * columnWidth))/2,
+          right: (width - (idealNumColumns * columnWidth))/2,
+          bottom: 25
+        };
+
+        margin.top = margin.top + (numRows * 20) + legendMargin.bottom;
+      }
+
+      // Set-up the scales of the chart.
+      var x0 = d3.scale.ordinal()
+        .rangeRoundBands([0, width], .1);
+      var x1 = d3.scale.ordinal();
+      var y = d3.scale.linear()
+        .range([height, 0]);
+
+      // Now set-up the axis functions.
+      var xAxis = d3.svg.axis()
+        .scale(x0)
+        .orient('bottom');
+      var yAxis = d3.svg.axis()
+        .scale(y)
+        .orient('left')
+        .ticks(10, '');
+
+      // Create our chart canvas.
+      var svg = d3.select('#tripal-feature-admin-summary-chart').append('svg')
+          .attr('width', width + margin.left + margin.right)
+          .attr('height', height + margin.top + margin.bottom)
+        .append('g')
+          .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+      // map the data to the x & y axis' of our chart.
+      data = Drupal.settings.tripalFeature.admin.summary;
+      x0.domain(data.map(function(d) { return d.name; }));
+      x1.domain(Drupal.settings.tripalFeature.admin.organisms).rangeRoundBands([0, x0.rangeBand()]);
+      y.domain([0, Drupal.settings.tripalFeature.admin.maxBarHeight]);
+
+      // Create the x-axis.
+      var xaxis = svg.append('g')
+        .attr('class', 'x axis')
+        .attr('transform', 'translate(0,' + height + ')')
+        .call(xAxis);
+
+      xaxis.selectAll("text")
+        .style("text-anchor", "end")
+        .attr("dx", "-.8em")
+        .attr("dy", ".15em")
+        .attr("transform", function(d) { return "rotate(-25)"; });
+
+      // Label the  x-axis.
+      xaxis.append('g')
+        .attr('class', 'axis-label')
+          .attr('transform', 'translate(' + width/2 + ',' + xAxisHeight + ')')
+        .append('text')
+          .attr('font-size', '16px')
+          .attr('dy', '.71em')
+          .style('text-anchor', 'middle')
+          .text('Types of Features');
+
+      // Create the y-axis.
+      var yaxis = svg.append('g')
+          .attr('class', 'y axis')
+          .call(yAxis);
+
+      // Label the y-axis.
+      yaxis.append('g')
+        .attr('class', 'axis-label')
+          .attr('transform', 'translate(-70,' + height/2 + ')')
+        .append('text')
+          .attr('transform', 'rotate(-90)')
+          .attr('font-size', '16px')
+          .attr('dy', '.71em')
+          .style('text-anchor', 'middle')
+          .text('Total Number of Features');
+
+      // Add a g element to contain each set of bars (1 per type).
+      var type = svg.selectAll(".type")
+          .data(data)
+        .enter().append("g")
+          .attr("class", "g")
+          .attr("transform", function(d) { return "translate(" + x0(d.name) + ",0)"; });
+
+      // Now add the bars :)
+      // Keep in mind some processing was done in the preprocess function to
+      // generate the bars array based on the organisms array
+      // and pre-calculated the y0 & y1 used here.
+      type.selectAll("rect")
+          .data(function(d) { return d.bars; })
+        .enter().append("rect")
+          .attr("width", x0.rangeBand())
+          .attr("y", function(d) { return y(d.y1); })
+          .attr("height", function(d) { return y(d.y0) - y(d.y1); })
+          .style("fill", function(d) { return color(d.name); })
+        .append("svg:title")
+          .text(function(d) { return formatNum(d.y1 - d.y0); });
+
+      // Add the total to the top of the bar.
+      svg.selectAll("g.bar-totals")
+        .data(data)
+      .enter().append('g')
+        .classed('bar-totals', true)
+      .append("text")
+        .attr("class", "bar-label")
+        .attr("text-anchor", "middle")
+        .attr('font-size', '10px')
+        .attr("x", function(d) { return x0(d.name) + x0.rangeBand()/2; })
+        .attr("y", function(d) { return y(d.total_features) -5; })
+        .text(function(d) { return formatNum(d.total_features); });
+
+      // Legend
+      //---------
+      // NOTE: we would prefer to have the legend overlapping the chart on the
+      // right side but this could be a problem if you have a similar count
+      // for all features (legend overlaps bars) or a large number of types
+      // (legend gets pushed out of the viewable screen area). In these cases
+      // switch to a bottom legend.
+      if (Drupal.settings.tripalFeature.admin.legendPosition == 'top') {
+        // Draw a bottom legend in the margin.
+        var legend = svg.append('g')
+          .classed('legend', true)
+          .attr('transform', 'translate(' + (legendMargin.left - 20) + ',-' + margin.top + ')');
+
+        var legendItem = legend.selectAll('g')
+            .data(Drupal.settings.tripalFeature.admin.organisms.slice().reverse())
+          .enter().append("g")
+            .attr("class", "legend-item")
+            .attr("transform", function(d,i) {
+              xOff = (i % idealNumColumns) * columnWidth;
+              yOff = Math.floor(i  / idealNumColumns) * 25;
+              return "translate(" + xOff + "," + yOff + ")"
+          });
+        legendItem.append("rect")
+            //.attr("x", width - 18)
+            .attr("width", 18)
+            .attr("height", 18)
+            .style("fill", color);
+        legendItem.append("text")
+            .attr("x", 24)
+            .attr("y", 9)
+            .attr("dy", ".35em")
+            .style("text-anchor", "start")
+            .attr('font-style','italic')
+            .text(function(d) { return d; });
+      }
+      else {
+        // Draw a right inset legend.
+        var legend = svg.append('g')
+          .classed('legend', true)
+          .attr('transform', 'translate(0,-' + margin.top + ')');
+
+        var legendItem = legend.selectAll('g')
+            .data(Drupal.settings.tripalFeature.admin.organisms.slice().reverse())
+          .enter().append("g")
+            .attr("class", "legend")
+            .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
+        legendItem.append("rect")
+            .attr("x", width - 18)
+            .attr("width", 18)
+            .attr("height", 18)
+            .style("fill", color);
+        legendItem.append("text")
+            .attr("x", width - 24)
+            .attr("y", 9)
+            .attr("dy", ".35em")
+            .style("text-anchor", "end")
+            .attr('font-style','italic')
+            .text(function(d) { return d; });
+      }
+    }
+  }
+};

+ 28 - 0
tripal_feature/theme/templates/tripal_feature_bar_chart_type_organism_summary.tpl.php

@@ -0,0 +1,28 @@
+<?php
+/**
+ * Feature Summary Bar Chart (broken down by type & organism).
+ *
+ * This template displays a feature summary stacked bar chart
+ * where each bar depicts the total number of features per
+ * feature type and the stacked portions of a given bar
+ * respresent the organism breakdown of those featurs.
+ *
+ * Most of the functionality is in the preprocess for this template and
+ * the acompanying javascript file.
+ * @see tripal_feature/theme/js/tripalFeature.adminChart.js
+ * @see tripal_feature/theme/tripal_feature.theme.inc:tripal_feature_preprocess_tripal_feature_bar_chart_type_organism_summary()
+ */
+?>
+
+<div id="tripal-feature-admin-summary" class="tripal-admin-summary">
+  <div id="tripal-feature-admin-summary-chart" "tripal-admin-chart"></div>
+  <div id="tripal-feature-admin-summary-figure-desc" "tripal-admin-figure-desc">
+    <span class="figure-title">Feature Composition</span>:
+    This figure depicts the type and source organism of features in your Tripal
+    site. It is populated from the <em><?php print $chart_details['mviewTable']; ?></em>
+    materialized view which was last updated on <em><?php print $chart_details['mviewLastUpdate']; ?></em>.
+    <strong><em>To update this chart, <a href="<?php print $chart_details['mviewUrl'];?>">
+    submit a job to update the materialized view</a></em></strong>.
+  </div>
+</div>
+

+ 93 - 26
tripal_feature/theme/templates/tripal_feature_relationships.tpl.php

@@ -1,17 +1,17 @@
 <?php
 /* Typically in a Tripal template, the data needed is retrieved using a call to
- * chado_expand_var function.  For example, to retrieve all 
+ * chado_expand_var function.  For example, to retrieve all
  * of the feature relationships for this node, the following function call would be made:
- * 
+ *
  *   $feature = chado_expand_var($feature,'table','feature_relationship');
- * 
+ *
  * However, this function call can be extremely slow when there are numerous relationships.
- * This is because the chado_expand_var function is recursive and expands 
+ * This is because the chado_expand_var function is recursive and expands
  * all data following the foreign key relationships tree.  Therefore, to speed retrieval
  * of data, a special variable is provided to this template:
- * 
+ *
  *   $feature->all_relationships;
- *   
+ *
  * This variable is an array with two sub arrays with the keys 'object' and 'subject'.  The array with
  * key 'object' contains relationships where the feature is the object, and the array with
  * the key 'subject' contains relationships where the feature is the subject
@@ -24,23 +24,57 @@ $subject_rels = $all_relationships['subject'];
 
 if (count($object_rels) > 0 or count($subject_rels) > 0) { ?>
   <div class="tripal_feature-data-block-desc tripal-data-block-desc"></div> <?php
-  
-  // first add in the subject relationships.  
+
+  // first add in the subject relationships.
   foreach ($subject_rels as $rel_type => $rels){
-    foreach ($rels as $obj_type => $objects){ ?>
-      <p>This <?php print $feature->type_id->name;?> is <?php print $rel_type ?> the following <b><?php print $obj_type ?></b> feature(s): <?php
-       
+    foreach ($rels as $obj_type => $objects){
+
+      // Make the verb in the sentence make sense in English.
+      switch ($rel_type) {
+        case 'integral part of':
+        case 'instance of':
+          $verb = 'is an';
+          break;
+        case 'proper part of':
+        case 'transformation of':
+        case 'genome of':
+        case 'part of':
+        case 'position of':
+        case 'sequence of':
+        case 'variant of':
+          $verb = 'is a';
+          break;
+        case 'derives from':
+        case 'connects on':
+        case 'contains':
+        case 'finishes':
+        case 'guides':
+        case 'has origin':
+        case 'has part':
+        case 'has quality':
+        case 'is consecutive sequence of':
+        case 'maximally overlaps':
+        case 'overlaps':
+        case 'starts':
+          $verb = '';
+          break;
+        default:
+          $verb = 'is';
+      } ?>
+
+      <p>This <?php print $feature->type_id->name;?> <?php print $verb ?> <?php print $rel_type ?> the following <b><?php print $obj_type ?></b> feature(s): <?php
+
       // the $headers array is an array of fields to use as the colum headers.
       // additional documentation can be found here
       // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
       $headers = array('Feature Name' ,'Unique Name', 'Species', 'Type');
-      
+
       // the $rows array contains an array of rows where each row is an array
       // of values for each column of the table in that row.  Additional documentation
       // can be found here:
       // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
       $rows = array();
-      
+
       foreach ($objects as $object){
         // link the feature to it's node
         $feature_name = $object->record->object_id->name;
@@ -54,12 +88,12 @@ if (count($object_rels) > 0 or count($subject_rels) > 0) { ?>
           $organism_name = l("<i>" . $organism->genus . " " . $organism->species . "</i>", "node/" . $organism->nid, array('html' => TRUE));
         }
         $rows[] = array(
-          array('data' => $feature_name, 'width' => '30%'), 
+          array('data' => $feature_name, 'width' => '30%'),
           array('data' => $object->record->object_id->uniquename, 'width' => '30%'),
           array('data' => $organism_name, 'width' => '30%'),
           array('data' => $object->record->object_id->type_id->name, 'width' => '10%'),
-        ); 
-       } 
+        );
+       }
        // the $table array contains the headers and rows array as well as other
        // options for controlling the display of the table.  Additional
        // documentation can be found here:
@@ -76,7 +110,7 @@ if (count($object_rels) > 0 or count($subject_rels) > 0) { ?>
          'colgroups' => array(),
          'empty' => '',
        );
-       
+
        // once we have our table array structure defined, we call Drupal's theme_table()
        // function to generate the table.
        print theme_table($table); ?>
@@ -84,22 +118,55 @@ if (count($object_rels) > 0 or count($subject_rels) > 0) { ?>
        <br><?php
      }
   }
-  
-  // second add in the object relationships.  
+
+  // second add in the object relationships.
   foreach ($object_rels as $rel_type => $rels){
-    foreach ($rels as $subject_type => $subjects){?>
-      <p>The following <b><?php print $subjects[0]->record->subject_id->type_id->name ?></b> feature(s) are <?php print $rel_type ?> this <?php print $feature->type_id->name;?>: <?php 
+    foreach ($rels as $subject_type => $subjects){
+
+      // Make the verb in the sentence make sense in English.
+      switch ($rel_type) {
+        case 'integral part of':
+        case 'instance of':
+          $verb = 'are an';
+          break;
+        case 'proper part of':
+        case 'transformation of':
+        case 'genome of':
+        case 'part of':
+        case 'position of':
+        case 'sequence of':
+        case 'variant of':
+          $verb = 'are a';
+          break;
+        case 'derives from':
+        case 'connects on':
+        case 'contains':
+        case 'finishes':
+        case 'guides':
+        case 'has origin':
+        case 'has part':
+        case 'has quality':
+        case 'is consecutive sequence of':
+        case 'maximally overlaps':
+        case 'overlaps':
+        case 'starts':
+          $verb = '';
+          break;
+        default:
+          $verb = 'are';
+      } ?>
+      <p>The following <b><?php print $subjects[0]->record->subject_id->type_id->name ?></b> feature(s) <?php print $verb ?> <?php print $rel_type ?> this <?php print $feature->type_id->name;?>: <?php
       // the $headers array is an array of fields to use as the colum headers.
       // additional documentation can be found here
       // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
       $headers = array('Feature Name' ,'Unique Name', 'Species', 'Type');
-      
+
       // the $rows array contains an array of rows where each row is an array
       // of values for each column of the table in that row.  Additional documentation
       // can be found here:
       // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
       $rows = array();
-      
+
       foreach ($subjects as $subject){
         // link the feature to it's node
         $feature_name = $subject->record->subject_id->name;
@@ -117,8 +184,8 @@ if (count($object_rels) > 0 or count($subject_rels) > 0) { ?>
           array('data' =>$subject->record->subject_id->uniquename, 'width' => '30%'),
           array('data' =>$organism_name, 'width' => '30%'),
           array('data' =>$subject->record->subject_id->type_id->name, 'width' => '10%'),
-        ); 
-       } 
+        );
+       }
        // the $table array contains the headers and rows array as well as other
        // options for controlling the display of the table.  Additional
        // documentation can be found here:
@@ -135,7 +202,7 @@ if (count($object_rels) > 0 or count($subject_rels) > 0) { ?>
          'colgroups' => array(),
          'empty' => '',
        );
-       
+
        // once we have our table array structure defined, we call Drupal's theme_table()
        // function to generate the table.
        print theme_table($table); ?>

+ 106 - 121
tripal_feature/theme/templates/tripal_organism_feature_browser.tpl.php

@@ -5,141 +5,126 @@ $organism = $variables['node']->organism;
 // get the list of available sequence ontology terms for which
 // we will build drupal pages from features in chado.  If a feature
 // is not one of the specified typse we won't build a node for it.
-$allowed_types = variable_get('chado_browser_feature_types', 'gene mRNA');
+$allowed_types = variable_get('chado_browser_feature_types');
 $allowed_types = preg_replace("/[\s\n\r]+/", " ", $allowed_types);
 $so_terms = explode(' ', $allowed_types);
 
-// get the feature_id's of the feature that belong to this organism.  But we only
-// want 25 and we want a pager to let the user cycle between pages of features.
-// so we, use the chado_select_record API function to get the results and
-// generate the pager.  The function is smart enough to know which page the user is
-// on and retrieves the proper set of features
-$element = 0;        // an index to specify the pager if more than one is on the page
-$num_per_page = 25;  // the number of features to show per page
-$values = array(
-  'organism_id' => $organism->organism_id,
-  'type_id' => array(
-    'name' => $so_terms
-  ),
-);
-$columns = array('feature_id');
-$options = array(
-  'pager' => array(
-    'limit' => $num_per_page,
-    'element' => $element
-   ),
-  'order_by' => array('name' => 'ASC'),
-);
-$results = chado_select_record('feature', $columns, $values, $options);
+// Don't show the browser if there are no terms
+if (count($so_terms) > 0) {
 
-// now that we have all of the feature IDs, we want to expand each one so that we
-// have all of the neccessary values, including the node ID, if one exists, and the
-// cvterm type name.
-$features = array();
-foreach ($results as $result) {
-  $values = array('feature_id' => $result->feature_id);
+  // get the feature_id's of the feature that belong to this organism.  But we only
+  // want 25 and we want a pager to let the user cycle between pages of features.
+  // so we, use the chado_select_record API function to get the results and
+  // generate the pager.  The function is smart enough to know which page the user is
+  // on and retrieves the proper set of features
+  $element = 0;        // an index to specify the pager if more than one is on the page
+  $num_per_page = 25;  // the number of features to show per page
+  $values = array(
+    'organism_id' => $organism->organism_id,
+    'type_id' => array(
+      'name' => $so_terms
+    ),
+  );
+  $columns = array('feature_id');
   $options = array(
-    'include_fk' => array(
-      'type_id' => 1
-    )
+    'pager' => array(
+      'limit' => $num_per_page,
+      'element' => $element
+     ),
+    'order_by' => array('name' => 'ASC'),
   );
-  $features[] = chado_generate_var('feature', $values, $options);
-}
+  $results = chado_select_record('feature', $columns, $values, $options);
 
-if (count($features) > 0) { ?>
-  <div class="tripal_organism-data-block-desc tripal-data-block-desc">The following browser provides a quick view for new visitors.  Use the searching mechanism to find specific features.</div> <?php
+  // now that we have all of the feature IDs, we want to expand each one so that we
+  // have all of the neccessary values, including the node ID, if one exists, and the
+  // cvterm type name.
+  $features = array();
+  foreach ($results as $result) {
+    $values = array('feature_id' => $result->feature_id);
+    $options = array(
+      'include_fk' => array(
+        'type_id' => 1
+      )
+    );
+    $features[] = chado_generate_var('feature', $values, $options);
+  }
 
-  // the $headers array is an array of fields to use as the colum headers.
-  // additional documentation can be found here
-  // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
-  $headers = array('Feature Name' ,'Unique Name', 'Type');
+  if (count($features) > 0) { ?>
+    <div class="tripal_organism-data-block-desc tripal-data-block-desc">The following browser provides a quick view for new visitors.  Use the searching mechanism to find specific features.</div> <?php
 
-  // the $rows array contains an array of rows where each row is an array
-  // of values for each column of the table in that row.  Additional documentation
-  // can be found here:
-  // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
-  $rows = array();
+    // the $headers array is an array of fields to use as the colum headers.
+    // additional documentation can be found here
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $headers = array('Feature Name' ,'Unique Name', 'Type');
 
-  // let admins know they can customize the terms that appear in the list
-  print tripal_set_message("Administrators, you can specify the feature types ".
-    "that should appear in this browser or remove it from the list of resources ".
-    "by navigating to the ".
-    l("Tripal feature settings page", "admin/tripal/chado/tripal_feature/configuration", array('attributes' => array('target' => '_blank'))),
-    TRIPAL_INFO,
-    array('return_html' => 1)
-  );
+    // the $rows array contains an array of rows where each row is an array
+    // of values for each column of the table in that row.  Additional documentation
+    // can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $rows = array();
+
+    // let admins know they can customize the terms that appear in the list
+    print tripal_set_message("Administrators, you can specify the feature types ".
+      "that should appear in this browser or remove it from the list of resources ".
+      "by navigating to the ".
+      l("Tripal feature settings page", "admin/tripal/chado/tripal_feature/configuration", array('attributes' => array('target' => '_blank'))),
+      TRIPAL_INFO,
+      array('return_html' => 1)
+    );
 
-  foreach ($features as $feature){
-    $fname =  $feature->name;
-    if (property_exists($feature, 'nid')) {
-      $fname =   l($fname, "node/$feature->nid", array('attributes' => array('target' => '_blank')));
+    foreach ($features as $feature){
+      $fname =  $feature->name;
+      if (property_exists($feature, 'nid')) {
+        $fname =   l($fname, "node/$feature->nid", array('attributes' => array('target' => '_blank')));
+      }
+      $rows[] = array(
+        $fname,
+        $feature->uniquename,
+        $feature->type_id->name
+      );
     }
-    $rows[] = array(
-      $fname,
-      $feature->uniquename,
-      $feature->type_id->name
+    // the $table array contains the headers and rows array as well as other
+    // options for controlling the display of the table.  Additional
+    // documentation can be found here:
+    // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+    $table = array(
+      'header' => $headers,
+      'rows' => $rows,
+      'attributes' => array(
+        'id' => 'tripal_organism-table-features',
+        'class' => 'tripal-data-table'
+      ),
+      'sticky' => FALSE,
+      'caption' => '',
+      'colgroups' => array(),
+      'empty' => '',
     );
-  }
-  // the $table array contains the headers and rows array as well as other
-  // options for controlling the display of the table.  Additional
-  // documentation can be found here:
-  // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
-  $table = array(
-    'header' => $headers,
-    'rows' => $rows,
-    'attributes' => array(
-      'id' => 'tripal_organism-table-features',
-      'class' => 'tripal-data-table'
-    ),
-    'sticky' => FALSE,
-    'caption' => '',
-    'colgroups' => array(),
-    'empty' => '',
-  );
-  // once we have our table array structure defined, we call Drupal's theme_table()
-  // function to generate the table.
-  print theme_table($table);
+    // once we have our table array structure defined, we call Drupal's theme_table()
+    // function to generate the table.
+    print theme_table($table);
 
-  // the $pager array values that control the behavior of the pager.  For
-  // documentation on the values allows in this array see:
-  // https://api.drupal.org/api/drupal/includes!pager.inc/function/theme_pager/7
-  // here we add the paramter 'block' => 'feature_browser'. This is because
-  // the pager is not on the default block that appears. When the user clicks a
-  // page number we want the browser to re-appear with the page is loaded.
-  $pager = array(
-    'tags' => array(),
-    'element' => $element,
-    'parameters' => array(
-      'block' => 'feature_browser'
-    ),
-    'quantity' => $num_per_page,
-  );
-  print theme_pager($pager);
-}
-else {  ?>
-  <p>There are no results.</p><?php
-  print tripal_set_message("
-    Administrators, perform the following to show features in this browser:
-    <ul>
-      <li>Load features for this organism using the " .
-        l("FASTA loader", 'admin/tripal/loaders/fasta_loader') . ",  ".
-        l("GFF Loader",   'admin/tripal/loaders/gff3_load') . " or ".
-        l("Bulk Loader",  'admin/tripal/loaders/bulk'). "</li>
-      <li>Sync the features that should have pages using the ".
-        l("Sync features page", 'admin/tripal/chado/tripal_feature/sync'). "</li>
-      <li>Return to this page to browse features.</li>
-      <li>Ensure the user " .
-       l("has permission", 'admin/people/permissions') . " to view the feature content</li>
-    </ul>
-    <br>
-    <br>
-    You can specify the feature types
-    that should appear in this browser or remove it from the list of resources by navigating to the " .
-    l("Tripal feature settings page", "admin/tripal/chado/tripal_feature/configuration", array('attributes' => array('target' => '_blank')))  . "
-    The feature browser will not appear to site visitors unless features are present. ",
-    TRIPAL_INFO,
-    array('return_html' => 1));
-}
+    // the $pager array values that control the behavior of the pager.  For
+    // documentation on the values allows in this array see:
+    // https://api.drupal.org/api/drupal/includes!pager.inc/function/theme_pager/7
+    // here we add the paramter 'block' => 'feature_browser'. This is because
+    // the pager is not on the default block that appears. When the user clicks a
+    // page number we want the browser to re-appear with the page is loaded.
+    $pager = array(
+      'tags' => array(),
+      'element' => $element,
+      'parameters' => array(
+        'block' => 'feature_browser'
+      ),
+      'quantity' => $num_per_page,
+    );
+    print theme_pager($pager);
 
+    print tripal_set_message("
+      Administrators, please note that the feature browser will be retired in
+      a future version of Tripal.",
+      TRIPAL_INFO,
+      array('return_html' => 1));
+  }
+}
 
 

+ 121 - 8
tripal_feature/theme/tripal_feature.theme.inc

@@ -1,4 +1,4 @@
-<?php 
+<?php
 
 
 /**
@@ -27,9 +27,9 @@ function tripal_feature_preprocess_tripal_feature_sequence(&$variables) {
   // now extract the sequences
   $featureloc_sequences = tripal_feature_load_featureloc_sequences($feature->feature_id, $ffeaturelocs);
   $feature->featureloc_sequences = $featureloc_sequences;
-  
+
   // if this feature has associated protein sequences (or others via relationships
-  // then we want to make sure the relationships are added so that we can 
+  // then we want to make sure the relationships are added so that we can
   // show the protein sequences
   if (!$feature->all_relationships) {
     $feature->all_relationships = tripal_feature_get_feature_relationships($feature);
@@ -145,7 +145,7 @@ function tripal_feature_load_featureloc_sequences($feature_id, $featurelocs) {
       $args = array(':feature_id' => $featureloc->srcfeature_id->feature_id);
       $start = $featureloc->fmin + 1;
       $size = $featureloc->fmax - $featureloc->fmin;
-      
+
       // TODO: fix the hard coded $start and $size
       // the $start and $size variables are hard-coded in the SQL statement
       // because the db_query function places quotes around all placeholders
@@ -852,11 +852,11 @@ function tripal_feature_get_feature_relationships($feature) {
         }
       }
       $rel->record = $relationship;
-       
+
       // get the relationship and child types
       $rel_type = t(preg_replace('/_/', " ", $relationship->type_id->name));
       $child_type = $relationship->subject_id->type_id->name;
-       
+
       // get the node id of the subject
       $sql = "SELECT nid FROM {chado_feature} WHERE feature_id = :feature_id";
       $n = db_query($sql, array(':feature_id' => $relationship->subject_id->feature_id))->fetchObject();
@@ -893,14 +893,14 @@ function tripal_feature_get_feature_relationships($feature) {
       $rel->record = $relationship;
       $rel_type = t(preg_replace('/_/', " ", $relationship->type_id->name));
       $parent_type = $relationship->object_id->type_id->name;
-       
+
       // get the node id of the subject
       $sql = "SELECT nid FROM {chado_feature} WHERE feature_id = :feature_id";
       $n = db_query($sql, array(':feature_id' => $relationship->object_id->feature_id))->fetchObject();
       if ($n) {
         $rel->record->nid = $n->nid;
       }
-       
+
       if (!array_key_exists($rel_type, $relationships['subject'])) {
         $relationships['subject'][$rel_type] = array();
       }
@@ -912,3 +912,116 @@ function tripal_feature_get_feature_relationships($feature) {
   }
   return $relationships;
 }
+
+/**
+ *
+ */
+function tripal_feature_preprocess_tripal_feature_bar_chart_type_organism_summary(&$vars) {
+
+  // Add in all the javascript/css files.
+  tripal_add_d3js();
+  drupal_add_css(drupal_get_path('module','tripal_feature') . '/theme/css/tripal_feature.css');
+  drupal_add_js(drupal_get_path('module','tripal_feature') . '/theme/js/tripalFeature.adminChart.js');
+
+  // Retrieve and process all the data and save it as javascript settings.
+  //'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
+
+  // We are using the organism_feature_count materialized view as the source for our data.
+  // Thus grab all the records from this materialized view.
+  $organism_feature_count = chado_select_record(
+    'organism_feature_count',
+    array('*'),
+    array(),
+    array('order_by' => array('genus' => 'ASC', 'species' => 'ASC', 'feature_type' => 'ASC', 'num_features' => 'DESC'))
+  );
+
+  // Initialize variables.
+  $chart = array();
+  $type_names = array();
+  $organism_names = array();
+  $max_bar_height = 0;
+
+  // Process each row of the materialzied view into the chart array.
+  // Note: it's first keyed by type since each type will be a bar. Each type will have
+  // a "bars" array with the start (y0) and end (y1) height on the bar for a given
+  // organism. Finally we keep a record of the names of the types & organisms
+  // for axis' and legend generation respectively.
+  foreach ($organism_feature_count as $row) {
+
+    // Build up the easy details for the current row's type. These will be overridden
+    // multiple times but that's more efficient than checking each time.
+    $chart[$row->cvterm_id]['cvterm_id'] = $row->cvterm_id;
+    $chart[$row->cvterm_id]['name'] = str_replace('_', ' ', $row->feature_type);
+
+    // Save the name of the type and organism into their respective arrays
+    // for generation of axis' and legends for the chart.
+    $type_names[$row->cvterm_id] = $row->feature_type;
+    $organism_names[$row->organism_id] = $row->genus . ' ' . $row->species;
+
+    // Save information about the current organism. This isn't actually used by the
+    // chart but can be used to debug the bar generation to follow.
+    $chart[$row->cvterm_id]['organisms'][] = array(
+      'name' => $row->genus . ' ' . $row->species,
+      'value' => (int) $row->num_features
+    );
+
+    // Now to build the bar array with the start (y0) and end (y1) height on the
+    // bar for a given organism.
+    // NOTE: we cannot assume the types are all in order so store y0 & y1 in the
+    // $chart[type] array.
+    // If y0 has not yet been set for this type then we're starting with the first
+    // chunk (organism) on the bar.
+    if (!isset($chart[$row->cvterm_id]['y0'])) {
+      $chart[$row->cvterm_id]['y0'] = 0;
+      $chart[$row->cvterm_id]['y1'] = $row->num_features;
+    }
+    // Otherwise, add the next chunk (organism) on top of the pre-existing bar.
+    else {
+      $chart[$row->cvterm_id]['y0'] = $chart[$row->cvterm_id]['y1'];
+      $chart[$row->cvterm_id]['y1'] = $chart[$row->cvterm_id]['y0'] + $row->num_features;
+    }
+    // Now save the bar chunk we just determined.
+    $chart[$row->cvterm_id]['bars'][] = array(
+      'name' => $row->genus . ' ' . $row->species,
+      'y0' => $chart[$row->cvterm_id]['y0'],
+      'y1' => $chart[$row->cvterm_id]['y1'],
+    );
+
+    // We also need to keep track of the total number of features for a single bar (Type).
+    $chart[$row->cvterm_id]['total_features'] = (int) $chart[$row->cvterm_id]['y1'];
+    // And the maximum "height" for all bars.
+    if ($max_bar_height < $chart[$row->cvterm_id]['total_features']) {
+      $max_bar_height = (int) $chart[$row->cvterm_id]['total_features'];
+    }
+  }
+
+  // Sort based on the total number of features.
+  // NOTE: This changes the keys so it's no longer the organism/type_id.
+  usort($chart, 'tripal_feature_admin_summary_sort');
+  sort($type_names);
+  sort($organism_names);
+
+  // We also need to add information about the materialized views
+  // so that admin can update it and know how recent the data is.
+  $mview = db_query('
+    SELECT mview_id, name, last_update
+    FROM tripal_mviews
+    WHERE mv_table=:mv_table',
+    array(':mv_table' => 'organism_feature_count')
+  )->fetchObject();
+
+  $vars['chart_details'] = array(
+    'summary' => $chart,
+    'types' => $type_names,
+    'organisms' => $organism_names,
+    'legendPosition' => 'top',
+    'maxBarHeight' => $max_bar_height,
+    'mviewUrl' => url('admin/tripal/schema/mviews/update/' . $mview->mview_id),
+    'mviewTable' => $mview->name,
+    'mviewLastUpdate' => $mview->last_update ? format_date($mview->last_update) : '',
+  );
+
+  // Save everything we just determined as a Drupal JS settings so that we have access to
+  // it in our js script.
+  drupal_add_js(array('tripalFeature' => array('admin' => $vars['chart_details'])), 'setting');
+}

+ 1 - 1
tripal_feature/tripal_feature.info

@@ -3,7 +3,7 @@ description = Supports the sequence (feature) tables of Chado by providing pages
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 
 stylesheets[all][] = theme/css/tripal_feature.css
 scripts[]          = theme/js/tripal_feature.js

+ 140 - 35
tripal_feature/tripal_feature.install

@@ -48,19 +48,18 @@ function tripal_feature_requirements($phase) {
  * @ingroup tripal_feature
  */
 function tripal_feature_install() {
-  // create the module's data directory
-  tripal_create_files_dir('tripal_feature');
-
-  // add the materialized view
+  // Add the materialized view.
   tripal_feature_add_organism_count_mview();
 
-  // create the temp table we will use for loading GFF files
-  tripal_cv_create_tripal_gff_temp();
+  // Add the custom tables.
+  tripal_feature_add_tripal_gff_temp_table();
+  tripal_feature_add_tripal_gffcds_temp_table();
+  tripal_feature_add_tripal_gffprotein_temp_table();
 
-  // add the vocabularies used by the feature module:
+  // Add the vocabularies used by the feature module.
   tripal_feature_add_cvs();
 
-  // set the default vocabularies
+  // Set the default vocabularies.
   tripal_set_default_cv('feature', 'type_id', 'sequence');
   tripal_set_default_cv('featureprop', 'type_id', 'feature_property');
   tripal_set_default_cv('feature_relationship', 'type_id', 'feature_relationship');
@@ -75,36 +74,114 @@ function tripal_feature_uninstall() {
 
 }
 
+function tripal_feature_add_tripal_gff_temp_table() {
+  $schema = array(
+    'table' => 'tripal_gff_temp',
+    'fields' => array(
+      'feature_id' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'organism_id' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'uniquename' => array(
+        'type' => 'text',
+        'not null' => TRUE,
+      ),
+      'type_name' => array(
+        'type' => 'varchar',
+        'length' => '1024',
+        'not null' => TRUE,
+      ),
+    ),
+    'indexes' => array(
+      'tripal_gff_temp_idx0' => array('feature_id'),
+      'tripal_gff_temp_idx0' => array('organism_id'),
+      'tripal_gff_temp_idx1' => array('uniquename'),
+    ),
+    'unique keys' => array(
+      'tripal_gff_temp_uq0' => array('feature_id'),
+      'tripal_gff_temp_uq1' => array('uniquename', 'organism_id', 'type_name'),
+    ),
+  );
+  chado_create_custom_table('tripal_gff_temp', $schema, TRUE);
+}
+
 /**
- * Create a temporary table used for loading gff3 files
  *
- * @ingroup tripal_feature
  */
-function tripal_cv_create_tripal_gff_temp() {
-  // the tripal_obo_temp table is used for temporary housing of records when loading OBO files
-  // we create it here using plain SQL because we want it to be in the chado schema but we
-  // do not want to use the Tripal Custom Table API because we don't want it to appear in the
-  // list of custom tables.  It needs to be available for the Tripal Chado API so we create it
-  // here and then define it in the tripal_cv/api/tripal_cv.schema.api.inc
-  if (!db_table_exists('chado.tripal_gff_temp')) {
-    $sql = "
-      CREATE TABLE {tripal_gff_temp} (
-        feature_id integer NOT NULL,
-        organism_id integer NOT NULL,
-        uniquename text NOT NULL,
-        type_name character varying(1024) NOT NULL,
-        CONSTRAINT tripal_gff_temp_uq0 UNIQUE (feature_id),
-        CONSTRAINT tripal_gff_temp_uq1 UNIQUE (uniquename, organism_id, type_name)
-      );
-    ";
-    chado_query($sql);
-    $sql = "CREATE INDEX tripal_gff_temp_idx0 ON {tripal_gff_temp} USING btree (feature_id)";
-    chado_query($sql);
-    $sql = "CREATE INDEX tripal_gff_temp_idx1 ON {tripal_gff_temp} USING btree (organism_id)";
-    chado_query($sql);
-    $sql = "CREATE INDEX tripal_gff_temp_idx2 ON {tripal_gff_temp} USING btree (uniquename)";
-    chado_query($sql);
-  }
+function tripal_feature_add_tripal_gffcds_temp_table($skip_recreate = TRUE) {
+  $schema = array(
+    'table' => 'tripal_gffcds_temp',
+    'fields' => array(
+      'feature_id' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'parent_id' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'phase' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'strand' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'fmin' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'fmax' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+    ),
+    'indexes' => array(
+      'tripal_gff_temp_idx0' => array('feature_id'),
+      'tripal_gff_temp_idx0' => array('parent_id'),
+    ),
+  );
+  chado_create_custom_table('tripal_gffcds_temp', $schema, $skip_recreate);
+}
+
+/**
+ *
+ */
+function tripal_feature_add_tripal_gffprotein_temp_table() {
+  $schema = array(
+    'table' => 'tripal_gffprotein_temp',
+    'fields' => array(
+      'feature_id' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'parent_id' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'fmin' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+      'fmax' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
+    ),
+    'indexes' => array(
+      'tripal_gff_temp_idx0' => array('feature_id'),
+      'tripal_gff_temp_idx0' => array('parent_id'),
+    ),
+    'unique keys' => array(
+      'tripal_gff_temp_uq0' => array('feature_id'),
+    ),
+  );
+  chado_create_custom_table('tripal_gffprotein_temp', $schema, TRUE);
 }
 
 /**
@@ -412,4 +489,32 @@ function tripal_feature_update_7201() {
     $error = $e->getMessage();
     throw new DrupalUpdateException('Failed to complete update' . $error);
   }
+}
+
+/**
+ * Adds the temporary tables used for loading GFF files.
+ */
+function tripal_feature_update_7202() {
+  try {
+    tripal_feature_add_tripal_gff_temp_table();
+    tripal_feature_add_tripal_gffcds_temp_table();
+    tripal_feature_add_tripal_gffprotein_temp_table();
+  }
+  catch (\PDOException $e) {
+    $error = $e->getMessage();
+    throw new DrupalUpdateException('Failed to complete update' . $error);
+  }
+}
+
+/**
+ * Removes the unique constraint on the tripal_gffcds_temp table.
+ */
+function tripal_feature_update_7203() {
+  try {
+    tripal_feature_add_tripal_gffcds_temp_table(FALSE);
+  }
+  catch (\PDOException $e) {
+    $error = $e->getMessage();
+    throw new DrupalUpdateException('Failed to complete update' . $error);
+  }
 }

+ 42 - 33
tripal_feature/tripal_feature.module

@@ -13,7 +13,6 @@
  */
 
 require_once 'api/tripal_feature.api.inc';
-require_once 'api/tripal_feature.schema.api.inc';
 require_once 'api/tripal_feature.DEPRECATED.inc';
 
 require_once 'theme/tripal_feature.theme.inc';
@@ -170,7 +169,7 @@ function tripal_feature_menu() {
     'access arguments' => array('administer tripal feature'),
     'type' => MENU_LOCAL_TASK,
     'file' =>  'includes/tripal_core.toc.inc',
-    'file path' => drupal_get_path('module', 'tripal_core'), 
+    'file path' => drupal_get_path('module', 'tripal_core'),
     'weight' => 3
   );
   $items['admin/tripal/chado/tripal_feature/configuration'] = array(
@@ -250,6 +249,7 @@ function tripal_feature_search_biological_data_views() {
 function tripal_feature_theme($existing, $type, $theme, $path) {
   $core_path = drupal_get_path('module', 'tripal_core');
 
+  // Feature Node Page Templates.
   $items = array(
     'node__chado_feature' => array(
       'template' => 'node--chado-generic',
@@ -312,36 +312,46 @@ function tripal_feature_theme($existing, $type, $theme, $path) {
       'template' => 'tripal_feature_relationships',
       'path' => "$path/theme/templates",
     ),
-    // help template
-    'tripal_feature_help' => array(
-      'template' => 'tripal_feature_help',
-      'variables' =>  array(NULL),
-      'path' => "$path/theme/templates"
-    ),
+  );
 
-    // template for the organism page
-    'tripal_organism_feature_browser' => array(
-      'variables' => array('node' => NULL),
-      'template' => 'tripal_organism_feature_browser',
-      'path' => "$path/theme/templates",
-    ),
-    'tripal_organism_feature_counts' => array(
-      'variables' => array('node' => NULL),
-      'template' => 'tripal_organism_feature_counts',
-      'path' => "$path/theme/templates",
-    ),
+  // Feature Node Teaser
+  $items['tripal_feature_teaser'] = array(
+    'variables' => array('node' => NULL),
+    'template' => 'tripal_feature_teaser',
+    'path' => "$path/theme/templates",
+  );
 
-    // themed forms
-    'tripal_feature_seq_extract_form' => array(
-       'arguments' => array('form'),
-    ),
+  // Templates for other node pages.
+  // Organism Feature Browser.
+  $items['tripal_organism_feature_browser'] = array(
+    'variables' => array('node' => NULL),
+    'template' => 'tripal_organism_feature_browser',
+    'path' => "$path/theme/templates",
+  );
+  $items['tripal_organism_feature_counts'] = array(
+    'variables' => array('node' => NULL),
+    'template' => 'tripal_organism_feature_counts',
+    'path' => "$path/theme/templates",
+  );
 
-    // themed teaser
-    'tripal_feature_teaser' => array(
-      'variables' => array('node' => NULL),
-      'template' => 'tripal_feature_teaser',
-      'path' => "$path/theme/templates",
-    ),
+  // Administrative Help Template.
+  $items['tripal_feature_help'] = array(
+    'template' => 'tripal_feature_help',
+    'variables' =>  array(NULL),
+    'path' => "$path/theme/templates"
+  );
+
+  // Themed Forms
+  $items['tripal_feature_seq_extract_form'] = array(
+     'arguments' => array('form'),
+  );
+
+  // D3 Charts.
+  // Feature Type/Organism Stacked Bar Chart.
+  $items['tripal_feature_bar_chart_type_organism_summary'] = array(
+    'template' => 'tripal_feature_bar_chart_type_organism_summary',
+    'variables' =>  array(NULL),
+    'path' => "$path/theme/templates"
   );
 
   return $items;
@@ -401,9 +411,8 @@ function tripal_feature_job_describe_args($callback, $args) {
     }
 
     $new_args['Sequence Type'] = $args[2];
-    $new_args['Is Unique Name'] = $args[3];
+    $new_args['Is Unique Name'] = $args[3] ? 'Yes' : 'No';
     $new_args['Features Names'] = $args[4];
-
   }
   elseif ($callback == 'tripal_feature_load_gff3') {
 
@@ -492,8 +501,8 @@ function tripal_feature_match_features_page($id) {
     GROUP BY F.name, F.uniquename, F.feature_id, O.genus, O.species,
       O.organism_id, CVT.cvterm_id, CVT.name, CF.nid
   ";
-  
-  $args = array(':uname' => $id, ':fname' => $id, ':sname' => $id); 
+
+  $args = array(':uname' => $id, ':fname' => $id, ':sname' => $id);
   $results = chado_query($sql, $args);
 
   $num_matches = 0;

+ 10 - 14
tripal_feature/tripal_feature.views_default.inc

@@ -13,7 +13,7 @@ function tripal_feature_views_default_views() {
   $views = array();
 
   // User View ("Search Biological Content")
-  // Remember, if you change the name/path of this view, you also want to 
+  // Remember, if you change the name/path of this view, you also want to
   // change it's description in tripal_feature_search_biological_data_views()
   $view = tripal_feature_defaultvalue_user_features();
   $view = tripal_make_view_compatible_with_external($view);
@@ -286,7 +286,10 @@ function tripal_feature_defaultview_admin_features() {
   $handler->display->display_options['access']['perm'] = 'access chado_feature content';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of features that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of features or to find a specific feature.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -472,18 +475,11 @@ function tripal_feature_defaultview_admin_features() {
   $handler->display->display_options['fields']['nothing']['label'] = '';
   $handler->display->display_options['fields']['nothing']['alter']['text'] = '[edit_node]   [delete_node]';
   $handler->display->display_options['fields']['nothing']['element_label_colon'] = FALSE;
-  /* Sort criterion: Chado Organism: Common Name */
-  $handler->display->display_options['sorts']['common_name']['id'] = 'common_name';
-  $handler->display->display_options['sorts']['common_name']['table'] = 'organism';
-  $handler->display->display_options['sorts']['common_name']['field'] = 'common_name';
-  /* Sort criterion: Chado Cvterm: Name */
-  $handler->display->display_options['sorts']['name']['id'] = 'name';
-  $handler->display->display_options['sorts']['name']['table'] = 'cvterm';
-  $handler->display->display_options['sorts']['name']['field'] = 'name';
-  /* Sort criterion: Chado Feature: Name */
-  $handler->display->display_options['sorts']['name_1']['id'] = 'name_1';
-  $handler->display->display_options['sorts']['name_1']['table'] = 'feature';
-  $handler->display->display_options['sorts']['name_1']['field'] = 'name';
+  /* Sort criterion: Chado Feature: Feature Id */
+  $handler->display->display_options['sorts']['feature_id']['id'] = 'feature_id';
+  $handler->display->display_options['sorts']['feature_id']['table'] = 'feature';
+  $handler->display->display_options['sorts']['feature_id']['field'] = 'feature_id';
+  $handler->display->display_options['sorts']['feature_id']['order'] = 'DESC';
   /* Filter criterion: Chado Organism: Common Name */
   $handler->display->display_options['filters']['common_name']['id'] = 'common_name';
   $handler->display->display_options['filters']['common_name']['table'] = 'organism';

+ 8 - 6
tripal_featuremap/includes/tripal_featuremap.chado_node.inc

@@ -100,7 +100,7 @@ function chado_featuremap_form($node, &$form_state) {
     '#maxlength'     => 255
   );
   $form['description']= array(
-    '#type'          => 'textarea',
+    '#type'          => 'text_format',
     '#title'         => t('Map Description'),
     '#description'   => t('A description of the map.'),
     '#required'      => TRUE,
@@ -155,6 +155,9 @@ function chado_featuremap_form($node, &$form_state) {
  */
 function chado_featuremap_validate($node, $form, &$form_state) {
 
+  if ($node->unittype_id == 0) {
+    form_set_error('unittype_id', 'Please provide a unit type for this map.');
+  }
   // We only want to validate when the node is saved.
   // Since this validate can be called on AJAX and Deletion of the node
   // we need to make this check to ensure queries are not executed
@@ -171,7 +174,6 @@ function chado_featuremap_validate($node, $form, &$form_state) {
 
   // trim white space from text fields
   $node->fmapname = property_exists($node, 'fmapname') ? trim($node->fmapname) : '';
-  $node->description = property_exists($node, 'description') ? trim($node->description) : '';
 
   $featuremap = 0;
   // check to make sure the unique name on the map is unique
@@ -268,8 +270,8 @@ function chado_featuremap_insert($node) {
   // we do need to proceed with insertion into the chado/drupal linking table.
   if (!property_exists($node, 'featuremap_id')) {
 
-    $node->fmapname       = trim($node->fmapname);
-    $node->description    = trim($node->description);
+    $node->fmapname = trim($node->fmapname);
+    $node->description  = trim($node->description['value']);
 
     $values = array(
       'name'        => $node->fmapname,
@@ -345,8 +347,8 @@ function chado_featuremap_insert($node) {
  */
 function chado_featuremap_update($node) {
 
-  $node->fmapname          = trim($node->fmapname);
-  $node->description    = trim($node->description);
+  $node->fmapname  = trim($node->fmapname);
+  $node->description  = trim($node->description['value']);
 
   $featuremap_id = chado_get_id_from_nid('featuremap', $node->nid) ;
 

+ 1 - 1
tripal_featuremap/tripal_featuremap.info

@@ -3,7 +3,7 @@ description = Supports the map tables of Chado by providing pages for viewing an
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 
 dependencies[] = tripal_core
 dependencies[] = tripal_views

+ 0 - 2
tripal_featuremap/tripal_featuremap.install

@@ -47,8 +47,6 @@ function tripal_featuremap_requirements($phase) {
  * @ingroup tripal_featuremap
  */
 function tripal_featuremap_install() {
-  // create the module's data directory
-  tripal_create_files_dir('tripal_featuremap');
 
   // add the featuremapprop table to Chado
   tripal_featuremap_add_custom_tables();

+ 9 - 6
tripal_featuremap/tripal_featuremap.views_default.inc

@@ -95,8 +95,10 @@ function tripal_featuremap_defaultview_admin_featuremaps() {
   $handler->display->display_options['access']['perm'] = 'access chado_featuremap content';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
-  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Filter';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of feature maps that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of maps or to find a specific map.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -211,10 +213,11 @@ function tripal_featuremap_defaultview_admin_featuremaps() {
   $handler->display->display_options['fields']['nothing']['label'] = '';
   $handler->display->display_options['fields']['nothing']['alter']['text'] = '[edit_node]   [delete_node]';
   $handler->display->display_options['fields']['nothing']['element_label_colon'] = FALSE;
-  /* Sort criterion: Chado Featuremap: Name */
-  $handler->display->display_options['sorts']['name']['id'] = 'name';
-  $handler->display->display_options['sorts']['name']['table'] = 'featuremap';
-  $handler->display->display_options['sorts']['name']['field'] = 'name';
+  /* Sort criterion: Chado Featuremap: Id */
+  $handler->display->display_options['sorts']['featuremap_id']['id'] = 'featuremap_id';
+  $handler->display->display_options['sorts']['featuremap_id']['table'] = 'featuremap';
+  $handler->display->display_options['sorts']['featuremap_id']['field'] = 'featuremap_id';
+  $handler->display->display_options['sorts']['featuremap_id']['order'] = 'DESC';
   /* Filter criterion: Chado Featuremap: Name */
   $handler->display->display_options['filters']['name_1']['id'] = 'name_1';
   $handler->display->display_options['filters']['name_1']['table'] = 'featuremap';

+ 1 - 1
tripal_genetic/tripal_genetic.info

@@ -3,7 +3,7 @@ description = Supports the genetic tables of Chado by providing pages for viewin
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 
 dependencies[] = tripal_core
 dependencies[] = tripal_views

+ 9 - 2
tripal_genetic/tripal_genetic.views_default.inc

@@ -91,8 +91,10 @@ function tripal_genetic_defaultviews_admin_genotypes() {
   $handler->display->display_options['access']['type'] = 'perm';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
-  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Filter';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of genotypes that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of genotypes or to find a specific genotype.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -138,6 +140,11 @@ function tripal_genetic_defaultviews_admin_genotypes() {
   $handler->display->display_options['fields']['description']['id'] = 'description';
   $handler->display->display_options['fields']['description']['table'] = 'genotype';
   $handler->display->display_options['fields']['description']['field'] = 'description';
+  /* Sort criterion: Chado Genotype: Id */
+  $handler->display->display_options['sorts']['genotype_id']['id'] = 'genotype_id';
+  $handler->display->display_options['sorts']['genotype_id']['table'] = 'genotype';
+  $handler->display->display_options['sorts']['genotype_id']['field'] = 'genotype_id';
+  $handler->display->display_options['sorts']['genotype_id']['order'] = 'DESC';
   /* Filter criterion: Chado Genotype: Uniquename */
   $handler->display->display_options['filters']['uniquename']['id'] = 'uniquename';
   $handler->display->display_options['filters']['uniquename']['table'] = 'genotype';

+ 3 - 4
tripal_library/includes/tripal_library.chado_node.inc

@@ -165,7 +165,7 @@ function chado_library_form($node, &$form_state) {
   );
 
   $form['description']= array(
-    '#type'          => 'textarea',
+    '#type'          => 'text_format',
     '#title'         => t('Library Description'),
     '#description'   => t('A brief description of the library'),
     '#required'      => TRUE,
@@ -235,7 +235,6 @@ function chado_library_validate($node, $form, &$form_state) {
   // trim white space from text fields
   $node->libraryname = property_exists($node, 'libraryname') ? trim($node->libraryname) : '';
   $node->uniquename  = property_exists($node, 'uniquename') ? trim($node->uniquename) : '';
-  $node->description = property_exists($node, 'description') ? trim($node->description) : '';
 
   $lib = 0;
   // check to make sure the unique name on the library is unique
@@ -276,7 +275,7 @@ function chado_library_insert($node) {
   if (!property_exists($node, 'library_id')) {
     $node->libraryname = trim($node->libraryname);
     $node->uniquename  = trim($node->uniquename);
-    $node->description = trim($node->description);
+    $node->description  = trim($node->description['value']);
 
     $values = array(
       'name' => $node->libraryname,
@@ -344,7 +343,7 @@ function chado_library_update($node) {
 
   $node->libraryname = trim($node->libraryname);
   $node->uniquename  = trim($node->uniquename);
-  $node->description = trim($node->description);
+  $node->description  = trim($node->description['value']);
 
   // update the library record
   $library_id = chado_get_id_from_nid('library', $node->nid);

+ 1 - 1
tripal_library/tripal_library.info

@@ -3,7 +3,7 @@ description = Supports the library tables of Chado by providing pages for viewin
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 
 dependencies[] = tripal_core
 dependencies[] = tripal_views

+ 0 - 3
tripal_library/tripal_library.install

@@ -48,9 +48,6 @@ function tripal_library_requirements($phase) {
  */
 function tripal_library_install() {
 
-  // create the module's data directory
-  tripal_create_files_dir('tripal_library');
-
   // add the materialized view
   tripal_library_add_mview_library_feature_count();
 

+ 9 - 6
tripal_library/tripal_library.views_default.inc

@@ -95,7 +95,10 @@ function tripal_library_admin_defaultviews_library() {
   $handler->display->display_options['access']['perm'] = 'access chado_library content';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of libraries that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of libraries or to find a specific library.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -234,10 +237,11 @@ function tripal_library_admin_defaultviews_library() {
   $handler->display->display_options['fields']['nothing']['label'] = '';
   $handler->display->display_options['fields']['nothing']['alter']['text'] = '[edit_node]   [delete_node]';
   $handler->display->display_options['fields']['nothing']['element_label_colon'] = FALSE;
-  /* Sort criterion: Chado Library: Name */
-  $handler->display->display_options['sorts']['name']['id'] = 'name';
-  $handler->display->display_options['sorts']['name']['table'] = 'library';
-  $handler->display->display_options['sorts']['name']['field'] = 'name';
+  /* Sort criterion: Chado Library: Id */
+  $handler->display->display_options['sorts']['library_id']['id'] = 'library_id';
+  $handler->display->display_options['sorts']['library_id']['table'] = 'library';
+  $handler->display->display_options['sorts']['library_id']['field'] = 'library_id';
+  $handler->display->display_options['sorts']['library_id']['order'] = 'DESC';
   /* Filter criterion: Chado Organism: Common Name */
   $handler->display->display_options['filters']['common_name']['id'] = 'common_name';
   $handler->display->display_options['filters']['common_name']['table'] = 'organism';
@@ -258,7 +262,6 @@ function tripal_library_admin_defaultviews_library() {
   $handler->display->display_options['filters']['type_id']['id'] = 'type_id';
   $handler->display->display_options['filters']['type_id']['table'] = 'library';
   $handler->display->display_options['filters']['type_id']['field'] = 'type_id';
-  $handler->display->display_options['filters']['type_id']['value'] = 'All';
   $handler->display->display_options['filters']['type_id']['group'] = '0';
   $handler->display->display_options['filters']['type_id']['exposed'] = TRUE;
   $handler->display->display_options['filters']['type_id']['expose']['operator_id'] = 'type_id_op';

+ 1 - 1
tripal_natural_diversity/tripal_natural_diversity.info

@@ -3,7 +3,7 @@ description = Supports the natural diversity (ND) tables of Chado by providing p
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 
 dependencies[] = tripal_core
 dependencies[] = tripal_views

+ 0 - 3
tripal_natural_diversity/tripal_natural_diversity.install

@@ -48,9 +48,6 @@ function tripal_natural_diversity_requirements($phase) {
  */
 function tripal_natural_diversity_install() {
 
-  // create the module's data directory
-  tripal_create_files_dir('tripal_natural_diversity');
-
   // add cvterms
   tripal_natural_diversity_add_cvterms();
 

+ 36 - 16
tripal_natural_diversity/tripal_natural_diversity.views_default.inc

@@ -59,8 +59,10 @@ function tripal_natural_diversity_defaultview_admin_natdiv_exp() {
   $handler->display->display_options['access']['type'] = 'perm';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
-  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Filter';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of natural diversity experiments that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of experiments or to find a specific experiment.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -99,14 +101,11 @@ function tripal_natural_diversity_defaultview_admin_natdiv_exp() {
   $handler->display->display_options['fields']['description']['table'] = 'nd_geolocation';
   $handler->display->display_options['fields']['description']['field'] = 'description';
   $handler->display->display_options['fields']['description']['label'] = 'Location Experiment Performed';
-  /* Sort criterion: Chado Cvterm: Name */
-  $handler->display->display_options['sorts']['name']['id'] = 'name';
-  $handler->display->display_options['sorts']['name']['table'] = 'cvterm';
-  $handler->display->display_options['sorts']['name']['field'] = 'name';
-  /* Sort criterion: Chado Nd Geolocation: Description */
-  $handler->display->display_options['sorts']['description']['id'] = 'description';
-  $handler->display->display_options['sorts']['description']['table'] = 'nd_geolocation';
-  $handler->display->display_options['sorts']['description']['field'] = 'description';
+  /* Sort criterion: Chado Nd Experiment: Id */
+  $handler->display->display_options['sorts']['nd_experiment_id']['id'] = 'nd_experiment_id';
+  $handler->display->display_options['sorts']['nd_experiment_id']['table'] = 'nd_experiment';
+  $handler->display->display_options['sorts']['nd_experiment_id']['field'] = 'nd_experiment_id';
+  $handler->display->display_options['sorts']['nd_experiment_id']['order'] = 'DESC';
   /* Filter criterion: Chado Nd Experiment: Type Id */
   $handler->display->display_options['filters']['type_id']['id'] = 'type_id';
   $handler->display->display_options['filters']['type_id']['table'] = 'nd_experiment';
@@ -181,8 +180,10 @@ function tripal_natural_diversity_defaultview_admin_geolocations() {
   $handler->display->display_options['access']['type'] = 'perm';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
-  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Filter';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of natural diversity experiment locations that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of locations or to find a specific location.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -247,6 +248,11 @@ function tripal_natural_diversity_defaultview_admin_geolocations() {
   $handler->display->display_options['fields']['geodetic_datum']['id'] = 'geodetic_datum';
   $handler->display->display_options['fields']['geodetic_datum']['table'] = 'nd_geolocation';
   $handler->display->display_options['fields']['geodetic_datum']['field'] = 'geodetic_datum';
+  /* Sort criterion: Chado Nd Geolocation: Id */
+  $handler->display->display_options['sorts']['nd_geolocation_id']['id'] = 'nd_geolocation_id';
+  $handler->display->display_options['sorts']['nd_geolocation_id']['table'] = 'nd_geolocation';
+  $handler->display->display_options['sorts']['nd_geolocation_id']['field'] = 'nd_geolocation_id';
+  $handler->display->display_options['sorts']['nd_geolocation_id']['order'] = 'DESC';
   /* Filter criterion: Chado Nd Geolocation: Geodetic Datum */
   $handler->display->display_options['filters']['geodetic_datum']['id'] = 'geodetic_datum';
   $handler->display->display_options['filters']['geodetic_datum']['table'] = 'nd_geolocation';
@@ -355,8 +361,10 @@ function tripal_natural_diversity_defaultview_admin_reagents() {
   $handler->display->display_options['access']['type'] = 'perm';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
-  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Filter';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of natural diversity protocol reagents that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of reagents or to find a specific reagent.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -378,6 +386,11 @@ function tripal_natural_diversity_defaultview_admin_reagents() {
   $handler->display->display_options['fields']['name']['table'] = 'cvterm';
   $handler->display->display_options['fields']['name']['field'] = 'name';
   $handler->display->display_options['fields']['name']['label'] = 'Type';
+  /* Sort criterion: Chado Nd Reagent: Id */
+  $handler->display->display_options['sorts']['nd_reagent_id']['id'] = 'nd_reagent_id';
+  $handler->display->display_options['sorts']['nd_reagent_id']['table'] = 'nd_reagent';
+  $handler->display->display_options['sorts']['nd_reagent_id']['field'] = 'nd_reagent_id';
+  $handler->display->display_options['sorts']['nd_reagent_id']['order'] = 'DESC';
   /* Filter criterion: Chado Nd Reagent: Type Id */
   $handler->display->display_options['filters']['type_id']['id'] = 'type_id';
   $handler->display->display_options['filters']['type_id']['table'] = 'nd_reagent';
@@ -462,8 +475,10 @@ function tripal_natural_diversity_defaultview_admin_protocols() {
   $handler->display->display_options['access']['type'] = 'perm';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
-  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Filter';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of natural diversity experiment protocols that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of protocols or to find a specific protocol.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'none';
   $handler->display->display_options['style_plugin'] = 'table';
   $handler->display->display_options['style_options']['grouping'] = '';
@@ -487,6 +502,11 @@ function tripal_natural_diversity_defaultview_admin_protocols() {
   $handler->display->display_options['fields']['name']['id'] = 'name';
   $handler->display->display_options['fields']['name']['table'] = 'nd_protocol';
   $handler->display->display_options['fields']['name']['field'] = 'name';
+  /* Sort criterion: Chado Nd Protocol: Id */
+  $handler->display->display_options['sorts']['nd_protocol_id']['id'] = 'nd_protocol_id';
+  $handler->display->display_options['sorts']['nd_protocol_id']['table'] = 'nd_protocol';
+  $handler->display->display_options['sorts']['nd_protocol_id']['field'] = 'nd_protocol_id';
+  $handler->display->display_options['sorts']['nd_protocol_id']['order'] = 'DESC';
   /* Filter criterion: Chado Nd Protocol: Name */
   $handler->display->display_options['filters']['name']['id'] = 'name';
   $handler->display->display_options['filters']['name']['table'] = 'nd_protocol';

+ 38 - 2
tripal_organism/api/tripal_organism.api.inc

@@ -153,7 +153,6 @@ function tripal_get_organism_select_options($syncd_only = TRUE) {
 
     // iterate through the organisms and build an array of those that are synced
     foreach ($orgs as $org) {
-      $args = array(':organism_id' => $org->organism_id);
       $org_list[$org->organism_id] = $org->genus . ' ' . $org->species;
     }
   }
@@ -182,7 +181,7 @@ function tripal_get_organism_select_options($syncd_only = TRUE) {
  */
 function tripal_get_organism_image_url($organism) {
   $url = '';
-  
+
   if (!is_object($organism)) {
     return NULL;
   }
@@ -230,3 +229,40 @@ function tripal_get_organism_image_url($organism) {
   return NULL;
 }
 
+/**
+ * This function is intended to be used in autocomplete forms
+ * for searching for organisms that begin with the provided string
+ *
+ * @param $text
+ *   The string to search for
+ *
+ * @return
+ *   A json array of terms that begin with the provided string
+ *
+ * @ingroup tripal_organism_api
+ */
+function tripal_autocomplete_organism($text) {
+  $matches = array();
+  $genus = $text;
+  $species = '';
+  if (preg_match('/^(.*?) (.*)$/', $text, $matches)) {
+    $genus = $matches[1];
+    $species = $matches[2];
+  }
+  $sql = "SELECT * FROM {organism} WHERE lower(genus) like lower(:genus) ";
+  $args = array();
+  $args[':genus'] = $genus . '%';
+  if ($species) {
+    $sql .= "AND lower(species) like lower(:species) ";
+    $args[':species'] =  $species . '%';
+  }
+  $sql .= "ORDER BY genus, species ";
+  $sql .= "LIMIT 25 OFFSET 0 ";
+  $results = chado_query($sql, $args);
+  $items = array();
+  foreach ($results as $organism) {
+    $name = $organism->genus . ' ' .$organism->species;
+    $items[$name] = $name;
+  }
+  drupal_json_output($items);
+}

+ 3 - 4
tripal_organism/includes/tripal_organism.chado_node.inc

@@ -166,7 +166,7 @@ function chado_organism_form($node, $form_state) {
     '#default_value' => $common_name,
   );
   $form['description']= array(
-    '#type' => 'textarea',
+    '#type' => 'text_format',
     '#rows' => 15,
     '#title' => t('Description'),
     '#default_value' => $description,
@@ -235,7 +235,6 @@ function chado_organism_validate($node, $form, &$form_state) {
   $node->species      = property_exists($node, 'species') ? trim($node->species) : '';
   $node->abbreviation = property_exists($node, 'abbreviation') ? trim($node->abbreviation) : '';
   $node->common_name  = property_exists($node, 'common_name') ? trim($node->common_name) : '';
-  $node->description  = property_exists($node, 'description') ? trim($node->description) : '';
 
   // Validating for an update
   if (property_exists($node, 'organism_id')) {
@@ -296,7 +295,7 @@ function chado_organism_insert($node) {
     $node->species      = trim($node->species);
     $node->abbreviation = trim($node->abbreviation);
     $node->common_name  = trim($node->common_name);
-    $node->description  = trim($node->description);
+    $node->description  = trim($node->description['value']);
 
     $values = array(
       'genus'        => $node->genus,
@@ -367,7 +366,7 @@ function chado_organism_update($node) {
   $node->species      = trim($node->species);
   $node->abbreviation = trim($node->abbreviation);
   $node->common_name  = trim($node->common_name);
-  $node->description  = trim($node->description);
+  $node->description  = trim($node->description['value']);
 
   $organism_id = chado_get_id_from_nid('organism', $node->nid);
 

+ 1 - 1
tripal_organism/tripal_organism.info

@@ -3,7 +3,7 @@ description = Supports the organism tables of Chado by providing pages for viewi
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 configure = admin/tripal/chado/tripal_organism
 
 stylesheets[all][] = theme/css/tripal_organism.css

+ 0 - 7
tripal_organism/tripal_organism.install

@@ -27,13 +27,6 @@ function tripal_organism_disable() {
  * @ingroup tripal_organism
  */
 function tripal_organism_install() {
-
-  // create the module's data directory
-  tripal_create_files_dir('tripal_organism');
-
-  // create the directory where image files will be stored.  We create this
-  tripal_create_files_dir('tripal_organism', '/images');
-
   // cvs & cvterms
   tripal_organism_add_cvs();
   tripal_organism_add_cvterms();

+ 27 - 22
tripal_organism/tripal_organism.module

@@ -41,13 +41,13 @@ function tripal_organism_menu() {
   );
 
   $items['admin/tripal/chado/tripal_organism/sync'] = array(
-      'title' => ' Sync',
-      'description' => 'Create pages on this site for organisms stored in Chado',
-      'page callback' => 'drupal_get_form',
-      'page arguments' => array('chado_node_sync_form', 'tripal_organism', 'chado_organism'),
-      'access arguments' => array('administer tripal organism'),
-      'type' => MENU_LOCAL_TASK,
-      'weight' => 1
+    'title' => ' Sync',
+    'description' => 'Create pages on this site for organisms stored in Chado',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('chado_node_sync_form', 'tripal_organism', 'chado_organism'),
+    'access arguments' => array('administer tripal organism'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 1
   );
   $items['admin/tripal/chado/tripal_organism/delete'] = array(
     'title' => ' Delete',
@@ -72,22 +72,22 @@ function tripal_organism_menu() {
     'weight' => 3
   );
   $items['admin/tripal/chado/tripal_organism/configuration'] = array(
-      'title' => 'Settings',
-      'description' => 'Manage integration of Chado organisms including associated features',
-      'page callback' => 'drupal_get_form',
-      'page arguments' => array('tripal_organism_admin'),
-      'access arguments' => array('administer tripal organism'),
-      'type' => MENU_LOCAL_TASK,
-      'weight' => 5
+    'title' => 'Settings',
+    'description' => 'Manage integration of Chado organisms including associated features',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('tripal_organism_admin'),
+    'access arguments' => array('administer tripal organism'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 5
   );
   $items['admin/tripal/chado/tripal_organism/help'] = array(
-      'title' => 'Help',
-      'description' => "A description of the Tripal Organism module including a short description of it's usage.",
-      'page callback' => 'theme',
-      'page arguments' => array('tripal_organism_help'),
-      'access arguments' => array('administer tripal organism'),
-      'type' => MENU_LOCAL_TASK,
-      'weight' => 10
+    'title' => 'Help',
+    'description' => "A description of the Tripal Organism module including a short description of it's usage.",
+    'page callback' => 'theme',
+    'page arguments' => array('tripal_organism_help'),
+    'access arguments' => array('administer tripal organism'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 10
   );
   $items['admin/tripal/chado/tripal_organism/views/organisms/enable'] = array(
     'title' => 'Enable Organism Administrative View',
@@ -96,7 +96,12 @@ function tripal_organism_menu() {
     'access arguments' => array('administer tripal organism'),
     'type' => MENU_CALLBACK,
   );
-
+  $items['admin/tripal/chado/tripal_organism/organism/auto_name/%'] = array(
+    'page callback' => 'tripal_autocomplete_organism',
+    'page arguments' => array(6),
+    'access arguments' => array('administer tripal organism'),
+    'type' => MENU_CALLBACK,
+  );
 
   return $items;
 }

+ 4 - 2
tripal_organism/tripal_organism.views_default.inc

@@ -224,8 +224,10 @@ function tripal_organism_admin_defaultview_organisms() {
   $handler->display->display_options['access']['perm'] = 'administer tripal organism';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
-  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Filter';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of organisms that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of organisms or to find a specific organism.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';

+ 1 - 1
tripal_phenotype/tripal_phenotype.info

@@ -3,7 +3,7 @@ description = Supports the phenotype tables of Chado by providing pages for view
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 
 dependencies[] = tripal_core
 dependencies[] = tripal_views

+ 9 - 2
tripal_phenotype/tripal_phenotype.views_default.inc

@@ -92,8 +92,10 @@ function tripal_phenotype_defaultview_admin_phenotypes() {
   $handler->display->display_options['access']['perm'] = 'administer tripal';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
-  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Filter';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of phenotypes that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of phenotypes or to find a specific phenotype.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -175,6 +177,11 @@ function tripal_phenotype_defaultview_admin_phenotypes() {
   $handler->display->display_options['fields']['name']['field'] = 'name';
   $handler->display->display_options['fields']['name']['relationship'] = 'assay_id_to_cvterm';
   $handler->display->display_options['fields']['name']['label'] = 'Evidence Type';
+  /* Sort criterion: Chado Phenotype: Id */
+  $handler->display->display_options['sorts']['phenotype_id']['id'] = 'phenotype_id';
+  $handler->display->display_options['sorts']['phenotype_id']['table'] = 'phenotype';
+  $handler->display->display_options['sorts']['phenotype_id']['field'] = 'phenotype_id';
+  $handler->display->display_options['sorts']['phenotype_id']['order'] = 'DESC';
   /* Filter criterion: Chado Phenotype: Attr Id */
   $handler->display->display_options['filters']['attr_id']['id'] = 'attr_id';
   $handler->display->display_options['filters']['attr_id']['table'] = 'phenotype';

+ 3 - 4
tripal_project/includes/tripal_project.chado_node.inc

@@ -131,7 +131,7 @@ function chado_project_form(&$node, $form_state) {
   );
 
   $form['description']= array(
-    '#type'          => 'textarea',
+    '#type'          => 'text_format',
     '#title'         => t('Project Description'),
     '#description'   => t('A brief description of the project'),
     '#required'      => TRUE,
@@ -212,7 +212,6 @@ function chado_project_validate($node, $form, &$form_state) {
 
   // trim white space from text fields
   $node->title = property_exists($node, 'title') ? trim($node->title) : '';
-  $node->description = property_exists($node, 'description') ? trim($node->description) : '';
 
   $project = 0;
   // check to make sure the name on the project is unique
@@ -247,7 +246,7 @@ function chado_project_insert($node) {
   // we do need to proceed with insertion into the chado/drupal linking table.
   if (!property_exists($node, 'project_id')) {
     $node->title = trim($node->title);
-    $node->description = trim($node->description);
+    $node->description  = trim($node->description['value']);
 
     $values = array(
       'name' => $node->title,
@@ -348,7 +347,7 @@ function chado_project_delete($node) {
 function chado_project_update($node) {
 
   $node->title = trim($node->title);
-  $node->description = trim($node->description);
+  $node->description  = trim($node->description['value']);
 
   // update the project and the description
   $project_id = chado_get_id_from_nid('project', $node->nid) ;

+ 1 - 1
tripal_project/tripal_project.info

@@ -3,7 +3,7 @@ description = Supports the project tables of Chado by providing pages for viewin
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 configure = admin/tripal/chado/tripal_project
 
 dependencies[] = tripal_core

+ 0 - 3
tripal_project/tripal_project.install

@@ -48,9 +48,6 @@ function tripal_project_requirements($phase) {
  */
 function tripal_project_install() {
 
-  // create the module's data directory
-  tripal_create_files_dir('tripal_project');
-
   tripal_project_add_cvs();
   tripal_project_add_cvterms();
 

+ 9 - 1
tripal_project/tripal_project.views_default.inc

@@ -95,7 +95,10 @@ function tripal_project_defaultview_admin_projects() {
   $handler->display->display_options['access']['perm'] = 'administer tripal project';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of projects that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of projects or to find a specific project.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['style_plugin'] = 'table';
@@ -220,6 +223,11 @@ function tripal_project_defaultview_admin_projects() {
   $handler->display->display_options['fields']['nothing']['element_class'] = 'short-column';
   $handler->display->display_options['fields']['nothing']['element_label_class'] = 'short-column';
   $handler->display->display_options['fields']['nothing']['element_label_colon'] = FALSE;
+  /* Sort criterion: Chado Project: Id */
+  $handler->display->display_options['sorts']['project_id']['id'] = 'project_id';
+  $handler->display->display_options['sorts']['project_id']['table'] = 'project';
+  $handler->display->display_options['sorts']['project_id']['field'] = 'project_id';
+  $handler->display->display_options['sorts']['project_id']['order'] = 'DESC';
   /* Filter criterion: Chado Project: Name */
   $handler->display->display_options['filters']['name']['id'] = 'name';
   $handler->display->display_options['filters']['name']['table'] = 'project';

+ 14 - 13
tripal_pub/api/tripal_pub.api.inc

@@ -75,7 +75,7 @@ function tripal_get_publication($identifiers, $options = array()) {
     unset($identifiers['property']);
     $pub = chado_get_record_with_property(
       array('table' => 'pub', 'base_records' => $identifiers),
-      array('type_name' => $property), 
+      array('type_name' => $property),
       $options
     );
   }
@@ -84,21 +84,22 @@ function tripal_get_publication($identifiers, $options = array()) {
       $dbname = $matches[1];
       $accession = $matches[2];
 
+      // First make sure the dbxref is present.
       $values = array(
-        'dbxref_id' => array (
-          'accession' => $accession,
-          'db_id' => array(
-            'name' => $dbname
-          ),
+        'accession' => $accession,
+        'db_id' => array(
+          'name' => $dbname
         ),
       );
-      $pub_dbxref = chado_select_record('pub_dbxref', array('pub_id'), $values);
-      if (count($pub_dbxref) > 0) {
-        $pub = chado_generate_var('pub', array('pub_id' => $pub_dbxref[0]->pub_id), $options);
+      $dbxref = chado_select_record('dbxref', array('dbxref_id'), $values);
+      if (count($dbxref) == 0) {
+        return FALSE;
       }
-      else {
+      $pub_dbxref = chado_select_record('pub_dbxref', array('pub_id'), array('dbxref_id' => $dbxref[0]->dbxref_id));
+      if (count($pub_dbxref) == 0) {
         return FALSE;
       }
+      $pub = chado_generate_var('pub', array('pub_id' => $pub_dbxref[0]->pub_id), $options);
     }
     else {
       tripal_report_error('tripal_pub_api', TRIPAL_ERROR,
@@ -215,13 +216,13 @@ function tripal_publication_exists($pub_details) {
     $pub_type = tripal_get_cvterm($identifiers);
   }
   else {
-    tripal_report_error('tripal_pub', TRIPAL_ERROR, 
+    tripal_report_error('tripal_pub', TRIPAL_ERROR,
       "tripal_publication_exists(): The Publication Type is a " .
       "required property but is missing", array());
     return array();
   }
   if (!$pub_type) {
-    tripal_report_error('tripal_pub', TRIPAL_ERROR, 
+    tripal_report_error('tripal_pub', TRIPAL_ERROR,
      "tripal_publication_exists(): Cannot find publication type: '%type'",
       array('%type' => $pub_details['Publication Type'][0]));
     return array();
@@ -238,7 +239,7 @@ function tripal_publication_exists($pub_details) {
   if (array_key_exists('Conference Name', $pub_details)) {
     $series_name = substr($pub_details['Conference Name'], 0, 255);
   }
-  
+
   // make sure the publication is unique using the prefereed import duplication check
   $import_dups_check = variable_get('tripal_pub_import_duplicate_check', 'title_year_media');
   $pubs = array();

+ 31 - 24
tripal_pub/includes/tripal_pub.chado_node.inc

@@ -1,37 +1,41 @@
 <?php
 /**
  * @file
- * Implements the pub node content type
+ * Implements Drupal Node hooks to create the chado_analysis node content type.
+ *
+ * @ingroup tripal_pub
  */
 
 /**
  * Implements hook_node_info().
  *
+ * Provide information to drupal about the node types that we're creating
+ * in this module
+ *
  * @ingroup tripal_pub
  */
 function tripal_pub_node_info() {
-
-  return array(
-    'chado_pub' => array(
-      'name'        => t('Publication'),
-      'base'        => 'chado_pub',
-      'description' => t('A publication from the Chado database'),
-      'has_title'   => TRUE,
-      'locked'      => TRUE,
-      'chado_node_api' => array(
-        'base_table' => 'pub',
-        'hook_prefix' => 'chado_pub',
-        'record_type_title' => array(
-          'singular' => t('Publication'),
-          'plural' => t('Publications')
-        ),
-        'sync_filters' => array(
-          'type_id' => FALSE,
-          'organism_id' => FALSE
-        ),
+  $nodes = array();
+  $nodes['chado_pub'] = array(
+    'name'        => t('Publication'),
+    'base'        => 'chado_pub',
+    'description' => t('A publication from the Chado database'),
+    'has_title'   => TRUE,
+    'locked'      => TRUE,
+    'chado_node_api' => array(
+      'base_table' => 'pub',
+      'hook_prefix' => 'chado_pub',
+      'record_type_title' => array(
+        'singular' => t('Publication'),
+        'plural' => t('Publications')
+      ),
+      'sync_filters' => array(
+        'type_id' => FALSE,
+        'organism_id' => FALSE,
       ),
     ),
   );
+  return $nodes;
 }
 
 /**
@@ -371,7 +375,7 @@ function chado_pub_validate($node, $form, &$form_state) {
     return;
   }
 
-  // get the media name looking at the properties
+  // Get the media name looking at the properties
   $series_name = '';
   $properties = chado_retrieve_node_form_properties($node);
   foreach ($properties as $key => $prop_values) {
@@ -380,13 +384,16 @@ function chado_pub_validate($node, $form, &$form_state) {
     if ($prop_type[0]->name == 'Conference Name' or
         $prop_type[0]->name == 'Journal Name' or
         $prop_type[0]->name == 'Series Name') {
-      $series_name = $prop_values[0];
+      $v = array_values($prop_values);
+      $series_name = $v[0];
     }
     if ($prop_type[0]->name == 'Citation') {
-      $uniquename = $prop_values[0];
+      $v = array_values($prop_values);
+      $uniquename = $v[0];
     }
     if (count($prop_values) == 1) {
-      $pub[$prop_type[0]->name] = $prop_values[0];
+      $v = array_values($prop_values);
+      $pub[$prop_type[0]->name] = $v[0];
     }
     else {
       $pub[$prop_type[0]->name] = $prop_values;

+ 48 - 29
tripal_pub/includes/tripal_pub.pub_citation.inc

@@ -161,7 +161,7 @@ function tripal_pub_create_citation($pub) {
   // Therefore, we need to select the type that makes most sense for
   // construction of the citation. Here we'll iterate through them all
   // and select the one that matches best.
-  if(is_array($pub['Publication Type'])) {
+  if (is_array($pub['Publication Type'])) {
     foreach ($pub['Publication Type'] as $ptype) {
       if ($ptype == 'Journal Article' ) {
         $pub_type = $ptype;
@@ -193,16 +193,10 @@ function tripal_pub_create_citation($pub) {
         // we prefer that type
       }
     }
+    // If we don't have a recognized publication type, then just use the
+    // first one in the list.
     if (!$pub_type) {
-      tripal_report_error('tripal_pub', TRIPAL_ERROR,
-        "Cannot generate citation for publication type: %types.\nTitle: %title.\nDbxref: %dbxref",
-        array(
-          '%types' => print_r($pub['Publication Type'], TRUE),
-          '%title' => $pub['Title'],
-          '%dbxref' => $pub['Publication Dbxref'],
-        )
-      );
-      return FALSE;
+      $pub_type = $pub['Publication Type'][0];
     }
   }
   else {
@@ -215,7 +209,7 @@ function tripal_pub_create_citation($pub) {
     if (array_key_exists('Authors', $pub)) {
       $citation = $pub['Authors'] . '. ';
     }
-    
+
     $citation .= $pub['Title'] .  '. ';
 
     if (array_key_exists('Journal Name', $pub)) {
@@ -260,7 +254,7 @@ function tripal_pub_create_citation($pub) {
     if (array_key_exists('Authors', $pub)) {
       $citation = $pub['Authors'] . '. ';
     }
-    
+
     $citation .= $pub['Title'] .  '. ';
 
     if (array_key_exists('Journal Name', $pub)) {
@@ -305,7 +299,7 @@ function tripal_pub_create_citation($pub) {
     if (array_key_exists('Authors', $pub)) {
       $citation = $pub['Authors'] . '. ';
     }
-    
+
     $citation .= $pub['Title'] .  '. ';
 
     if (array_key_exists('Journal Name', $pub)) {
@@ -326,7 +320,7 @@ function tripal_pub_create_citation($pub) {
     if (array_key_exists('Authors', $pub)) {
       $citation = $pub['Authors'] . '. ';
     }
-    
+
     $citation .= $pub['Title'] .  '. ';
     if (array_key_exists('Journal Name', $pub)) {
       $citation .= $pub['Journal Name'] . '. ';
@@ -363,26 +357,14 @@ function tripal_pub_create_citation($pub) {
     }
     $citation .= '.';
   }
-  //----------------------
-  // Book
-  //----------------------
-  elseif ($pub_type == 'Book') {
-
-  }
-  //----------------------
-  // Book Chapter
-  //----------------------
-  elseif ($pub_type == 'Book Chapter') {
-
-  }
-  //----------------------
+  //-----------------------
   // Conference Proceedings
-  //----------------------
+  //-----------------------
   elseif ($pub_type == 'Conference Proceedings') {
     if (array_key_exists('Authors', $pub)) {
       $citation = $pub['Authors'] . '. ';
     }
-    
+
     $citation .= $pub['Title'] .  '. ';
     if (array_key_exists('Conference Name', $pub)) {
       $citation .= $pub['Conference Name'] . '. ';
@@ -416,6 +398,43 @@ function tripal_pub_create_citation($pub) {
     }
     $citation .= '.';
   }
+  //-----------------------
+  // Default
+  //-----------------------
+  else {
+    if (array_key_exists('Authors', $pub)) {
+      $citation = $pub['Authors'] . '. ';
+    }
+    $citation .= $pub['Title'] .  '. ';
+    if (array_key_exists('Series Name', $pub)) {
+      $citation .= $pub['Series Name'] . '. ';
+    }
+    elseif (array_key_exists('Series Abbreviation', $pub)) {
+      $citation .= $pub['Series Abbreviation'] . '. ';
+    }
+    if (array_key_exists('Publication Date', $pub)) {
+      $citation .= $pub['Publication Date'];
+    }
+    elseif (array_key_exists('Year', $pub)) {
+      $citation .= $pub['Year'];
+    }
+    if (array_key_exists('Volume', $pub) or array_key_exists('Issue', $pub) or array_key_exists('Pages',$pub)) {
+      $citation .= '; ';
+    }
+    if (array_key_exists('Volume', $pub)) {
+      $citation .= $pub['Volume'];
+    }
+    if (array_key_exists('Issue', $pub)) {
+      $citation .= '(' . $pub['Issue'] . ')';
+    }
+    if (array_key_exists('Pages', $pub)) {
+      if (array_key_exists('Volume', $pub)) {
+        $citation .= ':';
+      }
+      $citation .= $pub['Pages'];
+    }
+    $citation .= '.';
+  }
 
   return $citation;
 }

+ 153 - 0
tripal_pub/theme/templates/pub_types/default.inc

@@ -0,0 +1,153 @@
+<?php
+// ========================================================================
+// TO CUSTOMIZE A SPECIFIC PUBLICATION TYPE, CUT-AND-PASTE THE CODE
+// BELOW INTO A NEW FILE WITH THE SAME NAME AS THE TYPE (SEE INSTRUCTIONS
+// ABOVE), AND EDIT.
+// ========================================================================
+
+// the $headers array is an array of fields to use as the colum headers.
+// additional documentation can be found here
+// https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+// This table for the analysis has a vertical header (down the first column)
+// so we do not provide headers here, but specify them in the $rows array below.
+$headers = array();
+
+// the $rows array contains an array of rows where each row is an array
+// of values for each column of the table in that row.  Additional documentation
+// can be found here:
+// https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+$rows = array();
+
+// Title row
+$title = '';
+if ($url) {
+  $title =  l(htmlspecialchars($pub->title), $url, array('attributes' => array('target' => '_blank')));
+}
+elseif ($dbxref and $dbxref->db_id->urlprefix) {
+  $title =  l(htmlspecialchars($pub->title), $dbxref->db_id->urlprefix . $dbxref->accession, array('attributes' => array('target' => '_blank')));
+}
+else {
+  $title =  htmlspecialchars($pub->title);
+}
+$rows[] = array(
+  array(
+    'data' => 'Title',
+    'header' => TRUE,
+    'width' => '20%',
+  ),
+  $title,
+);
+// Authors row
+$rows[] = array(
+  array(
+    'data' => 'Authors',
+    'header' => TRUE
+  ),
+  $authors_list,
+);
+// Type row
+$rows[] = array(
+  array(
+    'data' => 'Type',
+    'header' => TRUE
+  ),
+  $pub->type_id->name,
+);
+// Media Title
+$rows[] = array(
+  array(
+    'data' => 'Media Title',
+    'header' => TRUE,
+    'nowrap' => 'nowrap'
+  ),
+  $pub->series_name,
+);
+// Volume
+$rows[] = array(
+  array(
+    'data' => 'Volume',
+    'header' => TRUE
+  ),
+  $pub->volume ? $pub->volume : 'N/A',
+);
+// Issue
+$rows[] = array(
+  array(
+    'data' => 'Issue',
+    'header' => TRUE
+  ),
+  $pub->issue ? $pub->issue : 'N/A'
+);
+// Year
+$rows[] = array(
+  array(
+    'data' => 'Year',
+    'header' => TRUE
+  ),
+  $pub->pyear
+);
+// Pages
+$rows[] = array(
+  array(
+    'data' => 'Page(s)',
+    'header' => TRUE
+  ),
+  $pub->pages ? $pub->pages : 'N/A'
+);
+// Citation row
+$rows[] = array(
+  array(
+    'data' => 'Citation',
+    'header' => TRUE
+  ),
+  htmlspecialchars($citation->value)
+);
+// allow site admins to see the pub ID
+if (user_access('view ids')) {
+  // Pub ID
+  $rows[] = array(
+    array(
+      'data' => 'Pub ID',
+      'header' => TRUE,
+      'class' => 'tripal-site-admin-only-table-row',
+    ),
+    array(
+      'data' => $pub->pub_id,
+      'class' => 'tripal-site-admin-only-table-row',
+    ),
+  );
+}
+// Is Obsolete Row
+if($pub->is_obsolete == TRUE){
+  $rows[] = array(
+    array(
+      'data' => '<div class="tripal_pub-obsolete">This publication is obsolete</div>',
+      'colspan' => 2
+    ),
+  );
+}
+// the $table array contains the headers and rows array as well as other
+// options for controlling the display of the table.  Additional
+// documentation can be found here:
+// https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
+$table = array(
+  'header' => $headers,
+  'rows' => $rows,
+  'attributes' => array(
+    'id' => 'tripal_pub-table-base',
+    'class' => 'tripal-data-table'
+  ),
+  'sticky' => FALSE,
+  'caption' => '',
+  'colgroups' => array(),
+  'empty' => '',
+);
+
+// once we have our table array structure defined, we call Drupal's theme_table()
+// function to generate the table.
+print theme_table($table);
+
+if ($abstract_text) { ?>
+  <p><b>Abstract</b></p>
+  <div style="text-align: justify"><?php print $abstract_text; ?></div> <?php
+}

+ 27 - 187
tripal_pub/theme/templates/tripal_pub_base.tpl.php

@@ -1,15 +1,10 @@
 <?php
-/*
- * Note, the table generated by this template that lists the publication
- * details is generic. It can be customized to look different for different
- * publication types.  To create a custom template for a given type, create a 
- * new file in the pub_types directory.  Name the file using the name of the
- * type. The name must be all lower-case and spaces should be replaced with
- * and underscore symbol.  For example, to create a custom table for the
- * "Conference Proceedings", create the file conference_proceedings.inc inside
- * of the pub_types folder.  Cut and paste the code below that generates the table
- * structure into the new file.  Then edit to your liking.
- * 
+/**
+ * The page generated by this template depends on the publication type.  Each
+ * publication type may appear differently. You can find the publication
+ * templates for each type in the pub_types folder. The files have the same
+ * name as the type.  If a publication type is not found in that file then the
+ * generic.inc template is used for display of the publication.
  */
 
 $pub = $variables['node']->pub;
@@ -20,22 +15,22 @@ $pub = chado_expand_var($pub, 'field', 'pub.volumetitle');
 
 // get the citation
 $values = array(
-  'pub_id' => $pub->pub_id, 
+  'pub_id' => $pub->pub_id,
   'type_id' => array(
     'name' => 'Citation',
   ),
 );
-$citation = chado_generate_var('pubprop', $values); 
+$citation = chado_generate_var('pubprop', $values);
 $citation = chado_expand_var($citation, 'field', 'pubprop.value');
 
 // get the abstract
 $values = array(
-  'pub_id' => $pub->pub_id, 
+  'pub_id' => $pub->pub_id,
   'type_id' => array(
     'name' => 'Abstract',
   ),
 );
-$abstract = chado_generate_var('pubprop', $values); 
+$abstract = chado_generate_var('pubprop', $values);
 $abstract = chado_expand_var($abstract, 'field', 'pubprop.value');
 $abstract_text = '';
 if ($abstract) {
@@ -44,23 +39,23 @@ if ($abstract) {
 
 // get the author list
 $values = array(
-  'pub_id' => $pub->pub_id, 
+  'pub_id' => $pub->pub_id,
   'type_id' => array(
     'name' => 'Authors',
   ),
 );
-$authors = chado_generate_var('pubprop', $values); 
+$authors = chado_generate_var('pubprop', $values);
 $authors = chado_expand_var($authors, 'field', 'pubprop.value');
 $authors_list = 'N/A';
 if ($authors) {
   $authors_list = $authors->value;
-} 
+}
 
 // get the first database cross-reference with a url
 $options = array('return_array' => 1);
 $pub = chado_expand_var($pub, 'table', 'pub_dbxref', $options);
 $dbxref = NULL;
-if ($pub->pub_dbxref) { 
+if ($pub->pub_dbxref) {
   foreach ($pub->pub_dbxref as $index => $pub_dbxref) {
     if ($pub_dbxref->dbxref_id->db_id->urlprefix) {
       $dbxref = $pub_dbxref->dbxref_id;
@@ -71,184 +66,29 @@ if ($pub->pub_dbxref) {
 // get the URL
 // get the author list
 $values = array(
-  'pub_id' => $pub->pub_id, 
+  'pub_id' => $pub->pub_id,
   'type_id' => array(
     'name' => 'URL',
   ),
 );
 $options = array('return_array' => 1);
-$urls = chado_generate_var('pubprop', $values, $options); 
+$urls = chado_generate_var('pubprop', $values, $options);
 $urls = chado_expand_var($urls, 'field', 'pubprop.value');
 $url = '';
 if (count($urls) > 0) {
-  $url = $urls[0]->value; 
-}?>
-
-<div class="tripal_pub-data-block-desc tripal-data-block-desc"></div> <?php 
+  $url = $urls[0]->value;
+} ?>
+<div class="tripal_pub-data-block-desc tripal-data-block-desc"></div> <?php
 
-// to simplify the template, we have a subdirectory named 'pub_types'.  This directory
-// should have include files each specific to a publication type. If the type is 
+// To simplify the template, we have a subdirectory named 'pub_types'.  This directory
+// should have include files each specific to a publication type. If the type is
 // not present then the base template will be used, otherwise the template in the
 // include file is used.
 $inc_name = strtolower(preg_replace('/ /', '_', $pub->type_id->name)) . '.inc';
-$inc_path = drupal_realpath(drupal_get_path('module', 'tripal_pub') . "/theme/tripal_pub/pub_types/$inc_name");
+$inc_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'tripal_pub') . "/theme/templates/pub_types/$inc_name";
 if (file_exists($inc_path)) {
-  require_once "pub_types/$inc_name";  
-} 
-else { 
-  // ========================================================================
-  // TO CUSTOMIZE A SPECIFIC PUBLICATION TYPE, CUT-AND-PASTE THE CODE
-  // BELOW INTO A NEW FILE WITH THE SAME NAME AS THE TYPE (SEE INSTRUCTIONS
-  // ABOVE), AND EDIT.
-  // ========================================================================
-  
-  // the $headers array is an array of fields to use as the colum headers. 
-  // additional documentation can be found here 
-  // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
-  // This table for the analysis has a vertical header (down the first column)
-  // so we do not provide headers here, but specify them in the $rows array below.
-  $headers = array();
-  
-  // the $rows array contains an array of rows where each row is an array
-  // of values for each column of the table in that row.  Additional documentation
-  // can be found here:
-  // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7 
-  $rows = array();
-
-  // Title row
-  $title = '';
-  if ($url) {
-    $title =  l(htmlspecialchars($pub->title), $url, array('attributes' => array('target' => '_blank')));
-  }
-  elseif ($dbxref and $dbxref->db_id->urlprefix) {
-    $title =  l(htmlspecialchars($pub->title), $dbxref->db_id->urlprefix . $dbxref->accession, array('attributes' => array('target' => '_blank')));
-  }
-  else {
-    $title =  htmlspecialchars($pub->title);
-  }
-  $rows[] = array(
-    array(
-      'data' => 'Title',
-      'header' => TRUE,
-      'width' => '20%',
-    ),
-    $title,
-  );
-  // Authors row
-  $rows[] = array(
-    array(
-      'data' => 'Authors',
-      'header' => TRUE
-    ),
-    $authors_list,
-  );
-  // Type row
-  $rows[] = array(
-    array(
-      'data' => 'Type',
-      'header' => TRUE
-    ),
-    $pub->type_id->name,
-  );
-  // Media Title
-  $rows[] = array(
-    array(
-      'data' => 'Media Title',
-      'header' => TRUE,
-      'nowrap' => 'nowrap'
-    ),
-    $pub->series_name,
-  );
-  // Volume
-  $rows[] = array(
-    array(
-      'data' => 'Volume',
-      'header' => TRUE
-    ),
-    $pub->volume ? $pub->volume : 'N/A',
-  );
-  // Issue
-  $rows[] = array(
-    array(
-      'data' => 'Issue',
-      'header' => TRUE
-    ),
-    $pub->issue ? $pub->issue : 'N/A'
-  );
-  // Year
-  $rows[] = array(
-    array(
-      'data' => 'Year',
-      'header' => TRUE
-    ),
-    $pub->pyear
-  );
-  // Pages
-  $rows[] = array(
-    array(
-      'data' => 'Page(s)',
-      'header' => TRUE
-    ),
-    $pub->pages ? $pub->pages : 'N/A'
-  );
-  // Citation row
-  $rows[] = array(
-    array(
-      'data' => 'Citation',
-      'header' => TRUE
-    ),
-    htmlspecialchars($citation->value)
-  );
-  // allow site admins to see the pub ID
-  if (user_access('view ids')) {
-    // Pub ID
-    $rows[] = array(
-      array(
-        'data' => 'Pub ID',
-        'header' => TRUE,
-        'class' => 'tripal-site-admin-only-table-row',
-      ),
-      array(
-        'data' => $pub->pub_id,
-        'class' => 'tripal-site-admin-only-table-row',
-      ),
-    );
-  }
-  // Is Obsolete Row
-  if($pub->is_obsolete == TRUE){
-    $rows[] = array(
-      array(
-        'data' => '<div class="tripal_pub-obsolete">This publication is obsolete</div>',
-        'colspan' => 2
-      ),
-    );
-  }
-  // the $table array contains the headers and rows array as well as other
-  // options for controlling the display of the table.  Additional
-  // documentation can be found here:
-  // https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme_table/7
-  $table = array(
-    'header' => $headers,
-    'rows' => $rows,
-    'attributes' => array(
-      'id' => 'tripal_pub-table-base',
-      'class' => 'tripal-data-table'
-    ),
-    'sticky' => FALSE,
-    'caption' => '',
-    'colgroups' => array(),
-    'empty' => '',
-  );
-  
-  // once we have our table array structure defined, we call Drupal's theme_table()
-  // function to generate the table.
-  print theme_table($table);
-  if ($abstract_text) { ?>
-    <p><b>Abstract</b></p>
-    <div style="text-align: justify"><?php print $abstract_text; ?></div> <?php 
-  } 
-  
-  // ========================================================================
-  // END OF CUT-AND-PASTE REGION
-  // ========================================================================
-} 
+  require_once "pub_types/$inc_name";
+}
+else {
+  require_once "pub_types/default.inc";
+}

+ 8 - 3
tripal_pub/tripal_pub.drush.inc

@@ -31,7 +31,7 @@ function tripal_pub_drush_help($command) {
  */
 function tripal_pub_drush_command() {
   $items = array();
-  
+
   $items['trp-import-pubs'] = array(
     'description' => dt('Imports publications from remote databases using saved configuration settings.'),
     'options' => array(
@@ -39,6 +39,9 @@ function tripal_pub_drush_command() {
       'dbxref' => dt('An accession number for a publication from a remote database (e.g. PMID:23582642).'),
       'report' => dt("Set to the email address of the recipient who should receive an HTML report of the publications that have been added."),
       'update' => dt("Set to 'Y' to update existing pubs.  By default only new pubs are inserted."),
+      'username' => array(
+        'description' => dt('The Drupal user name for which the job should be run.  The permissions for this user will be used.'),
+      ),
     ),
     'examples' => array(
       'Standard example' => 'drush tripal-pubs-import',
@@ -60,7 +63,7 @@ function tripal_pub_drush_command() {
       'Update all records for a single database' => 'drush tripal-pubs-update --db=PMID'
     ),
   );
-  
+
   // Deprecated commands
   $items['tripal-pubs-import'] = array(
     'description' => dt('DEPRECATED. Please see: trp-import-pubs.'),
@@ -106,6 +109,8 @@ function drush_tripal_pub_trp_import_pubs() {
   $dbxref = drush_get_option('dbxref');
   $do_report = drush_get_option('report');
   $update = drush_get_option('update');
+  $uname = drush_get_option('username');
+  drush_tripal_core_set_user($uname);
 
   if($update == 'Y') {
     $update = TRUE;
@@ -154,6 +159,6 @@ function drush_tripal_pub_trp_update_pubs() {
 function drush_tripal_pub_tripal_pubs_update() {
   drush_print("\n\nDEPRECATED: This drush command is outdated.\nIt will ".
       "continue to work but please consider using the 'trp-update-pubs' command.\n\n");
-  
+
   drush_tripal_pub_trp_update_pubs();
 }

+ 1 - 1
tripal_pub/tripal_pub.info

@@ -3,7 +3,7 @@ description = Supports the pub (publication) tables of Chado by providing pages
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 
 stylesheets[all][] = theme/css/tripal_pub.css
 

+ 0 - 3
tripal_pub/tripal_pub.install

@@ -49,9 +49,6 @@ function tripal_pub_requirements($phase) {
 function tripal_pub_install() {
   global $base_path;
 
-  // create the module's data directory
-  tripal_create_files_dir('tripal_pub');
-
   // add loading of the the tripal pub ontology to the job queue
   $obo_path = '{tripal_pub}/files/tpub.obo';
   $obo_id = tripal_insert_obo('Tripal Publication', $obo_path);

+ 5 - 7
tripal_pub/tripal_pub.module

@@ -65,8 +65,6 @@ function tripal_pub_menu() {
     'access arguments' => array('access chado_pub content'),
     'type' => MENU_CALLBACK
   );
-  
-
 
   $items['find/publications/criteria/%/%'] = array(
     'page callback' => 'tripal_pub_search_page_update_criteria',
@@ -112,7 +110,7 @@ function tripal_pub_menu() {
     'type' => MENU_LOCAL_TASK,
     'weight' => 2
   );
-  
+
   $items['admin/tripal/chado/tripal_pub/chado_pub_toc'] = array(
     'title' => ' TOC',
     'description' => 'Manage the table of contents for pub nodes.',
@@ -399,7 +397,7 @@ function tripal_pub_form_alter(&$form, &$form_state, $form_id) {
   if ($form_id == "chado_pub_node_form") {
     // turn of preview button for insert/updates
     $form['actions']['preview']['#access'] = FALSE;
-    
+
     //remove the body field
     unset($form['body']);
   }
@@ -445,9 +443,9 @@ function tripal_pub_job_describe_args($callback, $args) {
 }
 
 
-/** 
+/**
  * A simple wrapper function to put <pre> tags around the raw results
- * returned by the 
+ * returned by the
  * @param unknown $dbxref
  * @return string
  */
@@ -462,7 +460,7 @@ function tripal_get_remote_pub_raw_page($dbxref) {
 }
 
 /**
- * 
+ *
  */
 function tripal_pub_search_biological_data_views() {
   return array(

+ 9 - 13
tripal_pub/tripal_pub.views_default.inc

@@ -45,7 +45,10 @@ function tripal_pub_defaultview_admin_publications() {
   $handler->display->display_options['access']['perm'] = 'access chado_pub content';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of publications that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of publications or to find a specific publication.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -149,18 +152,11 @@ function tripal_pub_defaultview_admin_publications() {
   $handler->display->display_options['fields']['type']['table'] = 'cvterm';
   $handler->display->display_options['fields']['type']['field'] = 'name';
   $handler->display->display_options['fields']['type']['label'] = 'Type';
-  /* Sort criterion: Chado Pub: Pyear */
-  $handler->display->display_options['sorts']['pyear']['id'] = 'year';
-  $handler->display->display_options['sorts']['pyear']['table'] = 'pub';
-  $handler->display->display_options['sorts']['pyear']['field'] = 'pyear';
-  /* Sort criterion: Chado Pub: Title */
-  $handler->display->display_options['sorts']['title']['id'] = 'title';
-  $handler->display->display_options['sorts']['title']['table'] = 'pub';
-  $handler->display->display_options['sorts']['title']['field'] = 'title';
-  /* Sort criterion: Chado Cvterm: Name */
-  $handler->display->display_options['sorts']['type']['id'] = 'type';
-  $handler->display->display_options['sorts']['type']['table'] = 'cvterm';
-  $handler->display->display_options['sorts']['type']['field'] = 'name';
+  /* Sort criterion: Chado Pub: Id */
+  $handler->display->display_options['sorts']['pub_id']['id'] = 'pub_id';
+  $handler->display->display_options['sorts']['pub_id']['table'] = 'pub';
+  $handler->display->display_options['sorts']['pub_id']['field'] = 'pub_id';
+  $handler->display->display_options['sorts']['pub_id']['order'] = 'DESC';
   /* Filter criterion: Chado Pub: Title */
   $handler->display->display_options['filters']['title']['id'] = 'title';
   $handler->display->display_options['filters']['title']['table'] = 'pub';

+ 21 - 10
tripal_stock/includes/tripal_stock.chado_node.inc

@@ -196,8 +196,17 @@ function chado_stock_form($node, $form_state) {
 
   // TODO: Should we make this a textfield with an autocomplete field like the
   // feature type_id field?.
+  $st_cv = tripal_get_default_cv("stock", "type_id");
   $type_options = tripal_get_cvterm_default_select_options('stock', 'type_id', 'stock types');
   $type_options[0] = 'Select a Type';
+  $st_message = tripal_set_message("To add additional items to the stock type drop down list,
+     add a term to the " .
+    l($st_cv->name . " controlled vocabulary",
+      "admin/tripal/chado/tripal_cv/cv/" . $st_cv->cv_id . "/cvterm/add",
+      array('attributes' => array('target' => '_blank'))
+    ),
+    TRIPAL_INFO, array('return_html' => TRUE)
+  );
 
   $form['type_id'] = array(
     '#type' => 'select',
@@ -205,7 +214,8 @@ function chado_stock_form($node, $form_state) {
     '#description' => t('Select the stock type.'),
     '#options' => $type_options,
     '#default_value' => $type_id,
-    '#required'    => TRUE,
+    '#required' => TRUE,
+    '#suffix' => $st_message,
   );
 
   // get the list of organisms
@@ -226,7 +236,7 @@ function chado_stock_form($node, $form_state) {
   );
 
   $form['stock_description'] = array(
-    '#type' => 'textarea',
+    '#type' => 'text_format',
     '#title' => t('Notes'),
     '#default_value' => $sdescription,
     '#description' => t('Briefly enter any notes on the above stock. This should not include phenotypes or genotypes.'),
@@ -336,7 +346,6 @@ function chado_stock_validate(&$node, $form, &$form_state) {
   // remove surrounding whitespace
   $node->uniquename = property_exists($node, 'uniquename') ? trim($node->uniquename) : '';
   $node->sname = property_exists($node, 'sname') ? trim($node->sname) : '';
-  $node->stock_description = property_exists($node, 'stock_description') ? trim($node->stock_description) : '';
   $node->accession = property_exists($node, 'accession') ? trim($node->accession) : '';
   $node->db_description = property_exists($node, 'db_description') ? trim($node->db_description) : '';
 
@@ -365,10 +374,10 @@ function chado_stock_validate(&$node, $form, &$form_state) {
 
   // if this is an insert then we just need to make sure this name doesn't
   // already exist for this organism if it does then we need to throw an error
-  else {
+  elseif (!empty($node->organism_id) AND !empty($node->type_id)) {
     $sql = "
       SELECT *
-      FROM {Stock} S
+      FROM {stock} S
         INNER JOIN {cvterm} CVT ON S.type_id = CVT.cvterm_id
       WHERE uniquename = :uname AND organism_id = :organism_id AND CVT.name = :cvtname";
     $result = chado_query($sql, array(':uname' => $node->uniquename,
@@ -449,9 +458,10 @@ function chado_stock_insert($node) {
   // we do need to proceed with insertion into the chado/drupal linking table.
   if (!property_exists($node, 'stock_id')) {
 
-    $node->uniquename   = trim($node->uniquename);
-    $node->sname        = trim($node->sname);
-    $node->accession    = trim($node->accession);
+    $node->uniquename = trim($node->uniquename);
+    $node->sname = trim($node->sname);
+    $node->accession = trim($node->accession);
+    $node->stock_description = trim($node->stock_description['value']);
 
     // before we can add the stock, we must add the dbxref if one has been
     // provided by the user.
@@ -554,8 +564,9 @@ function chado_stock_insert($node) {
  */
 function chado_stock_update($node) {
 
-  $node->uniquename   = trim($node->uniquename);
-  $node->sname        = trim($node->sname);
+  $node->uniquename = trim($node->uniquename);
+  $node->sname = trim($node->sname);
+  $node->stock_description = trim($node->stock_description['value']);
 
   if ($node->revision) {
     // there is no way to handle revisions in Chado but leave

+ 1 - 1
tripal_stock/tripal_stock.info

@@ -3,7 +3,7 @@ description = Supports the stock tables of Chado by providing pages for viewing,
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-2.0-rc1
+version = 7.x-2.0
 
 dependencies[] = tripal_core
 dependencies[] = tripal_views

+ 0 - 3
tripal_stock/tripal_stock.install

@@ -47,9 +47,6 @@ function tripal_stock_requirements($phase) {
  * @ingroup tripal_stock
  */
 function tripal_stock_install() {
-  // create the module's data directory
-  tripal_create_files_dir('tripal_stock');
-
   // add some controlled vocabularies
   tripal_stock_add_cvs();
 

+ 9 - 9
tripal_stock/tripal_stock.views_default.inc

@@ -95,7 +95,10 @@ function tripal_stock_defaultview_admin_stocks() {
   $handler->display->display_options['access']['perm'] = 'access chado_stock content';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
+  $handler->display->display_options['exposed_form']['type'] = 'input_required';
+  $handler->display->display_options['exposed_form']['options']['submit_button'] = 'Search';
+  $handler->display->display_options['exposed_form']['options']['text_input_required'] = 'Click search to see a listing of stocks that meet the filter requirements. Use the filters to restrict this set to a more reasonable number of stocks or to find a specific stock.';
+  $handler->display->display_options['exposed_form']['options']['text_input_required_format'] = 'full_html';
   $handler->display->display_options['pager']['type'] = 'full';
   $handler->display->display_options['pager']['options']['items_per_page'] = '25';
   $handler->display->display_options['pager']['options']['offset'] = '0';
@@ -230,14 +233,11 @@ function tripal_stock_defaultview_admin_stocks() {
   $handler->display->display_options['fields']['nothing']['label'] = '';
   $handler->display->display_options['fields']['nothing']['alter']['text'] = '[edit_node]   [delete_node]';
   $handler->display->display_options['fields']['nothing']['element_label_colon'] = FALSE;
-  /* Sort criterion: Chado Organism: Common Name */
-  $handler->display->display_options['sorts']['common_name']['id'] = 'common_name';
-  $handler->display->display_options['sorts']['common_name']['table'] = 'organism';
-  $handler->display->display_options['sorts']['common_name']['field'] = 'common_name';
-  /* Sort criterion: Chado Stock: Uniquename */
-  $handler->display->display_options['sorts']['uniquename']['id'] = 'uniquename';
-  $handler->display->display_options['sorts']['uniquename']['table'] = 'stock';
-  $handler->display->display_options['sorts']['uniquename']['field'] = 'uniquename';
+  /* Sort criterion: Chado Stock: Id */
+  $handler->display->display_options['sorts']['stock_id']['id'] = 'stock_id';
+  $handler->display->display_options['sorts']['stock_id']['table'] = 'stock';
+  $handler->display->display_options['sorts']['stock_id']['field'] = 'stock_id';
+  $handler->display->display_options['sorts']['stock_id']['order'] = 'DESC';
   /* Filter criterion: Chado Organism: Common Name */
   $handler->display->display_options['filters']['common_name']['id'] = 'common_name';
   $handler->display->display_options['filters']['common_name']['table'] = 'organism';

+ 0 - 1
tripal_views/api/tripal_views.api.inc

@@ -650,7 +650,6 @@ function tripal_add_views_integration($defn_array, $setup_id = FALSE) {
     $view_record['setup_id'] = $setup_id;
   }
   if ($defn_array['type'] == 'mview') {
-    // D7 TODO: Check DBTNG changes work
     $mview = db_query("SELECT mview_id FROM {tripal_mviews} WHERE mv_table=:table", array(':table' => $defn_array['table']));
     $mview = $mview->fetchObject();
     $view_record['mview_id'] = $mview->mview_id;

+ 25 - 1
tripal_views/includes/tripal_views_integration.inc

@@ -31,6 +31,16 @@ function tripal_views_integrate_all_chado_tables() {
   // First integrate all of the Chado tables. Those that are base tables
   // get special treatment.
   $tables = chado_get_table_names(TRUE);
+
+  // Some chado tables might have been created via the Tripal Custom Tables
+  // or Tripal Materialized Views interfaces. We need to ensure that the
+  // corresponding mview_id and table_id are associated with these tables.
+  // @TODO: Add some way to show which integrations are for custom tables.
+  //$custom_tables = chado_get_custom_table_names();
+  $mview_tables = chado_get_mview_table_names();
+
+  // Hardcode a list of base tables since there isn't really a programatic way
+  // to determine which tables in the chado schema should be base tables.
   $base_tables = array(
     'acquisition', 'analysis', 'assay', 'biomaterial', 'contact', 'cv', 'cvterm',
     'db', 'dbxref', 'environment', 'expression', 'feature', 'featuremap', 'genotype',
@@ -39,15 +49,29 @@ function tripal_views_integrate_all_chado_tables() {
     'project', 'protocol', 'pub', 'stock', 'study', 'synonym'
   );
 
+  // For each chado table, generate an integration array, keeping the above
+  // details in mind, and save that integration with Tripal Views through the API.
   foreach ($tables as $tablename) {
     $priority = 10;
     if (!tripal_is_table_integrated($tablename, $priority)) {
-      if (in_array($tablename, $base_tables)) {
+
+      // Assuming that we have a default chado table, genereate an integration
+      // array describing it's Tripal Views integration.
+      if (in_array($tablename, $base_tables) OR (is_array($mview_tables) and in_array($tablename, $mview_tables))) {
         $table_integration_array = tripal_views_get_integration_array_for_chado_table($tablename, TRUE, $priority);
       }
       else {
         $table_integration_array = tripal_views_get_integration_array_for_chado_table($tablename, FALSE, $priority);
       }
+
+      // Check to see if this table is a Materialized view and if it is,
+      // treat it specially :).
+      if (is_array($mview_tables) and in_array($tablename, $mview_tables)) {
+        $table_integration_array['type'] = 'mview';
+      }
+
+      // As long as we were able to generate an integration array,
+      // Integrate It!
       if ($table_integration_array) {
         tripal_add_views_integration($table_integration_array);
       }

+ 23 - 20
tripal_views/includes/tripal_views_integration_UI.inc

@@ -306,7 +306,7 @@ function tripal_views_integration_form($form, &$form_state) {
   $chado_tables = array_merge(array('Select'), $chado_tables);
   $default = '';
   if ($setup_id) {
-    $default = (!$setup_obj->mview_id) ? $setup_obj->table_name : '';
+    $default = ($setup_obj->table_name) ? $setup_obj->table_name : '';
   }
   $form['base_table_type']['table_name'] = array(
     '#title' => t('Chado/Custom Table'),
@@ -326,7 +326,6 @@ function tripal_views_integration_form($form, &$form_state) {
 
 
   // build the form element that lists the materialized views
-  // D7 TODO: Check DBTNG changes work
   $query = db_query("SELECT mview_id, name FROM {tripal_mviews} WHERE mv_schema is NULL or mv_schema = '' ORDER BY name");
   $mview_tables = array();
   $mview_tables['0'] = 'Select';
@@ -334,24 +333,29 @@ function tripal_views_integration_form($form, &$form_state) {
     $mview_tables[$mview->mview_id] = $mview->name;
   }
   $default = '';
-  if ($setup_id && isset($setup_obj->mview_id)) {
+  $legacy_mview = FALSE;
+  if ($setup_id && !empty($setup_obj->mview_id)) {
     $default = $setup_obj->mview_id;
   }
-  $form['base_table_type']['mview_id'] = array(
-    '#title' => t('Legacy Materialized View'),
-    '#type' => 'select',
-    '#options' => $mview_tables,
-    '#description' => 'Which materialized view to use.',
-    '#default_value' => $default,
-    '#ajax' => array(
-       //D6: 'path' => 'tripal/views-integration/ajax/view_setup_table',
-       'callback' => 'tripal_views_integration_ajax_view_setup_table',
-       'wrapper' => 'tripal-views-integration-form',
-       'effect' => 'fade',
-       'event' => 'change',
-       'method' => 'replace',
-    ),
-  );
+  if (isset($mview_tables[$setup_obj->mview_id])) {
+    $legacy_mview = TRUE;
+
+    $form['base_table_type']['mview_id'] = array(
+      '#title' => t('Legacy Materialized View'),
+      '#type' => 'select',
+      '#options' => $mview_tables,
+      '#description' => 'Which materialized view to use.',
+      '#default_value' => $default,
+      '#ajax' => array(
+         //D6: 'path' => 'tripal/views-integration/ajax/view_setup_table',
+         'callback' => 'tripal_views_integration_ajax_view_setup_table',
+         'wrapper' => 'tripal-views-integration-form',
+         'effect' => 'fade',
+         'event' => 'change',
+         'method' => 'replace',
+      ),
+    );
+  }
 
   $form['views_type'] = array(
     '#type' => 'fieldset',
@@ -470,8 +474,7 @@ function tripal_views_integration_form($form, &$form_state) {
     // get the columns in this materialized view.  They are separated by commas
     // where the first word is the column name and the rest is the type
     $columns = array();
-    if ($mview_id) {
-      // D7 TODO: Check DBTNG changes work
+    if ($legacy_mview) {
       $sql = "SELECT mv_specs FROM {tripal_mviews} WHERE mview_id = :id";
       $mview = db_query($sql, array(':id' => $mview_id));
       $mview = $mview->fetchObject();

Unele fișiere nu au fost afișate deoarece prea multe fișiere au fost modificate în acest diff