Sfoglia il codice sorgente

Changed the way URLs for features and stocks are created. Admins can now buil the URL string using place holders

spficklin 11 anni fa
parent
commit
921557cca7

+ 42 - 45
tripal_feature/includes/tripal_feature.admin.inc

@@ -346,64 +346,61 @@ function get_tripal_feature_admin_form_title_set(&$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']['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/FID1034, where the element just after the final 
-      slash is the unique identifier for the feature.'),
-  );
+
   $options = array(
-    'internal ID'          => 'Internal ID (Uses the Chado feature_id. Please set the ID Prefix below.)',
-    '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]',
-    'genus_species_type_uname'  => 'Genus + species + type + unique name (e.g. http://your.site.url/[genus]/[genus]_[species]/[type]/[unique name]',
+    '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: \'feature\', [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('Unique Identifier'),
-    '#type'          => 'radios',
-    '#description'   => t('Choose an identifier type from the list above that is 
-      guaranteed to be unique for all features you want to sync. If in doubt it 
-      is safest to coose the internal ID or the final option with the genus, species, 
-      type and unique name. Click the \'Save Configuration\' button at the bottom to save 
-      your selection Click the \'Set Feature URLs\' button to submit a job to reset 
-      the URLs for all synced features.'),
+    '#title'         => t('URL components'),
+    '#type'          => 'checkboxes',
     '#required'      => FALSE,
     '#options'       => $options,
-    '#default_value' => variable_get('chado_feature_url', 'internal ID'),
-  );
-
-  $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', 'FID'),
-  );
+    '#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']['note'] = array(
-    '#type'        => 'markup',
-    '#value' => t('<b>Note:</b> It is important to set a unique URL for each feature, and that
-       URL must be unique. However, it may be difficult for 3rd party programs (e.g. GBrowse) to
-       link to features when the intenal ID or the URL contains genus, species and or type information.
-       Therefore, Tripal provides a way to link to any feature simply using the name, unique name or
-       synonym.  If you use the URL http://your.site.url/feature/[feature], where [feature] is a name,
-       unique name, or synonym, then Tripal will automatically redirect to the feature page that matches.
-       If there are more than one match, a list of available features will be provided from which the 
-       user can select.<br>'),
-  );
-
   $form['url']['button'] = array(
     '#type' => 'submit',
     '#value' => t('Set Feature URLs'),

+ 13 - 22
tripal_feature/includes/tripal_feature.sync_features.inc

@@ -140,9 +140,12 @@ function tripal_feature_set_urls($job_id = NULL) {
  */
 function tripal_feature_get_feature_url($node) {
 
-  // determine which URL alias to use
-  $alias_type = variable_get('chado_feature_url', 'internal ID');
-  $aprefix = variable_get('chado_feature_accession_prefix', 'SID');
+  // get the starting 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
 
   // get the feature 
   $values = array('feature_id' => $node->feature_id);        
@@ -161,25 +164,13 @@ function tripal_feature_get_feature_url($node) {
   $cvterm = tripal_core_chado_select('cvterm', array('name'), $values);
   $type = preg_replace('/\s/', '_', $cvterm[0]->name);
   
-  switch ($alias_type) {
-    case 'feature_name':
-      $url_alias = "feature/" . $feature->name;
-      break;
-    case 'feature_unique_name':
-      $url_alias = "feature/" . $feature->uniquename;
-      break;
-    case 'genus_species_uqname':
-      $url_alias = "feature/" . $genus . "_" . $species . "/" . $feature->uniquename;
-      break;
-    case 'genus_species_name':
-      $url_alias = "feature/" . $genus . "_" . $species . "/" . $feature->name;
-      break;
-    case 'genus_species_type_uname':
-      $url_alias = "feature/" . $genus . "_" . $species . "/" . $type . "/" . $feature->uniquename;
-      break;
-    default:
-      $url_alias = "$aprefix$node->feature_id";
-  }
+  // 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);
  
   return $url_alias;
 }

+ 14 - 4
tripal_feature/tripal_feature.module

@@ -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;
@@ -2304,6 +2302,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
@@ -2350,7 +2360,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>";

+ 48 - 62
tripal_stock/includes/tripal_stock.admin.inc

@@ -15,30 +15,6 @@
 function tripal_stock_admin() {
   $form = array();
 
-  /*
-  // before proceeding check to see if we have any
-  // currently processing jobs.
-  $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'),
-       '#collapsible' => FALSE,
-       '#collapsed' => FALSE,
-    );
-    $form['notice']['message'] = array(
-       '#value' => t("Currently, 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);
-  }
-*/  
   get_tripal_stock_admin_form_title_set($form);
   get_tripal_stock_admin_form_url_set($form);
   get_tripal_stock_admin_form_vocabulary_set($form);       
@@ -62,12 +38,14 @@ function tripal_stock_admin() {
 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') :      
-      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']);
       break;
       
     case t('Clean up orphaned stocks') :
@@ -76,11 +54,11 @@ function tripal_stock_admin_validate($form, &$form_state) {
       break;
     
     case t('Set Stock URLs') :
-      variable_set('chado_stock_url', $form_state['values']['stock_url']);
       tripal_add_job('Set Stock URLs', 'tripal_stock',
         'tripal_stock_set_urls', $job_args, $user->uid);
       break;
   }
+
 }
 
 /**
@@ -132,46 +110,54 @@ function get_tripal_stock_admin_form_url_set(&$form) {
     '#collapsible' => TRUE,
     '#collapsed' => TRUE,
   );
-  $form['url']['desc'] = array(
-    '#type'        => 'markup',
-    '#value' => t('Each synced stock will have a unique URL which consists of 
-      the site domain followed by a unique identifer: for example 
-      http://my-tripal-site.org/SID1034, where the element just after the final 
-      slash is the unique identifier for the stock.'),
-  );
+
   $options = array(
-    'internal ID'               => 'Internal ID (Uses the Chado stock_id. Please set the ID Prefix below)',
-    'stock_unique_name'         => 'Stock unique name',
-    'stock_name'                => 'Stock 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]',
-    'genus_species_type_uname'  => 'Genus + species + type + unique name (e.g. http://your.site.url/[genus]/[genus]_[species]/[type]/[unique name]',
+    '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: \'stock\', [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('Unique Identifier'),
-    '#type'          => 'radios',
-    '#description'   => t('Choose an identifier type from the list above that is 
-      guaranteed to be unique for all stocks. If in doubt it is safest to choose the 
-      internal ID. Click the \'Save Configuration\' button at the bottom to save 
-      your selection Click the \'Set Stock URLs\' button to submit a job to reset 
-      the URLs for all synced stocks.'),
+    '#title'         => t('URL components'),
+    '#type'          => 'checkboxes',
     '#required'      => FALSE,
     '#options'       => $options,
-    '#default_value' => variable_get('chado_stock_url', 'internal ID'),
-  );
-
-  $form['url']['chado_stock_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 stock name or unique name then 
-      this prfix is not used"),
-    '#required'    => TRUE,
-    '#default_value' => variable_get('chado_stock_accession_prefix', 'SID'),
-  );
-
+    '#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'),

+ 13 - 22
tripal_stock/includes/tripal_stock.sync_stocks.inc

@@ -138,9 +138,12 @@ function tripal_stock_set_urls($job_id = NULL) {
  */
 function tripal_stock_get_stock_url($node) {
 
-  // determine which URL alias to use
-  $alias_type = variable_get('chado_stock_url', 'internal ID');
-  $aprefix = variable_get('chado_stock_accession_prefix', 'SID');
+  // get the starting 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);        
@@ -159,25 +162,13 @@ function tripal_stock_get_stock_url($node) {
   $cvterm = tripal_core_chado_select('cvterm', array('name'), $values);
   $type = preg_replace('/\s/', '_', $cvterm[0]->name);
   
-  switch ($alias_type) {
-    case 'stock_name':
-      $url_alias = "stock/" . $stock->name;
-      break;
-    case 'stock_unique_name':
-      $url_alias = "stock/" . $stock->uniquename;
-      break;
-    case 'genus_species_uqname':
-      $url_alias = "stock/" . $genus . "_" . $species . "/" . $stock->uniquename;
-      break;
-    case 'genus_species_name':
-      $url_alias = "stock/" . $genus . "_" . $species . "/" . $stock->name;
-      break;
-    case 'genus_species_type_uname':
-      $url_alias = "stock/" . $genus . "_" . $species . "/" . $type . "/" . $stock->uniquename;
-      break;
-    default:
-      $url_alias = "$aprefix$node->stock_id";
-  }
+  // 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);
  
   return $url_alias;
 }

+ 103 - 12
tripal_stock/tripal_stock.module

@@ -126,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;
 }
 
@@ -559,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) {
@@ -1158,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;
+}