Browse Source

Merge branch '7.x-2.x' of git.drupal.org:sandbox/spficklin/1337878 into 7.x-2.x

Lacey Sanderson 11 years ago
parent
commit
f429f288c4
52 changed files with 2598 additions and 1646 deletions
  1. 8 8
      tripal_analysis/api/tripal_analysis.api.inc
  2. 36 36
      tripal_analysis/includes/tripal_analysis.admin.inc
  3. 39 36
      tripal_analysis/includes/tripal_analysis.form.inc
  4. 20 13
      tripal_analysis/includes/tripal_analysis_privacy.inc
  5. 23 83
      tripal_analysis/tripal_analysis.install
  6. 71 66
      tripal_analysis/tripal_analysis.module
  7. 2 2
      tripal_core/api/tripal_core_ahah.api.inc
  8. 25 25
      tripal_core/api/tripal_core_chado.api.inc
  9. 18 18
      tripal_core/api/tripal_core_custom_tables.api.inc
  10. 1 1
      tripal_core/api/tripal_core_files.api.inc
  11. 3 3
      tripal_core/api/tripal_core_mviews.api.inc
  12. 3 3
      tripal_core/includes/custom_tables.inc
  13. 2 1
      tripal_core/includes/form_elements.inc
  14. 5 5
      tripal_core/includes/jobs.inc
  15. 65 65
      tripal_core/includes/mviews.inc
  16. 4 4
      tripal_cv/api/tripal_cv.api.inc
  17. 96 96
      tripal_cv/includes/cvterm_form.inc
  18. 6 6
      tripal_cv/includes/obo_loader.inc
  19. 4 3
      tripal_cv/includes/tripal_cv_admin.inc
  20. 2 2
      tripal_db/api/tripal_db.api.inc
  21. 38 38
      tripal_db/includes/tripal_db.admin.inc
  22. 146 74
      tripal_feature/api/tripal_feature.api.inc
  23. 2 2
      tripal_feature/includes/fasta_loader.inc
  24. 13 13
      tripal_feature/includes/gff_loader.inc
  25. 5 5
      tripal_feature/includes/indexFeatures.inc
  26. 11 10
      tripal_feature/includes/seq_extract.inc
  27. 2 2
      tripal_feature/includes/tripal_feature-db_references.inc
  28. 1 1
      tripal_feature/includes/tripal_feature-delete.inc
  29. 2 2
      tripal_feature/includes/tripal_feature-properties.inc
  30. 3 3
      tripal_feature/includes/tripal_feature-relationships.inc
  31. 157 101
      tripal_feature/includes/tripal_feature.admin.inc
  32. 203 87
      tripal_feature/includes/tripal_feature.sync_features.inc
  33. 12 3
      tripal_feature/tripal_feature.drush.inc
  34. 2 2
      tripal_feature/tripal_feature.info
  35. 4 4
      tripal_feature/tripal_feature.install
  36. 221 119
      tripal_feature/tripal_feature.module
  37. 0 1
      tripal_library/includes/tripal_library.admin.inc
  38. 5 5
      tripal_organism/api/tripal_organism.api.inc
  39. 14 13
      tripal_organism/includes/tripal_organism.admin.inc
  40. 64 63
      tripal_organism/tripal_organism.module
  41. 10 10
      tripal_project/includes/tripal_project.admin.inc
  42. 2 2
      tripal_project/tripal_project.info
  43. 5 14
      tripal_project/tripal_project.install
  44. 105 91
      tripal_project/tripal_project.module
  45. 77 78
      tripal_pub/api/tripal_pub.api.inc
  46. 7 1
      tripal_pub/includes/importers/AGL.inc
  47. 7 0
      tripal_pub/includes/importers/PMID.inc
  48. 4 2
      tripal_pub/theme/tripal_pub_admin.tpl.php
  49. 0 347
      tripal_stock/includes/tripal_stock-administration.inc
  50. 262 0
      tripal_stock/includes/tripal_stock.admin.inc
  51. 538 0
      tripal_stock/includes/tripal_stock.sync_stocks.inc
  52. 243 77
      tripal_stock/tripal_stock.module

+ 8 - 8
tripal_analysis/api/tripal_analysis.api.inc

@@ -16,10 +16,10 @@
  * @ingroup tripal_analysis
  */
 function tripal_analysis_register_child($modulename) {
-  $sql = "SELECT * FROM {tripal_analysis} WHERE modulename = '%s'";
-  if(!db_result($sql, $modulename)) {
-    $sql = "INSERT INTO {tripal_analysis} (modulename) VALUES ('%s')";
-    db_query($sql, $modulename);
+  $sql = "SELECT * FROM {tripal_analysis} WHERE modulename = :modname";
+  if (!db_query($sql, array(':modname' => $modulename))->fetchField()) {
+    $sql = "INSERT INTO {tripal_analysis} (modulename) VALUES (:modname)";
+    db_query($sql, array(':modname' => $modulename));
   }
 }
 
@@ -33,8 +33,8 @@ function tripal_analysis_register_child($modulename) {
  */
 function tripal_analysis_unregister_child($modulename) {
   if (db_table_exists('tripal_analysis')) {
-      $sql = "DELETE FROM {tripal_analysis} WHERE modulename = '%s'";
-      db_query($sql, $modulename);
+      $sql = "DELETE FROM {tripal_analysis} WHERE modulename = :modname";
+      db_query($sql, array(':modname' => $modulename));
   }
 }
 /**
@@ -142,7 +142,7 @@ function tripal_analysis_get_node($analysis_id) {
   $sql = "SELECT *
            FROM {chado_analysis} CA
               INNER JOIN {node} N on CA.nid = N.nid
-           WHERE analysis_id = %d";
-  $node = db_fetch_object(db_query($sql, $analysis_id));
+           WHERE analysis_id = :analysis_id";
+  $node = db_query($sql, array(':analysis_id' => $analysis_id))->fetchObject();
   return $node;
 }

+ 36 - 36
tripal_analysis/includes/tripal_analysis.admin.inc

@@ -55,7 +55,7 @@ function tripal_analysis_admin() {
   $sql = "SELECT modulename FROM {tripal_analysis}";
   $result = db_query($sql);
   $counter = 0;  //keep track of the number of sub-modules
-  while ($data = db_fetch_object($result)) {
+  while ($data = $result->fetchObject()) {
 
     // Check if the hook_get_settings() function is already defined.
     $func = $data->modulename . "_get_settings";
@@ -105,14 +105,14 @@ function get_tripal_analysis_admin_form_taxonomy_set(&$form) {
 
   // iterate through all of the libraries
   $lib_boxes = array();
-  while ($analysis = db_fetch_object($lib_rset)) {
+  while ($analysis = $lib_rset->fetchObject()) {
     $lib_boxes[$analysis->analysis_id] = "$analysis->name";
   }
 
   $form['taxonify']['description'] = array(
        '#type' => 'item',
        '#value' => t("Drupal allows for assignment of \"taxonomy\" or catagorical terms to " .
-          "nodes. These terms allow for advanced filtering during searching. This option allows ".
+          "nodes. These terms allow for advanced filtering during searching. This option allows " .
           "for setting taxonomy only for features that belong to the selected analyses below.  All other features will be unaffected.  To set taxonomy for all features in the site see the Feature Administration page."),
      '#weight' => 1,
   );
@@ -158,7 +158,7 @@ function get_tripal_analysis_admin_form_reindex_set(&$form) {
 
   // iterate through all of the libraries
   $lib_boxes = array();
-  while ($analysis = db_fetch_object($lib_rset)) {
+  while ($analysis = $lib_rset->fetchObject()) {
     $lib_boxes[$analysis->analysis_id] = "$analysis->name";
   }
   $form['reindex']['description'] = array(
@@ -202,10 +202,10 @@ function get_tripal_analysis_admin_form_cleanup_set(&$form) {
   );
   $form['cleanup']['description'] = array(
        '#type' => 'item',
-       '#value' => t("With Drupal and chado residing in different databases ".
-          "it is possible that nodes in Drupal and analyses in Chado become ".
-          "\"orphaned\".  This can occur if an analysis node in Drupal is ".
-          "deleted but the corresponding chado analysis is not and/or vice ".
+       '#value' => t("With Drupal and chado residing in different databases " .
+          "it is possible that nodes in Drupal and analyses in Chado become " .
+          "\"orphaned\".  This can occur if an analysis node in Drupal is " .
+          "deleted but the corresponding chado analysis is not and/or vice " .
           "versa. Click the button below to resolve these discrepancies."),
        '#weight' => 1,
   );
@@ -254,11 +254,11 @@ function get_tripal_analysis_admin_form_sync_set(&$form) {
     // a message stating that all analyses are currently synced.
     $ana_boxes = array();
     $added = 0;
-    while ($analysis = db_fetch_object($ana_rset)) {
+    while ($analysis = $ana_rset->fetchObject()) {
       // check to see if the analysis is already present as a node in drupal.
       // if so, then skip it.
-      $sql = "SELECT * FROM {chado_analysis} WHERE analysis_id = %d";
-      if (!db_fetch_object(db_query($sql, $analysis->analysis_id))) {
+      $sql = "SELECT * FROM {chado_analysis} WHERE analysis_id = :analysis_id";
+      if (!db_query($sql, array(':analysis_id' => $analysis->analysis_id))->fetchObject()) {
         $ana_boxes[$analysis->analysis_id] = "$analysis->name";
         $added++;
       }
@@ -272,8 +272,8 @@ function get_tripal_analysis_admin_form_sync_set(&$form) {
       $form['sync']['analyses'] = array(
            '#title'       => t('Available analyses'),
            '#type'        => t('checkboxes'),
-           '#description' => t("Check the analyses you want to sync.  Drupal ".
-              "content will be created for each of the analyses listed above. ".
+           '#description' => t("Check the analyses you want to sync.  Drupal " .
+              "content will be created for each of the analyses listed above. " .
               "Select 'All analyses' to sync all of them."),
            '#required'    => FALSE,
            '#prefix'      => '<div id="ana_boxes">',
@@ -329,8 +329,8 @@ function tripal_analysis_admin_validate($form, &$form_state) {
       }
       if ($analysis_id and preg_match("/^\d+$/i", $analysis_id)) {
         // get the list of analyses
-        $sql = "SELECT * FROM {analysis} WHERE analysis_id = %d";
-        $analysis = db_fetch_object(chado_query($sql, $analysis_id));
+        $sql = "SELECT * FROM {analysis} WHERE analysis_id = :analysis_id";
+        $analysis = chado_query($sql, array(':analysis_id' => $analysis_id))->fetchObject();
         $to_sync[$analysis_id] = $analysis->name;
       }
     }
@@ -355,8 +355,8 @@ function tripal_analysis_admin_validate($form, &$form_state) {
     foreach ($analyses as $analysis_id) {
       if ($analysis_id and preg_match("/^\d+$/i", $analysis_id)) {
         // get the analysis info
-        $sql = "SELECT * FROM {analysis} WHERE analysis_id = %d";
-        $analysis = db_fetch_object(chado_query($sql, $analysis_id));
+        $sql = "SELECT * FROM {analysis} WHERE analysis_id = :analysis_id";
+        $analysis = chado_query($sql, array(':analysis_id' => $analysis_id))->fetchObject();
         $job_args[0] = $analysis_id;
         tripal_add_job("Reindex features for analysis: $analysis->name", 'tripal_analysis',
              'tripal_analysis_reindex_features', $job_args, $user->uid);
@@ -373,8 +373,8 @@ function tripal_analysis_admin_validate($form, &$form_state) {
     foreach ($analyses as $analysis_id) {
       if ($analysis_id and preg_match("/^\d+$/i", $analysis_id)) {
         // get the analysis info
-        $sql = "SELECT * FROM {analysis} WHERE analysis_id = %d";
-        $analysis = db_fetch_object(chado_query($sql, $analysis_id));
+        $sql = "SELECT * FROM {analysis} WHERE analysis_id = :analysis_id";
+        $analysis = chado_query($sql, array(':analysis_id' => $analysis_id))->fetchObject();
         $job_args[0] = $analysis_id;
         tripal_add_job("Set taxonomy for features in analysis: $analysis->name", 'tripal_analysis',
              'tripal_analysis_taxonify_features', $job_args, $user->uid);
@@ -400,28 +400,28 @@ function tripal_analysis_sync_analyses($analysis_id = NULL, $job_id = NULL) {
   $page_content = '';
 
   if (!$analysis_id) {
-    $sql = "SELECT Analysis_id, name, description, program, ".
-        "  programversion, algorithm, sourcename, sourceversion, sourceuri, ".
-          "  timeexecuted ".
-          "FROM {Analysis} ";
+    $sql = "SELECT Analysis_id, name, description, program, " .
+        "  programversion, algorithm, sourcename, sourceversion, sourceuri, " .
+          "  timeexecuted " .
+          "FROM {analysis} ";
     $results = chado_query($sql);
   }
   else {
-    $sql = "SELECT Analysis_id, name, description, program, ".
-         "  programversion, algorithm, sourcename, sourceversion, sourceuri, ".
-          "  timeexecuted ".
-          "FROM {Analysis} ".
-          "WHERE analysis_id = %d";
-    $results = chado_query($sql, $analysis_id);
+    $sql = "SELECT Analysis_id, name, description, program, " .
+         "  programversion, algorithm, sourcename, sourceversion, sourceuri, " .
+          "  timeexecuted " .
+          "FROM {analysis} " .
+          "WHERE analysis_id = :analysis_id";
+    $results = chado_query($sql, array(':analysis_id' => $analysis_id));
   }
 
 
   // We'll use the following SQL statement for checking if the analysis
   // already exists as a drupal node.
-  $sql = "SELECT * FROM {chado_analysis} ".
-          "WHERE analysis_id = %d";
+  $sql = "SELECT * FROM {chado_analysis} " .
+          "WHERE analysis_id = :analysis_id";
 
-  while ($analysis = db_fetch_object($results)) {
+  while ($analysis = $results->fetchObject()) {
         print "syncing analysis ";
         print $analysis->name;
         print ", ";
@@ -430,17 +430,17 @@ function tripal_analysis_sync_analyses($analysis_id = NULL, $job_id = NULL) {
 
     // check if this analysis already exists in the drupal database. if it
     // does then skip this analysis and go to the next one.
-    if (!db_fetch_object(db_query($sql, $analysis->analysis_id))) {
+    if (!db_query($sql, array(':analysis_id' => $analysis->analysis_id))->fetchObject()) {
 
       $new_node = new stdClass();
 
       // try to access analysis type for this analysis
       $sql = "SELECT * FROM {analysisprop}
-                    WHERE analysis_id = %d
+                    WHERE analysis_id = :analysis_id
                     AND type_id =
-                        (SELECT cvterm_id from {cvterm} where name = '%s')
+                        (SELECT cvterm_id from {cvterm} where name = :name)
             ";
-      $analysis_type = db_fetch_object(chado_query($sql, $analysis->analysis_id, "analysis_type"));
+      $analysis_type = chado_query($sql, array(':analysis_id' => $analysis->analysis_id, ':name' => "analysis_type"))->fetchObject();
 
       // Get the type of analysis using cvterm_id
             // Current possibilities: kegg, unigene, interpro, blast

+ 39 - 36
tripal_analysis/includes/tripal_analysis.form.inc

@@ -137,10 +137,10 @@ function chado_analysis_form(&$node, $form_state = NULL) {
   $day = preg_replace("/^\d+-\d+-0?(\d+) .*/", "$1", $default_time);
   // If the time is not set, use current time
   if (!$default_time) {
-    $default_time = time();
-    $year = format_date($default_time, 'custom', 'Y');
+    $default_time = REQUEST_TIME;
+    $year  = format_date($default_time, 'custom', 'Y');
     $month = format_date($default_time, 'custom', 'n');
-    $day = format_date($default_time, 'custom', 'j');
+    $day   = format_date($default_time, 'custom', 'j');
   }
   $form['timeexecuted']= array(
       '#type' => 'date',
@@ -177,9 +177,9 @@ function chado_analysis_form(&$node, $form_state = NULL) {
     ORDER BY CVT.name ASC
   ";
   $prop_types = chado_query($sql);
-  while ($prop = db_fetch_object($prop_types)) {
-  	$properties_select[$prop->cvterm_id] = $prop->name;
-  	$properties_list[$prop->cvterm_id] = $prop;
+  while ($prop = $prop_types->fetchObject()) {
+    $properties_select[$prop->cvterm_id] = $prop->name;
+    $properties_list[$prop->cvterm_id] = $prop;
   }
   
   $form['properties'] = array(
@@ -243,10 +243,10 @@ function tripal_analysis_validate($node, &$form) {
     $analysis = $result[0];
       
     // if the name has changed make sure it doesn't conflict with an existing name
-    if($analysis->name != $node->analysisname) {
+    if ($analysis->name != $node->analysisname) {
       $values = array('name' => $node->analysisname);
       $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
-      if($result and count($result) > 0) {
+      if ($result and count($result) > 0) {
         form_set_error('analysisname', 'Cannot update the analysis with this analysis name. An analysis with this name already exists.');
         return;
       }  
@@ -254,7 +254,7 @@ function tripal_analysis_validate($node, &$form) {
     
     // if the unique constraint has changed check to make sure it doesn't conflict with an
     // existing record
-    if($analysis->program != $node->program or $analysis->programversion != $node->programversion or 
+    if ($analysis->program != $node->program or $analysis->programversion != $node->programversion or 
        $analysis->sourcename != $node->sourcename) {
       $values = array(
         'program' => $node->program,
@@ -278,7 +278,7 @@ function tripal_analysis_validate($node, &$form) {
       }  
     }
   }
-  else{
+  else {
     // To differentiate if we are syncing or creating a new analysis altogther, see if an
     // analysis_id already exists
     if ($node->analysis_id and $node->analysis_id != 0) {
@@ -305,7 +305,7 @@ function tripal_analysis_validate($node, &$form) {
       // title, so it should be unique      
       $values = array('name' => $node->analysisname);
       $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
-      if($result and count($result) > 0) {
+      if ($result and count($result) > 0) {
         form_set_error('analysisname', 'Cannot add the analysis with this analysis name. An analysis with this name already exists.');
         return;
       }
@@ -322,11 +322,12 @@ function chado_analysis_node_form_add_new_empty_props(&$form, $properties_select
   $form['properties']['table']['new']["new_id"] = array(
     '#type'          => 'select',
     '#options'       => $properties_select,
-    '#ahah' => array(
-      'path'    => "tripal_analysis/properties/description",
-      'wrapper' => 'tripal-analysis-new_value-desc',
-      'event'   => 'change',
-      'method'  => 'replace',          
+    '#ajax' => array(
+      'callback' => "tripal_analysis_property_get_description",
+      'wrapper'  => 'tripal-analysis-new_value-desc',
+      'effect'   => 'fade',
+      'event'    => 'change',
+      'method'   => 'replace',          
   ),
   );
   $form['properties']['table']['new']["new_value"] = array(
@@ -340,11 +341,12 @@ function chado_analysis_node_form_add_new_empty_props(&$form, $properties_select
     '#type'         => 'image_button',      
     '#value'        => t('Add'),
     '#src'          => drupal_get_path('theme', 'tripal') . '/images/add.png',
-    '#ahah' => array(
-      'path'    => "tripal_analysis/properties/add",
-      'wrapper' => 'tripal-analysis-edit-properties-table',
-      'event'   => 'click',
-      'method'  => 'replace',          
+    '#ajax' => array(
+      'callback' => "tripal_analysis_property_add",
+      'wrapper'  => 'tripal-analysis-edit-properties-table',
+      'effect'   => 'fade',
+      'event'    => 'click',
+      'method'   => 'replace',          
     ),
     '#attributes' => array('onClick' => 'return false;'),
     );
@@ -367,10 +369,10 @@ function chado_analysis_node_form_add_new_props(&$form, $form_state, &$d_propert
 
         // skip any properties that the user requested to delete through a previous
         // AHAH callback or through the current AHAH callback
-        if($d_removed["$new_id-$rank"]) {
+        if ($d_removed["$new_id-$rank"]) {
           continue;
         }
-        if($form_state['post']['remove-' . $new_id . '-' . $rank]) {
+        if ($form_state['post']['remove-' . $new_id . '-' . $rank]) {
           $d_removed["$new_id-$rank"] = 1;
           continue;
         }
@@ -406,11 +408,12 @@ function chado_analysis_node_form_add_new_props(&$form, $form_state, &$d_propert
           '#type'         => 'image_button',
           '#value'        => t('Remove'),
           '#src'          => drupal_get_path('theme', 'tripal') . '/images/minus.png',
-          '#ahah' => array(
-            'path'    => "tripal_analysis/properties/minus/$new_id/$rank",
-            'wrapper' => 'tripal-analysis-edit-properties-table',
-            'event'   => 'click',
-            'method'  => 'replace',
+          '#ajax' => array(
+            'callback' => "tripal_analysis/properties/minus/$new_id/$rank",
+            'wrapper'  => 'tripal-analysis-edit-properties-table',
+            'effect'   => 'fade',
+            'event'    => 'click',
+            'method'   => 'replace',
         ),
           '#attributes' => array('onClick' => 'return false;'),
         );
@@ -420,7 +423,7 @@ function chado_analysis_node_form_add_new_props(&$form, $form_state, &$d_propert
 
 
   // second add in any new properties added during this callback
-  if($form_state['post']['add']) {
+  if ($form_state['post']['add']) {
     $new_id = $form_state['values']['new_id'];
     $new_value = $form_state['values']['new_value'];
 
@@ -478,7 +481,7 @@ function chado_analysis_node_form_add_analysisprop_table_props(&$form, $form_sta
   // get the properties for this analysis
   $num_properties = 0;
 
-  if(!$analysis_id) {
+  if (!$analysis_id) {
     return $num_properties;
   }
 
@@ -487,21 +490,21 @@ function chado_analysis_node_form_add_analysisprop_table_props(&$form, $form_sta
     FROM {analysisprop} PP
       INNER JOIN {cvterm} CVT on CVT.cvterm_id = PP.type_id
       INNER JOIN {cv} CV on CVT.cv_id = CV.cv_id
-    WHERE PP.analysis_id = %d and CV.name = 'analysis_property'
+    WHERE PP.analysis_id = :analysis_id and CV.name = 'analysis_property'
     ORDER BY CVT.name, PP.rank
   ";
-  $analysis_props = chado_query($sql, $analysis_id);
-  while ($prop = db_fetch_object($analysis_props)) {
+  $analysis_props = chado_query($sql, array(':analysis_id' => $analysis_id));
+  while ($prop = $analysis_props->fetchObject()) {
 
     $type_id = $prop->cvterm_id;
     $rank = count($d_properties[$type_id]);
     
     // skip any properties that the user requested to delete through a previous
     // AHAH callback or through the current AHAH callback
-    if($d_removed["$type_id-$rank"]) {
+    if ($d_removed["$type_id-$rank"]) {
       continue;
     }
-    if($form_state['post']['remove-' . $type_id . '-' . $rank]) {
+    if ($form_state['post']['remove-' . $type_id . '-' . $rank]) {
       $d_removed["$type_id-$rank"] = 1;
       continue;
     }
@@ -591,7 +594,7 @@ function tripal_analysis_theme_node_form_properties($form) {
     );
   }
 
-  $headers = array('Property Type','Value', '');
+  $headers = array('Property Type', 'Value', '');
   return theme('table', $headers, $rows);
 }
 

+ 20 - 13
tripal_analysis/includes/tripal_analysis_privacy.inc

@@ -16,8 +16,12 @@ function tripal_analysis_check_permission($analysis_id) {
     $roles = $user->roles;
     $node_access = 0;
     foreach ($roles AS $rid => $role) {
-      $p_sql = "SELECT grant_view FROM {node_access} NA INNER JOIN {chado_analysis} CA ON NA.nid = CA.nid WHERE analysis_id=%d AND gid = %d";
-      $access = db_result(db_query($p_sql, $analysis_id, $rid));
+      $p_sql = "
+        SELECT grant_view 
+        FROM {node_access} NA 
+          INNER JOIN {chado_analysis} CA ON NA.nid = CA.nid 
+        WHERE analysis_id = :analysis_id AND gid = :gid";
+      $access = db_query($p_sql, array(':analysis_id' => $analysis_id, ':gid' => $rid))->fetchField();
       if ($access == 1) {
         $node_access = 1;
         break;
@@ -51,20 +55,20 @@ function tripal_analysis_set_feature_permission($analysis_id, $nid) {
   print "Updating feature permissions:\n";
 
   // Get features associated with the analysis
-  $sql = "SELECT feature_id FROM {analysisfeature} WHERE analysis_id = %d";
-  $features = chado_query($sql, $analysis_id);
+  $sql = "SELECT feature_id FROM {analysisfeature} WHERE analysis_id = :analysis_id";
+  $features = chado_query($sql, array(':analysis_id' => $analysis_id));
 
   // Convert feature_id into node_id
   $feature_nids = array();
   $counter = 0;
-  $sql = "SELECT nid FROM {chado_feature} WHERE feature_id = %d";
-  while ($feature = db_fetch_object($features)) {
-    $feature_nids [$counter] = db_result(db_query($sql, $feature->feature_id));
+  $sql = "SELECT nid FROM {chado_feature} WHERE feature_id = :feature_id";
+  while ($feature = $features->fetchObject()) {
+    $feature_nids[$counter] = db_query($sql, array(':feature_id' => $feature->feature_id))->fetchField();
     $counter ++;
   }
 
   //Convert analysis_id into node_id
-  $ana_nid = db_result(db_query("SELECT nid FROM {chado_analysis} WHERE analysis_id = %analysis_id", $analysis_id));
+  $ana_nid = db_result(db_query("SELECT nid FROM {chado_analysis} WHERE analysis_id = :analysis_id", array(':analysis_id' => $analysis_id)));
   // Get available roles
   $roles = array_keys(user_roles());
 
@@ -79,14 +83,17 @@ function tripal_analysis_set_feature_permission($analysis_id, $nid) {
       print $percentage . "% ";
     }
 
-    db_query("DELETE FROM {node_privacy_byrole} WHERE nid = %d AND realm = 'node_privacy_byrole_role'", $fnid);
+    db_query("DELETE FROM {node_privacy_byrole} WHERE nid = :nid AND realm = 'node_privacy_byrole_role'", array(':nid' => $fnid));
     foreach ($roles AS $rid) {
       // Get permissions of this analysis for this role
-      $rsql = "SELECT * FROM {node_privacy_byrole} WHERE gid = %d AND nid = %d AND realm = 'node_privacy_byrole_role'";
-      $ana_perm = db_fetch_object(db_query($rsql, $rid, $ana_nid));
+      $rsql = "SELECT * FROM {node_privacy_byrole} WHERE gid = :gid AND nid = :nid AND realm = 'node_privacy_byrole_role'";
+      $ana_perm = db_query($rsql, array(':gid' => $rid, ':nid' => $ana_nid))->fetchObject();
       db_query("INSERT INTO {node_privacy_byrole} (nid, gid, realm, grant_view, grant_update, grant_delete)
-                  VALUES (%d, %d, '%s', %d, %d, %d)", $fnid, $rid, 'node_privacy_byrole_role', $ana_perm->grant_view,
-                  $ana_perm->grant_update, $ana_perm->grant_delete);
+                VALUES (:nid, :gid, :realm, :grant_view, :grant_update, :grant_delete)", 
+                array(':nid' => $fnid, ':gid' => $rid, ':realm' => 'node_privacy_byrole_role', 
+                      ':grant_view' => $ana_perm->grant_view, 
+                      ':grant_update' => $ana_perm->grant_update, 
+                      ':grant_delete' => $ana_perm->grant_delete));
     }
     $node = node_load($fnid);
     node_save($node);

+ 23 - 83
tripal_analysis/tripal_analysis.install

@@ -12,9 +12,6 @@ function tripal_analysis_install() {
   // create the module's data directory
   tripal_create_moddir('tripal_analysis');
 
-  // Use schema API to create database table.
-  drupal_install_schema('tripal_analysis');
-
   // we may need the analysisfeatureprop table if it doesn't already exist
   tripal_analysis_create_analysisfeatureprop();
   
@@ -28,19 +25,20 @@ function tripal_analysis_install() {
 /*
  * 
  */
-function tripal_analysis_create_analysisfeatureprop(){
+function tripal_analysis_create_analysisfeatureprop() {
+  
   // Create analysisfeatureprop table in chado.  This is needed for Chado 
   // version 1.11, the table exists in Chado 1.2. 
   if (!db_table_exists('analysisfeatureprop')) {
-    $sql = "CREATE TABLE analysisfeatureprop (".
-            "  analysisfeatureprop_id SERIAL PRIMARY KEY, ".
+    $sql = "CREATE TABLE analysisfeatureprop (" .
+            "  analysisfeatureprop_id SERIAL PRIMARY KEY, " .
             "  analysisfeature_id     INTEGER NOT NULL REFERENCES analysisfeature(analysisfeature_id) " .
-            "    ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, ".
-            "  type_id                INTEGER NOT NULL REFERENCES cvterm(cvterm_id) ".
-            "    ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, ".
-            "  value                  TEXT, ".
-            "  rank                   INTEGER NOT NULL, ".
-            "  CONSTRAINT analysisfeature_id_type_id_rank UNIQUE(analysisfeature_id, type_id, rank)".
+            "    ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, " .
+            "  type_id                INTEGER NOT NULL REFERENCES cvterm(cvterm_id) " .
+            "    ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, " .
+            "  value                  TEXT, " .
+            "  rank                   INTEGER NOT NULL, " .
+            "  CONSTRAINT analysisfeature_id_type_id_rank UNIQUE(analysisfeature_id, type_id, rank)" .
             ")";
     chado_query($sql);
   } 
@@ -48,7 +46,7 @@ function tripal_analysis_create_analysisfeatureprop(){
 /*
  * 
  */
-function tripal_analysis_add_cvterms(){
+function tripal_analysis_add_cvterms() {
   
   tripal_cv_add_cv('tripal_analysis', 'Terms used for managing analyses in Tripal');
   
@@ -56,8 +54,8 @@ function tripal_analysis_add_cvterms(){
   // change change this property
   $term = array(
     'name' => 'analysis_type', 
-    'def' => 'The type of analysis was performed. This value is automatically set by '.
-             'each Tripal Analysis module and should be equal to the module name '.
+    'def' => 'The type of analysis was performed. This value is automatically set by ' .
+             'each Tripal Analysis module and should be equal to the module name ' .
              '(e.g. tripal_analysis_blast, tripal_analysis_go).'
   );
   tripal_cv_add_cvterm($term, 'tripal_analysis', 0, 1, 'tripal');
@@ -83,21 +81,22 @@ function tripal_analysis_add_cvterms(){
   // an analysis_type term in the 'tripal_analysis' vocabular we duplicate it here because the 
   // tripal_analysis vocabulary is intended for use by the extension modules.  user's should not be able
   // to directly modify properties set by extension modules for an analysis.
-  tripal_cv_add_cvterm(array('name' => 'Analysis Type','def' => 'The type of analysis was performed.'), 
+  tripal_cv_add_cvterm(array('name' => 'Analysis Type', 'def' => 'The type of analysis was performed.'), 
      'analysis_property', 0, 1, 'tripal');
 }
 /**
  * Implementation of hook_uninstall().
  */
 function tripal_analysis_uninstall() {
-  // Use schema API to delete database table.
-  drupal_uninstall_schema('tripal_analysis');
+
   // Remove analysis nodes from drupal.
-  $sql_ana_id = "SELECT nid, vid ".
-                 "FROM {node} ".
-                 "WHERE type like 'chado_analysi%'";
+  $sql_ana_id = "
+    SELECT nid, vid 
+    FROM {node} 
+    WHERE type like 'chado_analysi%'
+  ";
   $result = db_query($sql_ana_id);
-  while ($ana = db_fetch_object($result)) {
+  while ($ana = $result->fetchObject()) {
     node_delete($ana->nid);
   }
 }
@@ -145,14 +144,13 @@ function tripal_analysis_schema() {
 
   // tripal_analysis table
   $schema['tripal_analysis'] = array(
-    'description' => t('Table to store analysis sub-modules'),
+    'description' => 'Table to store analysis sub-modules',
     'fields' => array(
       'modulename' => array(
         'type' => 'text',
         'size' => 'small',
         'not null' => TRUE,
-        'description' => t('The module name. Tripal Analysis will use the '.
-                            'module name to call module_setting_form()')
+        'description' => 'The module name. Tripal Analysis will use the module name to call module_setting_form()'
       )
     ),
     'unique keys' => array(
@@ -163,64 +161,6 @@ function tripal_analysis_schema() {
   return $schema;
 }
 
-/**
- *  Update for Drupal 6.x, Tripal 1.1, Analysis Module 1.1
- *  This update adds a new analysis_organism materialized view
- *
- */
-function tripal_analysis_update_6100() {
-  // add the new materialized view
-  tripal_analysis_add_mview_analysis_organism();
-  
-  // move the analysis_type property into a new CV so that user's can change this property if
-  // they want too  
-  tripal_cv_add_cv('tripal_analysis', 'Terms used for managing analyses in Tripal');
-  
-  $sql = "
-    UPDATE {cvterm} SET cv_id = 
-      (SELECT cv_id FROM {cv} WHERE name = 'tripal_analysis')
-    WHERE cv_id = (SELECT cv_id FROM {cv} WHERE name = 'tripal') AND
-      name = 'analysis_type'
-  ";
-  chado_query($sql);
-  
-  $ret = array(
-    '#finished' => 1,
-  );
-
-  return $ret;
-}
-
-/**
- * Provide update script for adding new cvterms
- */
-function tripal_analysis_update_6001() {
-   // we have some new cvterms to add
-  tripal_cv_add_cvterm(array('name' => 'based_on_analysis', 'def' => 'The analysis that this analysis was based on. For example, blast/kegg/interpro analyses are based on a unigene analysis. The unigene analysis_id should be stored in analysisprop as the rank using this cvterm. The name of said unigene analysis can be inserted as the value in analysisprop.'), 'tripal', 0, 1, 'tripal');
-  tripal_cv_add_cvterm(array('name' => 'additional_files', 'def' => 'Additional files for this analysis. Each file should be separated by a semi-colon and have this format: <file description>, <file path>;'), 'tripal', 0, 1, 'tripal');
-  $ret = array(
-      '#finished' => 1,
-  );
-  return $ret;
-}
-
-/**
- *  Update for Drupal 6.x, Tripal 1.1, Analysis Module 1.1
- *  This update adds a new analysis_property cv and 'Analysis Type' cvterm
- */
-function tripal_analysis_update_6101() {
-  // the 'analysis_property' vocabulary is for user definable properties.  Even though we already have
-  // an analysis_type term in the 'tripal_analysis' vocabular we duplicate it here because the 
-  // tripal_analysis vocabulary is intended for use by the extension modules.  user's should not be able
-  // to directly modify properties set by extension modules for an analysis.
-   tripal_cv_add_cvterm(array('name' => 'Analysis Type','def' => 'The type of analysis was performed.'), 
-     'analysis_property', 0, 1, 'tripal');
-
-  $ret = array(
-      '#finished' => 1,
-  );
-  return $ret;
-}
 /**
  * Implementation of hook_requirements(). 
  */

+ 71 - 66
tripal_analysis/tripal_analysis.module

@@ -82,19 +82,8 @@ function tripal_analysis_menu() {
       'type' => MENU_NORMAL_ITEM,
       'file' => 'includes/tripal_analysis.admin.inc',
   );
- 
-    
-  // AJAX calls for adding/removing properties to a contact
-  $items['tripal_analysis/properties/add'] = array(
-    'page callback' => 'tripal_analysis_property_add',
-    'access arguments' => array('edit chado_analysis content'),
-    'type ' => MENU_CALLBACK,
-  );
-  $items['tripal_analysis/properties/description'] = array(
-    'page callback' => 'tripal_analysis_property_get_description',
-    'access arguments' => array('edit chado_analysis content'),
-    'type ' => MENU_CALLBACK,
-  );
+     
+
   $items['tripal_analysis/properties/minus/%/%'] = array(
     'page callback' => 'tripal_analysis_property_delete',
     'page arguments' => array(3, 4),
@@ -128,7 +117,7 @@ function chado_analysis_insert($node) {
   if ($analysis_id) {
     $values = array('analysis_id' => $node->analysis_id);
     $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
-    if($result and count($result) > 0) {
+    if ($result and count($result) > 0) {
       $analysis = $result[0];
     }
   }
@@ -155,12 +144,12 @@ function chado_analysis_insert($node) {
 
   // Make sure the entry for this analysis doesn't already exist in the
   // chado_analysis table if it doesn't exist then we want to add it.
-  $node_check_sql = "SELECT * FROM {chado_analysis} ".
+  $node_check_sql = "SELECT * FROM {chado_analysis} " .
                     "WHERE analysis_id = :analysis_id";
   $node_check = db_query($node_check_sql, array(':analysis_id' => $analysis_id))->fetchObject();
   if (!$node_check) {
     // next add the item to the drupal table
-    $sql = "INSERT INTO {chado_analysis} (nid, vid, analysis_id) ".
+    $sql = "INSERT INTO {chado_analysis} (nid, vid, analysis_id) " .
            "VALUES (:nid, :vid, :analysis_id)";
     db_query($sql, array(':nid' => $node->nid, ':vid' => $node->vid, ':analysis_id' => $analysis_id));
     // Create a title for the analysis node using the unique keys so when the
@@ -200,7 +189,7 @@ function chado_analysis_insert($node) {
       ORDER BY CVT.name ASC
   ";
   $prop_types = chado_query($sql);
-  while ($prop = db_fetch_object($prop_types)) {
+  while ($prop = $prop_types->fetchObject()) {
     $properties_list[$prop->cvterm_id] = $prop->name;
   }
     
@@ -254,16 +243,16 @@ function chado_analysis_delete($node) {
   }
 
   // Remove data from the {chado_analysis}, {node}, and {node_revisions} tables
-  $sql_del = "DELETE FROM {chado_analysis} ".
-              "WHERE nid = :nid ".
+  $sql_del = "DELETE FROM {chado_analysis} " .
+              "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 ".
+  $sql_del = "DELETE FROM {node} " .
+              "WHERE nid = :nid " .
               "AND vid = :vid";
   db_query($sql_del, array(':nid' => $node->nid, ':vid' => $node->vid));
-  $sql_del = "DELETE FROM {node_revisions} ".
-              "WHERE nid = :nid ".
+  $sql_del = "DELETE FROM {node_revision} " .
+              "WHERE nid = :nid " .
               "AND vid = :vid";
   db_query($sql_del, array(':nid' => $node->nid, ':vid' => $node->vid));
 
@@ -359,7 +348,7 @@ function chado_analysis_update($node) {
       ORDER BY CVT.name ASC
       ";
   $prop_types = chado_query($sql);
-  while ($prop = db_fetch_object($prop_types)) {
+  while ($prop = $prop_types->fetchObject()) {
     $properties_list[$prop->cvterm_id] = $prop->name;
   }
 
@@ -391,14 +380,26 @@ function chado_analysis_update($node) {
 
   // now add in the properties by first removing any the analysis
   // already has and adding the ones we have
-  tripal_core_chado_delete('analysisprop', array('analysis_id' => $analysis_id));
+  $sql = "
+    DELETE FROM {analysisprop} WHERE analysis_id = :analysis_id AND type_id IN (
+      SELECT CVT.cvterm_id
+      FROM  {cvterm} CVT
+        INNER JOIN {cv} ON CVT.cv_id = CV.cv_id
+      WHERE CV.name = 'analysis_property')
+  ";
+  $success = chado_query($sql, array(':analysis_id' => $analysis_id));
+  if (!$success) {
+    drupal_set_message("Cannot update analysis properties", "error");
+    watchdog('t_analysis', "Cannot update analysis properties.", array(), WATCHDOG_ERROR);
+    return;
+  }
   foreach ($properties as $property => $elements) {
     foreach ($elements as $rank => $value) {
       $status = tripal_analysis_insert_property($analysis_id, $property, $value, FALSE, 'analysis_property');
       if (!$status) {
         drupal_set_message("Error cannot add property: '$property'", "error");
         watchdog('t_analysis', "Error cannot add property: '%prop'",
-        array('%prop' => $property), WATCHDOG_ERROR);
+          array('%prop' => $property), WATCHDOG_ERROR);
       }
     }
   }
@@ -477,12 +478,12 @@ function tripal_analysis_help($path, $arg) {
  *
  * This hook allows node modules to limit access to the node types they define.
  *
- *  @param $op
- *  The operation to be performed
- *
  *  @param $node
  *  The node on which the operation is to be performed, or, if it does not yet exist, the
  *  type of node to be created
+ *  
+ *  @param $op
+ *  The operation to be performed
  *
  *  @param $account
  *  A user object representing the user for whom the operation is to be performed
@@ -495,7 +496,7 @@ function tripal_analysis_help($path, $arg) {
  *
  * @ingroup tripal_analysis
  */
-function chado_analysis_access($op, $node, $account) {
+function chado_analysis_node_access($node, $op, $account) {
 
   if ($op == 'create') {
     if (!user_access('create chado_analysis content', $account)) {
@@ -528,25 +529,25 @@ function chado_analysis_access($op, $node, $account) {
  *
  * @ingroup tripal_analysis
  */
-function tripal_analysis_perm() {
+function tripal_analysis_permission() {
   return array(
     'access chado_analysis content' => array(
       'title' => t('View Analyses'),
       'description' => t('Allow users to view analysis pages.'),
     ),
-    'create chado_analysis content'=> array(
+    'create chado_analysis content' => array(
       'title' => t('Create Analyses'),
       'description' => t('Allow users to create new analysis pages.'),
     ),
-    'delete chado_analysis content'=> array(
+    'delete chado_analysis content' => array(
       'title' => t('Delete Analyses'),
       'description' => t('Allow users to delete analysis pages.'),
     ),
-    'edit chado_analysis content'=> array(
+    'edit chado_analysis content' => array(
       'title' => t('Edit Analyses'),
       'description' => t('Allow users to edit analysis pages.'),
     ),
-    'adminster tripal analysis'=> array(
+    'adminster tripal analysis' => array(
       'title' => t('Administer Analyses'),
       'description' => t('Allow users to administer all analyses.'),
     ),
@@ -589,38 +590,42 @@ function tripal_analysis_theme() {
 /**
  *
  *
- * @ingroup tripal_feature
+ * @ingroup tripal_analysis
  */
-function tripal_analysis_block($op = 'list', $delta = 0, $edit=array()) {
-  switch ($op) {
-    case 'list':
-      $blocks['base']['info'] = t('Tripal Analysis Details');
-      $blocks['base']['cache'] = BLOCK_NO_CACHE;
-      
-      $blocks['featureblast']['info'] = t('Tripal Feature Analyses');
-      $blocks['featureblast']['cache'] = BLOCK_NO_CACHE;
-      
-      return $blocks;
-
-  case 'view':
-    if (user_access('access chado_analysis content') and arg(0) == 'node' and is_numeric(arg(1))) {
-      $nid = arg(1);
-      $node = node_load($nid);
-
-      $block = array();
-      switch ($delta) {       
-        case 'base':
-          $block['subject'] = t('Analysis Details');
-          $block['content'] = theme('tripal_analysis_base', $node);
-          break;
-        case 'tripal_feature_analyses':
-          $block['subject'] = t('Feature Analyses');
-          $block['content'] = theme('tripal_feature_analyses', $node);
-          break;
-        default :
-      }
-      return $block;
+function tripal_analysis_block_info() {
+  $blocks['base']['info'] = t('Tripal Analysis Details');
+  $blocks['base']['cache'] = BLOCK_NO_CACHE;
+  
+  $blocks['featureblast']['info'] = t('Tripal Feature Analyses');
+  $blocks['featureblast']['cache'] = BLOCK_NO_CACHE;
+  
+  return $blocks;
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_block_view($delta = '') {
+
+  if (user_access('access chado_analysis content') and arg(0) == 'node' and is_numeric(arg(1))) {
+    $nid = arg(1);
+    $node = node_load($nid);
+
+    $block = array();
+    switch ($delta) {       
+      case 'base':
+        $block['subject'] = t('Analysis Details');
+        $block['content'] = theme('tripal_analysis_base', $node);
+        break;
+      case 'tripal_feature_analyses':
+        $block['subject'] = t('Feature Analyses');
+        $block['content'] = theme('tripal_feature_analyses', $node);
+        break;
+      default :
     }
+    return $block;
   }
 }
 
@@ -628,7 +633,7 @@ function tripal_analysis_block($op = 'list', $delta = 0, $edit=array()) {
  * 
  * Implements hook_node_view()
  */
-function tripal_analysis_node_view ($node, $view_mode, $langcode) {
+function tripal_analysis_node_view($node, $view_mode, $langcode) {
   if ($node->type == 'chado_feature') {
     if ($view_mode == 'search_index') {
        // return results for searching

+ 2 - 2
tripal_core/api/tripal_core_ahah.api.inc

@@ -48,10 +48,10 @@ function tripal_core_ahah_init_form() {
  *
  * @ingroup tripal_ahah_api
  */
-function tripal_core_ahah_prepare_form() {
+function tripal_core_ahah_prepare_form(&$form_state = array()) {
   
   // Retrieve the form from the cache
-  $form_state = array('storage' => NULL);
+  $form_state['storage'] = NULL;
   $form_build_id = filter_xss($_POST['form_build_id']);
   if (!$form_build_id) {
     return FALSE;

+ 25 - 25
tripal_core/api/tripal_core_chado.api.inc

@@ -616,7 +616,7 @@ function tripal_core_chado_delete($table, $match, $options = NULL) {
       $sql .= "$field IN (";
       $index = 0;
       foreach ($value as $v) {
-      	$sql .= ":$field" . $index . ", ";
+        $sql .= ":$field" . $index . ", ";
         $args[":$field" . $index] = $v;
         $index++;
       }
@@ -970,7 +970,7 @@ function tripal_core_chado_select($table, $columns, $values, $options = NULL) {
         $sql .= "$field IN (";
         $index = 0;
         foreach ($value as $v) {
-        	$sql .= ":$field" . $index . ', ';
+          $sql .= ":$field" . $index . ', ';
           $args[":$field" . $index] = $v;
           $index++;
         }
@@ -993,8 +993,8 @@ function tripal_core_chado_select($table, $columns, $values, $options = NULL) {
           $args[":$field"] = $value[0];
         }
         else {
-	        $sql .= "$field $operator :$field AND ";
-	        $args[":$field"] = $value[0];        	
+          $sql .= "$field $operator :$field AND ";
+          $args[":$field"] = $value[0];          
         }
       }
     } // end foreach item in where clause
@@ -1441,7 +1441,7 @@ function tripal_core_generate_chado_var($table, $values, $base_options = array()
   // convert the results into an array
   $results_arr = array(); 
   foreach ($results as $record) {
-  	$results_arr[] = $record;
+    $results_arr[] = $record;
   }
   // check only one result returned
   if (!$return_array) {
@@ -1664,7 +1664,7 @@ function tripal_core_expand_chado_vars($object, $type, $to_expand, $table_option
         // if we did not expand this table we should return a message that the foreign table
         // could not be expanded
         if (!$did_expansion) {
-          watchdog('tripal_core', 'tripal_core_expand_chado_vars: Could not expand table, %table. It is ',
+          watchdog('tripal_core', 'tripal_core_expand_chado_vars: Could not expand table, %table. It is ' .
             'not in a foreign key relationship with the base object nor with any other expanded table. ' .
             'Check the table definition to ensure that a proper foreign key relationship is present.',
             array('%table' => $foreign_table), WATCHDOG_ERROR);
@@ -1900,13 +1900,13 @@ function chado_query_range($query, $args, $from, $count) {
  * @ingroup tripal_chado_api
  */
 function chado_query($sql, $args = array()) {
-	
+  
   $is_local = tripal_core_is_chado_local();
     
   // if Chado is local to the database then prefix the Chado table
   // names with 'chado'.
   if ($is_local) {
-  	$sql = preg_replace('/\n/', '', $sql);  // remove carriage returns
+    $sql = preg_replace('/\n/', '', $sql);  // remove carriage returns
     $sql = preg_replace('/\{(.*?)\}/', 'chado.$1', $sql);
     $results = db_query($sql, $args);
   }
@@ -1935,10 +1935,10 @@ function chado_query($sql, $args = array()) {
  * @ingroup tripal_chado_api
  */
 function chado_get_id_for_node($table, $nid) {
-	$sql = "SELECT " . $table . "_id as id FROM {chado_$table} WHERE nid = :nid";
-	$result = db_query($sql, array(':nid' => $nid))->fetchObject();
-	
-	return $result->id;
+  $sql = "SELECT " . $table . "_id as id FROM {chado_$table} WHERE nid = :nid";
+  $result = db_query($sql, array(':nid' => $nid))->fetchObject();
+  
+  return $result->id;
 }
 
 /**
@@ -1952,7 +1952,7 @@ function chado_get_id_for_node($table, $nid) {
  *  @ingroup tripal_chado_api
  */
 function chado_get_node_id($table, $id) {
-	$sql = "SELECT nid FROM {chado_$table} WHERE " . $table . "_id = :" . $table . "_id";
+  $sql = "SELECT nid FROM {chado_$table} WHERE " . $table . "_id = :" . $table . "_id";
   $result = db_query($sql, array(":" . $table . "_id" => $id))->fetchObject();
   return $results->nid;
 }
@@ -2384,9 +2384,9 @@ function tripal_get_chado_custom_schema($table) {
  *   TRUE/FALSE depending upon whether it exists
  */
 function chado_table_exists($table) {
-	global $databases;
-	
-	$default_db = $databases['default']['default']['database'];
+  global $databases;
+  
+  $default_db = $databases['default']['default']['database'];
 
   $sql = "
     SELECT 1
@@ -2399,7 +2399,7 @@ function chado_table_exists($table) {
   $results = db_query($sql, array(':table_name' => $table));
   $exists = $results->fetchObject();
   if (!$exists) {
-  	return FALSE;
+    return FALSE;
   }
   return TRUE;
 }
@@ -2527,10 +2527,10 @@ function tripal_core_set_chado_version() {
 
   // check that Chado is installed if not return 'uninstalled as the version'
   $chado_exists = tripal_core_chado_schema_exists();
-  if (!$chado_exists) {  	
+  if (!$chado_exists) {    
     // if it's not in the drupal database check to see if it's specified in the $db_url
     // in the settings.php    
-    if (!array_key_exists('chado', $databases)) {    	
+    if (!array_key_exists('chado', $databases)) {      
       // if it's not in the drupal database or specified in the $db_url then
       // return uninstalled as the version
       return 'not installed';
@@ -2540,8 +2540,8 @@ function tripal_core_set_chado_version() {
     tripal_db_set_active($previous_db);
   }
   else {
-  	$is_local = 1;
-  	$prop_exists = db_table_exists('chado.chadoprop');
+    $is_local = 1;
+    $prop_exists = db_table_exists('chado.chadoprop');
   }
   
   // if the table doesn't exist then we don't know what version but we know
@@ -2558,9 +2558,9 @@ function tripal_core_set_chado_version() {
     WHERE CV.name = 'chado_properties' and CVT.name = 'version'
   ";
   if (!$is_local) {
-	  $previous_db = tripal_db_set_active('chado');
-	  $results = db_query($sql);
-	  tripal_db_set_active($previous_db);
+    $previous_db = tripal_db_set_active('chado');
+    $results = db_query($sql);
+    tripal_db_set_active($previous_db);
   }
   else {
     $results = chado_query($sql);
@@ -2852,7 +2852,7 @@ function tripal_db_set_active($dbname  = 'default') {
   
   $chado_exists = variable_get('chado_schema_exists', FALSE);
   if ($chado_exists) {
-    if($dbname == 'chado') {
+    if ($dbname == 'chado') {
       db_query('set search_path to chado');
       return 'default';
     }

+ 18 - 18
tripal_core/api/tripal_core_custom_tables.api.inc

@@ -103,10 +103,10 @@ function tripal_core_edit_custom_table($table_id, $table_name, $schema, $skip_cr
  * @ingroup tripal_custom_tables_api
  */
 function tripal_core_create_custom_table($table, $schema, $skip_creation = 1) {
-	global $databases;
-	$created = 0;
-	$recreated = 0;
-	
+  global $databases;
+  $created = 0;
+  $recreated = 0;
+  
   // see if the table entry already exists in the tripal_custom_tables table.
   $sql = "SELECT * FROM {tripal_custom_tables} WHERE table_name = :table_name";
   $results = db_query($sql, array(':table_name' => $table));
@@ -117,15 +117,15 @@ function tripal_core_create_custom_table($table, $schema, $skip_creation = 1) {
   
   // if the table does not exist then create it
   if (!$exists) { 
-  	$previous_db = tripal_db_set_active('chado');  // use chado database   
-    try {    	
+    $previous_db = tripal_db_set_active('chado');  // use chado database   
+    try {      
       $ret = db_create_table($table, $schema);
       tripal_db_set_active($previous_db);  // now use drupal database
       $created = 1;
     }
     catch (Exception $e) {
-    	tripal_db_set_active($previous_db);  // now use drupal database
-    	$error = $e->getMessage();
+      tripal_db_set_active($previous_db);  // now use drupal database
+      $error = $e->getMessage();
       watchdog('tripal_core', "Error adding custom table: @message",
         array('@message' => $error), WATCHDOG_ERROR);
       drupal_set_message("Could not add custom table. $error.", "error");
@@ -138,12 +138,12 @@ function tripal_core_create_custom_table($table, $schema, $skip_creation = 1) {
   if ($exists and is_object($centry) and !$skip_creation) {    
     
     // drop the table we'll recreate it with the new schema    
-    try {	    
-	    chado_query('DROP TABLE {' . $table . '}');
-	    $previous_db = tripal_db_set_active('chado');  // use chado database
-	    db_create_table($table, $schema);
-	    tripal_db_set_active($previous_db);  // now use drupal database    
-	    $recreated = 1;
+    try {      
+      chado_query('DROP TABLE {' . $table . '}');
+      $previous_db = tripal_db_set_active('chado');  // use chado database
+      db_create_table($table, $schema);
+      tripal_db_set_active($previous_db);  // now use drupal database    
+      $recreated = 1;
     }
     catch (Exception $e) {
       tripal_db_set_active('default');  // now use drupal database
@@ -193,11 +193,11 @@ function tripal_core_create_custom_table($table, $schema, $skip_creation = 1) {
           chado_query($sql);
         }
         catch (Exception $e) {
-        	$error = $e->getMessage();
+          $error = $e->getMessage();
           watchdog('tripal_core', "Error, could not add foreign key contraint to custom table: %error",
             array('%error' => $error), WATCHDOG_ERROR);
           drupal_set_message("Could not add foreign key contraint to table: $error", 'error');      
-          return FALSE;        	
+          return FALSE;          
         }
       }
     }
@@ -207,10 +207,10 @@ function tripal_core_create_custom_table($table, $schema, $skip_creation = 1) {
     drupal_set_message("Custom table created successfully.", 'status');
   }
   elseif ($recreated) {
-  	drupal_set_message("Custom table re-created successfully.", 'status');
+    drupal_set_message("Custom table re-created successfully.", 'status');
   }
   else {
-  	drupal_set_message("Custom table already exists. Table structure not changed, but definition array has been saved.", 'status');
+    drupal_set_message("Custom table already exists. Table structure not changed, but definition array has been saved.", 'status');
   }
   return TRUE;
 }

+ 1 - 1
tripal_core/api/tripal_core_files.api.inc

@@ -66,7 +66,7 @@ function tripal_create_moddir($module_name) {
 function tripal_create_mod_subdir($module_name, $path) {
 
   // make sure the module data directory exists
-	tripal_create_moddir($module_name);
+  tripal_create_moddir($module_name);
   
   // now make sure the sub dir exists
   $sub_dir = tripal_file_directory_path() . $module_name . $path;

+ 3 - 3
tripal_core/api/tripal_core_mviews.api.inc

@@ -270,8 +270,8 @@ function tripal_edit_mview($mview_id, $name, $modulename, $mv_table, $mv_specs,
  */
 function tripal_mviews_get_mview_id($view_name) {
   if (db_table_exists('tripal_mviews')) {
-  	$sql = "SELECT * FROM {tripal_mviews} WHERE name = :name";
-  	$results = db_query($sql, array(':name' => $view_name));
+    $sql = "SELECT * FROM {tripal_mviews} WHERE name = :name";
+    $results = db_query($sql, array(':name' => $view_name));
     $mview = $results->fetchObject();
     if ($mview) {
       return $mview->mview_id;
@@ -307,7 +307,7 @@ function tripal_mviews_action($op, $mview_id, $redirect = FALSE) {
 
   // add a job or perform the action based on the given operation
   if ($op == 'update') {
-  	$args = array("$mview_id");
+    $args = array("$mview_id");
     tripal_add_job("Populate materialized view '$mview->name'", 'tripal_core',
        'tripal_update_mview', $args, $user->uid);
   }

+ 3 - 3
tripal_core/includes/custom_tables.inc

@@ -12,9 +12,9 @@
  * 
  */
 function tripal_custom_table_new_page() {
-	$output = drupal_render(drupal_get_form('tripal_custom_tables_form'));
-	return $output;   	
-	
+  $output = drupal_render(drupal_get_form('tripal_custom_tables_form'));
+  return $output;     
+  
 }
 /**
  * A template function which returns markup to display details for the custom table

+ 2 - 1
tripal_core/includes/form_elements.inc

@@ -117,7 +117,8 @@ function file_upload_combo_validate($element, &$form) {
       foreach ($vals as $i => $value) {
         $values[] = trim($value);
       }
-    }       
+    }
+    fclose($fh);       
   }
   
   // add a new 'items_array' element that contains the array of 

+ 5 - 5
tripal_core/includes/jobs.inc

@@ -229,10 +229,10 @@ function tripal_jobs_view($job_id) {
   } 
   
   // build the links
-  $links  = l('Return to jobs list', "admin/tripal/tripal_jobs/") . ' | ';
-  $links .= l('Re-run this job', "admin/tripal/tripal_jobs/rerun/" . $job->job_id)  . ' | ';
+  $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)  . ' | ';
+    $links .= l('Cancel this job', "admin/tripal/tripal_jobs/cancel/" . $job->job_id) . ' | ';
   }
   
   // make our start and end times more legible
@@ -245,7 +245,7 @@ function tripal_jobs_view($job_id) {
   
   // construct the table rows
   $rows[] = array('Job Description', $job->job_name);
-  $rows[] = array('Submitting Module',$job->modulename);
+  $rows[] = array('Submitting Module', $job->modulename);
   $rows[] = array('Callback function', $job->callback);
   $rows[] = array('Arguments', $arguments);
   $rows[] = array('Progress', $job->progress . "%");
@@ -266,7 +266,7 @@ function tripal_jobs_view($job_id) {
     'caption' => '',
     'colgroups' => array(), 
     'empty' => '', 
-  );  	
+  );    
   
   $output = '<p>' . substr($links, 0, -2) . '</p>'; // remove trailing |
   $output .= theme_table($table);

+ 65 - 65
tripal_core/includes/mviews.inc

@@ -64,10 +64,10 @@ function tripal_mview_report($mview_id) {
     $rows[] = array('Special Indexed Fields', $mview->special_index);
   }
   if ($mview->mv_schema) {
-    $rows[] = array('Drupal Schema API Definition', "<textarea rows=\"20\" cols=\"120\" style=\"font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;\">"  . $mview->mv_schema . "</textarea>");
+    $rows[] = array('Drupal Schema API Definition', "<textarea rows=\"20\" cols=\"120\" style=\"font-family:Consolas,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New, monospace;\">" . $mview->mv_schema . "</textarea>");
   }
   
-  $header = array('Detail','Value');
+  $header = array('Detail', 'Value');
   $table = array(
     'header' => $header, 
     'rows' => $rows, 
@@ -194,14 +194,14 @@ function tripal_mviews_form($form, &$form_state = NULL, $mview_id = NULL) {
     // form_state then let's use that, otherwise, we'll pull
     // the values from the database
     if (array_key_exists('values', $form_state)) {
-	    $default_name = $form_state['values']['name'];
-	    $default_mv_table = $form_state['values']['mv_table'];
-	    $default_mv_specs = $form_state['values']['mv_specs'];
-	    $default_indexed = $form_state['values']['indexed'];
-	    $default_mvquery = $form_state['values']['mvquery'];
-	    $default_special_index = $form_state['values']['special_index'];
-	    $default_comment = $form_state['values']['comment'];
-	    $default_modulename = $form_state['values']['modulename'];
+      $default_name = $form_state['values']['name'];
+      $default_mv_table = $form_state['values']['mv_table'];
+      $default_mv_specs = $form_state['values']['mv_specs'];
+      $default_indexed = $form_state['values']['indexed'];
+      $default_mvquery = $form_state['values']['mvquery'];
+      $default_special_index = $form_state['values']['special_index'];
+      $default_comment = $form_state['values']['comment'];
+      $default_modulename = $form_state['values']['modulename'];
     }
 
     if (!$default_name) {
@@ -233,7 +233,7 @@ function tripal_mviews_form($form, &$form_state = NULL, $mview_id = NULL) {
     }
     
     if ($mview->mv_specs) {
-    	$is_legacy = 1;
+      $is_legacy = 1;
     }
 
     // the mv_table column of the tripal_mviews table always has the table
@@ -320,51 +320,51 @@ function tripal_mviews_form($form, &$form_state = NULL, $mview_id = NULL) {
 
   // only let folks edit legacy MViews, not create new ones 
   if ($is_legacy) {
-	  // add a fieldset for the Original Table Description fields
-	  $form['traditional'] = array(
-	    '#type' => 'fieldset',
-	    '#title' => 'Legacy MViews Setup',
-	    '#description' => t('Traditionally MViews were created by specifying PostgreSQL style ' .
-	                       'column types.  This method can be used but is deprecated in favor of the ' .
-	                       'newer Drupal schema API method provided above. In rare cases where the Drupal Schema API ' .
-	                       'does not support a desired data type the Legacy Mviews should be used'),
-	    '#collapsible' => 1,
-	    '#collapsed' => $traditional_collapsed,
-	  );
-	
-	  $form['traditional']['mv_table']= array(
-	    '#type'          => 'textfield',
-	    '#title'         => t('Table Name'),
-	    '#description'   => t('Please enter the table name that this view will generate in the database.  You can use the schema and table name for querying the view'),
-	    '#required'      => FALSE,
-	    '#default_value' => $default_mv_table,
-	  );
-	
-	  $form['traditional']['mv_specs']= array(
-	    '#type'          => 'textarea',
-	    '#title'         => t('Table Definition'),
-	    '#description'   => t('Please enter the field definitions for this view. Each field should be separated by a comma or enter each field definition on each line.'),
-	    '#required'      => FALSE,
-	    '#default_value' => $default_mv_specs,
-	  );
-	
-	  $form['traditional']['indexed']= array(
-	    '#type'          => 'textarea',
-	    '#title'         => t('Indexed Fields'),
-	    '#description'   => t('Please enter the field names (as provided in the table definition above) that will be indexed for this view.  Separate by a comma or enter each field on a new line.'),
-	    '#required'      => FALSE,
-	    '#default_value' => $default_indexed,
-	  );
-	
-	  /**
-	  $form['traditional']['special_index']= array(
-	    '#type'          => 'textarea',
-	    '#title'         => t('View Name'),
-	    '#description'   => t('Please enter the name for this materialized view.'),
-	    '#required'      => TRUE,
-	    '#default_value' => $default_special_index,
-	  );
-	  */
+    // add a fieldset for the Original Table Description fields
+    $form['traditional'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Legacy MViews Setup',
+      '#description' => t('Traditionally MViews were created by specifying PostgreSQL style ' .
+                         'column types.  This method can be used but is deprecated in favor of the ' .
+                         'newer Drupal schema API method provided above. In rare cases where the Drupal Schema API ' .
+                         'does not support a desired data type the Legacy Mviews should be used'),
+      '#collapsible' => 1,
+      '#collapsed' => $traditional_collapsed,
+    );
+  
+    $form['traditional']['mv_table']= array(
+      '#type'          => 'textfield',
+      '#title'         => t('Table Name'),
+      '#description'   => t('Please enter the table name that this view will generate in the database.  You can use the schema and table name for querying the view'),
+      '#required'      => FALSE,
+      '#default_value' => $default_mv_table,
+    );
+  
+    $form['traditional']['mv_specs']= array(
+      '#type'          => 'textarea',
+      '#title'         => t('Table Definition'),
+      '#description'   => t('Please enter the field definitions for this view. Each field should be separated by a comma or enter each field definition on each line.'),
+      '#required'      => FALSE,
+      '#default_value' => $default_mv_specs,
+    );
+  
+    $form['traditional']['indexed']= array(
+      '#type'          => 'textarea',
+      '#title'         => t('Indexed Fields'),
+      '#description'   => t('Please enter the field names (as provided in the table definition above) that will be indexed for this view.  Separate by a comma or enter each field on a new line.'),
+      '#required'      => FALSE,
+      '#default_value' => $default_indexed,
+    );
+  
+    /**
+    $form['traditional']['special_index']= array(
+      '#type'          => 'textarea',
+      '#title'         => t('View Name'),
+      '#description'   => t('Please enter the name for this materialized view.'),
+      '#required'      => TRUE,
+      '#default_value' => $default_special_index,
+    );
+    */
   }
 
   $form['mvquery']= array(
@@ -409,13 +409,13 @@ function tripal_mviews_form_validate($form, &$form_state) {
   $is_legacy = $form_state['values']['is_legacy'];
   $query = $form_state['values']['mvquery'];
   if ($is_legacy) {
-	  $mv_table = $form_state['values']['mv_table'];
-	  $mv_specs = $form_state['values']['mv_specs'];
-	  $indexed = $form_state['values']['indexed'];	  
-	  $special_index = '';//$form_state['values']['special_index'];
+    $mv_table = $form_state['values']['mv_table'];
+    $mv_specs = $form_state['values']['mv_specs'];
+    $indexed = $form_state['values']['indexed'];    
+    $special_index = '';//$form_state['values']['special_index'];
   }
   else {
-  	$mv_table = '';
+    $mv_table = '';
     $mv_specs = '';
     $indexed = '';
     $special_index = '';
@@ -471,10 +471,10 @@ function tripal_mviews_form_submit($form, &$form_state) {
   $is_legacy = $form_state['values']['is_legacy'];
   $query = $form_state['values']['mvquery'];
   if ($is_legacy) {
-	  $mv_table = $form_state['values']['mv_table'];
-	  $mv_specs = $form_state['values']['mv_specs'];
-	  $indexed = $form_state['values']['indexed'];	  
-	  $special_index = '';//$form_state['values']['special_index'];
+    $mv_table = $form_state['values']['mv_table'];
+    $mv_specs = $form_state['values']['mv_specs'];
+    $indexed = $form_state['values']['indexed'];    
+    $special_index = '';//$form_state['values']['special_index'];
   }
   else {
     $mv_table = '';

+ 4 - 4
tripal_cv/api/tripal_cv.api.inc

@@ -265,7 +265,7 @@ function tripal_cv_get_cvterm_by_name($name, $cv_id = NULL, $cv_name = 'tripal')
     return FALSE;
   }
   if (count($r) == 0) {
-  	return FALSE;
+    return FALSE;
   }
   return $r[0];
 }
@@ -307,10 +307,10 @@ function tripal_cv_get_cvterm_by_synonym($synonym, $cv_id = NULL, $cv_name = 'tr
   $synonym = tripal_core_chado_select('cvtermsynonym', array('cvterm_id'), $values, $options);
   
   // if the synonym doens't exist or more than one record is returned then return false
-  if(count($synonym) == 0) {
+  if (count($synonym) == 0) {
     return FALSE;
   }
-  if(count($synonym) > 1) {
+  if (count($synonym) > 1) {
     return FALSE;
   }
   
@@ -381,7 +381,7 @@ function tripal_cv_update_cvtermpath($cvid, $job_id = NULL) {
     tripal_db_set_active($previous); 
   }
   catch (Exception $e) {
-    tripal_db_set_active($previous);	
+    tripal_db_set_active($previous);  
     $error = $e->getMessage();
     watchdog('tripal_cv', "Could not fill cvtermpath table: @error", array('@error' => $error), WATCHDOG_ERROR);
     return FALSE;

+ 96 - 96
tripal_cv/includes/cvterm_form.inc

@@ -7,26 +7,26 @@
  */
 function tripal_cv_cvterm_edit_form($form, &$form_state) {
 
-	$step = 0;
+  $step = 0;
   if (empty($form_state['storage']['step'])) {
     $form_state['storage']['step'] = 0;
   }
   else {
-  	$step = $form_state['storage']['step'];
+    $step = $form_state['storage']['step'];
   }
 
   $cv_id = 0;
   if ($step == 1) {
-  	$cv_id = $form_state['storage']['cv_id'];
-  	$cvterm_name = $form_state['storage']['name'];
-  	$cvterm_id = $form_state['storage']['cvterm_id'];
-  }	
+    $cv_id = $form_state['storage']['cv_id'];
+    $cvterm_name = $form_state['storage']['name'];
+    $cvterm_id = $form_state['storage']['cvterm_id'];
+  }  
   // get the cv if form was submitted via AJAX
   $cvterm = '';
   if (array_key_exists('values', $form_state)) {
     $cv_id = $form_state['values']['cv_id'];   
     if (array_key_exists('cvterm', $form_state['values'])) {
-    	$cvterm = $form_state['values']['cvterm'];
+      $cvterm = $form_state['values']['cvterm'];
     }    
   }   
     
@@ -54,22 +54,22 @@ function tripal_cv_cvterm_edit_form($form, &$form_state) {
   );
     
   if ($cv_id and $step == 0) {
-  	
-  	$form['name']= array(
+    
+    $form['name']= array(
       '#type'          => 'textfield',
       '#title'         => t("Term Name"),
       '#default_value' => $cvterm,
       '#required'      => TRUE,
       '#autocomplete_path' => "admin/tripal/tripal_cv/cvterm/auto_name/$cv_id",
-  	  '#description'   => t('Enter the term to edit.')
+      '#description'   => t('Enter the term to edit.')
     );
     $form['continue']= array(
       '#type'          => 'submit',
       '#value'         => 'continue',
     );
   }
-  elseif ($step == 1) {  	
-  	   
+  elseif ($step == 1) {    
+       
     tripal_cv_add_cvterm_form_fields($form, $form_state, $cv_id, $cvterm_name);
     
     // when editing there are certain fields the user should not change for a term
@@ -100,7 +100,7 @@ function tripal_cv_cvterm_edit_form($form, &$form_state) {
   } 
   
   if ($step == 0) {
-  	// if we don't have a cv_id then this is the first time the form has
+    // if we don't have a cv_id then this is the first time the form has
     // benn loaded and we need to create the div where ajax replacement elements get stored
     $form['div_replace'] = array(
       '#type' => 'item',
@@ -116,12 +116,12 @@ function tripal_cv_cvterm_edit_form($form, &$form_state) {
  * @param $form_state
  */
 function tripal_cv_cvterm_add_form($form, &$form_state) {
-	$cv_id = 0;
-	if (array_key_exists('values', $form_state)) {
-	  $cv_id = $form_state['values']['cv_id'];	 
-	}
-	
-	// get a list of CVs
+  $cv_id = 0;
+  if (array_key_exists('values', $form_state)) {
+    $cv_id = $form_state['values']['cv_id'];   
+  }
+  
+  // get a list of CVs
   $cvs = array();
   $sql = "SELECT * FROM {cv} WHERE NOT name = 'tripal' ORDER BY name ";
   $results = chado_query($sql);
@@ -248,23 +248,23 @@ function tripal_cv_add_cvterm_form_fields(&$form, $form_state, $cv_id = 0, $cvte
  * @param $form_state
  */
 function tripal_cv_cvterm_edit_form_validate($form, &$form_state) {
-	$cv_id = array_key_exists('cv_id', $form_state['values']) ? $form_state['values']['cv_id'] : '';
+  $cv_id = array_key_exists('cv_id', $form_state['values']) ? $form_state['values']['cv_id'] : '';
   $db_id = array_key_exists('db_id', $form_state['values']) ? $form_state['values']['db_id'] : '';
   $name = array_key_exists('name', $form_state['values']) ? $form_state['values']['name'] : '';
   $cvterm_id = array_key_exists('cvterm_id', $form_state['values']) ? $form_state['values']['cvterm_id'] : '';
   $accession = array_key_exists('accession', $form_state['values']) ? $form_state['values']['accession'] : '';
   
   $step = $form_state['storage']['step'];
-	 
+   
   // make sure the cv term name is unique for this vocabulary
   if ($step == 1) {
-	  $values = array('name' => $name, 'cv_id' => $cv_id);
-	  $results = tripal_core_chado_select('cvterm', array('cvterm_id'), $values);
-	  foreach ($results as $r) {   
-	    if ($r->cvterm_id != $cvterm_id) {
-	      form_set_error('name', 'The term name must be unique for this vocabulary. Another term with this name already exists.');
-	    }
-	  }
+    $values = array('name' => $name, 'cv_id' => $cv_id);
+    $results = tripal_core_chado_select('cvterm', array('cvterm_id'), $values);
+    foreach ($results as $r) {   
+      if ($r->cvterm_id != $cvterm_id) {
+        form_set_error('name', 'The term name must be unique for this vocabulary. Another term with this name already exists.');
+      }
+    }
   }
 }
 /**
@@ -273,24 +273,24 @@ function tripal_cv_cvterm_edit_form_validate($form, &$form_state) {
  * @param $form_state
  */
 function tripal_cv_cvterm_add_form_validate($form, &$form_state) {
-	$cv_id = array_key_exists('cv_id', $form_state['values']) ? $form_state['values']['cv_id'] : '';
+  $cv_id = array_key_exists('cv_id', $form_state['values']) ? $form_state['values']['cv_id'] : '';
   $db_id = array_key_exists('db_id', $form_state['values']) ? $form_state['values']['db_id'] : '';
   $name = array_key_exists('name', $form_state['values']) ? $form_state['values']['name'] : '';
   $accession = array_key_exists('accession', $form_state['values']) ? $form_state['values']['accession'] : '';
-		
+    
   $values = array('cv_id' => $cv_id);
   $results = tripal_core_chado_select('cv', array('name'), $values);
-  if (!$results or count($results) == 0){
+  if (!$results or count($results) == 0) {
     form_set_error('cv_id', 'The controlled vocabulary does not exist');
   }
 
   // make sure the DB exists
   $values = array('db_id' => $db_id);
   $results = tripal_core_chado_select('db', array('name'), $values);
-  if (!$results or count($results) == 0){
+  if (!$results or count($results) == 0) {
     form_set_error('db_id', 'The database name does not exist');
   }
-	
+  
   // make sure the cv term name is unique for this vocabulary
   $values = array('name' => $name, 'cv_id' => $cv_id);
   $results = tripal_core_chado_select('cvterm', array('cvterm_id'), $values);   
@@ -314,8 +314,8 @@ function tripal_cv_cvterm_add_form_validate($form, &$form_state) {
  * @ingroup tripal_cv
  */
 function tripal_cv_cvterm_edit_form_submit($form, &$form_state) {
-	
-	$cv_id = array_key_exists('cv_id', $form_state['values']) ? $form_state['values']['cv_id'] : '';
+  
+  $cv_id = array_key_exists('cv_id', $form_state['values']) ? $form_state['values']['cv_id'] : '';
   $name = array_key_exists('name', $form_state['values']) ? $form_state['values']['name'] : '';
   $definition = array_key_exists('definition', $form_state['values']) ? $form_state['values']['definition'] : '';
   $is_relationship = array_key_exists('is_relationship', $form_state['values']) ? $form_state['values']['is_relationship'] : '';
@@ -328,15 +328,15 @@ function tripal_cv_cvterm_edit_form_submit($form, &$form_state) {
   $op    = array_key_exists('op', $form_state['values'])         ? trim($form_state['values']['op']) : '';
   
   
-	$step = $form_state['storage']['step'];
+  $step = $form_state['storage']['step'];
 
-	switch ($step) {
-		case 0:  // a cvterm name has been selected
-			$cv_id = array_key_exists('cv_id', $form_state['values']) ? trim($form_state['values']['cv_id']) : '';
-			$name  = array_key_exists('name', $form_state['values'])  ? trim($form_state['values']['name'])  : '';
-			
-			// get the original cvterm_id
-	    $values = array('name' => $name, 'cv_id' => $cv_id);
+  switch ($step) {
+    case 0:  // a cvterm name has been selected
+      $cv_id = array_key_exists('cv_id', $form_state['values']) ? trim($form_state['values']['cv_id']) : '';
+      $name  = array_key_exists('name', $form_state['values'])  ? trim($form_state['values']['name'])  : '';
+      
+      // get the original cvterm_id
+      $values = array('name' => $name, 'cv_id' => $cv_id);
       $results = tripal_core_chado_select('cvterm', array('cvterm_id'), $values);  
       $cvterm = $results[0]; 
     
@@ -345,50 +345,50 @@ function tripal_cv_cvterm_edit_form_submit($form, &$form_state) {
       $form_state['storage']['step'] = 1;
       $form_state['storage']['cvterm_id'] = $cvterm->cvterm_id;
       $form_state['rebuild'] = TRUE;
-			break;
-			
-		case 1:  // update/delete button has been clicked					
-		  
-			if ($op == 'Update') {
-			  // get the cv
-			  $values = array('cv_id' => $cv_id);
-			  $results = tripal_core_chado_select('cv', array('name'), $values);
-			  $cv = $results[0];
-			  
-			  // get the db
-			  $values = array('db_id' => $db_id);
-			  $results = tripal_core_chado_select('db', array('name'), $values);
-			  $db = $results[0];
-			  
-			  // now add the term
-			  $term = array(    
-			    'name' => $name,
-			    'namespace' => $cv->name,
-			    'id' => $accession,
-			    'def' => $definition,
-			    'is_obsolete' => $is_obsolete,
-			  );  
-			  
-			  $cvterm = tripal_cv_add_cvterm($term, $cv->name, $is_relationship, TRUE, $db->name);
-			  if ($cvterm) {
-			    drupal_set_message('Term updated successfully.');
-			  } 
-			  else {
-			    drupal_set_message('Could not add term. Check Drupal recent logs for error messages.', 'error');  
-			  }
-			}
-			if ($op == 'Delete') {
-			  $values = array('cvterm_id' => $cvterm_id);
-			  $success = tripal_core_chado_delete('cvterm', $values);
-			  if ($success) {
-			    drupal_set_message('Term deleted successfully.');	
-			  } 
-			  else {
-			    drupal_set_message('Could not delete term term. Check Drupal recent logs for error messages.', 'error');	
-			  }			     	
-			}
-			break;
-	} 		 
+      break;
+      
+    case 1:  // update/delete button has been clicked          
+      
+      if ($op == 'Update') {
+        // get the cv
+        $values = array('cv_id' => $cv_id);
+        $results = tripal_core_chado_select('cv', array('name'), $values);
+        $cv = $results[0];
+        
+        // get the db
+        $values = array('db_id' => $db_id);
+        $results = tripal_core_chado_select('db', array('name'), $values);
+        $db = $results[0];
+        
+        // now add the term
+        $term = array(    
+          'name' => $name,
+          'namespace' => $cv->name,
+          'id' => $accession,
+          'def' => $definition,
+          'is_obsolete' => $is_obsolete,
+        );  
+        
+        $cvterm = tripal_cv_add_cvterm($term, $cv->name, $is_relationship, TRUE, $db->name);
+        if ($cvterm) {
+          drupal_set_message('Term updated successfully.');
+        } 
+        else {
+          drupal_set_message('Could not add term. Check Drupal recent logs for error messages.', 'error');  
+        }
+      }
+      if ($op == 'Delete') {
+        $values = array('cvterm_id' => $cvterm_id);
+        $success = tripal_core_chado_delete('cvterm', $values);
+        if ($success) {
+          drupal_set_message('Term deleted successfully.');  
+        } 
+        else {
+          drupal_set_message('Could not delete term term. Check Drupal recent logs for error messages.', 'error');  
+        }             
+      }
+      break;
+  }      
 }
 
 /**
@@ -397,15 +397,15 @@ function tripal_cv_cvterm_edit_form_submit($form, &$form_state) {
  * @param unknown_type $form_state
  */
 function tripal_cv_cvterm_add_form_submit($form, &$form_state) {
-	$cv_id = array_key_exists('cv_id', $form_state['values']) ? $form_state['values']['cv_id'] : '';
-	$name = array_key_exists('name', $form_state['values']) ? $form_state['values']['name'] : '';
-	$definition = array_key_exists('definition', $form_state['values']) ? $form_state['values']['definition'] : '';
-	$is_relationship = array_key_exists('is_relationship', $form_state['values']) ? $form_state['values']['is_relationship'] : '';
-	$is_obsolete = array_key_exists('is_obsolete', $form_state['values']) ? $form_state['values']['is_obsolete'] : '';
-	
-	$db_id = array_key_exists('db_id', $form_state['values']) ? $form_state['values']['db_id'] : '';
-	$accession = array_key_exists('accession', $form_state['values']) ? $form_state['values']['accession'] : '';
-	
+  $cv_id = array_key_exists('cv_id', $form_state['values']) ? $form_state['values']['cv_id'] : '';
+  $name = array_key_exists('name', $form_state['values']) ? $form_state['values']['name'] : '';
+  $definition = array_key_exists('definition', $form_state['values']) ? $form_state['values']['definition'] : '';
+  $is_relationship = array_key_exists('is_relationship', $form_state['values']) ? $form_state['values']['is_relationship'] : '';
+  $is_obsolete = array_key_exists('is_obsolete', $form_state['values']) ? $form_state['values']['is_obsolete'] : '';
+  
+  $db_id = array_key_exists('db_id', $form_state['values']) ? $form_state['values']['db_id'] : '';
+  $accession = array_key_exists('accession', $form_state['values']) ? $form_state['values']['accession'] : '';
+  
   // get the database
   $values = array('db_id' => $db_id);
   $results = tripal_core_chado_select('db', array('name'), $values);

+ 6 - 6
tripal_cv/includes/obo_loader.inc

@@ -172,9 +172,9 @@ function tripal_cv_load_obo_v1_2_url($obo_name, $url, $jobid = NULL, $is_new = T
   $url_fh = fopen($url, "r");
   $obo_fh = fopen($temp, "w");
   if (!$url_fh) {
-    tripal_cv_obo_quiterror("Unable to download the remote OBO file at $url. Could a firewall be blocking outgoing connections? ".
-          " if you are unable to download the file you may manually downlod the OBO file and use the web interface to ".
-          " specify the location of the file on your server.");
+    tripal_cv_obo_quiterror("Unable to download the remote OBO file at $url. Could a firewall be blocking outgoing connections? " .
+      " if you are unable to download the file you may manually downlod the OBO file and use the web interface to " .
+      " specify the location of the file on your server.");
 
   }
   while (!feof($url_fh)) {
@@ -284,7 +284,7 @@ function tripal_cv_obo_quiterror($message) {
 /*
  * 
  */
-function tripal_cv_obo_load_typedefs($defaultcv, $newcvs, $default_db, $jobid){
+function tripal_cv_obo_load_typedefs($defaultcv, $newcvs, $default_db, $jobid) {
   $sql = "SELECT * FROM {tripal_obo_temp} WHERE type = 'Typedef' ";
   $typedefs = chado_query($sql);
   
@@ -912,7 +912,7 @@ function tripal_cv_obo_add_cvterm_dbxref($cvterm, $xref) {
       'return_record' => FALSE
     );
     $result = tripal_core_chado_insert('cvterm_dbxref', $values, $ins_options);
-    if (!$result){
+    if (!$result) {
       tripal_cv_obo_quiterror("Cannot add cvterm_dbxref: $xref");
       return FALSE;
     }
@@ -1001,7 +1001,7 @@ function tripal_cv_obo_add_dbxref($db_id, $accession, $version='', $description=
   );
   $options = array('statement_name' => 'sel_dbxref_idac');
   $result = tripal_core_chado_select('dbxref', array('dbxref_id'), $values, $options);
-  if (count($result) == 0){
+  if (count($result) == 0) {
     $ins_values = array(
       'db_id'       => $db_id,
       'accession'   => $accession,

+ 4 - 3
tripal_cv/includes/tripal_cv_admin.inc

@@ -334,7 +334,8 @@ function tripal_cv_cvterm_form(&$form_state, $action = 'add') {
       if ($name) {
         $form['add_cvterm']['name']['#attributes'] = array('readonly' => 'readonly');
         $form['add_cvterm']['name']['#description'] = 'The term name cannot be changed. If the name is incorrect, please create a new term and make this one as obsolete.';
-      } else {
+      } 
+      else {
         $form['add_cvterm']['name']['#autocomplete_path'] = "admin/tripal/tripal_cv/cvterm/auto_name/$cv_id";
         $form['add_cvterm']['name']['#ahah'] = array(
            'path'    => 'admin/tripal/tripal_cv/cvterm/ahah',
@@ -456,7 +457,7 @@ function tripal_cv_cvterm_form_submit($form, &$form_state) {
   // get the database
   $values = array('db_id' => $form_state['values']['db_id']);
   $results = tripal_core_chado_select('db', array('name'), $values);
-  if (!$results or count($results) == 0){
+  if (!$results or count($results) == 0) {
     drupal_set_message(t('Unable to add term.  Cannot find the database.'), 'error');
     return;
   }
@@ -465,7 +466,7 @@ function tripal_cv_cvterm_form_submit($form, &$form_state) {
   // get the cv
   $values = array('cv_id' => $form_state['values']['cv_id']);
   $results = tripal_core_chado_select('cv', array('name'), $values);
-  if (!$results or count($results) == 0){
+  if (!$results or count($results) == 0) {
     drupal_set_message(t('Unable to add term.  Cannot find the vocabulary.'), 'error');
     return;
   }

+ 2 - 2
tripal_db/api/tripal_db.api.inc

@@ -252,11 +252,11 @@ function tripal_db_get_dbxref($select_values) {
 function tripal_db_get_dbxref_by_accession($accession, $db_id=0) {
 
   if (!empty($db_id)) {
-  	$sql = "SELECT * FROM {dbxref} WHERE accession = :accession AND db_id = :db_id";
+    $sql = "SELECT * FROM {dbxref} WHERE accession = :accession AND db_id = :db_id";
     $r = chado_query($sql, array(':accession' => $accession, ':db_id' => $db_id));
   }
   else {
-  	$sql = "SELECT * FROM {dbxref} WHERE accession = :accession";
+    $sql = "SELECT * FROM {dbxref} WHERE accession = :accession";
     $r = chado_query($sql, array(':accession' => $accession));
   }
 

+ 38 - 38
tripal_db/includes/tripal_db.admin.inc

@@ -27,11 +27,11 @@ function tripal_db_select_form() {
  */
 function tripal_db_db_edit_form($form, &$form_state) {
   
-	// get the dbid if form was submitted via AJAX
-	$dbid = 0;
-	if (array_key_exists('values', $form_state)) {
+  // get the dbid if form was submitted via AJAX
+  $dbid = 0;
+  if (array_key_exists('values', $form_state)) {
     $dbid = $form_state['values']['dbid'];
-	}  
+  }  
   
   // get a list of db from chado for user to choose
   $sql = "SELECT * FROM {db} WHERE NOT name = 'tripal' ORDER BY name ";
@@ -62,25 +62,25 @@ function tripal_db_db_edit_form($form, &$form_state) {
   // add in the other fields
   if ($dbid) {
     tripal_db_add_db_form_fields($form, $form_state, $dbid);
-    	  
-	  $form['update'] = array(
-	    '#type'         => 'submit',
-	    '#value'        => t('Update'),
-	  );
-	  $form['delete'] = array(
-	    '#type'         => 'submit',
-	    '#value'        => t('Delete'),
-	    '#attributes'   => array('onclick' => 'if(!confirm("Really Delete?")){return false;}'), 
+        
+    $form['update'] = array(
+      '#type'         => 'submit',
+      '#value'        => t('Update'),
+    );
+    $form['delete'] = array(
+      '#type'         => 'submit',
+      '#value'        => t('Delete'),
+      '#attributes'   => array('onclick' => 'if(!confirm("Really Delete?")){return false;}'), 
     );
   }
   else {
-  	// if we don't have a dbid then this is the first time the form has
-  	// benn loaded and we need to create the div where ajax replacement elements get stored
-  	$form['div_replace'] = array(
-  	  '#type' => 'item',
-  	  '#prefix' => '<div id="db-edit-div">',
-  	  '#suffix' => '</div>',
-  	);
+    // if we don't have a dbid then this is the first time the form has
+    // benn loaded and we need to create the div where ajax replacement elements get stored
+    $form['div_replace'] = array(
+      '#type' => 'item',
+      '#prefix' => '<div id="db-edit-div">',
+      '#suffix' => '</div>',
+    );
   }
   return $form;
 }
@@ -92,8 +92,8 @@ function tripal_db_db_edit_form($form, &$form_state) {
  * @ingroup tripal_db
  */
 function tripal_db_db_add_form($form, $form_state) {
-	
-	// add in the form fields to this form
+  
+  // add in the form fields to this form
   tripal_db_add_db_form_fields($form, $form_state);
   
   $form['add'] = array(
@@ -112,8 +112,8 @@ function tripal_db_db_add_form($form, $form_state) {
  * @ingroup tripal_db
  */
 function tripal_db_add_db_form_fields(&$form, $form_state, $dbid = NULL) {
-	
-	$default_db        = '';
+  
+  $default_db        = '';
   $default_desc      = '';
   $default_url       = '';
   $default_urlprefix = '';
@@ -288,17 +288,17 @@ function tripal_db_db_edit_form_submit($form, &$form_state) {
  * @ingroup tripal_db
  */
 function tripal_db_edit_form_ajax($form, $form_state) {
-	
-	$elements = array();
-	
-	// add in the form fields and the buttons	
-	if (array_key_exists('dbid', $form_state['values'])) {
-		$elements['fields'] = $form['fields'];
-		$elements['update'] = $form['update'];
-		$elements['delete'] = $form['delete'];
-	}
-	
-	 // add back in the db-edit-div that is used for the next round of AJAX
+  
+  $elements = array();
+  
+  // add in the form fields and the buttons  
+  if (array_key_exists('dbid', $form_state['values'])) {
+    $elements['fields'] = $form['fields'];
+    $elements['update'] = $form['update'];
+    $elements['delete'] = $form['delete'];
+  }
+  
+   // add back in the db-edit-div that is used for the next round of AJAX
   $elements['fields']['#prefix'] =  '<div id="db-edit-div">';
   $elements['fields']['#suffix'] =  '</div">';
   
@@ -307,8 +307,8 @@ function tripal_db_edit_form_ajax($form, $form_state) {
   $elements['fields']['description']['#value'] = $elements['fields']['description']['#default_value'];
   $elements['fields']['url']['#value']         = $elements['fields']['url']['#default_value'];
   $elements['fields']['urlprefix']['#value']   = $elements['fields']['urlprefix']['#default_value'];
-	
-	//drupal_set_message('<pre>' . print_r($elements, TRUE) . '</pre>', "status");
-	
+  
+  //drupal_set_message('<pre>' . print_r($elements, TRUE) . '</pre>', "status");
+  
   return $elements;
 }

+ 146 - 74
tripal_feature/api/tripal_feature.api.inc

@@ -466,7 +466,16 @@ function tripal_feature_reverse_complement($sequence) {
  * @param $downstream
  *   An integer specifying the number of downstream bases to include in the 
  *   output.
- *
+ * @param $sub_features
+ *   Only include sub features (or child features) of the types provided in the array 
+ * @param $relationship
+ *   If a relationship name is provided (e.g. sequence_of) then any sequences that
+ *   are in relationships of this type with matched sequences are also included
+ * @param $rel_part
+ *   If a relationship is provided in the preceeding argument then the rel_part
+ *   must be either 'object' or 'subject' to indicate which side of the 
+ *   relationship the matched features belong
+ *      
  * @return
  *   The DNA/protein sequence formated as requested.
  *
@@ -474,7 +483,7 @@ function tripal_feature_reverse_complement($sequence) {
  */
 function tripal_feature_get_formatted_sequence($feature_id, $feature_name, 
   $num_bases_per_line, $derive_from_parent, $aggregate, $output_format,
-  $upstream, $downstream, $sub_features = array()) {
+  $upstream, $downstream, $sub_features = array(), $relationship = '', $rel_part = '') {
   
   // to speed things up we need to make sure we have a persistent connection
   $connection = tripal_db_persistent_chado(); 
@@ -486,6 +495,68 @@ function tripal_feature_get_formatted_sequence($feature_id, $feature_name,
      $downstream = 0;
   }
   
+  if ($rel_part == "object" or $rel_part == "subject") { 
+    if ($rel_part == "subject") {
+      $psql = '
+        PREPARE feature_rel_get_object (int, text) AS
+        SELECT FO.feature_id, FO.name, FO.uniquename, CVTO.name as feature_type, O.genus, O.species
+        FROM feature FS
+          INNER JOIN feature_relationship FR ON FR.subject_id   = FS.feature_id
+          INNER JOIN cvterm CVTFR            ON CVTFR.cvterm_id = FR.type_id
+          INNER JOIN feature FO              ON FO.feature_id   = FR.object_id
+          INNER JOIN cvterm CVTO             ON CVTO.cvterm_id  = FO.type_id
+          INNER JOIN organism O              ON O.organism_id   = FO.organism_id
+        WHERE 
+          FS.feature_id = $1 AND
+          CVTFR.name    = $2
+      ';  
+      $status = tripal_core_chado_prepare('feature_rel_get_object', $psql, array('int', 'text'));
+      if (!$status) {
+        watchdog('tripal_feature', "init: not able to prepare SQL statement '%name'", 
+          array('%name' => 'feature_by_subject'), 'WATCHDOG ERROR');
+      }    
+      $sql = "EXECUTE feature_rel_get_object(%d,'%s')";
+      $features = chado_query($sql, $feature_id, $relationship); 
+    }
+    if ($rel_part == "object") {
+      $psql = '
+        PREPARE feature_rel_get_subject (int, text) AS
+        SELECT FS.feature_id, FS.name, FS.uniquename, CVTO.name as feature_type, O.genus, O.species
+        FROM feature FO
+          INNER JOIN feature_relationship FR ON FR.object_id    = FO.feature_id
+          INNER JOIN cvterm CVTFR            ON CVTFR.cvterm_id = FR.type_id
+          INNER JOIN feature FS              ON FS.feature_id   = FR.subject_id
+          INNER JOIN cvterm CVTO             ON CVTO.cvterm_id  = FS.type_id
+          INNER JOIN organism O              ON O.organism_id   = FS.organism_id
+        WHERE 
+          FO.feature_id = $1 AND
+          CVTFR.name    = $2
+      ';
+      $status = tripal_core_chado_prepare('feature_rel_get_subject', $psql, array('int', 'text'));
+      if (!$status) {
+        watchdog('tripal_feature', "init: not able to prepare SQL statement '%name'", 
+          array('%name' => 'feature_by_object'), 'WATCHDOG ERROR');
+      }
+      $sql = "EXECUTE feature_rel_get_subject(%d,'%s')";
+      $features = chado_query($sql, $feature_id, $relationship);     
+    }
+    $sequences = '';
+    while ($feature = db_fetch_object($features)) {  
+
+      // recurse and get the sequences for these in the relationship
+      if ($rel_part == "subject") {
+        $defline = "$feature_name, $relationship, $feature->uniquename $feature->feature_type ($feature->genus $feature->species)";
+      }
+      if ($rel_part == "object") {
+        $defline = "$feature->uniquename $feature->feature_type ($feature->genus $feature->species), $relationship, $feature_name";
+      }
+      $sequences .= tripal_feature_get_formatted_sequence($feature->feature_id, $defline, 
+        $num_bases_per_line, $derive_from_parent, $aggregate, $output_format,
+        $upstream, $downstream, $sub_features, '', '');  
+    }  
+    return $sequences;  
+  }
+  
   // prepare statements we'll need to use later
   if (!tripal_core_is_sql_prepared('sequence_by_parent')) {
     // prepare the queries we're going to use later during the render phase
@@ -493,72 +564,73 @@ function tripal_feature_get_formatted_sequence($feature_id, $feature_name,
     // cases cases where the alignment is in the reverse direction and when
     // the upstream and downstream extensions go beyond the lenght of the 
     // parent sequence.
-    $psql ='PREPARE sequence_by_parent (int, int, int) AS 
-            SELECT srcname, srcfeature_id, strand, srctypename, typename,
-              fmin, fmax, upstream, downstream, adjfmin, adjfmax, 
-              substring(residues from (adjfmin + 1) for (upstream + (fmax - fmin) + downstream))  as residues,
-              genus, species
-            FROM (
-              SELECT
-                OF.name srcname, FL.srcfeature_id, FL.strand, 
-                OCVT.name as srctypename, SCVT.name as typename,
-                FL.fmin, FL.fmax, OO.genus, OO.species,
-                CASE 
-                  WHEN FL.strand >= 0 THEN 
-                    CASE 
-                       WHEN FL.fmin - $1 <= 0 THEN 0
-                       ELSE FL.fmin - $1
-                    END
-                  WHEN FL.strand < 0 THEN
-                    CASE 
-                       WHEN FL.fmin - $2 <= 0 THEN 0
-                       ELSE FL.fmin - $2
-                    END                   
-                END as adjfmin,                                                                
-                CASE 
-                  WHEN FL.strand >= 0 THEN
-                    CASE 
-                      WHEN FL.fmax + $2 > OF.seqlen THEN OF.seqlen 
-                      ELSE FL.fmax + $2
-                    END
-                  WHEN FL.strand < 0 THEN
-                    CASE
-                      WHEN FL.fmax + $1 > OF.seqlen THEN OF.seqlen
-                      ELSE FL.fmax + $1   
-                    END               
-                END as adjfmax,                     
-                CASE 
-                  WHEN FL.strand >= 0 THEN 
-                    CASE 
-                       WHEN FL.fmin - $1 <= 0 THEN FL.fmin
-                       ELSE $1
-                    END
-                  ELSE
-                    CASE 
-                       WHEN FL.fmax + $1 > OF.seqlen THEN OF.seqlen - FL.fmax
-                       ELSE $1
-                    END                   
-                END as upstream,                
-                CASE 
-                  WHEN FL.strand >= 0 THEN 
-                    CASE 
-                       WHEN FL.fmax + $2 > OF.seqlen THEN OF.seqlen - FL.fmax
-                       ELSE $2
-                    END
-                  ELSE
-                    CASE 
-                       WHEN FL.fmin - $2 <= 0 THEN FL.fmin
-                       ELSE $2
-                    END                   
-                END as downstream,  
-                OF.residues                                                     
-              FROM {featureloc} FL 
-                INNER JOIN {feature} SF on FL.feature_id = SF.feature_id
-                INNER JOIN {cvterm} SCVT on SF.type_id = SCVT.cvterm_id
-                INNER JOIN {feature} OF on FL.srcfeature_id = OF.feature_id                
-                INNER JOIN {cvterm} OCVT on OF.type_id = OCVT.cvterm_id
-                INNER JOIN {organism} OO on OF.organism_id = OO.organism_id
-              WHERE SF.feature_id = $3 and NOT (OF.residues = \'\' or OF.residues IS NULL)) as tbl1
+    $psql ='
+      PREPARE sequence_by_parent (int, int, int) AS 
+      SELECT srcname, srcfeature_id, strand, srctypename, typename,
+        fmin, fmax, upstream, downstream, adjfmin, adjfmax, 
+        substring(residues from (adjfmin + 1) for (upstream + (fmax - fmin) + downstream))  as residues,
+        genus, species
+      FROM (
+        SELECT
+          OF.name srcname, FL.srcfeature_id, FL.strand, 
+          OCVT.name as srctypename, SCVT.name as typename,
+          FL.fmin, FL.fmax, OO.genus, OO.species,
+          CASE 
+            WHEN FL.strand >= 0 THEN 
+              CASE 
+                 WHEN FL.fmin - $1 <= 0 THEN 0
+                 ELSE FL.fmin - $1
+              END
+            WHEN FL.strand < 0 THEN
+              CASE 
+                 WHEN FL.fmin - $2 <= 0 THEN 0
+                 ELSE FL.fmin - $2
+              END                   
+          END as adjfmin,                                                                
+          CASE 
+            WHEN FL.strand >= 0 THEN
+              CASE 
+                WHEN FL.fmax + $2 > OF.seqlen THEN OF.seqlen 
+                ELSE FL.fmax + $2
+              END
+            WHEN FL.strand < 0 THEN
+              CASE
+                WHEN FL.fmax + $1 > OF.seqlen THEN OF.seqlen
+                ELSE FL.fmax + $1   
+              END               
+          END as adjfmax,                     
+          CASE 
+            WHEN FL.strand >= 0 THEN 
+              CASE 
+                 WHEN FL.fmin - $1 <= 0 THEN FL.fmin
+                 ELSE $1
+              END
+            ELSE
+              CASE 
+                 WHEN FL.fmax + $1 > OF.seqlen THEN OF.seqlen - FL.fmax
+                 ELSE $1
+              END                   
+          END as upstream,                
+          CASE 
+            WHEN FL.strand >= 0 THEN 
+              CASE 
+                 WHEN FL.fmax + $2 > OF.seqlen THEN OF.seqlen - FL.fmax
+                 ELSE $2
+              END
+            ELSE
+              CASE 
+                 WHEN FL.fmin - $2 <= 0 THEN FL.fmin
+                 ELSE $2
+              END                   
+          END as downstream,  
+          OF.residues                                                     
+        FROM {featureloc} FL 
+          INNER JOIN {feature} SF on FL.feature_id = SF.feature_id
+          INNER JOIN {cvterm} SCVT on SF.type_id = SCVT.cvterm_id
+          INNER JOIN {feature} OF on FL.srcfeature_id = OF.feature_id                
+          INNER JOIN {cvterm} OCVT on OF.type_id = OCVT.cvterm_id
+          INNER JOIN {organism} OO on OF.organism_id = OO.organism_id
+        WHERE SF.feature_id = $3 and NOT (OF.residues = \'\' or OF.residues IS NULL)) as tbl1
     ';              
     $status = tripal_core_chado_prepare('sequence_by_parent', $psql, array('int', 'int', 'int'));
     if (!$status) {
@@ -627,7 +699,7 @@ function tripal_feature_get_formatted_sequence($feature_id, $feature_name,
         $i = 0;
         while ($child = db_fetch_object($children)) {
           // if the callee has specified that only certain sub features should be
-          // included then continue of this child is not one of those allowed
+          // included then continue if this child is not one of those allowed
           // subfeatures
           if (count($sub_features) > 0 and !in_array($child->type_name, $sub_features)) {
              continue;
@@ -704,7 +776,7 @@ function tripal_feature_get_formatted_sequence($feature_id, $feature_name,
       elseif ($output_format == 'fasta_txt') {
          $seq = wordwrap($seq, $num_bases_per_line, "\r\n", TRUE);
       }
-      $residues .= ">$feature_name. Sequence derived from $parent->srctypename of $parent->genus $parent->species: $parent->srcname:" . ($parent->adjfmin + 1) . ".." . $parent->adjfmax ." ($dir). ";
+      $residues .= ">$feature_name. Sequence derived from feature of type, '$parent->srctypename', of $parent->genus $parent->species: $parent->srcname:" . ($parent->adjfmin + 1) . ".." . $parent->adjfmax . " ($dir). ";
       if (count($types) > 0) {
         $residues .= "Excludes all bases but those of type(s): " . implode(', ', $types) . ". " ;
       }
@@ -966,7 +1038,7 @@ function tripal_feature_add_dbxref($feature_id, $dbname, $accession) {
     $options = array('statement_name' => 'ins_db_na');
     $success = tripal_core_chado_insert('db', $values, $options);
       if (!$success) {
-      watchdog('tripal_feature', 'tripal_feature_add_dbxref: The feature dbxref entry for feature, %feature_id, ". 
+      watchdog('tripal_feature', 'tripal_feature_add_dbxref: The feature dbxref entry for feature, %feature_id, " . 
         "could not be added because the database, %dbname, does not exist and cannot be added.', 
         array('%feature_id' => $feature_id, '%dbname' => $dbname), WATCHDOG_WARNING);
       return FALSE;
@@ -990,7 +1062,7 @@ function tripal_feature_add_dbxref($feature_id, $dbname, $accession) {
     $options = array('statement_name' => 'ins_featuredbxref_dbfe');
     $success = tripal_core_chado_insert('feature_dbxref', $values, $options);
     if (!$success) {
-      watchdog('tripal_feature', 'tripal_feature_add_dbxref: The feature dbxref entry for feature, %feature_id, '. 
+      watchdog('tripal_feature', 'tripal_feature_add_dbxref: The feature dbxref entry for feature, %feature_id, ' . 
         'could not be added: %db:%accession.', array('%feature_id' => $feature_id, '%db' => $dbname, 
         '%accession' => $accession), WATCHDOG_WARNING);
       return FALSE;
@@ -1024,7 +1096,7 @@ function tripal_feature_add_cvterm($feature_id, $cvname, $cvterm) {
     $options = array('statement_name' => 'ins_cv_na');
     $success = tripal_core_chado_insert('cv', $values, $options);
       if (!$success) {
-      watchdog('tripal_feature', 'tripal_feature_add_cvterm: The feature cvterm entry for feature, %feature_id, ". 
+      watchdog('tripal_feature', 'tripal_feature_add_cvterm: The feature cvterm entry for feature, %feature_id, " . 
         "could not be added because the CV, %cvname, does not exist and cannot be added.', 
         array('%feature_id' => $feature_id, '%cvname' => $cvname), WATCHDOG_WARNING);
       return FALSE;
@@ -1049,7 +1121,7 @@ function tripal_feature_add_cvterm($feature_id, $cvname, $cvterm) {
     $options = array('statement_name' => 'ins_featurecvterm_cvfepu');
     $success = tripal_core_chado_insert('feature_cvterm', $values, $options);
     if (!$success) {
-      watchdog('tripal_feature', 'tripal_feature_add_cvterm: The feature cvterm entry for feature, %feature_id, '. 
+      watchdog('tripal_feature', 'tripal_feature_add_cvterm: The feature cvterm entry for feature, %feature_id, ' . 
         'could not be added: %cvterm.', array('%feature_id' => $feature_id, '%cvterm' => $cvterm), WATCHDOG_WARNING);
       return FALSE;
     }

+ 2 - 2
tripal_feature/includes/fasta_loader.inc

@@ -528,7 +528,7 @@ function tripal_feature_load_fasta($dfile, $organism_id, $type,
         // if the match_type is name and no regular expression was provided
         // then use the first word as the name, otherwise we don't set the name
         if (strcmp($match_type, 'Name')==0) {
-          if(preg_match("/^\s*(.*?)[\s\|].*$/", $line, $matches)){
+          if (preg_match("/^\s*(.*?)[\s\|].*$/", $line, $matches)) {
             if (strlen($matches[1]) > $feature_tbl['fields']['name']['length']) {
               watchdog('trp-fasta', "WARNING: Regular expression retrieves a feature name too long for the feature name. Line %line.", array('%line' => $i), 'error');  
             }
@@ -553,7 +553,7 @@ function tripal_feature_load_fasta($dfile, $organism_id, $type,
         // if the match_type is name and no regular expression was provided
         // then use the first word as the name, otherwise, we don't set the unqiuename
         if (strcmp($match_type, 'Unique name')==0) {
-          if(preg_match("/^\s*(.*?)[\s\|].*$/", $line, $matches)){
+          if (preg_match("/^\s*(.*?)[\s\|].*$/", $line, $matches)) {
             $uname = trim($matches[1]);
           }
           else {

+ 13 - 13
tripal_feature/includes/gff_loader.inc

@@ -403,7 +403,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
                 LEFT JOIN {cvtermsynonym} CVTS on CVTS.cvterm_id = CVT.cvterm_id
              WHERE CV.cv_id = $1 and 
                (lower(CVT.name) = lower($2) or lower(CVTS.synonym) = lower($3))";
-     $status = tripal_core_chado_prepare('sel_cvterm_idnasy', $psql, array('int','text','text'));
+     $status = tripal_core_chado_prepare('sel_cvterm_idnasy', $psql, array('int', 'text', 'text'));
      if (!$status) {
        watchdog('T_gff3_loader', 'cannot prepare statement \'sel_cvterm_idnasy\'.', 
          array(), WATCHDOG_ERROR);
@@ -420,7 +420,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
     $num_read += $size;
     $intv_read += $size; 
     
-    if($line_num < $start_line) {
+    if ($line_num < $start_line) {
       continue;
     }    
     
@@ -435,7 +435,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
     // check to see if we have FASTA section, if so then set the variable
     // to start parsing
     if (preg_match('/^##FASTA/i', $line)) {
-      if($remove) {
+      if ($remove) {
         // we're done because this is a delete operation so break out of the loop.
         break;         
       }
@@ -609,7 +609,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
           }
         } 
         else {
-          watchdog('T_gff3_loader', "The organism attribute '%org' on line %line is not properly formated. It ".
+          watchdog('T_gff3_loader', "The organism attribute '%org' on line %line is not properly formated. It " .
             "should be of the form: organism=Genus:species.  Skipping this line.", 
             array('%org' => $attr_organism, '%line' => $line_num), WATCHDOG_ERROR);
           $skip_feature = 1;  
@@ -645,14 +645,14 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
       // the same parent.
       elseif (array_key_exists('Parent', $tags)) {
         $date = getdate();
-        $attr_uniquename = $tags['Parent'][0] . "-$type-$landmark-" . $date[0] . ":$fmin..$fmax";
+        $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
       else {
         $date = getdate();
-        $attr_uniquename = $date[0] . "-$type-$landmark:$fmin..$fmax";
+        $attr_uniquename = $date[0] . "-$type-$landmark:" . ($fmin + 1) . ".." . $fmax;
         $attr_name = $type;
       }      
     }
@@ -704,11 +704,11 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
         $count = tripal_core_chado_select('feature', $columns, $select, $options); 
         if (!$count or count($count) == 0 or $count[0]->num_landmarks == 0) {
           watchdog('T_gff3_loader', "The landmark '%landmark' cannot be found for this organism (%species) " .
-                "Please add the landmark and then retry the import of this GFF3 ".
+                "Please add the landmark and then retry the import of this GFF3 " .
                 "file", array('%landmark' => $landmark, '%species' => $organism->genus . " " . $organism->species), WATCHDOG_ERROR);
           return '';
         }
-        elseif($count[0]->num_landmarks > 1) {
+        elseif ($count[0]->num_landmarks > 1) {
           watchdog('T_gff3_loader', "The landmark '%landmark' has more than one entry for this organism (%species) " .
                 "Cannot continue", array('%landmark' => $landmark, '%species' => $organism->genus . " " . $organism->species), WATCHDOG_ERROR);
           return '';  
@@ -716,7 +716,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
 
       }
       if ($count[0]->num_landmarks > 1) {
-        watchdog('T_gff3_loader', "The landmark '%landmark' is not unique for this organism. ".
+        watchdog('T_gff3_loader', "The landmark '%landmark' is not unique for this organism. " .
               "The features cannot be associated", array('%landmark' => $landmark), WATCHDOG_ERROR);
         return '';
       }  
@@ -850,7 +850,7 @@ function tripal_feature_load_gff3($gff_file, $organism_id, $analysis_id,
             FROM {feature_relationship} FR              
               INNER JOIN {featureloc} FL on FL.feature_id = FR.subject_id";
     if (!$connection) {
-      $sql .= "WHERE FR.object_id = %d ".
+      $sql .= "WHERE FR.object_id = %d " .
               "ORDER BY FL.fmin ASC ";
     }
     else {
@@ -957,8 +957,8 @@ function tripal_feature_load_gff3_derives_from($feature, $subject, $organism) {
   $options = array('statement_name' => 'sel_feature_orunty');
   $sfeature = tripal_core_chado_select('feature', array('feature_id'), $match, $options);
   if (count($sfeature)==0) {
-    watchdog('T_gff3_loader', "Could not add 'Derives_from' relationship ".
-      "for %uniquename and %subject.  Subject feature, '%subject', ".
+    watchdog('T_gff3_loader', "Could not add 'Derives_from' relationship " .
+      "for %uniquename and %subject.  Subject feature, '%subject', " .
       "cannot be found", array('%uniquename' => $feature->uniquename, '%subject' => $subject), WATCHDOG_ERROR);
     return;
   }
@@ -1008,7 +1008,7 @@ function tripal_feature_load_gff3_parents($feature, $cvterm, $parents, $organism
                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 = $1 and (CVT.name = $2 or CVTS.synonym = $3)";
-    $status = tripal_core_chado_prepare('sel_cvterm_cvname_cvtname_synonym', $psql, array('text', 'text' ,'text'));
+    $status = tripal_core_chado_prepare('sel_cvterm_cvname_cvtname_synonym', $psql, array('text', 'text', 'text'));
     if (!$status) {
        watchdog("T_gff3_loader", "Cannot prepare statement 'sel_cvterm_cvname_cvtname_synonym' for ontology term", 
          array(), WATCHDOG_WARNING);

+ 5 - 5
tripal_feature/includes/indexFeatures.inc

@@ -74,9 +74,9 @@ function tripal_features_reindex($max_sync, $job_id = NULL) {
   // SQL statement is derived from the hook_search function in the Drupal API.
   // Essentially, this is the SQL statement that finds all nodes that need
   // reindexing, but adjusted to include the chado_feature
-  $sql = "SELECT N.nid, N.title, CF.feature_id ".
-        "FROM {node} N ".
-        "  INNER JOIN {chado_feature} CF ON CF.nid = N.nid ";
+  $sql = "SELECT N.nid, N.title, CF.feature_id " .
+         "FROM {node} N " .
+         "  INNER JOIN {chado_feature} CF ON CF.nid = N.nid ";
   $results = db_query($sql);
 
   // load into ids array
@@ -144,8 +144,8 @@ function tripal_feature_index_feature($feature_id, $nid) {
   // if we only have a feature_id then let's find a corresponding
   // node.  If we can't find a node then return.
   if (!$nid) {
-    $nsql = "SELECT N.nid,N.title FROM {chado_feature} CF ".
-            "  INNER JOIN {node} N ON N.nid = CF.nid ".
+    $nsql = "SELECT N.nid,N.title FROM {chado_feature} CF " .
+            "  INNER JOIN {node} N ON N.nid = CF.nid " .
             "WHERE CF.feature_id = %d";
     $node = db_fetch_object(db_query($nsql, $feature_id));
     if (!$node) {

+ 11 - 10
tripal_feature/includes/seq_extract.inc

@@ -4,7 +4,7 @@
  */
 function tripal_feature_seq_extract_page() {
   
-  if ($_SESSION['tripal_feature_seq_extract']['download']){   
+  if ($_SESSION['tripal_feature_seq_extract']['download']) {   
     $genus      = $_SESSION['tripal_feature_seq_extract']['genus'];
     $species    = $_SESSION['tripal_feature_seq_extract']['species'];
     $analysis   = $_SESSION['tripal_feature_seq_extract']['analysis'];
@@ -45,7 +45,7 @@ function tripal_feature_seq_extract_page() {
         views must be populated before using this form.  Those views should be re-populated 
         when new data is added.  If you use the " . l('jquery_update module', 'http://drupal.org/project/jquery_update') . ", it may break this form.
         You will need to update the  jquery_update/replace/jquery.form.js file with " .
-        l('a more recent version','https://raw.github.com/malsup/form/master/jquery.form.js') . "</p>         
+        l('a more recent version', 'https://raw.github.com/malsup/form/master/jquery.form.js') . "</p>         
       </div>
     ";
   }
@@ -455,7 +455,8 @@ function tripal_feature_seq_extract_form_submit($form, &$form_state) {
  * 
  */
 function tripal_feature_seq_extract_get_features($org_commonname, $genus, $species, $analysis_name, 
-  $type, $feature_name, $upstream, $downstream, $output_format, $derive_from_parent, $aggregate, $child) {
+  $type, $feature_name, $upstream, $downstream, $output_format, $derive_from_parent, $aggregate, 
+  $child, $relationship, $rel_part) {
     
   $sub_features = explode(',', $child);
     
@@ -465,17 +466,17 @@ function tripal_feature_seq_extract_get_features($org_commonname, $genus, $speci
   
   if (!$type and !$feature_name and !$genus) {
     print "Please provide a type, feature name or genus\n";
-     eturn;
+     return;
   }
 
   // get the list of features
   $vars = array();
-  $sql  = "SELECT DISTINCT F.feature_id, F.name, F.uniquename, O.genus, O.species, CVT.name as feature_type ".
-          "FROM {feature} F ".
-          "  INNER JOIN {organism} O on O.organism_id = F.organism_id ".
+  $sql  = "SELECT DISTINCT F.feature_id, F.name, F.uniquename, O.genus, O.species, CVT.name as feature_type " .
+          "FROM {feature} F " .
+          "  INNER JOIN {organism} O on O.organism_id = F.organism_id " .
           "  INNER JOIN {cvterm} CVT on CVT.cvterm_id = F.type_id ";
   if ($analysis_name) {
-     $sql .= "  INNER JOIN {analysisfeature} AF on AF.feature_id = F.feature_id ".
+     $sql .= "  INNER JOIN {analysisfeature} AF on AF.feature_id = F.feature_id " .
              "  INNER JOIN {analysis} A on AF.analysis_id = A.analysis_id ";
   }         
   $sql .= "WHERE (1=1) ";
@@ -523,7 +524,7 @@ function tripal_feature_seq_extract_get_features($org_commonname, $genus, $speci
     $feature_id = $feature->feature_id;
     
     // build the header for each FASTA entry
-    if($feature->uniquename == $feature->name) {
+    if ($feature->uniquename == $feature->name) {
       $feature_name = "$feature->uniquename $feature->feature_type ($feature->genus $feature->species)";
     } 
     else {
@@ -533,7 +534,7 @@ function tripal_feature_seq_extract_get_features($org_commonname, $genus, $speci
     // generate the sequence
     $sequence = tripal_feature_get_formatted_sequence($feature_id, $feature_name, 
       $num_bases_per_line, $derive_from_parent, $aggregate, $output_format,
-      $upstream, $downstream, $sub_features);
+      $upstream, $downstream, $sub_features, $relationship, $rel_part);
     
     // print the sequence
     print $sequence;

+ 2 - 2
tripal_feature/includes/tripal_feature-db_references.inc

@@ -312,8 +312,8 @@ function theme_tripal_feature_edit_ALL_db_references_form($form) {
 
   $output .= '<br /><fieldset>';
   $output .= '<legend>Edit Existing Database References<span class="form-optional" title="This field is optional">(optional)</span></legend>';
-  $output .= '<p>Below is a list of already existing database references, one per line. When entering a database reference, the accession '
-         .'is a unique identifier for this feature in the specified database.</p>';
+  $output .= '<p>Below is a list of already existing database references, one per line. When entering a database reference, the accession ' .
+             'is a unique identifier for this feature in the specified database.</p>';
   $output .= '<table>';
   $output .= '<tr><th>#</th><th>Database</th><th>Accession</th><th></th></tr>';
 

+ 1 - 1
tripal_feature/includes/tripal_feature-delete.inc

@@ -87,7 +87,7 @@ function tripal_feature_delete_form_validate($form, &$form_state) {
                      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 = '%s' and (CVT.name = '%s' or CVTS.synonym = '%s')";
-    $cvterm = db_fetch_object(db_query($cvtermsql, 'sequence', $seq_type, $seq_type));
+    $cvterm = db_fetch_object(chado_query($cvtermsql, 'sequence', $seq_type, $seq_type));
     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."));
     }

+ 2 - 2
tripal_feature/includes/tripal_feature-properties.inc

@@ -277,8 +277,8 @@ function theme_tripal_feature_edit_ALL_properties_form($form) {
 
   $output .= '<br /><fieldset>';
   $output .= '<legend>Edit Already Existing Properties<span class="form-optional" title="This field is optional">(optional)</span></legend>';
-  $output .= '<p>Below is a list of already existing properties for this feature, one property per line. The type refers to the type of '
-         .'property and the value is the value for that property. </p>';
+  $output .= '<p>Below is a list of already existing properties for this feature, one property per line. The type refers to the type of ' .
+             'property and the value is the value for that property. </p>';
   $output .= '<table>';
   $output .= '<tr><th>#</th><th>Type</th><th>Value</th><th></th></tr>';
 

+ 3 - 3
tripal_feature/includes/tripal_feature-relationships.inc

@@ -498,9 +498,9 @@ function theme_tripal_feature_edit_ALL_relationships_form($form) {
 
   $output .= '<br /><fieldset>';
   $output .= '<legend>Edit Already Existing Relationships<span class="form-optional" title="This field is optional">(optional)</span></legend>';
-  $output .= '<p>Each relationship for this stock is listed below, one per line. The textboxes indicating '
-        .'the subject and object of the relationship can contain the uniquename, name, database '
-      .'reference or synonym of a stock of the same organism.</p>';
+  $output .= '<p>Each relationship for this stock is listed below, one per line. The textboxes indicating ' .
+             'the subject and object of the relationship can contain the uniquename, name, database ' .
+             'reference or synonym of a stock of the same organism.</p>';
   $output .= '<table>';
   $output .= '<tr><th>#</th><th>Subject</th><th>Type</th><th>Object</th><th></th></tr>';
 

+ 157 - 101
tripal_feature/includes/tripal_feature.admin.inc

@@ -12,6 +12,7 @@
  */
 function tripal_feature_admin() {
 
+  /*
   // before proceeding check to see if we have any
   // currently processing jobs. If so, we don't want
   // to give the opportunity to sync libraries
@@ -20,14 +21,15 @@ function tripal_feature_admin() {
     $active_jobs = TRUE;
   }
   if (!$active_jobs) {
-
+*/
+    get_tripal_feature_admin_form_title_set($form);
     get_tripal_feature_admin_form_url_set($form);
 
     $form['browser'] = array(
        '#type' => 'fieldset',
        '#title' => t('Feature Browser'),
-       '#collapsible' => 1,
-       '#collapsed' => 1 ,
+       '#collapsible' => TRUE,
+       '#collapsed' => TRUE,
     );
     $allowedoptions1  = array(
       'show_feature_browser' => "Show the feature browser on the organism page. The browser loads when page loads. This may be slow for large sites.",
@@ -37,9 +39,9 @@ function tripal_feature_admin() {
 
     $form['browser']['browser_desc'] = array(
        '#type'        => 'markup',
-       '#value' => 'A feature browser can be added to an organism page to allow users to quickly '.
-          'access a feature.  This will most likely not be the ideal mechanism for accessing feature '.
-          'information, especially for large sites, but it will alow users exploring the site (such '.
+       '#value' => 'A feature browser can be added to an organism page to allow users to quickly ' .
+          'access a feature.  This will most likely not be the ideal mechanism for accessing feature ' .
+          'information, especially for large sites, but it will alow users exploring the site (such ' .
           'as students) to better understand the data types available on the site.',
 
     );
@@ -88,15 +90,15 @@ function tripal_feature_admin() {
     $form['summary'] = array(
        '#type' => 'fieldset',
        '#title' => t('Feature Summary Report'),
-       '#collapsible' => 1,
-       '#collapsed' => 1 ,
+       '#collapsible' => TRUE,
+       '#collapsed' => TRUE,
     );
     $allowedoptions2 ['show_feature_summary'] = "Show the feature summary on the organism page. The summary loads when page loads.";
     $allowedoptions2 ['hide_feature_summary'] = "Hide the feature summary on the organism page. Disables the feature summary.";
 
     $form['summary']['feature_summary'] = array(
        '#title' => 'Feature Summary on Organism Page',
-       '#description' => 'A feature summary can be added to an organism page to allow users to see the '.
+       '#description' => 'A feature summary can be added to an organism page to allow users to see the ' .
           'type and quantity of features available for the organism.',
        '#type' => 'radios',
        '#options' => $allowedoptions2,
@@ -104,10 +106,10 @@ function tripal_feature_admin() {
     );
     $form['summary']['feature_mapping'] = array(
        '#title' => 'Map feature types',
-       '#description' => t('You may specify which Sequence Ontology (SO) terms to show in the '.
-          'feature summary report by listing them in the following text area.   Enter one per line. '.
-          'If left blank, all SO terms for all features will be shown in the report. Only those terms '.
-          'listed below will be shown in the report. Terms will appear in the report in the same order listed. To rename a '.
+       '#description' => t('You may specify which Sequence Ontology (SO) terms to show in the ' .
+          'feature summary report by listing them in the following text area.   Enter one per line. ' .
+          'If left blank, all SO terms for all features will be shown in the report. Only those terms ' .
+          'listed below will be shown in the report. Terms will appear in the report in the same order listed. To rename a ' .
           'SO term to be more human readable form, use an \'=\' sign after the SO term (e.g. \'polypeptide = Protein\')'),
        '#type' => 'textarea',
        '#rows' => 15,
@@ -122,20 +124,23 @@ function tripal_feature_admin() {
     get_tripal_feature_admin_form_taxonomy_set($form);
     get_tripal_feature_admin_form_reindex_set($form);
     get_tripal_feature_admin_form_cleanup_set($form);
-  }
+/*  }
   else {
     $form['notice'] = array(
        '#type' => 'fieldset',
-       '#title' => t('Feature Management Temporarily Unavailable')
+       '#title' => t('Feature Management Temporarily Unavailable'),
+       '#collapsible' => FALSE,
+       '#collapsed' => FALSE,
     );
     $form['notice']['message'] = array(
-       '#value' => t('Currently, feature management jobs are waiting or ".
-          "are running. Managemment features have been hidden until these ".
-          "jobs complete.  Please check back later once these jobs have ".
-          "finished.  You can view the status of pending jobs in the Tripal ".
+       '#value' => t('Currently, feature management jobs are waiting or " .
+          "are running. Managemment features have been hidden until these " .
+          "jobs complete.  Please check back later once these jobs have " .
+          "finished.  You can view the status of pending jobs in the Tripal " .
           "jobs page.'),
     );
   }
+  */
   return system_settings_form($form);
 }
 
@@ -150,47 +155,46 @@ function tripal_feature_admin_validate($form, &$form_state) {
 
   variable_set('chado_browser_feature_types', $form_state['values']['feature_types']);
 
-  // if the user wants to sync up the chado features then
-  // add the job to the management queue
   switch ($form_state['values']['op']) {
 
     case  t('Sync all Features') :
       tripal_add_job('Sync all features', 'tripal_feature',
         'tripal_feature_sync_features', $job_args, $user->uid);
-    break;
+      break;
 
     case t('Set/Reset Taxonomy for all feature nodes') :
-    tripal_add_job('Set all feature taxonomy', 'tripal_feature',
-      'tripal_features_set_taxonomy', $job_args, $user->uid);
-    break;
+      tripal_add_job('Set all feature taxonomy', 'tripal_feature',
+        'tripal_features_set_taxonomy', $job_args, $user->uid);
+      break;
 
     case t('Reindex all feature nodes') :
-    tripal_add_job('Reindex all features', 'tripal_feature',
-      'tripal_features_reindex', $job_args, $user->uid);
-    break;
+      tripal_add_job('Reindex all features', 'tripal_feature',
+        'tripal_features_reindex', $job_args, $user->uid);
+      break;
 
     case t('Clean up orphaned features') :
-    tripal_add_job('Cleanup orphaned features', 'tripal_feature',
-      'tripal_features_cleanup', $job_args, $user->uid);
-    break;
+      tripal_add_job('Cleanup orphaned features', 'tripal_feature',
+        'tripal_features_cleanup', $job_args, $user->uid);
+      break;
 
     case t('Set Browser') :
       variable_set('tripal_feature_browse_setting', $form_state['values']['browse_features']);
       variable_set('tripal_library_feature_browse_setting', $form_state['values']['browse_features_library']);
       variable_set('tripal_analysis_feature_browse_setting', $form_state['values']['browse_features_analysis']);
-    break;
+      break;
 
     case t('Set Summary') :
       variable_set('tripal_feature_summary_setting', $form_state['values']['feature_summary']);
       variable_set('tripal_feature_summary_report_mapping', $form_state['values']['feature_mapping']);
-    break;
+      break;
 
     case t('Set Feature URLs') :
       variable_set('chado_feature_url', $form_state['values']['feature_url']);
       tripal_add_job('Set Feature URLs', 'tripal_feature',
         'tripal_feature_set_urls', $job_args, $user->uid);
-    break;
-    }
+      break;
+  }
+    
 }
 /**
  *
@@ -201,19 +205,19 @@ function get_tripal_feature_admin_form_cleanup_set(&$form) {
   $form['cleanup'] = array(
     '#type' => 'fieldset',
     '#title' => t('Clean Up'),
-    '#collapsible' => 1,
-    '#collapsed' => 1 ,
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
   );
   $form['cleanup']['description'] = array(
     '#type' => 'item',
-    '#value' => t("With Drupal and chado residing in different databases ".
-      "it is possible that nodes in Drupal and features in Chado become ".
-      "\"orphaned\".  This can occur if a feature node in Drupal is ".
-      "deleted but the corresponding chado feature is not and/or vice ".
-      "versa.  The Cleanup function will also remove nodes for features ".
-      "that are not in the list of allowed feature types as specified ".
-      "above.  This is helpful when a feature type needs to be ".
-      "removed but was previously present as Drupal nodes. ".
+    '#value' => t("With Drupal and chado residing in different databases " .
+      "it is possible that nodes in Drupal and features in Chado become " .
+      "\"orphaned\".  This can occur if a feature node in Drupal is " .
+      "deleted but the corresponding chado feature is not and/or vice " .
+      "versa.  The Cleanup function will also remove nodes for features " .
+      "that are not in the list of allowed feature types as specified " .
+      "above.  This is helpful when a feature type needs to be " .
+      "removed but was previously present as Drupal nodes. " .
       "Click the button below to resolve these discrepancies."),
     '#weight' => 1,
   );
@@ -233,15 +237,15 @@ function get_tripal_feature_admin_form_reindex_set(&$form) {
   $form['reindex'] = array(
     '#type' => 'fieldset',
     '#title' => t('Index/Reindex'),
-    '#collapsible' => 1,
-    '#collapsed' => 1 ,
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
   );
   $form['reindex']['description'] = array(
      '#type' => 'item',
-     '#value' => t("Indexing or reindexing of nodes is required for Drupal's full text searching. ".
-        "Index features for the first time to allow for searching of content, and later when content for features ".
-        "is updated.  Depending on the number of features this may take ".
-        "quite a while. Click the button below to begin reindexing of ".
+     '#value' => t("Indexing or reindexing of nodes is required for Drupal's full text searching. " .
+        "Index features for the first time to allow for searching of content, and later when content for features " .
+        "is updated.  Depending on the number of features this may take " .
+        "quite a while. Click the button below to begin reindexing of " .
         "features. "),
      '#weight' => 1,
   );
@@ -262,14 +266,14 @@ function get_tripal_feature_admin_form_taxonomy_set(&$form) {
   $form['taxonomy'] = array(
     '#type' => 'fieldset',
     '#title' => t('Set Taxonomy'),
-    '#collapsible' => 1,
-    '#collapsed' => 1 ,
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
   );
 
   $form['taxonomy']['description'] = array(
      '#type' => 'item',
-     '#value' => t("Drupal allows for assignment of \"taxonomy\" or ".
-        "catagorical terms to nodes. These terms allow for advanced ".
+     '#value' => t("Drupal allows for assignment of \"taxonomy\" or " .
+        "catagorical terms to nodes. These terms allow for advanced " .
         "filtering during searching."),
      '#weight' => 1,
   );
@@ -282,7 +286,7 @@ function get_tripal_feature_admin_form_taxonomy_set(&$form) {
   $form['taxonomy']['tax_classes'] = array(
    '#title'       => t('Available Taxonomic Classes'),
    '#type'        => t('checkboxes'),
-   '#description' => t("Please select the class of terms to assign to ".
+   '#description' => t("Please select the class of terms to assign to " .
       "chado features"),
    '#required'    => FALSE,
    '#prefix'      => '<div id="taxclass_boxes">',
@@ -299,55 +303,107 @@ function get_tripal_feature_admin_form_taxonomy_set(&$form) {
 
 }
 
-function get_tripal_feature_admin_form_url_set(&$form) {
+/**
+ * 
+ * @param $form
+ */
+function get_tripal_feature_admin_form_title_set(&$form) {
 
-  $form['url'] = array(
+  $form['title'] = array(
     '#type' => 'fieldset',
-    '#title' => t('Feature URL Path')
+    '#title' => t('Feature Page Titles'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
   );
-  $form['url']['desc'] = array(
+  $form['title']['desc'] = array(
     '#type'        => 'markup',
-    '#value' => t('Each synced feature will have a unique URL which consists of '.
-                        'the site domain followed by a unique identifer: for '.
-                        'example http://my-tripal-site.org/ID1034, where the '.
-                        'element just after the final slash is the unique '.
-                        'identifier for the feature.'),
+    '#value' => t('Each synced feature must have a unique page title, however, features
+                   may have the same name if they are of different types or from
+                   different organisms.  Therefore, we must be sure that the 
+                   page titles can uniquely identify the feature being viewed.  Select
+                   an option below that will uniquely identify all features on your site.'),
   );
   $options = array(
-    'internal ID'          => 'Internal ID (Chado feature_id)',
-    'feature unique name'  => 'Feature unique name',
-    'feature name'         => 'Feature name',
-    'genus_species_uqname' => 'Genus + species + unique name (e.g. http://your.site.url/[genus]/[genus]_[species]/[unique_name]',
-    'genus_species_name'   => 'Genus + species + name (e.g. http://your.site.url/[genus]/[genus]_[species]/[name]',
+    'feature_unique_name'  => 'Feature unique name',
+    'feature_name'         => 'Feature name',
+    'unique_constraint'    => 'Feature Name, uniquename, type and species',
   );
-  
-  $form['url']['chado_feature_url'] = array(
-    '#title'         => t('Unique Identifier'),
+  $form['title']['chado_feature_title'] = array(
+    '#title'         => t('Feature Page Titles'),
     '#type'          => 'radios',
-    '#description'   => t('Choose an identifier type '.
-                        'from the list above that is guaranteed to be unique in your synced '.
-                        'dataset. If in doubt it is safest to coose the internal ID. '.
-                        'The descrpitor need not be unique amont the total dataset. '.
-                        'It only need be unique among the synced dataset.'),
+    '#description'   => t('Choose a title type  from the list above that is 
+      guaranteed to be unique for all features.  If in doubt it is safest to 
+      choose the last option as that guarantees uniqueness. Click the 
+      \'Save Configuration\' button at the bottom to save your selection.'),
     '#required'      => FALSE,
     '#options'       => $options,
-    '#default_value' => variable_get('chado_feature_url', 'internal ID'),
+    '#default_value' => variable_get('chado_feature_title', 'unique_constraint'),
+  );
+}
+/**
+ * 
+ * @param $form
+ */
+function get_tripal_feature_admin_form_url_set(&$form) {
+
+ 
+  $form['url'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Feature URL Path'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
   );
 
-  $form['url']['chado_feature_accession_prefix'] = array(
-    '#title'       => t('ID Prefix'),
-    '#type'        => t('textfield'),
-    '#description' => t("If you choose an Internal ID above you must also enter an ID prefix.".
-                        "this prefix will be prepended to the internal ID number (e.g. ID38294). ".
-                        "if you chose to use the feature name or unique name then this prfix is not used"),
-    '#required'    => TRUE,
-    '#default_value' => variable_get('chado_feature_accession_prefix', 'ID'),
+  $options = array(
+    'SID[id]'      => '[id]:' . t('The Chado feature_id'),
+    'feature'      => 'feature:' . t('Chado table name'),
+    '[genus]'      => '[genus]:' . t('Genus to which the feature belongs'),
+    '[species]'    => '[species]:' . t('Species to which the feature belongs'),
+    '[type]'       => '[type]:' . t('The type of feature'),
+    '[uniquename]' => '[uniquename]:' . t('The feature unique name'),
+    '[name]'       => '[name]:' . t('The feature name'),
+    'reset'        => t('Reset'),
   );
+  
 
+  $form['url']['chado_feature_url_string'] = array(
+    '#title' => 'URL Syntax',
+    '#type' => 'textfield',
+    '#description' => t('You may rearrange elements in this text box to
+      customize the URLs.  The available tags include: [id],
+      [uniquename]. [name], [species], [genus], [type]. You can separate or
+      include any text between the tags. Click the "Set Feature URLs" button to 
+      reset the URLs for all feature pages.  Click the "Save Configuration" button to
+      simply save this setup. <b>Important</b>: be sure that whatever you choose will always be unique even considering
+      future data that may be added.  If you include the Chado table name, genus, species, type 
+      and uniquename you are guaranteed to have a unique URL. For example feature/[genus]/[species]/[type]/[uniquename]'),
+    '#size' => 150,
+    '#default_value' => variable_get('chado_feature_url_string', '/feature/[genus]/[species]/[type]/[uniquename]'), 
+  );
+  
+  $form['url']['chado_feature_url'] = array(
+    '#title'         => t('URL components'),
+    '#type'          => 'checkboxes',
+    '#required'      => FALSE,
+    '#options'       => $options,
+    '#description'   => t('Click the item above to make it appear in the URL Syntax box'),
+    '#attributes'    => array(
+      'onclick' => '
+        box = $(\'#edit-chado-feature-url-string\');
+        if (this.value == \'reset\') {
+          box.val(\'\');
+        }
+        else {        
+          box.val(box.val() + "/" + this.value);          
+        }
+        this.checked = false;
+      ',
+    ),
+  );  
+  
   $form['url']['button'] = array(
     '#type' => 'submit',
     '#value' => t('Set Feature URLs'),
-    '#weight' => 3,
   );
 }
 
@@ -364,7 +420,7 @@ function tripal_features_set_taxonomy($max_sync = 0, $job_id = NULL) {
 
   // iterate through all drupal feature nodes and set the taxonomy
   $results = db_query("SELECT * FROM {chado_feature}");
-  $nsql =  "SELECT * FROM {node} ".
+  $nsql =  "SELECT * FROM {node} " .
           "WHERE nid = %d";
   $i = 0;
 
@@ -444,10 +500,10 @@ function tripal_feature_set_taxonomy($node, $feature_id) {
   }
 
   // get the cvterm and the organism for this feature
-  $sql = "SELECT CVT.name AS cvname, O.genus, O.species ".
-        "FROM {CVTerm} CVT ".
-        "  INNER JOIN {Feature} F on F.type_id = CVT.cvterm_id ".
-        "  INNER JOIN {Organism} O ON F.organism_id = O.organism_id ".
+  $sql = "SELECT CVT.name AS cvname, O.genus, O.species " .
+        "FROM {CVTerm} CVT " .
+        "  INNER JOIN {Feature} F on F.type_id = CVT.cvterm_id " .
+        "  INNER JOIN {Organism} O ON F.organism_id = O.organism_id " .
         "WHERE F.feature_id = $feature_id";
   $feature = db_fetch_object(chado_query($sql));
 
@@ -462,9 +518,9 @@ function tripal_feature_set_taxonomy($node, $feature_id) {
 
   // get the library that this feature may belong to and add it as taxonomy
   if ($do_lb && $lb_vid) {
-    $sql = "SELECT L.name ".
-           "FROM {Library} L ".
-           "  INNER JOIN {Library_feature} LF ON LF.library_id = L.library_id ".
+    $sql = "SELECT L.name " .
+           "FROM {Library} L " .
+           "  INNER JOIN {Library_feature} LF ON LF.library_id = L.library_id " .
            "WHERE LF.feature_id = %d ";
     $library = db_fetch_object(chado_query($sql, $feature_id));
     $tags["$lb_vid"] = "$library->name";
@@ -478,9 +534,9 @@ function tripal_feature_set_taxonomy($node, $feature_id) {
   // get the analysis that this feature may belong to and add it as taxonomy
   // We'll add each one individually since there may be more than one analysis
   if ($do_an && $an_vid) {
-    $sql = "SELECT A.name ".
-           "FROM {Analysis} A ".
-           "  INNER JOIN {Analysisfeature} AF ON AF.analysis_id = A.analysis_id ".
+    $sql = "SELECT A.name " .
+           "FROM {Analysis} A " .
+           "  INNER JOIN {Analysisfeature} AF ON AF.analysis_id = A.analysis_id " .
            "WHERE AF.feature_id = $feature_id ";
     $results = chado_query($sql);
     $analysis_terms = array();
@@ -520,7 +576,7 @@ function theme_tripal_feature_search_index($node) {
   $content = '';
 
   // get the accession prefix
-  $aprefix = variable_get('chado_feature_accession_prefix', 'ID');
+  $aprefix = variable_get('chado_feature_accession_prefix', 'FID');
 
   $content .= "<h1>$feature->uniquename</h1>. ";
   $content .= "<strong>$aprefix$feature->feature_id.</strong> ";
@@ -549,7 +605,7 @@ function theme_tripal_feature_search_results($node) {
   $content = '';
 
   // get the accession prefix
-  $aprefix = variable_get('chado_feature_accession_prefix', 'ID');
+  $aprefix = variable_get('chado_feature_accession_prefix', 'FID');
 
   $content .= "Feature Name: <h1>$feature->uniquename</h1>. ";
   $content .= "<strong>Accession: $aprefix$feature->feature_id.</strong>";

+ 203 - 87
tripal_feature/includes/syncFeatures.inc → tripal_feature/includes/tripal_feature.sync_features.inc

@@ -10,9 +10,9 @@
 // Parameter f specifies the feature_id to sync
 // -f 0 will sync all features
 
-$arguments = getopt("f:");
+$arguments = getopt("f:t:");
 
-if (isset($arguments['f'])) {
+if (isset($arguments['f']) and isset($arguments['t']) and $arguments['t'] == 'chado_feature') {
   $drupal_base_url = parse_url('http://www.example.com');
   $_SERVER['HTTP_HOST'] = $drupal_base_url['host'];
   $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF'];
@@ -24,10 +24,10 @@ if (isset($arguments['f'])) {
 
   $feature_id = $arguments['f'];
 
-  if ($feature_id > 0 ) {
+  if ($feature_id > 0) {
     tripal_feature_sync_feature($feature_id);
   }
-  else{
+  else {
     print "syncing all features...\n";
     tripal_feature_sync_features();
   }
@@ -39,24 +39,23 @@ function tripal_feature_sync_form() {
 
   $form['description'] = array(
   '#type' => 'item',
-  '#value' => t("Add feature types, optionally select an organism and ".
-     "click the 'Sync all Features' button to create Drupal ".
-     "content for features in chado. Only features of the types listed ".
-     "below in the Feature Types box will be synced. You may limit the ".
-     "features to be synced by a specific organism. Depending on the ".
-     "number of features in the chado database this may take a long ".
+  '#value' => t("Add feature types, optionally select an organism and " .
+     "click the 'Sync all Features' button to create Drupal " .
+     "content for features in chado. Only features of the types listed " .
+     "below in the Feature Types box will be synced. You may limit the " .
+     "features to be synced by a specific organism. Depending on the " .
+     "number of features in the chado database this may take a long " .
      "time to complete. "),
   );
 
   $form['feature_types'] = array(
     '#title'       => t('Feature Types'),
     '#type'        => 'textarea',
-    '#description' => t('Enter the names of the sequence types that the ".
-       "site will support with independent pages.  Pages for these data ".
-       "types will be built automatically for features that exist in the ".
-       "chado database.  The names listed here should be spearated by ".
-       "spaces or entered separately on new lines. The names must match ".
-       "exactly (spelling and case) with terms in the sequence ontology'),
+    '#description' => t("Enter the names of the feature types to sync.  Pages for these feature " .
+       "types will be created automatically for features that exist in the " .
+       "chado database.  The names listed here should be spearated by " .
+       "spaces or entered separately on new lines. The names must match " .
+       "exactly (spelling and case) with terms in the sequence ontology"),
     '#required'    => TRUE,
     '#default_value' => variable_get('chado_sync_feature_types', 'gene contig'),
   );
@@ -71,7 +70,7 @@ function tripal_feature_sync_form() {
   $form['organism_id'] = array(
     '#title'       => t('Organism'),
     '#type'        => t('select'),
-    '#description' => t("Choose the organism for which features set above will be synced. Only organisms which also have been synced will appear in this list."),
+    '#description' => t("Choose the organism for which features types set above will be synced. Only organisms which also have been synced will appear in this list."),
     '#options'     => $organisms,
   );
 
@@ -107,7 +106,7 @@ function tripal_feature_sync_form_submit($form, &$form_state) {
 
   if ($organism_id) {
     $organism = tripal_core_chado_select('organism', array('genus', 'species'), array('organism_id' => $organism_id));
-    $title = "Sync all features for " .  $organism[0]->genus . " " . $organism[0]->species;
+    $title = "Sync all features for " . $organism[0]->genus . " " . $organism[0]->species;
   }
   else {
     $title = t('Sync all features for all synced organisms');
@@ -115,59 +114,183 @@ function tripal_feature_sync_form_submit($form, &$form_state) {
 
   variable_set('chado_sync_feature_types', $feature_types);
 
-  tripal_add_job($title, 'tripal_feature',
-    'tripal_feature_sync_features', $job_args, $user->uid);
+  tripal_add_job($title, 'tripal_feature', 'tripal_feature_sync_features', $job_args, $user->uid);
 }
 /**
- *
+ *  
+ * @param $na 
+ *   Tripal expects all jobs to have at least one argument. For this function
+ *   we don't need any, so we have this dummy argument as a filler
+ * @param $job_id
  */
-function tripal_feature_set_urls($job_id = NULL) {
-  // first get the list of features that have been synced
+function tripal_feature_set_urls($na = NULL, $job = NULL) {
+  
+  // begin the transaction
+  db_query("BEGIN");
+      
+  print "\nNOTE: Setting of URLs is performed using a database transaction. \n" .
+        "If the load fails or is terminated prematurely then the entire set of \n" .
+        "new URLs will be rolled back and no changes will be made\n\n";
+  
+  // get the number of records we need to set URLs for
+  $csql = "SELECT count(*) FROM {chado_feature}";
+  $num_nodes = db_result(db_query($csql));
+    
+  // calculate the interval at which we will print an update on the screen
+  $num_set = 0;
+  $num_per_interval = 100;
+  
+  // prepate the statements which will quickly add url alias. Because these
+  // are not Chado tables we must manually prepare them 
+  $psql = "
+    PREPARE del_url_alias_by_src (text) AS
+    DELETE FROM {url_alias} WHERE src = \$1
+  ";
+  db_query($psql);
+  $psql = "
+    PREPARE ins_url_alias_nisrds (text, text) AS
+    INSERT INTO url_alias (src, dst) VALUES (\$1, \$2)
+  ";
+  db_query($psql);
+  
+  // get the URL alias syntax string
+  $url_alias = variable_get('chado_feature_url_string', '/feature/[genus]/[species]/[type]/[uniquename]'); 
+  if (!$url_alias) {
+    $url_alias = '/feature/[genus]/[species]/[type]/[uniquename]';
+  } 
+  $url_alias = preg_replace('/^\//', '', $url_alias); // remove any preceeding forward slash
+  
+  
+  // get the list of features that have been synced
   $sql = "SELECT * FROM {chado_feature}";
-  $nodes = db_query($sql);
+  $nodes = db_query($sql);  
   while ($node = db_fetch_object($nodes)) {
-    // now get the feature details
-    $sql = "SELECT * 
-            FROM {feature} F
-              INNER JOIN {organism} O on O.organism_id = F.organism_id
-            WHERE F.feature_id = %d";     
-    $feature = db_fetch_object(chado_query($sql, $node->feature_id));
-    if ($feature) {
-      tripal_feature_set_feature_url($node, $feature);
+   
+    // get the URL alias
+    $src = "node/$node->nid";
+    $dst = tripal_feature_get_feature_url($node, $url_alias);
+    if (!$dst) {
+      db_query('DEALLOCATE "del_url_alias_by_src"');
+      db_query('DEALLOCATE "ins_url_alias_nisrds"');
+      db_query("ROLLBACK"); 
+      return; 
+    }    
+    
+    // if the src and dst is the same (the URL alias couldn't be set)
+    // then skip to the next one. There's nothing we can do about this one.
+    if ($src == $dst) {
+      continue;
+    }
+    
+    // remove any previous alias and then add the new one
+    $success = db_query("EXECUTE del_url_alias_by_src('%s')", $src);    
+    if (!$success) {
+      db_query('DEALLOCATE "del_url_alias_by_src"');
+      db_query('DEALLOCATE "ins_url_alias_nisrds"');
+      db_query("ROLLBACK");
+      watchdog('trp-seturl', "Failed Removing URL Alias: %src", array('%src' => $src), WATCHDOG_ERROR);
+      return;
+    }
+    $success = db_query("EXECUTE ins_url_alias_nisrds('%s', '%s')", $src, $dst);
+    if (!$success) {
+      db_query('DEALLOCATE "del_url_alias_by_src"');
+      db_query('DEALLOCATE "ins_url_alias_nisrds"');
+      db_query("ROLLBACK");
+      watchdog('trp-seturl', "Failed Adding URL Alias: %dst", array('%dst' => $dst), WATCHDOG_ERROR);
+      return;
     }
+
+    // update the job status every 1% features
+    if ($job and $num_set % $num_per_interval == 0) {
+      $percent = ($num_set / $num_nodes) * 100;
+      tripal_job_set_progress($job, intval($percent));
+      $percent = sprintf("%.2f", $percent);
+      print "Setting URLs (" . $percent . "%). Memory: " . number_format(memory_get_usage()) . " bytes.\r";
+      
+    }
+    $num_set++;
   }
+  $percent = ($num_set / $num_nodes) * 100;
+  tripal_job_set_progress($job, intval($percent));
+  $percent = sprintf("%.2f", $percent);
+  print "Setting URLs (" . $percent . "%). Memory: " . number_format(memory_get_usage()) . " bytes.\r";
+  print "\nDone. Set " . number_format($num_set) . " URLs\n";
+  
+  // unprepare the statements
+  db_query('DEALLOCATE "del_url_alias_by_src"');
+  db_query('DEALLOCATE "ins_url_alias_nisrds"');
+  
+  db_query("COMMIT");
 }
 /**
- *
+ * 
+ * @param $node
+ *   A node object containing at least the feature_id and nid
+ * @param $url_alias
+ *   Optional.  This should be the URL alias syntax string that contains
+ *   placeholders such as [id], [genus], [species], [name], [uniquename],
+ *   and [type].  These placeholders will be substituted for actual values.
+ *   If this parameter is not provided then the value of the 
+ *   chado_feature_url_string Drupal variable will be used.
  */
-function tripal_feature_set_feature_url($node, $feature) {
-
-  // determine which URL alias to use
-  $alias_type = variable_get('chado_feature_url', 'internal ID');
-  $aprefix = variable_get('chado_feature_accession_prefix', 'ID');  
-  $genus = preg_replace('/\s/', '_', strtolower($feature->genus));
-  $species = preg_replace('/\s/', '_', strtolower($feature->species));    
-  switch ($alias_type) {
-    case 'feature name':
-      $url_alias = $feature->name;
-      break;
-    case 'feature unique name':
-      $url_alias = $feature->uniquename;
-      break;
-    case 'genus_species_uqname':
-      $url_alias = $genus . "/" . $genus . "_" . $species . "/" . $feature->uniquename;
-      break;
-    case 'genus species name':
-      $url_alias = $genus . "/" . $genus . "_" . $species . "/" . $feature->name;
-      break;
-    default:
-      $url_alias = "$aprefix$feature->feature_id";
+function tripal_feature_get_feature_url($node, $url_alias = NULL) {
+
+  // get the starting URL alias
+  if (!$url_alias) {
+    $url_alias = variable_get('chado_feature_url_string', '/feature/[genus]/[species]/[type]/[uniquename]'); 
+    if (!$url_alias) {
+      $url_alias = '/feature/[genus]/[species]/[type]/[uniquename]';
+    } 
+    $url_alias = preg_replace('/^\//', '', $url_alias); // remove any preceeding forward slash
   }
-  print "Setting URL alias for $feature->name: node/$node->nid => $url_alias\n";
-  // remove any previous alias
-  db_query("DELETE FROM {url_alias} WHERE src = '%s'", "node/$node->nid");
-  // add the new alias
-  path_set_alias("node/$node->nid", $url_alias);
+
+  // get the feature 
+  $values = array('feature_id' => $node->feature_id); 
+  $options = array('statement_name' => 'sel_feature_id');       
+  $feature = tripal_core_chado_select('feature', array('*'), $values, $options);
+  if (!$feature) {
+    watchdog('trp-seturl', "Cannot find feature when setting URL alias for feature: %id", array('%id' => $node->feature_id), WATCHDOG_ERROR);
+    return FALSE;  
+  }
+  $feature = (object) $feature[0];
+  
+  // get the organism
+  $values = array('organism_id' => $feature->organism_id);
+  $options = array('statement_name' => 'sel_organism_id');
+  $organism  = tripal_core_chado_select('organism', array('*'), $values, $options);  
+  if (!$organism) {
+    watchdog('trp-seturl', "Cannot find organism when setting URL alias for feature: %id", array('%id' => $node->feature_id), WATCHDOG_ERROR);
+    return FALSE;  
+  }
+  $genus = preg_replace('/\s/', '_', strtolower($organism[0]->genus));
+  $species = preg_replace('/\s/', '_', strtolower($organism[0]->species)); 
+
+  // get the type
+  $values = array('cvterm_id' => $feature->type_id);
+  $options = array('statement_name' => 'sel_cvterm_id');
+  $cvterm = tripal_core_chado_select('cvterm', array('name'), $values, $options);
+  if (!$cvterm) {
+    watchdog('trp-seturl', "Cannot find type when setting URL alias for feature: %id", array('%id' => $node->feature_id), WATCHDOG_ERROR);
+    return FALSE;  
+  }
+  $type = preg_replace('/\s/', '_', $cvterm[0]->name);
+  
+  // now substitute in the values
+  $url_alias = preg_replace('/\[id\]/', $feature->feature_id, $url_alias);
+  $url_alias = preg_replace('/\[genus\]/', $genus, $url_alias);
+  $url_alias = preg_replace('/\[species\]/', $species, $url_alias);
+  $url_alias = preg_replace('/\[type\]/', $type, $url_alias);
+  $url_alias = preg_replace('/\[name\]/', $feature->name, $url_alias);
+  $url_alias = preg_replace('/\[uniquename\]/', $feature->uniquename, $url_alias);
+ 
+  // the dst field of the url_alias table is only 128 characters long. 
+  // if this is the case then simply return the node URL, we can't set this one
+  if (strlen($url_alias) > 128) {
+    watchdog('trp-seturl', "Cannot set alias longer than 128 characters: %alias.", array('%alias' => $url_alias), WATCHDOG_ERROR);
+    return "node/" . $node->nid;
+  }
+  
+  return $url_alias;
 }
 /**
  *
@@ -176,7 +299,7 @@ function tripal_feature_set_feature_url($node, $feature) {
  */
 function tripal_feature_sync_features($max_sync = 0, $organism_id = NULL,
   $feature_types = NULL, $job_id = NULL) {
-  //print "Syncing features (max of $max_sync)\n";
+  
   $i = 0;
 
   // get the list of available sequence ontology terms for which
@@ -218,11 +341,11 @@ function tripal_feature_sync_features($max_sync = 0, $organism_id = NULL,
   $where_org = drupal_substr($where_org, 0, drupal_strlen($where_org)-3);  # strip trailing 'OR'
 
   // use this SQL statement to get the features that we're going to upload
-  $sql = "SELECT feature_id ".
-        "FROM {FEATURE} F ".
-        "  INNER JOIN {Cvterm} CVT ON F.type_id = CVT.cvterm_id ".
-        "  INNER JOIN {CV} on CV.cv_id = CVT.cv_id ".
-        "WHERE ($where_cvt) AND ($where_org) AND CV.name = 'sequence' ".
+  $sql = "SELECT feature_id " .
+        "FROM {FEATURE} F " .
+        "  INNER JOIN {Cvterm} CVT ON F.type_id = CVT.cvterm_id " .
+        "  INNER JOIN {CV} on CV.cv_id = CVT.cv_id " .
+        "WHERE ($where_cvt) AND ($where_org) AND CV.name = 'sequence' " .
         "ORDER BY feature_id";
 
   // get the list of features
@@ -268,7 +391,7 @@ function tripal_feature_sync_features($max_sync = 0, $organism_id = NULL,
       # to avoid this problem we will call this script through an
       # independent system call
       print "$i of $num_ids Syncing feature id: $feature_id\n";
-      $cmd = "php " . drupal_get_path('module', 'tripal_feature') . "/includes/syncFeatures.inc -f $feature_id ";
+      $cmd = "php " . drupal_get_path('module', 'tripal_feature') . "/includes/tripal_feature.sync_features.inc -f $feature_id -t chado_feature";
       system($cmd);
 
     }
@@ -290,7 +413,7 @@ function tripal_feature_sync_feature($feature_id) {
   $create_node = 1;   // set to 0 if the node exists and we just sync and not create
 
   // get the accession prefix
-  $aprefix = variable_get('chado_feature_accession_prefix', 'ID');
+  $aprefix = variable_get('chado_feature_accession_prefix', 'FID');
 
   // if we don't have a feature_id then return
   if (!$feature_id) {
@@ -299,18 +422,18 @@ function tripal_feature_sync_feature($feature_id) {
   }
 
   // get information about this feature
-  $fsql = "SELECT F.feature_id, F.name, F.uniquename,O.genus, ".
-         "    O.species,CVT.name as cvname,F.residues,F.organism_id ".
-         "FROM {FEATURE} F ".
-         "  INNER JOIN {Cvterm} CVT ON F.type_id = CVT.cvterm_id ".
-         "  INNER JOIN {Organism} O ON F.organism_id = O.organism_ID ".
+  $fsql = "SELECT F.feature_id, F.name, F.uniquename,O.genus, " .
+         "    O.species,CVT.name as cvname,F.residues,F.organism_id " .
+         "FROM {FEATURE} F " .
+         "  INNER JOIN {Cvterm} CVT ON F.type_id = CVT.cvterm_id " .
+         "  INNER JOIN {Organism} O ON F.organism_id = O.organism_ID " .
          "WHERE F.feature_id = %d";
   $feature = db_fetch_object(chado_query($fsql, $feature_id));
 
   // get the synonyms for this feature
-  $synsql = "SELECT S.name ".
-            "FROM {feature_synonym} FS ".
-            "  INNER JOIN {synonym} S on FS.synonym_id = S.synonym_id ".
+  $synsql = "SELECT S.name " .
+            "FROM {feature_synonym} FS " .
+            "  INNER JOIN {synonym} S on FS.synonym_id = S.synonym_id " .
             "WHERE FS.feature_id = %d";
   $synonyms = chado_query($synsql, $feature_id);
 
@@ -325,9 +448,9 @@ function tripal_feature_sync_feature($feature_id) {
   // but without a corresponding entry in the chado_feature table if so then we want to
   // clean up that node.  (If a node is found we don't know if it belongs to our feature or
   // not since features can have the same name/title.)
-  $tsql =  "SELECT * FROM {node} N ".
+  $tsql =  "SELECT * FROM {node} N " .
            "WHERE title = '%s'";
-  $cnsql = "SELECT * FROM {chado_feature} ".
+  $cnsql = "SELECT * FROM {chado_feature} " .
            "WHERE nid = %d";
   $nodes = db_query($tsql, $feature->name);
   // cycle through all nodes that may have this title
@@ -341,10 +464,10 @@ function tripal_feature_sync_feature($feature_id) {
 
   // check if this feature already exists in the chado_feature table.
   // if we have a chado feature, we want to check to see if we have a node
-  $cfsql = "SELECT * FROM {chado_feature} ".
+  $cfsql = "SELECT * FROM {chado_feature} " .
            "WHERE feature_id = %d";
   // @coder-ignore: don't need to use db_rewrite_sql() since need all nodes regardless of access control
-  $nsql =  "SELECT * FROM {node} N ".
+  $nsql =  "SELECT * FROM {node} N " .
            "WHERE nid = %d";
   $chado_feature = db_fetch_object(db_query($cfsql, $feature->feature_id));
   if ($chado_feature) {
@@ -413,13 +536,6 @@ function tripal_feature_sync_feature($feature_id) {
   drupal_set_message(t("%feature_id ($node->nid): setting taxonomy", array('%feature_id' => $feature_id)));
   tripal_feature_set_taxonomy($node, $feature_id);
 
-  // reindex the node
-  // drupal_set_message(t("$feature_id( $node->nid): indexing"));
-  // tripal_feature_index_feature ($feature_id,$node->nid);
-
-  // set the URL alias for this node
-  tripal_feature_set_feature_url($node, $feature);
-
 
   return '';
 }

+ 12 - 3
tripal_feature/tripal_feature.drush.inc

@@ -41,6 +41,8 @@ function tripal_feature_drush_command() {
       'parent'   => dt('Set this argument to 1 to retrieve the sequence from the parent in an alignment rather than the residues column of the feature itself.'),
       'agg'      => dt('Set this argument to 1 to aggregate sub features into a single sequence.  This is useful, for example, for obtaining CDS sequence from an mRNA'),
       'child'    => dt('Set this argument to the sequence ontology term for the children to aggregate.  This is useful in the case where a gene has exons as well as CDSs and UTRs.  You may sepcify as many feature types as desired by separating each with a single comma (no spaces).'),
+      'relationship'  => dt('Retreives the sequence of any feature in the specified relationship with the matched features.'),
+      'rel_part' => dt('If a relationship is provided, then this will be "subject" or "object" indicating the side of the relationship for the matched features. If the matched features are the "object" then the "subject" features will have their sequences included in the output (and vice versa).'),
     ),
     'examples' => array(
       'Standard example' => 'drush tripal-current-job',
@@ -79,10 +81,17 @@ function drush_tripal_feature_tripal_get_sequence() {
   $derive_from_parent = drush_get_option('parent');
   $aggregate = drush_get_option('agg');
   $child = drush_get_option('child');
-  
+  $relationship = drush_get_option('relationship');
+  $rel_part = drush_get_option('rel_part');
+
+  if ($relationship and !$rel_part) {
+    print "Please specify both 'relationship' and a 'rel_part' arguments. Both must be used together\n";
+    return;
+  }
+    
   tripal_feature_seq_extract_get_features($org_commonname, $genus, $species, $analysis_name, 
     $type, $feature_name, $upstream, $downstream, $output_format, $derive_from_parent, 
-    $aggregate, $child);   
+    $aggregate, $child, $relationship, $rel_part);
 }
 /*
  * 
@@ -90,4 +99,4 @@ function drush_tripal_feature_tripal_get_sequence() {
 function drush_tripal_feature_sync() {
   $feature_id = drush_get_option('id');  
   tripal_feature_sync_feature($feature_id);
-}
+}

+ 2 - 2
tripal_feature/tripal_feature.info

@@ -1,9 +1,9 @@
 name = Tripal Feature
 description = A module for interfacing the GMOD chado database with Drupal, providing viewing, inserting and editing of chado features.
-core = 6.x
+core = 7.x
 project = tripal_feature
 package = Tripal
-version = 6.x-1.1
+version = 7.x-2.0
 dependencies[] = search
 dependencies[] = path
 dependencies[] = tripal_core

+ 4 - 4
tripal_feature/tripal_feature.install

@@ -72,10 +72,10 @@ function tripal_feature_add_organism_count_mview() {
     // table name
     $view_name,
     // table schema definition
-    'organism_id integer, genus character varying(255), '.
-    '  species character varying(255), '.
-    '  common_name character varying(255), '.
-    '  num_features integer, cvterm_id integer, '.
+    'organism_id integer, genus character varying(255), ' .
+    '  species character varying(255), ' .
+    '  common_name character varying(255), ' .
+    '  num_features integer, cvterm_id integer, ' .
     '  feature_type character varying(255)',
     // columns for indexing
     'organism_id,cvterm_id,feature_type',

+ 221 - 119
tripal_feature/tripal_feature.module

@@ -14,7 +14,7 @@
  */
 
 require_once "includes/tripal_feature.admin.inc";
-require_once "includes/syncFeatures.inc";
+require_once "includes/tripal_feature.sync_features.inc";
 require_once "includes/indexFeatures.inc";
 require_once "includes/fasta_loader.inc";
 require_once "includes/gff_loader.inc";
@@ -244,12 +244,10 @@ function tripal_feature_menu() {
 
   // the menu link for addressing any feature (by name, uniquename, synonym)
   $items['feature/%'] = array(
-    'title' => 'Matched Features',
-    'description' => 'Shows all features that match the provided ID.  If multiple features match even by name, uniquename or synonym then a page is presented to allow the user to select which one they intended.',
     'page callback' => 'tripal_feature_match_features_page',
     'page arguments' => array(1),
     'access arguments' => array('access chado_feature content'),
-    'type' => MENU_NORMAL_ITEM,
+    'type' => MENU_LOCAL_TASK,
   );
 
   return $items;
@@ -285,6 +283,10 @@ function tripal_feature_theme() {
        'arguments' => array('node' => NULL),
        'template' => 'tripal_feature_sequence',
     ),
+    'tripal_feature_proteins' => array(
+       'arguments' => array('node' => NULL),
+       'template' => 'tripal_feature_proteins',
+    ),
     'tripal_feature_synonyms' => array(
        'arguments' => array('node' => NULL),
        'template' => 'tripal_feature_synonyms',
@@ -499,7 +501,7 @@ function chado_feature_insert($node) {
     'type_id' => $type[0]->cvterm_id,
   );
   $feature = tripal_core_chado_select('feature', array('feature_id'), $values);
-
+    
   // add the genbank accession and synonyms
   chado_feature_add_synonyms($node->synonyms, $feature[0]->feature_id);
 
@@ -510,7 +512,7 @@ function chado_feature_insert($node) {
   $node_check = db_fetch_object(db_query($node_check_sql, $feature[0]->feature_id));
   if (!$node_check) {
     // next add the item to the drupal table
-    $sql = "INSERT INTO {chado_feature} (nid, vid, feature_id, sync_date) ".
+    $sql = "INSERT INTO {chado_feature} (nid, vid, feature_id, sync_date) " .
            "VALUES (%d, %d, %d, " . time() . ")";
       db_query($sql, $node->nid, $node->vid, $feature[0]->feature_id);
   }
@@ -560,7 +562,7 @@ function chado_feature_update($node) {
     );
     $options = array('return_record' => TRUE);
     $status = tripal_core_chado_update('feature', $match, $values, $options);
-
+    
     // add the genbank synonyms
     chado_feature_add_synonyms($node->synonyms, $feature_id);
   }
@@ -572,6 +574,8 @@ function chado_feature_update($node) {
     WATCHDOG_WARNING
     );
   }
+  
+
 }
 /**
  *
@@ -589,16 +593,16 @@ function chado_feature_delete($node) {
   }
 
   // remove the drupal content
-  $sql_del = "DELETE FROM {chado_feature} ".
-             "WHERE nid = %d ".
+  $sql_del = "DELETE FROM {chado_feature} " .
+             "WHERE nid = %d " .
              "AND vid = %d";
   db_query($sql_del, $node->nid, $node->vid);
-  $sql_del = "DELETE FROM {node} ".
-             "WHERE nid = %d ".
+  $sql_del = "DELETE FROM {node} " .
+             "WHERE nid = %d " .
              "AND vid = %d";
   db_query($sql_del, $node->nid, $node->vid);
-  $sql_del = "DELETE FROM {node_revisions} ".
-             "WHERE nid = %d ".
+  $sql_del = "DELETE FROM {node_revisions} " .
+             "WHERE nid = %d " .
              "AND vid = %d";
   db_query($sql_del, $node->nid, $node->vid);
 
@@ -645,27 +649,27 @@ function chado_feature_add_synonyms($synonyms, $feature_id) {
     // check to see if we have this accession number already in the database
     // if so then don't add it again. it messes up drupal if the insert fails.
     // It is possible for the accession number to be present and not the feature
-    $synonym_sql = "SELECT synonym_id FROM {synonym} ".
+    $synonym_sql = "SELECT synonym_id FROM {synonym} " .
                    "WHERE name = '%s'";
     $synonym = db_fetch_object(chado_query($synonym_sql, $syn));
     if (!$synonym) {
-      $synonym_isql = "INSERT INTO {synonym} (name,synonym_sgml,type_id) ".
-                      "VALUES ('%s','%s', ".
-                      "   (SELECT cvterm_id ".
-                      "    FROM {CVTerm} CVT ".
-                      "    INNER JOIN CV ON CVT.cv_id = CV.cv_id ".
+      $synonym_isql = "INSERT INTO {synonym} (name,synonym_sgml,type_id) " .
+                      "VALUES ('%s','%s', " .
+                      "   (SELECT cvterm_id " .
+                      "    FROM {CVTerm} CVT " .
+                      "    INNER JOIN CV ON CVT.cv_id = CV.cv_id " .
                       "    WHERE CV.name = 'feature_property' and CVT.name = 'synonym'))";
       if (!chado_query($synonym_isql, $syn, $syn)) {
         $error .= "Could not add synonym. ";
       }
       // now get the synonym we just added
-      $synonym_sql = "SELECT synonym_id FROM {synonym} ".
+      $synonym_sql = "SELECT synonym_id FROM {synonym} " .
                      "WHERE name = '%s'";
       $synonym = db_fetch_object(chado_query($synonym_sql, $syn));
     }
 
     // now add in our new sysnonym
-    $feature_syn_isql = "INSERT INTO {feature_synonym} (synonym_id,feature_id,pub_id) ".
+    $feature_syn_isql = "INSERT INTO {feature_synonym} (synonym_id,feature_id,pub_id) " .
                         "VALUES (%d,%d,1)";
     if (!chado_query($feature_syn_isql, $synonym->synonym_id, $feature_id)) {
       $error .= "Could not add synonyms to feature. ";
@@ -686,11 +690,11 @@ function chado_feature_add_gbaccession($accession, $feature_id) {
   // use chado database
 
   // remove any old accession from genbank dbEST
-  $fdbxref_dsql = "DELETE FROM {feature_dbxref} ".
-                 "WHERE feature_id = %d and dbxref_id IN ".
-                 "   (SELECT DBX.dbxref_id FROM {dbxref} DBX ".
-                 "    INNER JOIN DB  ON DB.db_id = DBX.db_id ".
-                 "    INNER JOIN feature_dbxref FDBX ON DBX.dbxref_id = FDBX.dbxref_id ".
+  $fdbxref_dsql = "DELETE FROM {feature_dbxref} " .
+                 "WHERE feature_id = %d and dbxref_id IN " .
+                 "   (SELECT DBX.dbxref_id FROM {dbxref} DBX " .
+                 "    INNER JOIN DB  ON DB.db_id = DBX.db_id " .
+                 "    INNER JOIN feature_dbxref FDBX ON DBX.dbxref_id = FDBX.dbxref_id " .
                  "    WHERE DB.name = 'DB:Genbank' and FDBX.feature_id = %d)";
   if (!chado_query($fdbxref_dsql, $feature_id, $feature_id)) {
     $error .= "Could not remove accession from feature. ";
@@ -701,32 +705,32 @@ function chado_feature_add_gbaccession($accession, $feature_id) {
     return;
   }
   // get the db_id
-  $db_sql = "SELECT db_id FROM {DB} ".
+  $db_sql = "SELECT db_id FROM {DB} " .
             "WHERE name = 'DB:Genbank_est'";
   $db = db_fetch_object(chado_query($db_sql));
 
   // check to see if we have this accession number already in the database
   // if so then don't add it again. it messes up drupal if the insert fails.
   // It is possible for the accession number to be present and not the feature
-  $dbxref_sql = "SELECT dbxref_id FROM {dbxref} ".
+  $dbxref_sql = "SELECT dbxref_id FROM {dbxref} " .
                 "WHERE db_id = %d and accession = '%s'";
   $dbxref = db_fetch_object(chado_query($dbxref_sql, $db->db_id, $accession));
   if (!$dbxref) {
     // add the accession number
-    $dbxref_isql = "INSERT INTO {dbxref} (db_id,accession) ".
+    $dbxref_isql = "INSERT INTO {dbxref} (db_id,accession) " .
                    "  VALUES (%d, '%s') ";
     if (!chado_query($dbxref_isql, $db->db_id, $accession)) {
       $error .= 'Could not add accession as a database reference ';
     }
     // get the dbxref_id for the just added accession number
-    $dbxref_sql = "SELECT dbxref_id FROM {dbxref} ".
+    $dbxref_sql = "SELECT dbxref_id FROM {dbxref} " .
                   "WHERE db_id = %d and accession = '%s'";
     $dbxref = db_fetch_object(chado_query($dbxref_sql, $db->db_id, $accession));
   }
 
 
   // associate the accession number with the feature
-  $feature_dbxref_isql = "INSERT INTO {feature_dbxref} (feature_id,dbxref_id) ".
+  $feature_dbxref_isql = "INSERT INTO {feature_dbxref} (feature_id,dbxref_id) " .
                          "  VALUES (%d, %d) ";
   if (!chado_query($feature_dbxref_isql, $feature_id, $dbxref->dbxref_id)) {
     $error .= 'Could not add feature database reference. ';
@@ -797,6 +801,7 @@ function chado_feature_form($node, $param) {
     '#value' => $feature->feature_id,
   );
 
+  /*
   $form['title']= array(
     '#type' => 'textfield',
     '#title' => t('Title'),
@@ -804,7 +809,7 @@ function chado_feature_form($node, $param) {
     '#default_value' => $node->title,
     '#description' => t('The title must be a unique identifier for this feature.  It is recommended to use a combination of uniquename, organism and feature type in the title as this is guranteed to be unique.'),
     '#maxlength' => 255
-  );
+  );*/
 
   $form['uniquename']= array(
     '#type' => 'textfield',
@@ -906,7 +911,7 @@ function chado_feature_validate($node) {
   // make sure the feature type is a real sequence ontology term
   $type = tripal_cv_get_cvterm_by_name($node->feature_type, NULL, 'sequence');
   if (!$type) {
-     form_set_error('feature_type', t("The feature type is not a valid name from the Sequence Ontology."));	
+    form_set_error('feature_type', t("The feature type is not a valid name from the Sequence Ontology."));
   }
 
   // if this is an update, we want to make sure that a different feature for
@@ -942,7 +947,6 @@ function chado_feature_validate($node) {
   if ($node->feature_type == 'contig' and $node->gbaccession) {
     form_set_error('gbaccession', t("Contigs cannot have a genbank accession number.  Please change the feature type or remove the accession number"));
   }
-
 }
 /**
  *  When a node is requested by the user this function is called to allow us
@@ -958,8 +962,26 @@ function chado_feature_load($node) {
   $values = array('feature_id' => $feature_id);
   $feature = tripal_core_generate_chado_var('feature', $values);
 
-  if (strcmp($feature->name, $feature->uniquename)==0) {
-     $node->title = $feature->name . " (" . $feature->type_id->name . ") " . $feature->organism_id->genus . " " . $feature->organism_id->species ;
+  // by default, the titles are saved using the unique constraint.  We will
+  // keep it the same, but remove the duplicate name if the unique name and name
+  // are identical
+  $title_type = variable_get('chado_feature_title', 'unique_constraint');
+  if ($title_type == 'unique_constraint') {
+    if (strcmp($feature->name, $feature->uniquename)==0) {
+      $node->title = $feature->name . " (" . $feature->type_id->name . ") " . $feature->organism_id->genus . " " . $feature->organism_id->species ;
+    }
+    // in previous version of Tripal, the feature title was simply the unique name. 
+    // so, we recreate the title just to be sure all of our feature pages are consistent
+    else {
+      $node->title = $feature->name . ", " . $feature->uniquename . " (" . $feature->type_id->name . ") " . $feature->organism_id->genus . " " . $feature->organism_id->species ;  
+    }
+  }
+  // set the title to be the feature name or uniquename as configured
+  if ($title_type == 'feature_name') {
+    $node->title = $feature->name;
+  }
+  if ($title_type == 'feature_unique_name') {
+    $node->title = $feature->uniquename;  
   }
 
   $additions = new stdClass();
@@ -984,11 +1006,11 @@ function tripal_feature_load_organism($organism_id) {
  */
 function tripal_feature_load_synonyms($feature_id) {
 
-  $sql = "SELECT S.name ".
-        "FROM {Feature_Synonym} FS ".
-        "  INNER JOIN {Synonym} S ".
-        "    ON FS.synonym_id = S.Synonym_id ".
-        "WHERE FS.feature_id = %d ".
+  $sql = "SELECT S.name " .
+        "FROM {Feature_Synonym} FS " .
+        "  INNER JOIN {Synonym} S " .
+        "    ON FS.synonym_id = S.Synonym_id " .
+        "WHERE FS.feature_id = %d " .
         "ORDER BY S.name ";
   $results = chado_query($sql, $feature_id);
   $synonyms = array();
@@ -1030,13 +1052,13 @@ function tripal_feature_load_properties($feature_id) {
  */
 function tripal_feature_load_references($feature_id) {
 
-  $sql = "SELECT F.uniquename,F.Feature_id,DBX.accession,DB.description as dbdesc, ".
-        "   DB.db_id, DB.name as db_name, DB.urlprefix,DBX.dbxref_id ".
-        "FROM {feature} F ".
-        "  INNER JOIN {feature_dbxref} FDBX on F.feature_id = FDBX.feature_id ".
-        "  INNER JOIN {dbxref} DBX on DBX.dbxref_id = FDBX.dbxref_id ".
-        "  INNER JOIN {db} on DB.db_id = DBX.db_id ".
-        "WHERE F.feature_id = %d ".
+  $sql = "SELECT F.uniquename,F.Feature_id,DBX.accession,DB.description as dbdesc, " .
+        "   DB.db_id, DB.name as db_name, DB.urlprefix,DBX.dbxref_id " .
+        "FROM {feature} F " .
+        "  INNER JOIN {feature_dbxref} FDBX on F.feature_id = FDBX.feature_id " .
+        "  INNER JOIN {dbxref} DBX on DBX.dbxref_id = FDBX.dbxref_id " .
+        "  INNER JOIN {db} on DB.db_id = DBX.db_id " .
+        "WHERE F.feature_id = %d " .
         "ORDER BY DB.name ";
   $results = chado_query($sql, $feature_id);
   $references = array();
@@ -1223,7 +1245,7 @@ function tripal_feature_load_featureloc_sequences($feature_id, $featurelocs) {
     $rel_featurelocs = tripal_feature_load_featurelocs($rel->subject_id, 'as_child', 0);
     foreach ($rel_featurelocs as $rfindex => $rel_featureloc) {
       // keep track of this unique source feature
-      $src = $rel_featureloc->src_feature_id ."-". $rel_featureloc->src_cvterm_id;
+      $src = $rel_featureloc->src_feature_id . "-" . $rel_featureloc->src_cvterm_id;
 
       // copy over the results to the relationship object.  Since there can
       // be more than one feature location for each relationship feature we
@@ -1249,14 +1271,14 @@ function tripal_feature_load_featureloc_sequences($feature_id, $featurelocs) {
   // where this feature is found.   We want to get the sequence for each
   // location and then annotate it with the parts found from the relationships
   // locations determiend above.
-  $sql = "SELECT substring(residues from %d for %d) as residues ".
-         "FROM {feature} ".
+  $sql = "SELECT substring(residues from %d for %d) as residues " .
+         "FROM {feature} " .
          "WHERE feature_id = %d";
   $floc_sequences = array();
   foreach ($featurelocs as $featureloc) {
 
     // build the src name so we can keep track of the different parts for each feature
-    $src = $featureloc->srcfeature_id->feature_id ."-". $featureloc->srcfeature_id->type_id->cvterm_id;
+    $src = $featureloc->srcfeature_id->feature_id . "-" . $featureloc->srcfeature_id->type_id->cvterm_id;
 
     // orient the parts to the beginning of the feature sequence
     if (!empty($rel_locs[$src]['parts'])) {
@@ -1343,45 +1365,48 @@ function tripal_feature_get_matched_alignments($feature) {
   // not want to include these, so we have to filter on the SO terms:
   // match, or %_match
   //
-   $sql = "SELECT  ".
-          "   FL1.featureloc_id    as left_featureloc_id, ".
-          "   FL1.srcfeature_id    as left_srcfeature_id, ".
-          "   FL1.feature_id       as left_feature_id, ".
-          "   FL1.fmin             as left_fmin, ".
-          "   FL1.is_fmin_partial  as left_is_fmin_partial, ".
-          "   FL1.fmax             as left_fmax, ".
-          "   FL1.is_fmax_partial  as left_is_fmax_partial, ".
-          "   FL1.strand           as left_strand,  ".
-          "   FL1.phase            as left_phase, ".
-          "   FL1.locgroup         as left_locgroup, ".
-          "   FL1.rank             as left_rank, ".
-          "   FL2.featureloc_id    as right_featureloc_id, ".
-          "   FL2.srcfeature_id    as right_srcfeature_id, ".
-          "   FL2.feature_id       as right_feature_id, ".
-          "   FL2.fmin             as right_fmin, ".
-          "   FL2.is_fmin_partial  as right_is_fmin_partial, ".
-          "   FL2.fmax             as right_fmax, ".
-          "   FL2.is_fmax_partial  as right_is_fmax_partial, ".
-          "   FL2.strand           as right_strand,  ".
-          "   FL2.phase            as right_phase, ".
-          "   FL2.locgroup         as right_locgroup, ".
-          "   FL2.rank             as right_rank ".
-          "FROM {feature} F1 ".
-          "  INNER JOIN {featureloc} FL1 on FL1.srcfeature_id = F1.feature_id ".
-          "  INNER JOIN {feature} F2 on FL1.feature_id = F2.feature_id ".
-          "  INNER JOIN {featureloc} FL2 on FL2.feature_id = F2.feature_id ".
-          "  INNER JOIN {feature} F3 on FL2.srcfeature_id = F3.feature_id ".
-          "  INNER JOIN {cvterm} CVT2 on F2.type_id = CVT2.cvterm_id ".
-          "WHERE F1.feature_id = %d and NOT F3.feature_id = %d ".
-          "  AND (CVT2.name = 'match' or CVT2.name like '%_match') ".
+   $sql = "SELECT  " .
+          "   FL1.featureloc_id    as left_featureloc_id, " .
+          "   FL1.srcfeature_id    as left_srcfeature_id, " .
+          "   FL1.feature_id       as left_feature_id, " .
+          "   FL1.fmin             as left_fmin, " .
+          "   FL1.is_fmin_partial  as left_is_fmin_partial, " .
+          "   FL1.fmax             as left_fmax, " .
+          "   FL1.is_fmax_partial  as left_is_fmax_partial, " .
+          "   FL1.strand           as left_strand,  " .
+          "   FL1.phase            as left_phase, " .
+          "   FL1.locgroup         as left_locgroup, " .
+          "   FL1.rank             as left_rank, " .
+          "   FL2.featureloc_id    as right_featureloc_id, " .
+          "   FL2.srcfeature_id    as right_srcfeature_id, " .
+          "   FL2.feature_id       as right_feature_id, " .
+          "   FL2.fmin             as right_fmin, " .
+          "   FL2.is_fmin_partial  as right_is_fmin_partial, " .
+          "   FL2.fmax             as right_fmax, " .
+          "   FL2.is_fmax_partial  as right_is_fmax_partial, " .
+          "   FL2.strand           as right_strand,  " .
+          "   FL2.phase            as right_phase, " .
+          "   FL2.locgroup         as right_locgroup, " .
+          "   FL2.rank             as right_rank " .
+          "FROM {feature} F1 " .
+          "  INNER JOIN {featureloc} FL1 on FL1.srcfeature_id = F1.feature_id " .
+          "  INNER JOIN {feature} F2 on FL1.feature_id = F2.feature_id " .
+          "  INNER JOIN {featureloc} FL2 on FL2.feature_id = F2.feature_id " .
+          "  INNER JOIN {cvterm} CVT2 on F2.type_id = CVT2.cvterm_id " .
+          "WHERE F1.feature_id = %d " .
+          "  AND (CVT2.name = 'match' or CVT2.name like '%_match') " .
           "ORDER BY FL1.fmin";
 
-   $results = chado_query($sql, $feature->feature_id, $feature->feature_id);
+   $results = chado_query($sql, $feature->feature_id);
 
    // iterate through the results and add them to our featurelocs array
    $featurelocs = array();
    while ($fl = db_fetch_object($results)) {
-      $featurelocs[] = $fl ;
+     // ignore featurelocs where the left and right srcfeature is the same
+     if (strcmp($fl->left_srcfeature_id, $fl->right_srcfeature_id) == 0) {
+       continue;
+     }
+     $featurelocs[] = $fl ;
    }
    return $featurelocs;
 }
@@ -1546,28 +1571,28 @@ function tripal_feature_load_library_feature_browser($library) {
   $where_cvt = drupal_substr($where_cvt, 0, drupal_strlen($where_cvt)-3);  # strip trailing 'OR'
 
   // get the features for this library
-  $sql  = "SELECT F.name,F.feature_id,F.uniquename,CVT.name as cvname ".
-         "FROM {feature} F ".
-            "  INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id ".
-            "  INNER JOIN {library_feature} LF on F.feature_id = LF.feature_id ".
-            "  INNER JOIN {library} L on LF.library_id = L.library_id ".
-          "WHERE LF.library_id = %d and ($where_cvt) ".
+  $sql  = "SELECT F.name,F.feature_id,F.uniquename,CVT.name as cvname " .
+         "FROM {feature} F " .
+            "  INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id " .
+            "  INNER JOIN {library_feature} LF on F.feature_id = LF.feature_id " .
+            "  INNER JOIN {library} L on LF.library_id = L.library_id " .
+          "WHERE LF.library_id = %d and ($where_cvt) " .
           "ORDER BY feature_id ASC";
 
   // the counting SQL
-  $csql  = "SELECT count(*) ".
-          "FROM {feature} F".
-            "  INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id ".
-            "  INNER JOIN {library_feature} LF on F.feature_id = LF.feature_id ".
-            "  INNER JOIN {library} L on LF.library_id = L.library_id ".
-          "WHERE LF.library_id = %d and ($where_cvt) ".
+  $csql  = "SELECT count(*) " .
+          "FROM {feature} F" .
+            "  INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id " .
+            "  INNER JOIN {library_feature} LF on F.feature_id = LF.feature_id " .
+            "  INNER JOIN {library} L on LF.library_id = L.library_id " .
+          "WHERE LF.library_id = %d and ($where_cvt) " .
           "GROUP BY L.library_id ";
 
   $org_features = chado_pager_query($sql, 10, 0, $csql, $library->library_id);
   $pager = theme('pager');
 
   // prepare the query that will lookup node ids
-  $sql = "SELECT nid FROM {chado_feature} ".
+  $sql = "SELECT nid FROM {chado_feature} " .
          "WHERE feature_id = %d";
   $i=0;
   $features = array();
@@ -1606,28 +1631,28 @@ function tripal_feature_load_analysis_feature_browser($analysis) {
   $where_cvt = drupal_substr($where_cvt, 0, drupal_strlen($where_cvt)-3);  # strip trailing 'OR'
 
   // get the features for this library
-  $sql  = "SELECT F.name,F.feature_id,F.uniquename,CVT.name as cvname ".
-         "FROM {feature} F ".
-            "  INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id ".
-            "  INNER JOIN {analysisfeature} AF on F.feature_id = AF.feature_id ".
-            "  INNER JOIN {analysis} A on AF.analysis_id = A.analysis_id ".
-          "WHERE A.analysis_id = %d and ($where_cvt) ".
+  $sql  = "SELECT F.name,F.feature_id,F.uniquename,CVT.name as cvname " .
+         "FROM {feature} F " .
+            "  INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id " .
+            "  INNER JOIN {analysisfeature} AF on F.feature_id = AF.feature_id " .
+            "  INNER JOIN {analysis} A on AF.analysis_id = A.analysis_id " .
+          "WHERE A.analysis_id = %d and ($where_cvt) " .
           "ORDER BY feature_id ASC";
 
   // the counting SQL
-  $csql  = "SELECT count(*) ".
-          "FROM {feature} F".
-            "  INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id ".
-            "  INNER JOIN {analysisfeature} AF on F.feature_id = AF.feature_id ".
-            "  INNER JOIN {analysis} A on AF.analysis_id = A.analysis_id ".
-          "WHERE A.analysis_id = %d and ($where_cvt) ".
+  $csql  = "SELECT count(*) " .
+          "FROM {feature} F" .
+            "  INNER JOIN {cvterm} CVT on F.type_id = CVT.cvterm_id " .
+            "  INNER JOIN {analysisfeature} AF on F.feature_id = AF.feature_id " .
+            "  INNER JOIN {analysis} A on AF.analysis_id = A.analysis_id " .
+          "WHERE A.analysis_id = %d and ($where_cvt) " .
           "GROUP BY A.analysis_id ";
 
   $org_features = chado_pager_query($sql, 10, 0, $csql, $analysis->analysis_id);
   $pager = theme('pager');
 
   // prepare the query that will lookup node ids
-  $sql = "SELECT nid FROM {chado_feature} ".
+  $sql = "SELECT nid FROM {chado_feature} " .
          "WHERE feature_id = %d";
   $i=0;
   $features = array();
@@ -1809,10 +1834,61 @@ function chado_feature_view($node, $teaser = FALSE, $page = FALSE) {
  * @ingroup tripal_feature
  */
 function tripal_feature_nodeapi(&$node, $op, $teaser, $page) {
-
+  
   switch ($op) {
-    // Note that this function only adds feature view to an organism node.
-    // The view of a feature node is controled by the theme *.tpl file
+    
+     // set the title to ensure it is always unique
+    case 'presave':
+      switch ($node->type) {
+        case 'chado_feature':
+          
+          $values = array('organism_id' => $node->organism_id);
+          $organism = tripal_core_chado_select('organism', array('genus', 'species'), $values);
+          $node->title = $node->fname . ', ' . $node->uniquename . ' (' . $node->feature_type . ') ' . $organism[0]->genus . ' ' . $organism[0]->species;
+          break;
+      }
+      break;
+      
+    // set the URL path after inserting.  We do it here because we do not 
+    // know the feature_id in the presave  
+    case 'insert':
+      switch ($node->type) {
+        case 'chado_feature':
+          if (!$node->feature_id) {
+            $sql = "SELECT * FROM {chado_feature} WHERE nid = %d";
+            $chado_feature = db_fetch_object(db_query($sql, $node->nid));
+            $node->feature_id = $chado_feature->feature_id;
+          }
+
+          
+          // remove any previous alias
+          db_query("DELETE FROM {url_alias} WHERE src = '%s'", "node/$node->nid");
+          
+          // set the URL for this feature page
+          $url_alias = tripal_feature_get_feature_url($node);
+          path_set_alias("node/$node->nid", $url_alias);
+          break;
+      }
+      break;
+      
+    // set the URL path after inserting.  We do it here because we do not 
+    // know the feature_id in the presave  
+    case 'update':
+      switch ($node->type) {
+        case 'chado_feature':
+          
+          // remove any previous alias
+          db_query("DELETE FROM {url_alias} WHERE src = '%s'", "node/$node->nid");
+          
+          // set the URL for this feature page
+          $url_alias = tripal_feature_get_feature_url($node);
+          path_set_alias("node/$node->nid", $url_alias);
+          break;
+      }
+      break;
+      
+      
+    // add items to other nodes, build index and search results
     case 'view':
       switch ($node->type) {
         case 'chado_organism':
@@ -1846,6 +1922,20 @@ function tripal_feature_preprocess_tripal_feature_relationships(&$variables) {
     $feature->all_relationships = tripal_feature_get_feature_relationships($feature);
   }
 }
+
+/**
+ *
+ *
+ * @ingroup tripal_feature
+ */
+function tripal_feature_preprocess_tripal_feature_proteins(&$variables) {
+  // we want to provide a new variable that contains the matched features.
+  $feature = $variables['node']->feature;
+
+  if (!$feature->all_relationships) {
+    $feature->all_relationships = tripal_feature_get_feature_relationships($feature);
+  }
+}
 /**
  *
  *
@@ -1876,7 +1966,7 @@ function tripal_feature_preprocess_tripal_feature_alignments(&$variables) {
 
   // get matched alignments (those with an itermediate 'match' or 'EST_match', etc
   $mfeaturelocs = tripal_feature_get_matched_alignments($feature);
-  $feature->matched_featurelocs = tripal_feature_get_matched_alignments($feature);
+  $feature->matched_featurelocs = mfeaturelocs;
 
   // combine all three alignments into a single array for printing together in
   // a single list
@@ -2092,7 +2182,7 @@ function tripal_feature_del_vocabulary() {
  * @ingroup tripal_feature
  */
 function tripal_feature_return_fasta($feature, $desc) {
-  $fasta  = ">" . variable_get('chado_feature_accession_prefix', 'ID') . "$feature->feature_id|$feature->name";
+  $fasta  = ">" . variable_get('chado_feature_accession_prefix', 'FID') . "$feature->feature_id|$feature->name";
   $fasta .= " $desc\n";
   $fasta .= wordwrap($feature->residues, 50, "\n", TRUE);
   $fasta .= "\n\n";
@@ -2208,6 +2298,18 @@ function tripal_feature_coder_ignore() {
  * features is shown.
  */
 function tripal_feature_match_features_page($id) {
+  
+  // if the URL alias configuration is set such that the URL
+  // always begins with 'feature' then we want to use the ID as it is and
+  // forward it on. Otherwise, try to find the matching feature.
+  $url_alias = variable_get('chado_feature_url_string', '/feature/[genus]/[species]/[type]/[uniquename]'); 
+  if (!$url_alias) {
+    $url_alias = '/feature/[genus]/[species]/[type]/[uniquename]';
+  } 
+  $url_alias = preg_replace('/^\//', '', $url_alias); // remove any preceeding forward slash  
+  if (preg_match('/^feature\//', $url_alias)) {
+    drupal_goto($id);
+  }
 
   $sql = "
     SELECT
@@ -2243,7 +2345,7 @@ function tripal_feature_match_features_page($id) {
     $synonyms = preg_replace('/[\"\{\}]/', '', $synonyms);
     $rows[] = array(
        $match->uniquename,
-       "<a href=\"" . url("node/". $match->nid) ."\">" . $match->name . "</a>",
+       "<a href=\"" . url("node/" . $match->nid) . "\">" . $match->name . "</a>",
        $match->type_name,
        '<i>' . $match->genus . ' ' . $match->species . '</i>',
        $synonyms,
@@ -2254,7 +2356,7 @@ function tripal_feature_match_features_page($id) {
   // if we have more than one match then generate the table, otherwise, redirect
   // to the matched feature
   if ($num_matches == 1) {
-    drupal_goto(url("node/" . $curr_match->nid));
+    drupal_goto("node/" . $curr_match->nid);
   }
   if ($num_matches == 0) {
     return "<p>No features matched the given name '$id'</p>";
@@ -2281,4 +2383,4 @@ function tripal_feature_form_alter(&$form, &$form_state, $form_id) {
     // to the normal form URL
     $form['#action'] = url("find/sequences");
   }
-} 
+} 

+ 0 - 1
tripal_library/includes/tripal_library.admin.inc

@@ -507,7 +507,6 @@ function tripal_library_reindex_features($library_id = NULL, $job_id = NULL) {
     if ($job_id and $i % interval == 0) {
       tripal_job_set_progress($job_id, intval(($i/$count)*100));
     }
-    tripal_feature_sync_feature($feature_id);
     $i++;
   }
 }

+ 5 - 5
tripal_organism/api/tripal_organism.api.inc

@@ -54,14 +54,14 @@ function tripal_organism_get_synced() {
   $orgs = db_query($dsql);
 
   // use this SQL statement for getting the organisms
-  $csql =  "SELECT * FROM {Organism} ".
+  $csql =  "SELECT * FROM {Organism} " .
            "WHERE organism_id = :organism_id";
 
   $org_list = array();
 
   // iterate through the organisms and build an array of those that are synced
   foreach ($orgs as $org) {
-  	$args = array(':organism_id' => $org->organism_id);
+    $args = array(':organism_id' => $org->organism_id);
     $org = chado_query($csql, $args)->fetchObject();
     $org_list[] = $org;
   }
@@ -74,9 +74,9 @@ function tripal_organism_get_synced() {
  * @param $nid
  */
 function tripal_organism_get_image_url($organism, $nid = NULL) {
-	$url = '';
-	
-	// first look for an image with the genus/species name.  This is old-style tripal
+  $url = '';
+  
+  // first look for an image with the genus/species name.  This is old-style tripal
   // and we keep it for backwards compatibility.  If we don't find that file
   // then look for the image with the node ID in the name. If we don't find that then
   // no image tag is generated  

+ 14 - 13
tripal_organism/includes/tripal_organism.admin.inc

@@ -48,10 +48,10 @@ function get_tripal_organism_admin_form_cleanup_set(&$form) {
   );
   $form['cleanup']['description'] = array(
     '#type' => 'item',
-    '#value' => t("With Drupal and chado residing in different databases ".
-    "it is possible that nodes in Drupal and organisms in Chado become ".
-    "\"orphaned\".  This can occur if an organism node in Drupal is ".
-    "deleted but the corresponding chado organism is not and/or vice ".
+    '#value' => t("With Drupal and chado residing in different databases " .
+    "it is possible that nodes in Drupal and organisms in Chado become " .
+    "\"orphaned\".  This can occur if an organism node in Drupal is " .
+    "deleted but the corresponding chado organism is not and/or vice " .
     "versa. Click the button below to resolve these discrepancies."),
     '#weight' => 1,
   );
@@ -84,9 +84,11 @@ function get_tripal_organism_admin_form_taxonomy_set(&$form) {
 
   $form['taxonify']['description'] = array(
     '#type' => 'item',
-    '#value' => t("Drupal allows for assignment of \"taxonomy\" or catagorical terms to " .
-                 "nodes. These terms allow for advanced filtering during searching. This option allows ".
-                 "for setting taxonomy only for features that belong to the selected organisms below.  All other features will be unaffected.  To set taxonomy for all features in the site see the Feature Administration page."),
+    '#value' => t(
+      "Drupal allows for assignment of \"taxonomy\" or catagorical terms to " .
+      "nodes. These terms allow for advanced filtering during searching. This option allows " .
+      "for setting taxonomy only for features that belong to the selected organisms below.  All " . 
+      "other features will be unaffected.  To set taxonomy for all features in the site see the Feature Administration page."),
     '#weight' => 1,
   );
 
@@ -274,7 +276,7 @@ function tripal_organism_admin_validate($form, &$form_state) {
         $sql = "SELECT * FROM {organism} WHERE organism_id = :organism_id";
         $organism = chado_query($sql, array(':organism_id' => $organism_id))->fetchObject();
         $job_args[0] = $organism_id;
-        tripal_add_job("Reindex features for organism: $organism->genus ".
+        tripal_add_job("Reindex features for organism: $organism->genus " .
          "$organism->species", 'tripal_organism' ,
          'tripal_organism_reindex_features', $job_args, $user->uid);
       }
@@ -291,7 +293,7 @@ function tripal_organism_admin_validate($form, &$form_state) {
         $sql = "SELECT * FROM {organism} WHERE organism_id = :organism_id";
         $organism = chado_query($sql, array(':organism_id' => $organism_id))->fetchObject();
         $job_args[0] = $organism_id;
-        tripal_add_job("Set taxonomy for features in organism: ".
+        tripal_add_job("Set taxonomy for features in organism: " .
           "$organism->genus $organism->species" , 'tripal_organism',
           'tripal_organism_taxonify_features', $job_args, $user->uid);
       }
@@ -326,7 +328,7 @@ function tripal_organism_sync_organisms($organism_id = NULL, $job_id = NULL) {
 
   // We'll use the following SQL statement for checking if the organism
   // already exists as a drupal node.
-  $sql = "SELECT * FROM {chado_organism} ".
+  $sql = "SELECT * FROM {chado_organism} " .
          "WHERE organism_id = :organism_id";
 
   foreach ($results as $organism) {
@@ -395,7 +397,6 @@ function tripal_organism_reindex_features($organism_id = NULL, $job_id = NULL) {
     if ($job_id and $i % $interval == 0) {
       tripal_job_set_progress($job_id , intval(($i/$count)*100));
     }
-    tripal_feature_sync_feature($feature_id);
     $i++;
   }
 }
@@ -430,8 +431,8 @@ function tripal_organism_taxonify_features($organism_id = NULL, $job_id = NULL)
   tripal_feature_set_vocabulary();
 
   // use this SQL for getting the nodes
-  $nsql =  "SELECT * FROM {chado_feature} CF ".
-          "  INNER JOIN {node} N ON N.nid = CF.nid ".
+  $nsql = "SELECT * FROM {chado_feature} CF " .
+          "  INNER JOIN {node} N ON N.nid = CF.nid " .
           "WHERE feature_id = :feature_id";
 
   // iterate through the features and set the taxonomy

+ 64 - 63
tripal_organism/tripal_organism.module

@@ -39,50 +39,50 @@ function tripal_organism_node_info() {
 }
 
 /**
- * Display block with organisms
- * @param op    - parameter to define the phase being called for the block
- * @param delta - id of the block to return (ignored when op is list)
- * @param edit  - when op is save, contains the submitted form data
  *
  * @ingroup tripal_organism
  */
-function tripal_organism_block($op = 'list', $delta = '0', $edit = array()) {
-  switch ($op) {
-    case 'list':
-      $blocks['base']['info'] = t('Tripal Organism Details');
-      $blocks['base']['cache'] = BLOCK_NO_CACHE;
-
-      $blocks['description']['info'] = t('Tripal Organism Description');
-      $blocks['description']['cache'] = BLOCK_NO_CACHE;
-
-      $blocks['image']['info'] = t('Tripal Organism Image');
-      $blocks['image']['cache'] = BLOCK_NO_CACHE;
-
-      return $blocks;
-
-    case 'view':
-      if (user_access('access chado_feature content') and arg(0) == 'node' and is_numeric(arg(1))) {
-        $nid = arg(1);
-        $node = node_load($nid);
-
-        $block = array();
-        switch ($delta) {
-          case 'base':
-            $block['subject'] = t('Organism Details');
-            $block['content'] = theme('tripal_organism_base', $node);
-            break;
-          case 'description':
-            $block['subject'] = t('Organism Description');
-            $block['content'] = theme('tripal_organism_description', $node);
-            break;
-          case 'image':
-            $block['subject'] = t('Organism Image');
-            $block['content'] = theme('tripal_organism_image', $node);
-            break;
-          default:
-        }
-        return $block;
-      }
+function tripal_organism_block_info() {
+
+  $blocks['base']['info'] = t('Tripal Organism Details');
+  $blocks['base']['cache'] = BLOCK_NO_CACHE;
+
+  $blocks['description']['info'] = t('Tripal Organism Description');
+  $blocks['description']['cache'] = BLOCK_NO_CACHE;
+
+  $blocks['image']['info'] = t('Tripal Organism Image');
+  $blocks['image']['cache'] = BLOCK_NO_CACHE;
+
+  return $blocks;
+   
+}
+/**
+ *
+ * @ingroup tripal_organism
+ */
+function tripal_organism_block_view($delta = '') {
+
+  if (user_access('access chado_feature content') and arg(0) == 'node' and is_numeric(arg(1))) {
+    $nid = arg(1);
+    $node = node_load($nid);
+  
+    $block = array();
+    switch ($delta) {
+      case 'base':
+        $block['subject'] = t('Organism Details');
+        $block['content'] = theme('tripal_organism_base', $node);
+        break;
+      case 'description':
+        $block['subject'] = t('Organism Description');
+        $block['content'] = theme('tripal_organism_description', $node);
+        break;
+      case 'image':
+        $block['subject'] = t('Organism Image');
+        $block['content'] = theme('tripal_organism_image', $node);
+        break;
+      default:
+    }
+    return $block;
   }
 }
 /**
@@ -153,13 +153,14 @@ function tripal_organism_theme() {
  * Implement hook_access().
  *
  * This hook allows node modules to limit access to the node types they define.
- *
- *  @param $op
- *  The operation to be performed
- *
+ * 
  *  @param $node
  *  The node on which the operation is to be performed, or, if it does not yet exist, the
  *  type of node to be created
+ *  
+ *  @param $op
+ *  The operation to be performed
+ *
  *
  *  @param $account
  *  A user object representing the user for whom the operation is to be performed
@@ -172,7 +173,7 @@ function tripal_organism_theme() {
  *
  * @ingroup tripal_organism
  */
-function chado_organism_access($op, $node, $account) {
+function chado_organism_node_access($node, $op, $account) {
   if ($op == 'create') {
     if (!user_access('create chado_organism content', $account)) {
       return FALSE;
@@ -210,19 +211,19 @@ function tripal_organism_permission() {
       'title' => t('View Organisms'),
       'description' => t('Allow users to view organism pages.'),
     ),
-    'create chado_organism content'=> array(
+    'create chado_organism content' => array(
       'title' => t('Create Organisms'),
       'description' => t('Allow users to create new organism pages.'),
     ),
-    'delete chado_organism content'=> array(
+    'delete chado_organism content' => array(
       'title' => t('Delete Organisms'),
       'description' => t('Allow users to delete organism pages.'),
     ),
-    'edit chado_organism content'=> array(
+    'edit chado_organism content' => array(
       'title' => t('Edit Organisms'),
       'description' => t('Allow users to edit organism pages.'),
     ),
-    'adminster tripal organism'=> array(
+    'adminster tripal organism' => array(
       'title' => t('Administer Organisms'),
       'description' => t('Allow users to administer all organisms.'),
     ),
@@ -316,7 +317,7 @@ function chado_organism_insert($node) {
 
   if (!chado_get_id_for_node('organism', $node->nid) ) {
     // next add the item to the drupal table
-    $sql = "INSERT INTO {chado_organism} (nid, vid, organism_id) ".
+    $sql = "INSERT INTO {chado_organism} (nid, vid, organism_id) " .
            "VALUES (:nid, :vid, :organism_id)";
     $args = array(':nid' => $node->nid, ':vid' => $node->vid, ':organism_id' => $organism_id);
     db_query($sql, $args);
@@ -380,16 +381,16 @@ function chado_organism_delete($node) {
   }
 
   // Remove data from the {chado_organism}, {node}, and {node_revisions} tables
-  $sql_del = "DELETE FROM {chado_organism} ".
-             "WHERE nid = :nid ".
+  $sql_del = "DELETE FROM {chado_organism} " .
+             "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 ".
+  $sql_del = "DELETE FROM {node} " .
+             "WHERE nid = :nid " .
              "AND vid = :vid";
   db_query($sql_del, array(':nid' => $node->nid, ':vid' => $node->vid));
-  $sql_del = "DELETE FROM {node_revisions} ".
-             "WHERE nid = ':nid' ".
+  $sql_del = "DELETE FROM {node_revision} " .
+             "WHERE nid = ':nid' " .
              "AND vid = ':vid'";
   db_query($sql_del, array(':nid' => $node->nid, ':vid' => $node->vid));
 
@@ -405,8 +406,8 @@ function chado_organism_delete($node) {
     tripal_core_chado_delete('organism', array('organism_id' => $organism_id));
   }
   else {
-    drupal_set_message(t("Organism deleted from drupal. Warning: at least one ".
-                       "library or feature depends on this organism. It was ".
+    drupal_set_message(t("Organism deleted from drupal. Warning: at least one " .
+                       "library or feature depends on this organism. It was " .
                "not removed from chado."));
   }
 }
@@ -421,7 +422,7 @@ function chado_organism_add_image($node) {
   if (isset($_FILES['files']) && 
       $_FILES['files']['name']['organism_image'] &&
       is_uploaded_file($_FILES['files']['tmp_name']['organism_image'])) {
-      	
+        
     // make sure the destination directory exists
     $dest = tripal_file_directory_path() . "/tripal_organism/images";    
     file_prepare_directory($dest, FILE_CREATE_DIRECTORY);
@@ -434,7 +435,7 @@ function chado_organism_add_image($node) {
       drupal_set_message(t("Organism image was not uploaded."));
     }
     else {
-    	file_move($file, $destination . "/" . $node->nid . ".jpg", FILE_EXISTS_REPLACE);
+      file_move($file, $destination . "/" . $node->nid . ".jpg", FILE_EXISTS_REPLACE);
     }
   }
 }
@@ -519,7 +520,7 @@ function chado_organism_form($node, $param) {
 function chado_organism_load($nodes) {
 
   foreach ($nodes as $nid => $node) {
-  	// find the organism and add in the details
+    // find the organism and add in the details
     $organism_id = chado_get_id_for_node('organism', $nid);
 
     // build the organism variable
@@ -555,7 +556,7 @@ function tripal_organism_job_describe_args($callback, $args) {
   $new_args = array();
   if ($callback == 'tripal_organism_sync_organisms') {
     $organism = tripal_core_chado_select('organism', array('genus', 'species'), array('organism_id' => $args[0]));
-    $new_args['Organism'] = $organism[0]->genus ." ". $organism[0]->species;
+    $new_args['Organism'] = $organism[0]->genus . " " . $organism[0]->species;
   }
   return $new_args;
 }

+ 10 - 10
tripal_project/includes/tripal_project.admin.inc

@@ -90,11 +90,11 @@ function get_tripal_project_admin_form_sync_set(&$form) {
     // a message stating that all projects are currently synced.
     $proj_boxes = array();
     $added = 0;
-    while ($project = db_fetch_object($org_rset)) {
+    while ($project = $org_rset->fetchObject()) {
       // check to see if the project is already present as a node in drupal.
       // if so, then skip it.
-      $sql = "SELECT * FROM {chado_project} WHERE project_id = %d";
-      if (!db_fetch_object(db_query($sql, $project->project_id))) {
+      $sql = "SELECT * FROM {chado_project} WHERE project_id = :project_id";
+      if (!db_query($sql, array(':project_id' => $project->project_id))->fetchObject()) { 
         $proj_boxes[$project->project_id] = $project->name;
         $added++;
       }
@@ -156,7 +156,7 @@ function tripal_project_admin_validate($form, &$form_state) {
       if ($project_id and preg_match("/^\d+$/i" , $project_id)) {
         // get the list of projects
         $sql = "SELECT * FROM {Project} WHERE project_id = %d";
-        $project = db_fetch_object(chado_query($sql, $project_id));
+        $project = chado_query($sql, $project_id)->fetchObject();
         $to_sync[$project_id] = "$project->genus $project->species";
       }
     }
@@ -183,7 +183,7 @@ function tripal_project_admin_validate($form, &$form_state) {
       if ($project_id and preg_match("/^\d+$/i" , $project_id)) {
         // get the project info
         $sql = "SELECT * FROM {project} WHERE project_id = %d";
-        $project = db_fetch_object(chado_query($sql , $project_id));
+        $project = chado_query($sql , $project_id)->fetchObject();
         $job_args[0] = $project_id;
         tripal_add_job("Reindex features for project: $project->genus ".
          "$project->species", 'tripal_project' ,
@@ -200,7 +200,7 @@ function tripal_project_admin_validate($form, &$form_state) {
       if ($project_id and preg_match("/^\d+$/i", $project_id)) {
         // get the project info
         $sql = "SELECT * FROM {project} WHERE project_id = %d";
-        $project = db_fetch_object(chado_query($sql , $project_id));
+        $project = chado_query($sql , $project_id)->fetchObject();
         $job_args[0] = $project_id;
         tripal_add_job("Set taxonomy for features in project: ".
           "$project->genus $project->species" , 'tripal_project',
@@ -239,11 +239,11 @@ function tripal_project_sync_projects($project_id = NULL, $job_id = NULL) {
   $sql = "SELECT * FROM {chado_project} ".
          "WHERE project_id = %d";
 
-  while ($project = db_fetch_object($results)) {
+  while ($project = $results->fetchObject()) {
 
     // check if this project already exists in the drupal database. if it
     // does then skip this project and go to the next one.
-    if (!db_fetch_object(db_query($sql, $project->project_id))) {
+    if (!db_query($sql, $project->project_id)->fetchObject()) {
 
       $new_node = new stdClass();
       $new_node->type = 'chado_project';
@@ -290,14 +290,14 @@ function tripal_project_sync_all_projects() {
   //retrieve all projects in drupal
   $resource = db_query('SELECT project_id FROM {chado_project}');
   $drupal_projects = array();
-  while ($r = db_fetch_object($resource)) {
+  while ($r = $resource->fetchObject()) {
     $drupal_projects[$r->project_id] = $r->project_id;
   }
 
   // retrieve all projects in chado
   $chado_projects = array();
   $resource = chado_query('SELECT project_id FROM {project}');
-  while ($r = db_fetch_object($resource)) {
+  while ($r = $resource->fetchObject()) {
     // if not already in drupal add to list to be sync'd
     if (!isset($drupal_projects[$r->project_id])) {
       $chado_projects[$r->project_id] = $r->project_id;

+ 2 - 2
tripal_project/tripal_project.info

@@ -1,8 +1,8 @@
 name = Tripal Project
 description = A module for interfacing the GMOD chado database with Drupal, providing viewing of projects
-core = 6.x
+core = 7.x
 project = tripal_project
 package = Tripal
-version = 6.x-1.1
+version = 7.x-2.0
 dependencies[] = tripal_core
 dependencies[] = tripal_cv

+ 5 - 14
tripal_project/tripal_project.install

@@ -14,7 +14,10 @@
  * Implementation of hook_install().
  */
 function tripal_project_install() {
-  drupal_install_schema('tripal_project');
+  
+  // create the module's data directory
+  tripal_create_moddir('tripal_project');
+  
   tripal_project_add_cvterms();
 }
 
@@ -22,7 +25,7 @@ function tripal_project_install() {
  * Implementation of hook_uninstall().
  */
 function tripal_project_uninstall() {
-  drupal_uninstall_schema('tripal_project');
+
 }
 
 /**
@@ -79,16 +82,4 @@ function tripal_project_add_cvterms() {
   // description in the libraryprop table.
   tripal_cv_add_cvterm(array('name' => 'project_description', 'def' => 'Description of a project'), 
     'tripal', 0, 1, 'tripal'); 
-}
-/**
- *  Update for Drupal 6.x, Tripal 1.0
- *  This update
- *   - adds the library types
- *
- * @ingroup tripal_library
- */
-function tripal_project_update_6000() {
-  // add in the missing library typ cv terms
-  tripal_project_add_cvterms();
-  return $ret;
 }

+ 105 - 91
tripal_project/tripal_project.module

@@ -66,13 +66,28 @@ function tripal_project_menu() {
  *
  * @ingroup tripal_project
  */
-function tripal_project_perm() {
+function tripal_project_permission() {
   return array(
-    'access chado_projects content',
-    'create chado_projects content',
-    'delete chado_projects content',
-    'edit chado_projects content',
-    'adminster tripal projects',
+    'access chado_project content' => array(
+      'title' => t('View Projects'),
+      'description' => t('Allow users to view project pages.'),
+    ),
+    'create chado_project content' => array(
+      'title' => t('Create Projects'),
+      'description' => t('Allow users to create new project pages.'),
+    ),
+    'delete chado_project content' => array(
+      'title' => t('Delete Projects'),
+      'description' => t('Allow users to delete project pages.'),
+    ),
+    'edit chado_project content' => array(
+      'title' => t('Edit Projects'),
+      'description' => t('Allow users to edit project pages.'),
+    ),
+    'adminster tripal project' => array(
+      'title' => t('Administer Projects'),
+      'description' => t('Allow users to administer all projects.'),
+    ),
   );
 }
 
@@ -81,12 +96,13 @@ function tripal_project_perm() {
  *
  * This hook allows node modules to limit access to the node types they define.
  *
- *  @param $op
- *  The operation to be performed
- *
  *  @param $node
  *  The node on which the operation is to be performed, or, if it does not yet exist, the
  *  type of node to be created
+ *  
+ *  @param $op
+ *  The operation to be performed
+ *
  *
  *  @param $account
  *  A user object representing the user for whom the operation is to be performed
@@ -99,7 +115,7 @@ function tripal_project_perm() {
  *  
  * @ingroup tripal_project
  */
-function chado_project_access($op, $node, $account) {
+function chado_project_node_access($node, $op, $account) {
 
   if ($op == 'create') {
     if (!user_access('create chado_projects content', $account)) {
@@ -139,7 +155,7 @@ function tripal_project_node_info() {
   return array(
     'chado_project' => array(
       'name' => t('Project'),
-      'module' => 'chado_project',
+      'base' => 'chado_project',
       'description' => t('A module for interfacing the GMOD chado database with Drupal, providing viewing of projects'),
       'has_title' => TRUE,
       'title_label' => t('Project Name'),
@@ -266,12 +282,12 @@ function chado_project_validate($node) {
   // check to make sure the name on the project is unique
   // before we try to insert into chado.
   if ($node->project_id) {
-    $sql = "SELECT * FROM {project} WHERE name = '%s' AND NOT project_id = %d";
-    $project = db_fetch_object(chado_query($sql, $node->title, $node->project_id));
+    $sql = "SELECT * FROM {project} WHERE name = :name AND NOT project_id = :project_id";
+    $project = chado_query($sql, array(':name' => $node->title, ':project_id' => $node->project_id))->fetchObject();
   }
   else {
-    $sql = "SELECT * FROM {project} WHERE name = '%s'";
-    $project = db_fetch_object(chado_query($sql, $node->title));
+    $sql = "SELECT * FROM {project} WHERE name = :name";
+    $project = chado_query($sql, array(':name' => $node->title))->fetchObject();
   }
   if ($project) {
     form_set_error('title', t('The unique project name already exists. Please choose another'));
@@ -310,8 +326,8 @@ function chado_project_insert($node) {
     if (!$project_id) {
        // next add the item to the drupal table
       $sql = "INSERT INTO {chado_project} (nid, vid, project_id) ".
-             "VALUES (%d, %d, %d)";
-      db_query($sql, $node->nid, $node->vid, $project['project_id']);
+             "VALUES (:nid, :vid, :project_id)";
+      db_query($sql, array(':nid' => $node->nid, ':vid' => $node->vid, ':project_id' => $project['project_id']));
     }
   }
   else {
@@ -345,22 +361,22 @@ function chado_project_delete($node) {
   // Remove data from {chado_project}, {node} and {node_revisions} tables of
   // drupal database
   $sql_del = "DELETE FROM {chado_project} ".
-             "WHERE nid = %d ".
-             "AND vid = %d";
-  db_query($sql_del, $node->nid, $node->vid);
-  $sql_del = "DELETE FROM {node_revisions} ".
-             "WHERE nid = %d ".
-             "AND vid = %d";
-  db_query($sql_del, $node->nid, $node->vid);
+             "WHERE nid = :nid ".
+             "AND vid = :vid";
+  db_query($sql_del, array(':nid' => $node->nid, ':vid' => $node->vid));
+  $sql_del = "DELETE FROM {node_revision} ".
+             "WHERE nid = :nid ".
+             "AND vid = :vod";
+  db_query($sql_del,  array(':nid' => $node->nid, ':vid' => $node->vid));
   $sql_del = "DELETE FROM {node} ".
-             "WHERE nid = %d ".
-             "AND vid = %d";
-  db_query($sql_del, $node->nid, $node->vid);
+             "WHERE nid = :nid ".
+             "AND vid = :vid";
+  db_query($sql_del,  array(':nid' => $node->nid, ':vid' => $node->vid));
 
 
   // Remove data from project and projectprop tables of chado database as well
-  chado_query("DELETE FROM {projectprop} WHERE project_id = %d", $project_id);
-  chado_query("DELETE FROM {project} WHERE project_id = %d", $project_id);
+  chado_query("DELETE FROM {projectprop} WHERE project_id = :project_id", array(':project_id' => $project_id));
+  chado_query("DELETE FROM {project} WHERE project_id = :project_id", array(':project_id' => $project_id));
 }
 
 /**
@@ -421,65 +437,63 @@ function chado_project_load($node) {
 }
 
 /**
- * Display block with projects
- * @param op    - parameter to define the phase being called for the block
- * @param delta - id of the block to return (ignored when op is list)
- * @param edit  - when op is save, contains the submitted form data
  *
  * @ingroup tripal_project
  */
-function tripal_project_block($op = 'list', $delta = '0', $edit = array()) {
-  switch ($op) {
-    case 'list':
-
-    $blocks['projectbase']['info'] = t('Tripal Project Details');
-    $blocks['projectbase']['cache'] = BLOCK_NO_CACHE;
-
-    $blocks['projectprops']['info'] = t('Tripal Project Properties');
-    $blocks['projectprops']['cache'] = BLOCK_NO_CACHE;
-
-    $blocks['projectpubs']['info'] = t('Tripal Project Publications');
-    $blocks['projectpubs']['cache'] = BLOCK_NO_CACHE;
-
-    $blocks['projectcont']['info'] = t('Tripal Project Contact');
-    $blocks['projectcont']['cache'] = BLOCK_NO_CACHE;
-
-    $blocks['projectrels']['info'] = t('Tripal Project Relationships');
-    $blocks['projectrels']['cache'] = BLOCK_NO_CACHE;
-
-    return $blocks;
-
-    case 'view':
-      if (user_access('access chado_project content') and arg(0) == 'node' and is_numeric(arg(1))) {
-        $nid = arg(1);
-        $node = node_load($nid);
-
-        $block = array();
-        switch ($delta) {
-          case 'projectbase':
-            $block['subject'] = t('Project Details');
-            $block['content'] = theme('tripal_project_base', $node);
-            break;
-          case 'projectprops':
-            $block['subject'] = t('Properties');
-            $block['content'] = theme('tripal_project_properties', $node);
-            break;
-          case 'projectpubs':
-            $block['subject'] = t('Publications');
-            $block['content'] = theme('tripal_project_publications', $node);
-            break;
-          case 'projectcont':
-            $block['subject'] = t('Contact');
-            $block['content'] = theme('tripal_project_contact', $node);
-            break;
-          case 'projectrels':
-            $block['subject'] = t('Relationships');
-            $block['content'] = theme('tripal_project_relationships', $node);
-            break;
-          default :
-        }
-        return $block;
-      }
+function tripal_project_block_info() {
+
+  $blocks['projectbase']['info'] = t('Tripal Project Details');
+  $blocks['projectbase']['cache'] = BLOCK_NO_CACHE;
+  
+  $blocks['projectprops']['info'] = t('Tripal Project Properties');
+  $blocks['projectprops']['cache'] = BLOCK_NO_CACHE;
+  
+  $blocks['projectpubs']['info'] = t('Tripal Project Publications');
+  $blocks['projectpubs']['cache'] = BLOCK_NO_CACHE;
+  
+  $blocks['projectcont']['info'] = t('Tripal Project Contact');
+  $blocks['projectcont']['cache'] = BLOCK_NO_CACHE;
+  
+  $blocks['projectrels']['info'] = t('Tripal Project Relationships');
+  $blocks['projectrels']['cache'] = BLOCK_NO_CACHE;
+  
+  return $blocks;
+}
+/**
+ *
+ * @ingroup tripal_project
+ */
+function tripal_project_block_view($delta = '') {
+ 
+  if (user_access('access chado_project content') and arg(0) == 'node' and is_numeric(arg(1))) {
+    $nid = arg(1);
+    $node = node_load($nid);
+  
+    $block = array();
+    switch ($delta) {
+      case 'projectbase':
+        $block['subject'] = t('Project Details');
+        $block['content'] = theme('tripal_project_base', $node);
+        break;
+      case 'projectprops':
+        $block['subject'] = t('Properties');
+        $block['content'] = theme('tripal_project_properties', $node);
+        break;
+      case 'projectpubs':
+        $block['subject'] = t('Publications');
+        $block['content'] = theme('tripal_project_publications', $node);
+        break;
+      case 'projectcont':
+        $block['subject'] = t('Contact');
+        $block['content'] = theme('tripal_project_contact', $node);
+        break;
+      case 'projectrels':
+        $block['subject'] = t('Relationships');
+        $block['content'] = theme('tripal_project_relationships', $node);
+        break;
+      default :
+    }
+    return $block;
   }
 }
 /**
@@ -501,18 +515,18 @@ function tripal_project_preprocess_tripal_project_relationships(&$variables) {
       INNER JOIN {project} P            ON PR.object_project_id = P.project_id
       INNER JOIN {cvterm} CVT           ON PR.type_id           = CVT.cvterm_id
       LEFT JOIN public.chado_project CP ON P.project_id         = CP.project_id
-    WHERE PR.subject_project_id = %d
+    WHERE PR.subject_project_id = :project_id
   ";
-  $as_subject = chado_query($sql, $project->project_id);
+  $as_subject = chado_query($sql, array(':project_id' => $project->project_id));
   $sql = "
     SELECT P.name, P.project_id, CP.nid, CVT.name as rel_type
     FROM project_relationship PR
       INNER JOIN {project} P            ON PR.subject_project_id = P.project_id
       INNER JOIN {cvterm} CVT           ON PR.type_id            = CVT.cvterm_id
       LEFT JOIN public.chado_project CP ON P.project_id          = CP.project_id
-    WHERE PR.object_project_id = %d
+    WHERE PR.object_project_id = :project_id
   ";
-  $as_object = chado_query($sql, $project->project_id);
+  $as_object = chado_query($sql, array(':project_id' => $project->project_id));
 
   // combine both object and subject relationshisp into a single array
   $relationships = array();
@@ -520,7 +534,7 @@ function tripal_project_preprocess_tripal_project_relationships(&$variables) {
   $relationships['subject'] = array();
 
   // iterate through the object relationships
-  while ($relationship = db_fetch_object($as_object)) {
+  while ($relationship = $as_object->fetchObject()) {
 
      // get the relationship and child types
      $rel_type = t(preg_replace('/_/', " ", $relationship->rel_type));
@@ -536,7 +550,7 @@ function tripal_project_preprocess_tripal_project_relationships(&$variables) {
   }
 
   // now add in the subject relationships
-  while ($relationship = db_fetch_object($as_subject)) {
+  while ($relationship = $as_subject->fetchObject()) {
 
      // get the relationship and child types
      $rel_type = t(preg_replace('/_/', " ", $relationship->rel_type));

+ 77 - 78
tripal_pub/api/tripal_pub.api.inc

@@ -664,7 +664,7 @@ function tripal_pub_add_publication($pub_details, &$action, $do_contact = FALSE,
       return FALSE;
     }
   }
-  print "PUB ID: $pub_id\n";
+  //print "PUB ID: $pub_id\n";
   // if there is a pub id and we've been told not to update then return
   if ($pub_id and !$update_if_exists) {
     $action = 'skipped';
@@ -1029,13 +1029,13 @@ function tripal_pub_get_publication_array($pub_id, $skip_existing = TRUE) {
   if ($citation) {
     $citation = tripal_core_expand_chado_vars($citation, 'field', 'pubprop.value', $options);
     if (count($citation) > 1) {
-    	watchdog('tripal_pub', "Publication has multiple citations already: %pub_id",
-    	array('%pub_id' => $pubid), WATCHDOG_ERROR);
-    	return FALSE;
+      watchdog('tripal_pub', "Publication has multiple citations already: %pub_id",
+      array('%pub_id' => $pubid), WATCHDOG_ERROR);
+      return FALSE;
     }
     elseif (count($citation) == 1 and $skip_existing == TRUE) {
-    	// skip this publication, it already has a citation
-    	return FALSE;
+      // skip this publication, it already has a citation
+      return FALSE;
     }
   }  
 
@@ -1080,7 +1080,7 @@ function tripal_pub_get_publication_array($pub_id, $skip_existing = TRUE) {
     $sql = "SELECT string_agg(surname || ' ' || givennames, ', ') FROM {pubauthor} WHERE pub_id = %d GROUP BY pub_id";
     $au = db_result(chado_query($sql));
     if ($au) {
-    	$pub_array['Authors'] = $au;
+      $pub_array['Authors'] = $au;
     }
   }
 
@@ -1156,95 +1156,94 @@ function tripal_pub_get_publication_array($pub_id, $skip_existing = TRUE) {
  *   A text string containing the citation
  */
 function tripal_pub_create_citation($pub) {
-	$citation = '';
-	$pub_type = '';
-	
-	// An article may have more than one publication type. For example,
-	// a publication type can be 'Journal Article' but also a 'Clinical Trial'.
-	// 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'])) {
-	  foreach ($pub['Publication Type'] as $ptype) {
-	    if ($ptype == 'Journal Article' ) {
-	      $pub_type = $ptype;
-	      break;
-	    } else if ($ptype == 'Conference Proceedings'){ 
-	      $pub_type = $ptype;
-	      break;
-	    } else if ($ptype == 'Book') {
-	      $pub_type = $ptype;
-	      break;
-	    } else if ($ptype == 'Book Chapter') {
-	      $pub_type = $ptype;
-	      break;
-	    }
-	  }
-	  if (!$pub_type) {
+  $citation = '';
+  $pub_type = '';
+  
+  // An article may have more than one publication type. For example,
+  // a publication type can be 'Journal Article' but also a 'Clinical Trial'.
+  // 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'])) {
+    foreach ($pub['Publication Type'] as $ptype) {
+      if ($ptype == 'Journal Article' ) {
+        $pub_type = $ptype;
+        break;
+      } else if ($ptype == 'Conference Proceedings'){ 
+        $pub_type = $ptype;
+        break;
+      } else if ($ptype == 'Book') {
+        $pub_type = $ptype;
+        break;
+      } else if ($ptype == 'Book Chapter') {
+        $pub_type = $ptype;
+        break;
+      }
+    }
+    if (!$pub_type) {
       watchdog('tripal_pub', "Cannot generate citation for publication type: %types", 
         array('%types' => print_r($pub['Publication Type'], TRUE)), WATCHDOG_ERROR);
       return FALSE;
     }
-	}
-	else {
-	  $pub_type = $pub['Publication Type'];
-	}		
-  print "[$pub_type]\n";
-	//----------------------
+  }
+  else {
+    $pub_type = $pub['Publication Type'];
+  }    
+  //----------------------
   // Journal Article
   //----------------------
-	if ($pub_type == 'Journal Article') {
-	  $citation = $pub['Authors'] . '. ' . $pub['Title'] .  '. ';
-	
-	  if ($pub['Journal Name']) {
-	    $citation .= $pub['Journal Name'] . '. ';
-	  }
-	  elseif ($pub['Journal Abbreviation']) {
-	    $citation .= $pub['Journal Abbreviation'] . '. ';
-	  }
-	  elseif ($pub['Series Name']) {
-	    $citation .= $pub['Series Name'] . '. ';	
-	  }
-	  elseif ($pub['Series Abbreviation']) {
+  if ($pub_type == 'Journal Article') {
+    $citation = $pub['Authors'] . '. ' . $pub['Title'] .  '. ';
+  
+    if ($pub['Journal Name']) {
+      $citation .= $pub['Journal Name'] . '. ';
+    }
+    elseif ($pub['Journal Abbreviation']) {
+      $citation .= $pub['Journal Abbreviation'] . '. ';
+    }
+    elseif ($pub['Series Name']) {
+      $citation .= $pub['Series Name'] . '. ';  
+    }
+    elseif ($pub['Series Abbreviation']) {
       $citation .= $pub['Series Abbreviation'] . '. ';
     }
     if ($pub['Publication Date']) {
-	    $citation .= $pub['Publication Date'];
+      $citation .= $pub['Publication Date'];
     }
     elseif ($pub['Year']) {
-    	$citation .= $pub['Year'];
+      $citation .= $pub['Year'];
     }
-	  if ($pub['Volume'] or $pub['Issue'] or $pub['Pages']) {
-	    $citation .= '; ';
-	  }
-	  if ($pub['Volume']) {
-	    $citation .= $pub['Volume'];
-	  }
-	  if ($pub['Issue']) {
-	    $citation .= '(' . $pub['Issue'] . ')';
-	  }
-	  if ($pub['Pages']) {
-	    if($pub['Volume']) {
-	      $citation .= ':';
-	    }
-	    $citation .= $pub['Pages'];
-	  }
-	  $citation .= '.';
-	}
-	//----------------------
+    if ($pub['Volume'] or $pub['Issue'] or $pub['Pages']) {
+      $citation .= '; ';
+    }
+    if ($pub['Volume']) {
+      $citation .= $pub['Volume'];
+    }
+    if ($pub['Issue']) {
+      $citation .= '(' . $pub['Issue'] . ')';
+    }
+    if ($pub['Pages']) {
+      if($pub['Volume']) {
+        $citation .= ':';
+      }
+      $citation .= $pub['Pages'];
+    }
+    $citation .= '.';
+  }
+  //----------------------
   // Book
   //----------------------
-	elseif ($pub_type == 'Book') {
-	
-	}
-	//----------------------
+  elseif ($pub_type == 'Book') {
+  
+  }
+  //----------------------
   // Book Chapter
   //----------------------
   elseif ($pub_type == 'Book Chapter') {
-  	
+    
   }
-	//----------------------
-	// Conference Proceedings
+  //----------------------
+  // Conference Proceedings
   //----------------------
   elseif ($pub_type == 'Conference Proceedings') {
     $citation = $pub['Authors'] . '. ' . $pub['Title'] .  '. ';

+ 7 - 1
tripal_pub/includes/importers/AGL.inc

@@ -333,7 +333,7 @@ function tripal_pub_remote_search_AGL($search_array, $num_to_retrieve, $pager_id
 
     if (!yaz_ccl_parse($yazc, $ccl, $cclresult)) {
       drupal_set_message('Error parsing search string: ' . $cclresult["errorstring"], "error");
-      watchdog('tripal_pub', 'Error: %errstr', array('%errstr' => $cclresult["errorstring"]), WATCHDOG_ERROR);
+      watchdog('tpub_import', 'Error: %errstr', array('%errstr' => $cclresult["errorstring"]), WATCHDOG_ERROR);
       return array();
     }
     $search_str = $cclresult["rpn"];
@@ -374,6 +374,8 @@ function tripal_pub_AGL_count($search_array) {
       $error_msg .= " $additional";
     }
     drupal_set_message("ERROR preparing search at AGL: ($error_no) $error_msg", "error");
+    watchdog('tpub_import', "ERROR preparing search at AGL: (%error_no) %error_msg",
+              array('%error_no' => $error_no, '%error_msg' => $error_msg), WATCHDOG_ERROR);
     return 0;
   }
   if (!yaz_wait()) {
@@ -384,6 +386,8 @@ function tripal_pub_AGL_count($search_array) {
       $error_msg .= " $additional";
     }
     drupal_set_message("ERROR waiting on search at AGL: ($error_no) $error_msg", "error");
+    watchdog('tpub_import', "ERROR waiting on search at AGL: (%error_no) %error_msg",
+              array('%error_no' => $error_no, '%error_msg' => $error_msg), WATCHDOG_ERROR);
     return 0;
   }
 
@@ -416,6 +420,8 @@ function tripal_pub_AGL_range($search_array, $start = 0, $limit = 10) {
       $error_msg .= " $additional";
     }
     drupal_set_message("ERROR waiting on search at AGL: ($error_no) $error_msg", "error");
+    watchdog('tpub_import', "ERROR waiting on search at AGL: (%error_no) %error_msg",
+              array('%error_no' => $error_no, '%error_msg' => $error_msg), WATCHDOG_ERROR);
     return $pubs;
   }
    

+ 7 - 0
tripal_pub/includes/importers/PMID.inc

@@ -100,6 +100,9 @@ function tripal_pub_remote_search_PMID($search_array, $num_to_retrieve, $pager_i
       $search_str = preg_replace('/PMID:([^\s]*)/', '$1', $search_str);
       $search_str = preg_replace('/\|SCOPE\|/', '[Uid]', $search_str);
     }
+    else {
+      $search_str = preg_replace('/\|SCOPE\|/', '', $search_str);  
+    }
   }
   if ($days) {
     // get the date of the day suggested
@@ -199,6 +202,8 @@ function tripal_pub_PMID_search_init($search_str, $retmax){
   $rfh = fopen($query_url, "r");
   if (!$rfh) {
     drupal_set_message('Could not perform Pubmed query. Cannot connect to Entrez.', 'error');
+    watchdog('tpub_import', "Could not perform Pubmed query. Cannot connect to Entrez.",
+              array(), WATCHDOG_ERROR);
     return 0;
   }
 
@@ -276,6 +281,8 @@ $retmod = 'null', $start = 0, $limit = 10, $args = array()){
   $rfh = fopen($fetch_url, "r");
   if (!$rfh) {
     drupal_set_message('ERROR: Could not perform PubMed query.', 'error');
+    watchdog('tpub_import', "Could not perform PubMed query: %fetch_url.",
+              array('%fetch_url' => $fetch_url), WATCHDOG_ERROR);
     return '';
   }
   $results = '';

+ 4 - 2
tripal_pub/theme/tripal_pub_admin.tpl.php

@@ -55,8 +55,10 @@ have been added to Chado database.</p>
 	<ol>
 		<li>Install the YAZ libraries: sudo apt-get install yaz libyaz4-dev</li>
 		<li>Install the PHP YAZ extension: sudo pecl install yaz</li>
-		<li>Add the text 'extesion=yaz.so' to the appropriate php.ini file
-		(e.g. /etc/php5/apache2filter/php.ini)</li>
+		<li>Add the text 'extension=yaz.so' to the appropriate php.ini file
+		(e.g. /etc/php5/apache2filter/php.ini). On Ubuntu you may need to
+		add it to the php.ini file specfic for the Apache webserver and 
+		also to the php.ini specific for the command-line.</li>
 		<li>Restart the webserver</li>
 	</ol>
 	</li>

+ 0 - 347
tripal_stock/includes/tripal_stock-administration.inc

@@ -1,347 +0,0 @@
-<?php
-/**
- * @file
- * @todo Add file header description
- */
-
-/**
- * Purpose: Provide administration options for chado_stocks
- *
- * @return
- *   Form array (as described by the drupal form api)
- *
- * @ingroup tripal_stock
- */
-function tripal_stock_admin() {
-  $form = array();
-
-  // before proceeding check to see if we have any
-  // currently processing jobs. If so, we don't want
-  // to give the opportunity to sync Stocks
-  $active_jobs = FALSE;
-  if (tripal_get_module_active_jobs('tripal_stock')) {
-    $active_jobs = TRUE;
-  }
-  if ($active_jobs) {
-    $form['notice'] = array(
-       '#type' => 'fieldset',
-       '#title' => t('Stock Management Temporarily Unavailable')
-    );
-    $form['notice']['message'] = array(
-       '#value' => t("Currently, stock management jobs are waiting or ".
-          "are running. Managemment features have been hidden until these ".
-          "jobs complete.  Please check back later once these jobs have ".
-          "finished.  You can view the status of pending jobs in the Tripal ".
-          "jobs page."),
-    );
-  }
-  else {
-
-    // SET Vocabularies -----------------------------------------------------------------------------------------
-    $form['set_cv'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Set Stock Controlled Vocabularies'),
-      '#weight' => -10
-    );
-
-    $form['set_cv']['message'] = array(
-         '#value' => t("This setting allows you to set which chado controlled vocabularies (cv)"
-                ." are used. Cvs are used to control user input for the type of stock,"
-          ." any properties they enter for a stock & the types of relationships"
-          ." between stocks. Only cvs already loaded into chado can be selected here.")
-    );
-
-    // get the list of CVs for the next form element
-    $sql = "SELECT * FROM {cv} ORDER BY name";
-    $results = chado_query($sql);
-    $cv_options = array();
-    while ($r = db_fetch_object($results)) {
-      $cv_options[$r->cv_id] = $r->name;
-    }
-
-    $form['set_cv']['stock_types_cv'] = array(
-     '#type' => 'select',
-     '#title' => t('Controlled Vocabulary governing Stock Types'),
-     '#options' => $cv_options,
-     '#default_value' => variable_get('chado_stock_types_cv', 0)
-    );
-
-    $form['set_cv']['stock_prop_types_cv'] = array(
-     '#type' => 'select',
-     '#title' => t('Controlled Vocabulary governing Types of Stock Properties'),
-     '#description' => t("This cv must contain a cvterm entry where name='synonym'."),
-     '#options' => $cv_options,
-     '#default_value' => variable_get('chado_stock_prop_types_cv', 0)
-    );
-
-    $form['set_cv']['stock_relationship_cv'] = array(
-     '#type' => 'select',
-     '#title' => t('Controlled Vocabulary governing Types of Relationsips between Stocks'),
-     '#options' => $cv_options,
-     '#default_value' => variable_get('chado_stock_relationship_cv', 0)
-    );
-
-    $form['set_cv']['button'] = array(
-      '#type' => 'submit',
-      '#value' => t('Set Controlled Vacabularies')
-    );
-
-    // SYNC STOCKS-----------------------------------------------------------------------------------------------
-    $form['sync'] = array(
-      '#type' => 'fieldset',
-      '#title' => t('Sync Stocks'),
-      '#weight' => -10
-    );
-
-    $form['sync']['description'] = array(
-      '#type' => 'item',
-      '#value' => t("Click the 'Sync all Germplasm' button to create Drupal ".
-         "content for stocks in chado. Depending on the ".
-         "number of stocks in the chado database this may take a long ".
-         "time to complete. ")
-    );
-
-    // get the list of organisms
-    $sql = "SELECT * FROM {Organism} ORDER BY genus, species";
-    $org_rset = chado_query($sql);
-    $organisms = array();
-    $organisms[''] = '';
-    while ($organism = db_fetch_object($org_rset)) {
-      $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
-    }
-    $form['sync']['organisms'] = array(
-      '#type' => 'checkboxes',
-      '#title' => t('Organisms for which Stocks should be sync\'d'),
-      '#description' => t('Only sync\'d Organisms are listed. Leaving an organism unchecked does not delete already sync\'d Stocks.'),
-      '#options' => $organisms,
-      '#required'    => FALSE,
-      '#prefix'      => '<div id="lib_boxes">',
-      '#suffix'      => '</div>'
-    );
-
-    $form['sync']['button'] = array(
-      '#type' => 'submit',
-      '#value' => t('Sync Stocks')
-    );
-    get_tripal_stock_admin_form_cleanup_set($form);
-  }
-
-  return system_settings_form($form);
-
-}
-
-/**
- * Implements hook_form_validate(): Validates user input
- *
- * @param $form
- *   An array describing the form that was rendered
- * @param $form_state
- *   An array describing the current state of the form including user input
- *
- * @ingroup tripal_stock
- */
-function tripal_stock_admin_validate($form, &$form_state) {
-  global $user;  // we need access to the user info
-  $job_args = array();
-
-  // Sync Stocks
-  if ($form_state['values']['op'] == t('Sync Stocks')) {
-    // Array organism_id => organims common_name
-    //  which only includes those organisms which the user wants to select stocks for
-    $organisms_2b_syncd = $form_state['values']['organisms'];
-
-    //for each organism selected submit job (handled by tripal_stock_sync_stock_set)
-    //  which syncs all stocks with an organism_id equal to the selelcted organism
-    foreach ( $organisms_2b_syncd as $organism_id ) {
-      if ($organism_id != 0) {
-        $job_args[0] = $organism_id;
-        tripal_add_job("Sync Stocks from Organism $organism_id", 'tripal_stock',
-          'tripal_stock_sync_stock_set', $job_args, $user->uid);
-      }
-    }
-  }
-
-  if ($form_state['values']['op'] == t('Set Controlled Vacabularies')) {
-    variable_set('chado_stock_types_cv', $form_state['values']['stock_types_cv']);
-    variable_set('chado_stock_prop_types_cv', $form_state['values']['stock_prop_types_cv']);
-    variable_set('chado_stock_relationship_cv', $form_state['values']['stock_relationship_cv']);
-  }
-
-  // Submit the Cleanup Job if selected
-  if ($form_state['values']['op'] == t('Clean up orphaned stocks')) {
-    tripal_add_job('Cleanup orphaned stocks', 'tripal_stock',
-       'tripal_stock_cleanup', $job_args, $user->uid);
-  }
-}
-
-/**
- * Sync stocks associated with a given organism or sync all stocks
- *
- * Note: This is essentially an API function to make tripal stock sync act similar to other tripal modules
- *
- * @param $organism_id
- *   The ID of the organism to sync all stocks for
- * @param $job_id
- *   The ID of the tripal job
- */
-function tripal_stock_sync_stocks($organism_id, $job_id) {
-
-  if ($organism_id) {
-    return tripal_stock_sync_stock_set($organism_id, $job_id);
-  }
-  else {
-    //get a list of all organisms and sync all stocks for all organisms
-    $organisms = tripal_core_chado_select('organism', array('organism_id','genus','species','common_name'), array());
-    foreach ($organisms as $o) {
-      print "Syncing stocks associated with $o->genus $o->species ($o->common_name)\n";
-      tripal_stock_sync_stock_set($o->organism_id, $job_id);
-    }
-  }
-
-}
-
-/**
- * Syncs all Stocks associated with an organism
- *
- * Note: Handling of multiple organisms is done in tripal_stock_admin_validate()
- *
- * @param $organism_id
- *   The chado primary key of the organism for which stocks should be sync'd
- * @param $job_id
- *   The tripal job ID
- *
- * @return
- *   TRUE if successful; FALSE otherwise
- *
- * @ingroup tripal_stock
- */
-function tripal_stock_sync_stock_set($organism_id, $job_id) {
-  global $user;
-
-  if (!$organism_id) {
-    print '0 Stocks to Sync -No Organisms Selected.\n';
-  }
-  else {
-
-  // Get list of stocks to sync
-  $result = chado_query(
-     "SELECT stock_id, uniquename, type_id, organism_id FROM {stock} WHERE organism_id=%d",
-      $organism_id
-  );
-
-  $stocks_created_count = 0; //keeps track of total number of stocks successfully created
-  $stocks_attempted = 0;
-  // foreach stock to be sync'd -> create node & add stock_id
-  while ( $r = db_fetch_object($result) ) {
-    // $r is the current stock to be sync'd
-    $stocks_attempted++;
-
-    print 'Processing ' . $r->uniquename . "... ";
-
-    // check not already in drupal
-    $in_drupal_query = db_query(
-      "SELECT * FROM {chado_stock} WHERE stock_id=%d",
-      $r->stock_id
-    );
-    if ( !db_fetch_object($in_drupal_query) ) {
-
-      //create new chado_stock node
-      $new_node = new stdClass();
-      $new_node->type = 'chado_stock';
-      $new_node->uid = $user->uid;
-      $new_node->title = $r->uniquename;
-      $new_node->type_id = $r->type_id;
-      $new_node->organism_id = $r->organism_id;
-      $new_node->stock_id = $r->stock_id;
-      $new_node->chado_stock_exists = TRUE;
-
-      //print 'New Node:';
-      //print_r($new_node);
-
-      node_validate($new_node);
-
-      if (!form_get_errors()) {
-        //print 'Try to Create Node ';
-        $node = node_submit($new_node);
-        node_save($node);
-        if ($node->nid) {
-          $stocks_created_count++;
-
-          //Add stock id to chado_stock table
-          /**
-           db_query(
-             "INSERT INTO {chado_stock} (stock_id, nid, vid) VALUES (%d, %d, %d)",
-             $r->stock_id,
-             $node->nid,
-             $node->vid
-           );
-           */
-          }
-        }
-        else {
-          print "Not completed due to errors:\nCreate Stock Form Errors: ";
-          print_r(form_get_errors());
-        }
-        print "Nid=" . $node->nid . "\n";
-      }
-      else {
-        print "Skipped $r->uniquename because it's already in drupal.\n";
-      } //end of if not already in drupal
-    } //end of while still stocks to be sync'd
-  } //end of if organism_id not supplied
-
-  if ($stocks_attempted == 0) {
-    print "No stocks retrieved for organism (" . $organism_id . ")\n";
-    return 1;
-  }
-  else {
-    if ($stocks_created_count > 0) {
-      print "$stocks_created_count Stocks Successfully Created\n";
-      return 1;
-    }
-    else {
-      return 0;
-    }
-  }
-}
-
-/**
- *
- *
- * @ingroup tripal_stock
- */
-function get_tripal_stock_admin_form_cleanup_set(&$form) {
-  $form['cleanup'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Clean Up')
-  );
-  $form['cleanup']['description'] = array(
-     '#type' => 'item',
-     '#value' => t("With Drupal and Chado residing in different databases ".
-        "it is possible that nodes in Drupal and stocks in Chado become ".
-        "\"orphaned\".  This can occur if an stock node in Drupal is ".
-        "deleted but the corresponding chado stock is not and/or vice ".
-        "versa. Click the button below to resolve these discrepancies."),
-     '#weight' => 1,
-  );
-  $form['cleanup']['button'] = array(
-    '#type' => 'submit',
-    '#value' => t('Clean up orphaned stocks'),
-    '#weight' => 2,
-  );
-}
-/**
- * Remove orphaned drupal nodes
- *
- * @param $dummy
- *   Not Used -kept for backwards compatibility
- * @param $job_id
- *   The id of the tripal job executing this function
- *
- * @ingroup tripal_stock
- */
-function tripal_stock_cleanup($dummy = NULL, $job_id = NULL) {
-
-  return tripal_core_clean_orphaned_nodes('stock', $job_id);
-
-}

+ 262 - 0
tripal_stock/includes/tripal_stock.admin.inc

@@ -0,0 +1,262 @@
+<?php
+/**
+ * @file
+ * @todo Add file header description
+ */
+
+/**
+ * Purpose: Provide administration options for chado_stocks
+ *
+ * @return
+ *   Form array (as described by the drupal form api)
+ *
+ * @ingroup tripal_stock
+ */
+function tripal_stock_admin() {
+  $form = array();
+
+  get_tripal_stock_admin_form_title_set($form);
+  get_tripal_stock_admin_form_url_set($form);
+  get_tripal_stock_admin_form_vocabulary_set($form);       
+  get_tripal_stock_admin_form_cleanup_set($form);
+  
+
+  return system_settings_form($form);
+
+}
+
+/**
+ * Implements hook_form_validate(): Validates user input
+ *
+ * @param $form
+ *   An array describing the form that was rendered
+ * @param $form_state
+ *   An array describing the current state of the form including user input
+ *
+ * @ingroup tripal_stock
+ */
+function tripal_stock_admin_validate($form, &$form_state) {
+  global $user;  // we need access to the user info
+  $job_args = array();
+
+  variable_set('chado_stock_types_cv', $form_state['values']['stock_types_cv']);
+  variable_set('chado_stock_prop_types_cv', $form_state['values']['stock_prop_types_cv']);
+  variable_set('chado_stock_relationship_cv', $form_state['values']['stock_relationship_cv']);  
+  variable_set('chado_stock_url_string', $form_state['values']['chado_stock_url_string']);
+  
+  switch ($form_state['values']['op']) {
+    case  t('Set Controlled Vacabularies') :      
+      break;
+      
+    case t('Clean up orphaned stocks') :
+      tripal_add_job('Cleanup orphaned stocks', 'tripal_stock',
+         'tripal_stock_cleanup', $job_args, $user->uid);
+      break;
+    
+    case t('Set Stock URLs') :
+      tripal_add_job('Set Stock URLs', 'tripal_stock',
+        'tripal_stock_set_urls', $job_args, $user->uid);
+      break;
+  }
+
+}
+
+/**
+ * 
+ * @param $form
+ */
+function get_tripal_stock_admin_form_title_set(&$form) {
+
+  $form['title'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Stock Page Titles'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  $form['title']['desc'] = array(
+    '#type'  => 'markup',
+    '#value' => t('Each synced stock must have a unique page title, however, stocks
+      may have the same name if they are of different types or from different 
+      organisms.  Therefore, we must be sure that the page titles can uniquely 
+      identify the stock being viewed.  Select an option below that will 
+      uniquely identify all stocks on your site.'),
+  );
+  $options = array(
+    'stock_unique_name'  => 'Only stock unique name',
+    'stock_name'         => 'Only stock name',
+    'unique_constraint'  => 'Includes stock name, uniquename, type and species',
+  );
+  $form['title']['chado_stock_title'] = array(
+    '#title'         => t('Stock Page Titles'),
+    '#type'          => 'radios',
+    '#description'   => t('Choose a title type  from the list above that is 
+      guaranteed to be unique for all stocks If in doubt it is safest to choose 
+      the last option as that guarantees uniqueness. Click the 
+      \'Save Configuration\' button at the bottom to save your selection.'),
+    '#required'      => FALSE,
+    '#options'       => $options,
+    '#default_value' => variable_get('chado_stock_title', 'unique_constraint'),
+  );
+}
+/**
+ * 
+ * @param  $form
+ */
+function get_tripal_stock_admin_form_url_set(&$form) {
+
+  $form['url'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Stock URL Path'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $options = array(
+    'SID[id]'      => '[id]:' . t('The Chado stock_id'),
+    'stock'        => 'stock:' . t('Chado table name'),
+    '[genus]'      => '[genus]:' . t('Genus to which the stock belongs'),
+    '[species]'    => '[species]:' . t('Species to which the stock belongs'),
+    '[type]'       => '[type]:' . t('The type of stock'),
+    '[uniquename]' => '[uniquename]:' . t('The stock unique name'),
+    '[name]'       => '[name]:' . t('The stock name'),
+    'reset'        => t('Reset'),
+  );
+  
+
+  $form['url']['chado_stock_url_string'] = array(
+    '#title' => 'URL Syntax',
+    '#type' => 'textfield',
+    '#description' => t('You may rearrange elements in this text box to
+      customize the URLs.  The available tags include: [id],
+      [uniquename]. [name], [species], [genus], [type]. You can separate or
+      include any text between the tags. Click the "Set Stock URLs" button to 
+      reset the URLs for all stock pages.  Click the "Save Configuration" button to
+      simply save this setup. <b>Important</b>: be sure that whatever you choose will always be unique even considering
+      future data that may be added.  If you include the Chado table name, genus, species, type 
+      and uniquename you are guaranteed to have a unique URL. For example stock/[genus]/[species]/[type]/[uniquename]'),
+    '#size' => 150,
+    '#default_value' => variable_get('chado_stock_url_string', '/stock/[genus]/[species]/[type]/[uniquename]'), 
+  );
+  
+  $form['url']['chado_stock_url'] = array(
+    '#title'         => t('URL components'),
+    '#type'          => 'checkboxes',
+    '#required'      => FALSE,
+    '#options'       => $options,
+    '#description'   => t('Click the item above to make it appear in the URL Syntax box'),
+    '#attributes'    => array(
+      'onclick' => '
+        box = $(\'#edit-chado-stock-url-string\');
+        if (this.value == \'reset\') {
+          box.val(\'\');
+        }
+        else {        
+          box.val(box.val() + "/" + this.value);          
+        }
+        this.checked = false;
+      ',
+    ),
+  );  
+  
+  $form['url']['button'] = array(
+    '#type' => 'submit',
+    '#value' => t('Set Stock URLs'),
+  );
+}
+/**
+ * 
+ * @param $form
+ */
+function get_tripal_stock_admin_form_vocabulary_set(&$form) {
+
+  $form['set_cv'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Set Stock Controlled Vocabularies'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+
+  $form['set_cv']['message'] = array(
+       '#value' => t("This setting allows you to set which chado controlled vocabularies (cv)"
+              ." are used. Cvs are used to control user input for the type of stock,"
+        ." any properties they enter for a stock & the types of relationships"
+        ." between stocks. Only cvs already loaded into chado can be selected here.")
+  );
+
+  // get the list of CVs for the next form element
+  $sql = "SELECT * FROM {cv} ORDER BY name";
+  $results = chado_query($sql);
+  $cv_options = array();
+  while ($r = db_fetch_object($results)) {
+    $cv_options[$r->cv_id] = $r->name;
+  }
+
+  $form['set_cv']['stock_types_cv'] = array(
+   '#type' => 'select',
+   '#title' => t('Controlled Vocabulary governing Stock Types'),
+   '#options' => $cv_options,
+   '#default_value' => variable_get('chado_stock_types_cv', 0)
+  );
+
+  $form['set_cv']['stock_prop_types_cv'] = array(
+   '#type' => 'select',
+   '#title' => t('Controlled Vocabulary governing Types of Stock Properties'),
+   '#description' => t("This cv must contain a cvterm entry where name='synonym'."),
+   '#options' => $cv_options,
+   '#default_value' => variable_get('chado_stock_prop_types_cv', 0)
+  );
+
+  $form['set_cv']['stock_relationship_cv'] = array(
+   '#type' => 'select',
+   '#title' => t('Controlled Vocabulary governing Types of Relationsips between Stocks'),
+   '#options' => $cv_options,
+   '#default_value' => variable_get('chado_stock_relationship_cv', 0)
+  );
+
+  $form['set_cv']['button'] = array(
+    '#type' => 'submit',
+    '#value' => t('Set Controlled Vacabularies')
+  );
+}
+/**
+ *
+ *
+ * @ingroup tripal_stock
+ */
+function get_tripal_stock_admin_form_cleanup_set(&$form) {
+  $form['cleanup'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Clean Up'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  $form['cleanup']['description'] = array(
+     '#type' => 'item',
+     '#value' => t("With Drupal and Chado residing in different databases ".
+        "it is possible that nodes in Drupal and stocks in Chado become ".
+        "\"orphaned\".  This can occur if an stock node in Drupal is ".
+        "deleted but the corresponding chado stock is not and/or vice ".
+        "versa. Click the button below to resolve these discrepancies."),
+     '#weight' => 1,
+  );
+  $form['cleanup']['button'] = array(
+    '#type' => 'submit',
+    '#value' => t('Clean up orphaned stocks'),
+    '#weight' => 2,
+  );
+}
+/**
+ * Remove orphaned drupal nodes
+ *
+ * @param $dummy
+ *   Not Used -kept for backwards compatibility
+ * @param $job_id
+ *   The id of the tripal job executing this function
+ *
+ * @ingroup tripal_stock
+ */
+function tripal_stock_cleanup($dummy = NULL, $job_id = NULL) {
+
+  return tripal_core_clean_orphaned_nodes('stock', $job_id);
+
+}

+ 538 - 0
tripal_stock/includes/tripal_stock.sync_stocks.inc

@@ -0,0 +1,538 @@
+<?php
+
+/**
+ * @file
+ * @todo Add file header description
+ */
+
+
+# This script can be run as a stand-alone script to sync all the stocks from chado to drupal
+// Parameter f specifies the stock_id to sync
+// -f 0 will sync all stocks
+
+$arguments = getopt("f:t:");
+
+if (isset($arguments['f']) and isset($arguments['t']) and $arguments['t'] == 'chado_stock') {
+  $drupal_base_url = parse_url('http://www.example.com');
+  $_SERVER['HTTP_HOST'] = $drupal_base_url['host'];
+  $_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] = $_SERVER['PHP_SELF'];
+  $_SERVER['REMOTE_ADDR'] = NULL;
+  $_SERVER['REQUEST_METHOD'] = NULL;
+
+  require_once 'includes/bootstrap.inc';
+  drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
+
+  $stock_id = $arguments['f'];
+
+  if ($stock_id > 0 ) {
+    tripal_stock_sync_stock($stock_id);
+  }
+  else{
+    print "syncing all stocks...\n";
+    tripal_stock_sync_stocks();
+  }
+}
+/**
+ *
+ */
+function tripal_stock_sync_form() {
+
+  $form['description'] = array(
+  '#type' => 'item',
+  '#value' => t("Stocks of the types listed ".
+     "below in the Stock Types box will be synced (leave blank to sync all types). You may limit the ".
+     "stocks to be synced by a specific organism. Depending on the ".
+     "number of stocks in the chado database this may take a long ".
+     "time to complete. "),
+  );
+
+  $form['stock_types'] = array(
+    '#title'       => t('Stock Types'),
+    '#type'        => 'textarea',
+    '#description' => t("Enter the names of the stock types to sync. " . 
+       "Leave blank to sync all stocks. Pages for these stock ".
+       "types will be created automatically for stocks that exist in the ".
+       "chado database.  The names listed here should be spearated by ".
+       "spaces or entered separately on new lines. The names must match ".
+       "exactly (spelling and case) with terms in the sequence ontology"),
+    '#default_value' => variable_get('chado_sync_stock_types', ''),
+  );
+
+  // get the list of organisms
+  $sql = "SELECT * FROM {organism} ORDER BY genus, species";
+  $orgs = tripal_organism_get_synced();
+  $organisms[] = '';
+  foreach ($orgs as $organism) {
+    $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
+  }
+  $form['organism_id'] = array(
+    '#title'       => t('Organism'),
+    '#type'        => t('select'),
+    '#description' => t("Choose the organism for which stocks types set above will be synced. Only organisms which also have been synced will appear in this list."),
+    '#options'     => $organisms,
+  );
+
+
+  $form['button'] = array(
+    '#type' => 'submit',
+    '#value' => t('Sync all Stocks'),
+    '#weight' => 3,
+  );
+
+  return $form;
+}
+/**
+ *
+ */
+function tripal_stock_sync_form_validate($form, &$form_state) {
+  $organism_id   = $form_state['values']['organism_id'];
+  $stock_types = $form_state['values']['stock_types'];
+
+  // nothing to do
+}
+/**
+ *
+ */
+function tripal_stock_sync_form_submit($form, &$form_state) {
+
+  global $user;
+
+  $organism_id   = $form_state['values']['organism_id'];
+  $stock_types = $form_state['values']['stock_types'];
+
+  $job_args = array(0, $organism_id, $stock_types);
+
+  if ($organism_id) {
+    $organism = tripal_core_chado_select('organism', array('genus', 'species'), array('organism_id' => $organism_id));
+    $title = "Sync stocks for " .  $organism[0]->genus . " " . $organism[0]->species;
+  }
+  else {
+    $title = 'Sync stocks';
+  }  
+
+  variable_set('chado_sync_stock_types', $stock_types);
+
+  tripal_add_job($title, 'tripal_stock', 'tripal_stock_sync_stocks', $job_args, $user->uid);
+}
+/**
+ *  
+ * @param $na 
+ *   Tripal expects all jobs to have at least one argument. For this function
+ *   we don't need any, so we have this dummy argument as a filler
+ * @param $job_id
+ */
+function tripal_stock_set_urls($na = NULL, $job = NULL) {
+  
+  // begin the transaction
+  db_query("BEGIN");
+      
+  print "\nNOTE: Setting of URLs is performed using a database transaction. \n" .
+        "If the load fails or is terminated prematurely then the entire set of \n" .
+        "new URLs will be rolled back and no changes will be made\n\n";
+  
+  // get the number of records we need to set URLs for
+  $csql = "SELECT count(*) FROM {chado_stock}";
+  $num_nodes = db_result(db_query($csql));
+    
+  // calculate the interval at which we will print an update on the screen
+  $num_set = 0;
+  $num_per_interval = 100;
+  
+  // prepate the statements which will quickly add url alias. Because these
+  // are not Chado tables we must manually prepare them 
+  $psql = "
+    PREPARE del_url_alias_by_src (text) AS
+    DELETE FROM {url_alias} WHERE src = \$1
+  ";
+  db_query($psql);
+  $psql = "
+    PREPARE ins_url_alias_nisrds (text, text) AS
+    INSERT INTO url_alias (src, dst) VALUES (\$1, \$2)
+  ";
+  db_query($psql);
+  
+  // get the URL alias syntax string
+  $url_alias = variable_get('chado_stock_url_string', '/stock/[genus]/[species]/[type]/[uniquename]'); 
+  if (!$url_alias) {
+    $url_alias = '/stock/[genus]/[species]/[type]/[uniquename]';
+  } 
+  $url_alias = preg_replace('/^\//', '', $url_alias); // remove any preceeding forward slash
+  
+  
+  // get the list of stocks that have been synced
+  $sql = "SELECT * FROM {chado_stock}";
+  $nodes = db_query($sql);  
+  while ($node = db_fetch_object($nodes)) {
+   
+    // get the URL alias
+    $src = "node/$node->nid";
+    $dst = tripal_stock_get_stock_url($node, $url_alias);
+    if (!$dst) {
+      db_query('DEALLOCATE "del_url_alias_by_src"');
+      db_query('DEALLOCATE "ins_url_alias_nisrds"');
+      db_query("ROLLBACK"); 
+      return; 
+    }
+    
+    // if the src and dst is the same (the URL alias couldn't be set)
+    // then skip to the next one. There's nothing we can do about this one.
+    if($src == $dst) {
+      continue;
+    }
+    
+    // remove any previous alias and then add the new one
+    $success = db_query("EXECUTE del_url_alias_by_src('%s')", $src);    
+    if (!$success) {
+      db_query('DEALLOCATE "del_url_alias_by_src"');
+      db_query('DEALLOCATE "ins_url_alias_nisrds"');
+      db_query("ROLLBACK");
+      watchdog('trp-seturl', "Failed Removing URL Alias: %src", array('%src' => $src), WATCHDOG_ERROR);
+      return;
+    }
+    $success = db_query("EXECUTE ins_url_alias_nisrds('%s', '%s')", $src, $dst);
+    if (!$success) {
+      db_query('DEALLOCATE "del_url_alias_by_src"');
+      db_query('DEALLOCATE "ins_url_alias_nisrds"');
+      db_query("ROLLBACK");
+      watchdog('trp-seturl', "Failed Adding URL Alias: %dst", array('%dst' => $dst), WATCHDOG_ERROR);
+      return;
+    }
+
+    // update the job status every 1% stocks
+    if ($job and $num_set % $num_per_interval == 0) {
+      $percent = ($num_set / $num_nodes) * 100;
+      tripal_job_set_progress($job, intval($percent));
+      $percent = sprintf("%.2f", $percent);
+      print "Setting URLs (" . $percent . "%). Memory: " . number_format(memory_get_usage()) . " bytes.\r";
+      
+    }
+    $num_set++;
+  }
+  $percent = ($num_set / $num_nodes) * 100;
+  tripal_job_set_progress($job, intval($percent));
+  $percent = sprintf("%.2f", $percent);
+  print "Setting URLs (" . $percent . "%). Memory: " . number_format(memory_get_usage()) . " bytes.\r";
+  print "\nDone. Set " . number_format($num_set) . " URLs\n";
+  
+  // unprepare the statements
+  db_query('DEALLOCATE "del_url_alias_by_src"');
+  db_query('DEALLOCATE "ins_url_alias_nisrds"');
+  
+  db_query("COMMIT");
+}
+/**
+ * 
+ * @param $node
+ *   A node object containing at least the stock_id and nid
+ * @param $url_alias
+ *   Optional.  This should be the URL alias syntax string that contains
+ *   placeholders such as [id], [genus], [species], [name], [uniquename],
+ *   and [type].  These placeholders will be substituted for actual values.
+ *   If this parameter is not provided then the value of the 
+ *   chado_stock_url_string Drupal variable will be used.
+ */
+function tripal_stock_get_stock_url($node, $url_alias = NULL) {
+
+  // get the starting URL alias
+  if(!$url_alias) {
+    $url_alias = variable_get('chado_stock_url_string', '/stock/[genus]/[species]/[type]/[uniquename]'); 
+    if (!$url_alias) {
+      $url_alias = '/stock/[genus]/[species]/[type]/[uniquename]';
+    } 
+    $url_alias = preg_replace('/^\//', '', $url_alias); // remove any preceeding forward slash
+  }
+
+  // get the stock 
+  $values = array('stock_id' => $node->stock_id); 
+  $options = array('statement_name' => 'sel_stock_id');       
+  $stock = tripal_core_chado_select('stock', array('*'), $values, $options);
+  if (!$stock) {
+    watchdog('trp-seturl', "Cannot find stock when setting URL alias for stock: %id", array('%id' => $node->stock_id), WATCHDOG_ERROR);
+    return FALSE;  
+  }
+  $stock = (object) $stock[0];
+  
+  // get the organism
+  $values = array('organism_id' => $stock->organism_id);
+  $options = array('statement_name' => 'sel_organism_id');
+  $organism  = tripal_core_chado_select('organism', array('*'), $values, $options);  
+  if (!$organism) {
+    watchdog('trp-seturl', "Cannot find organism when setting URL alias for stock: %id", array('%id' => $node->stock_id), WATCHDOG_ERROR);
+    return FALSE;  
+  }
+  $genus = preg_replace('/\s/', '_', strtolower($organism[0]->genus));
+  $species = preg_replace('/\s/', '_', strtolower($organism[0]->species)); 
+
+  // get the type
+  $values = array('cvterm_id' => $stock->type_id);
+  $options = array('statement_name' => 'sel_cvterm_id');
+  $cvterm = tripal_core_chado_select('cvterm', array('name'), $values, $options);
+  if (!$cvterm) {
+    watchdog('trp-seturl', "Cannot find type when setting URL alias for stock: %id", array('%id' => $node->stock_id), WATCHDOG_ERROR);
+    return FALSE;  
+  }
+  $type = preg_replace('/\s/', '_', $cvterm[0]->name);
+  
+  // now substitute in the values
+  $url_alias = preg_replace('/\[id\]/', $stock->stock_id, $url_alias);
+  $url_alias = preg_replace('/\[genus\]/', $genus, $url_alias);
+  $url_alias = preg_replace('/\[species\]/', $species, $url_alias);
+  $url_alias = preg_replace('/\[type\]/', $type, $url_alias);
+  $url_alias = preg_replace('/\[name\]/', $stock->name, $url_alias);
+  $url_alias = preg_replace('/\[uniquename\]/', $stock->uniquename, $url_alias);
+ 
+  // the dst field of the url_alias table is only 128 characters long. 
+  // if this is the case then simply return the node URL, we can't set this one
+  if (strlen($url_alias) > 128) {
+    watchdog('trp-seturl', "Cannot set alias longer than 128 characters: %alias.", array('%alias' => $url_alias), WATCHDOG_ERROR);
+    return "node/" . $node->nid;
+  }
+  
+  return $url_alias;
+}
+/**
+ *
+ *
+ * @ingroup tripal_stock
+ */
+function tripal_stock_sync_stocks($max_sync = 0, $organism_id = NULL,
+  $stock_types = NULL, $job_id = NULL) {
+  //print "Syncing stocks (max of $max_sync)\n";
+  $i = 0;
+
+  // get the list of available sequence ontology terms for which
+  // we will build drupal pages from stocks in chado.  If a stock
+  // is not one of the specified typse we won't build a node for it.
+  if (!$stock_types) {
+    $allowed_types = variable_get('chado_sync_stock_types', '');
+  }
+  else {
+    $allowed_types = $stock_types;
+  }
+  
+  if ($allowed_types) {
+    $allowed_types = preg_replace("/[\s\n\r]+/", " ", $allowed_types);
+    print "Looking for stocks of type: $allowed_types\n";
+  
+    $so_terms = split(' ', $allowed_types);
+    $where_cvt = "";
+    foreach ($so_terms as $term) {
+      $where_cvt .= "CVT.name = '$term' OR ";
+    }
+    $where_cvt = drupal_substr($where_cvt, 0, drupal_strlen($where_cvt)-3);  # strip trailing 'OR'
+  }
+  else {
+    $where_cvt = '1=1';
+  }
+
+  // get the list of organisms that are synced and only include stocks from
+  // those organisms
+  $orgs = tripal_organism_get_synced();
+  $where_org = "";
+  foreach ($orgs as $org) {
+    if ($organism_id) {
+      if ($org->organism_id and $org->organism_id == $organism_id) {
+        $where_org .= "S.organism_id = $org->organism_id OR ";
+      }
+    }
+    else {
+      if ($org->organism_id) {
+        $where_org .= "S.organism_id = $org->organism_id OR ";
+      }
+    }
+  }
+  $where_org = drupal_substr($where_org, 0, drupal_strlen($where_org)-3);  # strip trailing 'OR'
+
+  // use this SQL statement to get the stocks that we're going to upload
+  $sql = "SELECT stock_id ".
+        "FROM {stock} S ".
+        "  INNER JOIN {cvterm} CVT ON S.type_id = CVT.cvterm_id ".
+        "WHERE ($where_cvt) AND ($where_org) ".
+        "ORDER BY stock_id";
+
+  // get the list of stocks
+  $results = chado_query($sql);
+
+  // load into ids array
+  $count = 0;
+  $ids = array();
+  while ($id = db_fetch_object($results)) {
+    $ids[$count] = $id->stock_id;
+    $count++;
+  }
+
+  // make sure our vocabularies are set before proceeding
+//  tripal_stock_set_vocabulary();
+
+  // pre-create the SQL statement that will be used to check
+  // if a stock has already been synced.  We skip stocks
+  // that have been synced
+  $sql = "SELECT * FROM {chado_stock} WHERE stock_id = %d";
+
+  // Iterate through stocks that need to be synced
+  $interval = intval($count * 0.01);
+  if ($interval < 1) {
+    $interval = 1;
+  }
+  $num_ids = sizeof($ids);
+  $i = 0;
+  foreach ($ids as $stock_id) {
+    // update the job status every 1% stocks
+    if ($job_id and $i % $interval == 0) {
+      tripal_job_set_progress($job_id, intval(($i/$count)*100));
+    }
+    // if we have a maximum number to sync then stop when we get there
+    // if not then just continue on
+    if ($max_sync and $i == $max_sync) {
+      return '';
+    }
+    if (!db_fetch_object(db_query($sql, $stock_id))) {
+
+      # parsing all the stocks can cause memory overruns
+      # we are not sure why PHP does not clean up the memory as it goes
+      # to avoid this problem we will call this script through an
+      # independent system call
+      print ($i + 1) . " of $num_ids Syncing stock id: $stock_id\n";
+      $cmd = "php " . drupal_get_path('module', 'tripal_stock') . "/includes/tripal_stock.sync_stocks.inc -f $stock_id -t chado_stock";
+      print "$cmd\n";
+      system($cmd);
+
+    }
+    $i++;
+  }
+
+  return '';
+}
+
+/**
+ *
+ *
+ * @ingroup tripal_stock
+ */
+function tripal_stock_sync_stock($stock_id) {
+  
+print "\tSyncing stock $stock_id\n";
+  
+global $user;
+  $create_node = 1;   // set to 0 if the node exists and we just sync and not create
+
+  // get the accession prefix
+  $aprefix = variable_get('chado_stock_accession_prefix', 'SID');
+  
+  // if we don't have a stock_id then return
+  if (!$stock_id) {
+    drupal_set_message(t("Please provide a stock_id to sync"));
+    return '';
+  }
+
+  // get information about this stock
+  $fsql = "SELECT S.*, O.genus, O.species,CVT.name as cvname ".
+          "FROM {stock} S ".
+          "  INNER JOIN {cvterm} CVT ON S.type_id = CVT.cvterm_id ".
+          "  INNER JOIN {organism} O ON S.organism_id = O.organism_ID ".
+          "WHERE S.stock_id = %d";
+  $stock = db_fetch_object(chado_query($fsql, $stock_id));
+  
+  /*
+  // get the synonyms for this stock
+  $synsql = "SELECT S.name ".
+            "FROM {stock_synonym} SS ".
+            "  INNER JOIN {synonym} S on SS.synonym_id = S.synonym_id ".
+            "WHERE SS.stock_id = %d";
+  $synonyms = chado_query($synsql, $stock_id);  
+    
+  // now add these synonyms to the stock object as a single string
+  $synstring = '';
+  while ($synonym = db_fetch_object($synonyms)) {
+    $synstring .= "$synonym->name\n";
+  }
+  $stock->synonyms = $synstring;
+  */
+
+  // check to make sure that we don't have any nodes with this stock name as a title
+  // but without a corresponding entry in the chado_stock table if so then we want to
+  // clean up that node.  (If a node is found we don't know if it belongs to our stock or
+  // not since stocks can have the same name/title.)
+  $tsql =  "SELECT * FROM {node} N ".
+           "WHERE title = '%s'";
+  $cnsql = "SELECT * FROM {chado_stock} ".
+           "WHERE nid = %d";
+  $nodes = db_query($tsql, $stock->name);
+  // cycle through all nodes that may have this title
+  while ($node = db_fetch_object($nodes)) {
+    $stock_nid = db_fetch_object(db_query($cnsql, $node->nid));
+    if (!$stock_nid) {
+      drupal_set_message(t("%stock_id: A node is present but the chado_stock entry is missing... correcting", array('%stock_id' => $stock_id)));
+      node_delete($node->nid);
+    }
+  }
+
+  // check if this stock already exists in the chado_stock table.
+  // if we have a chado stock, we want to check to see if we have a node
+  $cfsql = "SELECT * FROM {chado_stock} ".
+           "WHERE stock_id = %d";
+  $nsql =  "SELECT * FROM {node} N ".
+           "WHERE nid = %d";
+  $chado_stock = db_fetch_object(db_query($cfsql, $stock->stock_id));
+  if ($chado_stock) {
+    drupal_set_message(t("%stock_id: A chado_stock entry exists", array('%stock_id' => $stock_id)));
+    $node = db_fetch_object(db_query($nsql, $chado_stock->nid));
+    if (!$node) {
+      // if we have a chado_stock but not a node then we have a problem and
+      // need to cleanup
+      drupal_set_message(t("%stock_id: The node is missing, but has a chado_stock entry... correcting", array('%stock_id' => $stock_id)));
+      $df_sql = "DELETE FROM {chado_stock} WHERE stock_id = %d";
+      db_query($df_sql, $stock_id);
+    }
+    else {
+      drupal_set_message(t("%stock_id: A corresponding node exists", array('%stock_id' => $stock_id)));
+      $create_node = 0;
+    }
+  }
+
+  // if we've encountered an error then just return.
+  if ($error_msg = db_error()) {
+    //print "$error_msg\n";
+    return '';
+  }
+
+  // if a drupal node does not exist for this stock then we want to
+  // create one.  Note that the node_save call in this block
+  // will call the hook_submit function which
+  if ($create_node) {
+    // get the organism for this stock
+    $sql = "SELECT * FROM {organism} WHERE organism_id = %d";
+    $organism = db_fetch_object(chado_query($sql, $stock->organism_id));
+
+    drupal_set_message(t("%stock_id: Creating node $stock->name", array('%stock_id' => $stock_id)));
+    $new_node = new stdClass();
+    $new_node->type = 'chado_stock';
+    $new_node->uid = $user->uid;
+    $new_node->title = "$stock->name, $stock->uniquename ($stock->cvname) $organism->genus $organism->species";
+    $new_node->sname = "$stock->name";
+    $new_node->uniquename = "$stock->uniquename";
+    $new_node->type_id = $stock->type_id;
+    $new_node->organism_id = $stock->organism_id;
+    $new_node->stock_id = $stock->stock_id;        
+    $new_node->chado_stock_exists = TRUE;
+    
+    // validate the node and if okay then submit
+    node_validate($new_node);
+    if ($errors = form_get_errors()) {
+      print "Error encountered validating new node. Cannot sync: $msg\n";
+      foreach ($errors as $key => $msg) {        
+        watchdog('trp-fsync', "%msg", array('%msg' => $msg), WATCHDOG_ERROR);
+      }
+      exit;
+    }
+    else {
+      $node = node_submit($new_node);
+      node_save($node);
+    }
+  }
+  else {
+    $node = $chado_stock;
+  }
+
+  return '';
+}

+ 243 - 77
tripal_stock/tripal_stock.module

@@ -21,7 +21,8 @@
  * Stock Module see the GMOD Wiki Page (http://gmod.org/wiki/Chado_Stock_Module)
  * @}
  */
-require_once("includes/tripal_stock-administration.inc");
+require_once("includes/tripal_stock.admin.inc");
+require_once("includes/tripal_stock.sync_stocks.inc");
 require_once("includes/other_module_api_functions.inc");
 require_once("includes/tripal_stock-secondary_tables.inc");
 require_once("includes/tripal_stock-properties.inc");
@@ -57,6 +58,15 @@ function tripal_stock_menu() {
     'access arguments' => array('administer tripal stocks'),
     'type' => MENU_NORMAL_ITEM
   );
+  
+  $items['admin/tripal/tripal_stock/sync'] = array(
+    'title' => ' Sync Stocks',
+    'description' => 'Sync stocks from Chado with Drupal',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('tripal_stock_sync_form'),
+    'access arguments' => array('administer tripal stocks'),
+    'type' => MENU_NORMAL_ITEM,
+  );
 
   // Adding Secondary Properties-----------------
   $items['node/%cs_node/properties'] = array(
@@ -116,6 +126,14 @@ function tripal_stock_menu() {
     'type' => MENU_LOCAL_TASK,
     'weight' => 10,
   );
+  
+  // the menu link for addressing any stock (by name, uniquename, synonym)
+  $items['stock/%'] = array(
+    'page callback' => 'tripal_stock_match_stocks_page',
+    'page arguments' => array(1),
+    'access arguments' => array('access chado_stock content'),
+    'type' => MENU_LOCAL_TASK,
+  );
   return $items;
 }
 
@@ -345,6 +363,29 @@ function chado_stock_load($node) {
   $values = array('stock_id' => $stock_id);
   $stock = tripal_core_generate_chado_var('stock', $values);
   
+  
+  // by default, the titles are saved using the unique constraint.  We will
+  // keep it the same, but remove the duplicate name if the unique name and name
+  // are identical
+  $title_type = variable_get('chado_stock_title', 'unique_constraint');
+  if($title_type == 'unique_constraint') {
+    if (strcmp($stock->name, $stock->uniquename)==0) {
+      $node->title = $stock->name . " (" . $stock->type_id->name . ") " . $stock->organism_id->genus . " " . $stock->organism_id->species ;
+    }
+    // in previous version of Tripal, the stock title was simply the unique name. 
+    // so, we recreate the title just to be sure all of our stock pages are consistent
+    else {
+      $node->title = $stock->name . ", " . $stock->uniquename . " (" . $stock->type_id->name . ") " . $stock->organism_id->genus . " " . $stock->organism_id->species ;  
+    }
+  }
+  // set the title to be the stock name or uniquename as configured
+  if($title_type == 'stock_name') {
+    $node->title = $stock->name;
+  }
+  if($title_type == 'stock_unique_name') {
+    $node->title = $stock->uniquename;  
+  }
+  
   // add this to the node
   $additions = new stdClass();
   $additions->stock = $stock;
@@ -413,7 +454,7 @@ function chado_stock_form($node, $form_state) {
     '#title' => t('Stock Name')
   );
 
-  $form['names']['title'] = array(
+  $form['names']['sname'] = array(
     '#type' => 'textfield',
     '#title' => t('Name'),
     '#default_value' => $node->stock->name,
@@ -440,8 +481,11 @@ function chado_stock_form($node, $form_state) {
   $type_options = tripal_cv_get_cvterm_options( variable_get('chado_stock_types_cv', 'NULL') );
   $type_options[0] = 'Select a Type';
   if ($node->nid == '') {
-  $type_default = 0; }
-  else { $type_default = $node->stock->type_id->cvterm_id; }
+    $type_default = 0; 
+  }
+  else { 
+    $type_default = $node->stock->type_id->cvterm_id; 
+  }
   $form['details']['type_id'] = array(
     '#type' => 'select',
     '#title' => t('Type of Stock'),
@@ -462,7 +506,7 @@ function chado_stock_form($node, $form_state) {
   $form['details']['organism_id'] = array(
     '#type' => 'select',
     '#title' => t('Source Organism for stock'),
-    '#default_value' => $organism_default,
+    '#default_value' => $node->stock->organism_id->organism_id,
     '#options' => $organisms,
     '#required'    => TRUE
   );
@@ -523,21 +567,35 @@ function chado_stock_validate($node, &$form) {
   $int_in_chado_sql = "SELECT count(*) as count FROM {%s} WHERE %s=%d";
   $string_in_chado_sql = "SELECT count(*) as count FROM {%s} WHERE %s='%s'";
 
-  // Validate Uniquename only if add
-  if (empty($node->stock_id)) {
-    $chado_row = db_fetch_object(chado_query("SELECT * FROM {stock} WHERE uniquename='" . $node->uniquename . "'"));
-    if (!empty($chado_row->stock_id)) {
-      $drupal_row = db_fetch_object(db_query("SELECT * FROM {chado_stock} WHERE stock_id=" . $chado_row->stock_id));
-      if (!empty($drupal_row->nid)) {
-        $link = l('node/' . $drupal_row->nid, $node->uniquename);
-        form_set_error('uniquename', "There is already a stock with that uniquename $link. Please enter another uniquename.");
-      }
-      else {
-        form_set_error('uniquename', "There is already a stock with that uniquename (although it's not sync'd with drupal). Please enter another uniquename.");
-      }
+  // if this is an update, we want to make sure that a different stock for
+  // the organism doesn't already have this uniquename. We don't want to give
+  // two sequences the same uniquename
+  if ($node->stock_id) {
+    $sql = "SELECT *
+            FROM {stock} S
+              INNER JOIN {cvterm} CVT ON S.type_id = CVT.cvterm_id
+            WHERE uniquename = '%s'
+             AND organism_id = %d AND CVT.name = '%s' AND NOT stock_id = %d";
+    $result = db_fetch_object(chado_query($sql, $node->uniquename, $node->organism_id, $node->stock_type, $node->stock_id));
+    if ($result) {
+      form_set_error('uniquename', t("Stock update cannot proceed. The stock name '$node->uniquename' is not unique for this organism. Please provide a unique name for this stock."));
     }
   }
 
+  // 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 {
+    $sql = "SELECT *
+            FROM {Stock} S
+              INNER JOIN {cvterm} CVT ON S.type_id = CVT.cvterm_id
+            WHERE uniquename = '%s'
+             AND organism_id = %d AND CVT.name = '%s'";
+    $result = db_fetch_object(chado_query($sql, $node->uniquename, $node->organism_id, $node->stock_type));
+    if ($result) {
+      form_set_error('uniquename', t("Stock insert cannot proceed. The stock name '$node->uniquename' already exists for this organism. Please provide a unique name for this stock."));
+    }
+  }
+  
 
   // Check Type of Stock is valid cvterm_id in chado ( $form['values']['details']['type_id'] )
   if ( $node->type_id == 0) {
@@ -579,9 +637,9 @@ function chado_stock_validate($node, &$form) {
     if ($num_rows->count != 1) {
       form_set_error('database', 'The database you selected is not valid. Please choose another one.'); }
   }
-
 }
 
+
 /**
  * Implements hook_insert(): Inserts data from chado_stock_form() into drupal and chado
  *
@@ -594,8 +652,8 @@ function chado_stock_validate($node, &$form) {
  * @ingroup tripal_stock
  */
 function chado_stock_insert($node) {
-
-  //If the chado stock exists
+  
+  // If the chado stock exists (e.g. this is only a syncing operation)
   // then don't create but simply link to node
   if ($node->chado_stock_exists) {
     if (!empty($node->stock_id)) {
@@ -607,12 +665,15 @@ function chado_stock_insert($node) {
         $node->stock_id
       );
     }
+    
     return $node;
   }
 
-  // create dbxref
-  if ( !empty($node->accession) ) {
-    if ( !empty($node->database) ) {
+  // before we can add the stock, we must add the dbxref if one has been
+  // provided by the user.
+  $dbxref_status = 0;
+  if (!empty($node->accession) ) {
+    if (!empty($node->database) ) {
       $values = array(
         'db_id' => $node->database,
         'accession' => $node->accession,
@@ -630,11 +691,14 @@ function chado_stock_insert($node) {
           );
         }
       }
-      else { $dbxref_status = 1; }
+      else { 
+        $dbxref_status = 1;
+      }
     }
   }
 
-  // create stock
+  // create stock including the dbxref
+  $stock = '';
   if ($dbxref_status) {
     $values = array(
       'dbxref_id' => array(
@@ -642,56 +706,41 @@ function chado_stock_insert($node) {
         'accession' => $node->accession
       ),
       'organism_id' => $node->organism_id,
-      'name' => $node->title,
+      'name' => $node->sname,
       'uniquename' => $node->uniquename,
       'description' => $node->stock_description,
       'type_id' => $node->type_id
     );
-    $stock_status = tripal_core_chado_insert('stock', $values);
+    $stock = tripal_core_chado_insert('stock', $values);
   }
+  // create a stock without a dbxref
   else {
     $values = array(
       'organism_id' => $node->organism_id,
-      'name' => $node->title,
-      'uniquename' => $node->uniquename,
+      'name'        => $node->sname,
+      'uniquename'  => $node->uniquename,
       'description' => $node->stock_description,
-      'type_id' => $node->type_id
+      'type_id'     => $node->type_id
     );
-    $stock_status = tripal_core_chado_insert('stock', $values);
+    $stock = tripal_core_chado_insert('stock', $values);
   }
-
-  // create drupal chado_stock entry
-  if ($stock_status) {
-    $values = array(
-      'organism_id' => $node->organism_id,
-      'uniquename' => $node->uniquename,
-      'type_id' => $node->type_id
-    );
-    $chado_stock = tripal_core_chado_select('stock', array('stock_id'), $values);
-    if (!empty($chado_stock[0]->stock_id)) {
-      db_query(
-        "INSERT INTO {chado_stock} (nid, vid, stock_id) "
-        ."VALUES (%d, %d, %d)",
-        $node->nid,
-        $node->vid,
-        $chado_stock[0]->stock_id
-      );
-
-        //Move on to next stage of Stock Creation based on next_stage_path field
-      if ($node->simulate_multipart) {
-        $next_stage_path = preg_replace('/%node/', $node->nid, $node->next_step_path);
-        $_REQUEST['destination'] = $next_stage_path;
-      }
-    }
-    else {
-      drupal_set_message(t('Error during stock creation.'), 'error');
-      watchdog('tripal_stock',
-        'Insert Stock: Unable to find newly created stock where values:%values',
-        array('%values' => print_r($values, TRUE)),
-        WATCHDOG_ERROR
-      );
-      return FALSE;
-    }
+  
+  // if the stock creation was succesful then add the URL and the entry in the
+  // chado_stock table
+  if (is_array($stock)) {
+    
+    // convert the stock into an object
+    $stock = (object) $stock;
+
+    // add the entry to the chado_stock table
+    $sql = "INSERT INTO {chado_stock} (nid, vid, stock_id) VALUES (%d, %d, %d)";
+    db_query($sql, $node->nid, $node->vid, $stock->stock_id);
+
+    // Move on to next stage of Stock Creation based on next_stage_path field
+    if ($node->simulate_multipart) {
+      $next_stage_path = preg_replace('/%node/', $node->nid, $node->next_step_path);
+      $_REQUEST['destination'] = $next_stage_path;
+    }    
   }
   else {
     drupal_set_message(t('Error during stock creation.'), 'error');
@@ -702,7 +751,6 @@ function chado_stock_insert($node) {
     );
     return FALSE;
   }
-
 }
 
 /**
@@ -724,10 +772,6 @@ function chado_stock_update($node) {
     // there is no way to handle revisions in Chado but leave
     // this here just to make not we've addressed it.
   }
-//  if ($node->revision) {
-//    chado_stock_insert($node);
-//  }
-//  else {
 
   //update dbxref
   if ($node->database) {
@@ -794,7 +838,7 @@ function chado_stock_update($node) {
   //can't change stock id which is all thats stored in drupal thus only update chado
   $update_values = array(
     'organism_id' => $node->organism_id,
-    'name' => $node->title,
+    'name' => $node->sname,
     'uniquename' => $node->uniquename,
     'description' => $node->stock_description,
     'type_id' => $node->type_id,
@@ -805,11 +849,8 @@ function chado_stock_update($node) {
       'accession' => $node->accession
     );
   }
-  $status = tripal_core_chado_update(
-    'stock',
-    array('stock_id' => $node->stock_id),
-    $update_values
-  );
+  $status = tripal_core_chado_update('stock', array('stock_id' => $node->stock_id), $update_values);
+    
 
   if (!$status) {
     drupal_set_message(t('Unable to update stock'), 'error');
@@ -820,6 +861,11 @@ function chado_stock_update($node) {
       WATCHDOG_ERROR
     );
   }
+  else {
+    // set the URL for this stock page
+    $values = array('stock_id' => $node->stock_id);
+    $stock = tripal_core_chado_select('stock', array('*'), $values);    
+  }
 }
 
 /**
@@ -1059,8 +1105,59 @@ function tripal_stock_preprocess_tripal_stock_relationships(&$variables) {
 function tripal_stock_nodeapi(&$node, $op, $teaser, $page) {
 
   switch ($op) {
-    // Note that this function only adds stock view to an organism/feature
-    // node.
+    
+    // set the title to ensure it is always unique
+    case 'presave':
+      switch ($node->type) {
+        case 'chado_stock':
+          
+          $values = array('organism_id' => $node->organism_id);
+          $organism = tripal_core_chado_select('organism', array('genus','species'), $values);
+          $values = array('cvterm_id' => $node->type_id);
+          $cvterm = tripal_core_chado_select('cvterm', array('name'), $values);
+          $node->title = $node->sname . ', ' . $node->uniquename . ' (' . $cvterm[0]->name . ') ' . $organism[0]->genus . ' ' . $organism[0]->species;
+          break;
+      }
+      break;
+      
+    // set the URL path after inserting.  We do it here because we do not 
+    // know the stock_id in the presave  
+    case 'insert':
+      switch ($node->type) {
+        case 'chado_stock':
+          if (!$node->stock_id) {
+            $sql = "SELECT * FROM {chado_stock} WHERE nid = %d";
+            $chado_stock = db_fetch_object(db_query($sql, $node->nid));
+            $node->stock_id = $chado_stock->stock_id;
+          }
+          
+          // remove any previous alias
+          db_query("DELETE FROM {url_alias} WHERE src = '%s'", "node/$node->nid");
+          
+          // set the URL for this stock page
+          $url_alias = tripal_stock_get_stock_url($node);
+          path_set_alias("node/$node->nid", $url_alias);
+          break;
+      }
+      break;
+      
+    // set the URL path after inserting.  We do it here because we do not 
+    // know the stock_id in the presave  
+    case 'update':
+      switch ($node->type) {
+        case 'chado_stock':
+          
+          // remove any previous alias
+          db_query("DELETE FROM {url_alias} WHERE src = '%s'", "node/$node->nid");
+          
+          // set the URL for this stock page
+          $url_alias = tripal_stock_get_stock_url($node);
+          path_set_alias("node/$node->nid", $url_alias);
+          break;
+      }
+      break;
+      
+    // add items to other nodes, build index and search results
     case 'view':
       // add the stock to the organism/feature search indexing
       if ($node->build_mode == NODE_BUILD_SEARCH_INDEX) {
@@ -1083,3 +1180,72 @@ function tripal_stock_nodeapi(&$node, $op, $teaser, $page) {
   }
 }
 
+/*
+ * Uses the value provided in the $id argument to find all stocks that match
+ * that ID by name, stockname or synonym.  If it matches uniquenly to a single
+ * stock it will redirect to that stock page, otherwise, a list of matching
+ * stocks is shown.
+ */
+function tripal_stock_match_stocks_page($id) {
+  
+  // if the URL alias configuration is set such that the URL
+  // always begins with 'stock' then we want to use the ID as it is and
+  // forward it on. Otherwise, try to find the matching stock.
+  $url_alias = variable_get('chado_stock_url_string', '/stock/[genus]/[species]/[type]/[uniquename]'); 
+  if (!$url_alias) {
+    $url_alias = '/stock/[genus]/[species]/[type]/[uniquename]';
+  } 
+  $url_alias = preg_replace('/^\//', '', $url_alias); // remove any preceeding forward slash  
+  if (preg_match('/^stock\//', $url_alias)) {
+    drupal_goto($id);
+  }
+  
+
+  $sql = "
+    SELECT
+      S.name, S.uniquename, S.stock_id,
+      O.genus, O.species, O.organism_id,
+      CVT.cvterm_id, CVT.name as type_name,
+      CS.nid
+    FROM {stock} S
+      INNER JOIN {organism} O on S.organism_id = O.organism_id
+      INNER JOIN {cvterm} CVT on CVT.cvterm_id = S.type_id
+      INNER JOIN public.chado_stock CS on CS.stock_id = S.stock_id
+    WHERE
+      S.uniquename = '%s' or S.name = '%s'
+  ";
+  $results = chado_query($sql, $id, $id);
+
+  $num_matches = 0;
+
+  // iterate through the matches and build the table for showing matches
+  $header = array('Uniquename', 'Name', 'Type', 'Species');
+  $rows = array();
+  $curr_match;
+  while ($match = db_fetch_object($results)) {
+    $curr_match = $match;
+    $rows[] = array(
+       $match->uniquename,
+       "<a href=\"" . url("node/". $match->nid) ."\">" . $match->name . "</a>",
+       $match->type_name,
+       '<i>' . $match->genus . ' ' . $match->species . '</i>',
+    );
+    $num_matches++;
+  }
+
+  // if we have more than one match then generate the table, otherwise, redirect
+  // to the matched stock
+  if ($num_matches == 1) {
+    drupal_goto("node/" . $curr_match->nid);
+  }
+  if ($num_matches == 0) {
+    return "<p>No stocks matched the given name '$id'</p>";
+  }
+
+  $table_attrs = array(
+    'class' => 'tripal-table tripal-table-horz'
+  );
+  $output = "<p>The following stocks match the name '$id'.</p>";
+  $output .= theme_table($header, $rows, $table_attrs, $caption);
+  return $output;
+}