Browse Source

Merge branch 'advancedoptions' into advancedoptions

Bradford Condon 6 years ago
parent
commit
2d0a764277

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+
+# Ignore an internal CViTjs installation.
+# Assumes it's been cloned in the js directory
+js/*

+ 127 - 42
api/blast_ui.api.inc

@@ -48,23 +48,8 @@ function get_blast_database($identifiers) {
 function get_blast_database_options($type) {
 function get_blast_database_options($type) {
   global $user;
   global $user;
 
 
-  // Use the Entity API to get a list of BLAST Nodes to load
-  // We use this function in order respect node access control so that
-  // administrators can use this module in combination with a node access module
-  // of their choice to limit access to specific BLAST databases.
-  $query = new EntityFieldQuery();
-  $query->entityCondition('entity_type', 'node')
-    // Restrict to BLASTDB nodes.
-    ->entityCondition('bundle', 'blastdb')
-    // Restrict to Published nodes.
-    ->propertyCondition('status', 1)
-    // Restrict to nodes the current user has permission to view.
-    ->addTag('node_access');
-  $entities = $query->execute();
-
-
   // Get all BlastDB nodes
   // Get all BlastDB nodes
-  $nodes  = node_load_multiple(array_keys($entities['node']));
+  $nodes = get_blast_database_nodes();
 
 
   // Support obsolete database type n/p
   // Support obsolete database type n/p
   $obs_type = '';
   $obs_type = '';
@@ -90,6 +75,32 @@ function get_blast_database_options($type) {
   return $options;
   return $options;
 }
 }
 
 
+/**
+ * Returns all blast database nodes.
+ *
+ * @return
+ *   An array of nodes.
+ */
+function get_blast_database_nodes() {
+  // Use the Entity API to get a list of BLAST Nodes to load
+  // We use this function in order respect node access control so that
+  // administrators can use this module in combination with a node access module
+  // of their choice to limit access to specific BLAST databases.
+  $query = new EntityFieldQuery();
+  $query->entityCondition('entity_type', 'node')
+    // Restrict to BLASTDB nodes.
+    ->entityCondition('bundle', 'blastdb')
+    // Restrict to Published nodes.
+    ->propertyCondition('status', 1)
+    // Restrict to nodes the current user has permission to view.
+    ->addTag('node_access');
+  $entities = $query->execute();
+
+  // Get all BlastDB nodes
+  return node_load_multiple(array_keys($entities['node']));
+}
+
+
 /**
 /**
  * Retrieve all the information for a blast job in a standardized node-like format.
  * Retrieve all the information for a blast job in a standardized node-like format.
  *
  *
@@ -101,6 +112,11 @@ function get_blast_database_options($type) {
 function get_BLAST_job($job_id) {
 function get_BLAST_job($job_id) {
 
 
   $blastjob = db_query('SELECT * FROM blastjob WHERE job_id=:id', array(':id' => $job_id))->fetchObject();
   $blastjob = db_query('SELECT * FROM blastjob WHERE job_id=:id', array(':id' => $job_id))->fetchObject();
+
+  if (!$blastjob) {
+    return false;
+  }
+
   $tripal_job = tripal_get_job($job_id);
   $tripal_job = tripal_get_job($job_id);
 
 
   $job = new stdClass();
   $job = new stdClass();
@@ -329,8 +345,8 @@ function run_BLAST_tripal_job($program, $query, $database, $output_filestub, $op
  */
  */
 function validate_fasta_sequence($type, $sequence) {
 function validate_fasta_sequence($type, $sequence) {
   //Includes IUPAC codes.
   //Includes IUPAC codes.
-  $fastaSeqRegEx = ($type == 'nucleotide') 
-                   ? '/^[ATCGNUKMBVSWDYRHatcgnukmbvswdyrh\s\n\r]*$/' 
+  $fastaSeqRegEx = ($type == 'nucleotide')
+                   ? '/^[ATCGNUKMBVSWDYRHatcgnukmbvswdyrh\[\/\]\s\n\r]*$/'
                    : '/^[ABCDEFGHIKLMNPQRSTUVWYZXabcdefghiklmnpqrstuvwyzx\*\-\s\n\r]*$/';
                    : '/^[ABCDEFGHIKLMNPQRSTUVWYZXabcdefghiklmnpqrstuvwyzx\*\-\s\n\r]*$/';
   $defRegEx      = '/^>.*(\\n|\\r)(.*)$/sm';
   $defRegEx      = '/^>.*(\\n|\\r)(.*)$/sm';
   if (preg_match($defRegEx, $sequence, $matches)) {
   if (preg_match($defRegEx, $sequence, $matches)) {
@@ -341,7 +357,7 @@ function validate_fasta_sequence($type, $sequence) {
   else if ($sequence != '' && preg_match($defRegEx, $sequence)) {
   else if ($sequence != '' && preg_match($defRegEx, $sequence)) {
     return true;
     return true;
   }
   }
-  
+
   return false;
   return false;
 }
 }
 
 
@@ -404,23 +420,21 @@ function get_recent_blast_jobs($programs = array()) {
       $add = TRUE;
       $add = TRUE;
 
 
       $job_id = blast_ui_reveal_secret($job_secret);
       $job_id = blast_ui_reveal_secret($job_secret);
-      $job = get_BLAST_job($job_id);
+      if ($job = get_BLAST_job($job_id)) {
 
 
-      // @TODO: Check that the results are still available.
-      // This is meant to replace the arbitrary only show jobs executed less than 48 hrs ago.
+        // @TODO: Check that the results are still available.
+        // This is meant to replace the arbitrary only show jobs executed less than 48 hrs ago.
 
 
-      // Remove jobs from the list that are not of the correct program.
-      if ($filter_jobs AND !in_array($job->program, $programs)) {
-        $add = FALSE;
-      }
+        // Remove jobs from the list that are not of the correct program.
+        if ($filter_jobs AND !in_array($job->program, $programs)) {
+          $add = FALSE;
+        }
 
 
-      if ($add) {
-
-        $job->query_summary = format_query_headers($job->files->query);
-
-        $jobs[] = $job;
+        if ($add) {
+          $job->query_summary = format_query_headers($job->files->query);
+          $jobs[] = $job;
+        }
       }
       }
-
     }
     }
 
 
     return $jobs;
     return $jobs;
@@ -745,7 +759,7 @@ function convert_tsv2gff3($blast_tsv,$blast_gff){
   $last_s = NULL;
   $last_s = NULL;
   $hsp = NULL;
   $hsp = NULL;
   $HitResult=array();
   $HitResult=array();
-  
+
   while(!feof($tsv)) {
   while(!feof($tsv)) {
     $line = fgets($tsv);
     $line = fgets($tsv);
     $line = rtrim($line);
     $line = rtrim($line);
@@ -780,9 +794,9 @@ function convert_tsv2gff3($blast_tsv,$blast_gff){
     $qs = $parts[6];
     $qs = $parts[6];
     $qe = $parts[7];
     $qe = $parts[7];
     $e = $parts[10];
     $e = $parts[10];
-     
 
 
-    // if this is a new hit print the last and 
+
+    // if this is a new hit print the last and
     // empty the $HitResult array and
     // empty the $HitResult array and
     // reset hsp counter
     // reset hsp counter
     if ($last_s != NULL and $s != $last_s ) {
     if ($last_s != NULL and $s != $last_s ) {
@@ -790,10 +804,10 @@ function convert_tsv2gff3($blast_tsv,$blast_gff){
       $HitResult = array();
       $HitResult = array();
       $hsp=0;
       $hsp=0;
     }
     }
-  
+
     // every line is a new hsp
     // every line is a new hsp
     $hsp++;
     $hsp++;
-  
+
     // determine query strand to use in match_part line, no need to store, just print
     // determine query strand to use in match_part line, no need to store, just print
     $q_strand = '+';
     $q_strand = '+';
     if ($qs > $qe) {
     if ($qs > $qe) {
@@ -808,12 +822,12 @@ function convert_tsv2gff3($blast_tsv,$blast_gff){
        list($start,$end) = array($se,$ss);
        list($start,$end) = array($se,$ss);
        $HitResult["$s,$q"]['strand']='-';
        $HitResult["$s,$q"]['strand']='-';
      }
      }
-  
+
     // store smallest start
     // store smallest start
      if (!array_key_exists('SS',$HitResult["$s,$q"]) or $ss < $HitResult["$s,$q"]['SS']) {
      if (!array_key_exists('SS',$HitResult["$s,$q"]) or $ss < $HitResult["$s,$q"]['SS']) {
        $HitResult["$s,$q"]['SS'] = $ss;
        $HitResult["$s,$q"]['SS'] = $ss;
      }
      }
-    
+
     // store largest end
     // store largest end
      if (!array_key_exists('SE',$HitResult["$s,$q"]) or $se > $HitResult["$s,$q"]['SE']) {
      if (!array_key_exists('SE',$HitResult["$s,$q"]) or $se > $HitResult["$s,$q"]['SE']) {
        $HitResult["$s,$q"]['SE'] = $se;
        $HitResult["$s,$q"]['SE'] = $se;
@@ -822,8 +836,8 @@ function convert_tsv2gff3($blast_tsv,$blast_gff){
      // store best evalue
      // store best evalue
      if (!array_key_exists('E',$HitResult["$s,$q"]) or $e < $HitResult["$s,$q"]['E']) {
      if (!array_key_exists('E',$HitResult["$s,$q"]) or $e < $HitResult["$s,$q"]['E']) {
        $HitResult["$s,$q"]['E'] = $e;
        $HitResult["$s,$q"]['E'] = $e;
-     }   
-    
+     }
+
      // generate the match_part line for each hsp
      // generate the match_part line for each hsp
      $HitResult["$s,$q"]['HSPs'][] = join("\t", array($s, "BLASTRESULT" , "match_part" , $start , $end , $e , $HitResult["$s,$q"]['strand'] , '.' , "ID=$s.$q.$hsp;Parent=$s.$q;Target=$q $qs $qe $q_strand"));
      $HitResult["$s,$q"]['HSPs'][] = join("\t", array($s, "BLASTRESULT" , "match_part" , $start , $end , $e , $HitResult["$s,$q"]['strand'] , '.' , "ID=$s.$q.$hsp;Parent=$s.$q;Target=$q $qs $qe $q_strand"));
      $last_s = $s;
      $last_s = $s;
@@ -847,7 +861,6 @@ function convert_tsv2gff3($blast_tsv,$blast_gff){
  *
  *
  *
  *
  */
  */
-
 function printGFF_parent_children ($gff,$blast_feature_array){
 function printGFF_parent_children ($gff,$blast_feature_array){
   foreach ($blast_feature_array as $sq => $value ) {
   foreach ($blast_feature_array as $sq => $value ) {
     list ($s,$q) = preg_split('/,/' , $sq);
     list ($s,$q) = preg_split('/,/' , $sq);
@@ -858,3 +871,75 @@ function printGFF_parent_children ($gff,$blast_feature_array){
     fwrite($gff,$child);
     fwrite($gff,$child);
   }
   }
 }
 }
+
+/**
+ * Get text from cvitjs conf file, if possible.
+ *
+ * @param $genome_target
+ *   The section of the config to return. Should consist of "data."+[blastdb name].
+ *
+ * @return
+ *   A string containing the entire contents of the cvitjs configuration file. FALSE otherwise.
+ */
+function blast_ui_get_cvit_conf_text($genome_target = FALSE) {
+
+  // Retrieve the full path and filename of the conf.
+  $cvit_conf = blast_ui_get_cvit_conf();
+  if ($cvit_conf) {
+
+    // Retrieve the contents of the file.
+    $contents = file_get_contents($cvit_conf);
+
+    // If no genome target was provided then return the full file.
+    if ($contents && $genome_target == FALSE) {
+      return $contents;
+    }
+
+    // If a genome target was provided, then only return that section.
+    if ($genome_target) {
+      $section = array();
+      $in_section = FALSE;
+
+      // For each line of the configuration file...
+      $section_header = '['.$genome_target.']';
+      $lines = preg_split('/\r\n|\n|\r/', trim($contents));
+      foreach($lines as $l) {
+
+        // Are we in the section for this genome target?
+        if (trim($l) == $section_header) {
+          $in_section = TRUE; }
+
+        // Id so and we haven't fallen out of it through an empty line,
+        // then add it to saved section for returning.
+        if ($in_section) {
+          if (trim($l) == '') { break; }
+          $section[] = trim($l);
+        }
+      }
+
+      // If we found the section, then return it ;-).
+      if (!empty($section)) {
+        return implode("\n", $section);
+      }
+    }
+  }
+
+  return false;
+}
+
+
+/**
+ * Get path to cvitjs conf file.
+ *
+ * @return
+ *  The path to the CViTjs codebase.
+ */
+function blast_ui_get_cvit_conf($cvitjs_location = NULL) {
+  if (!$cvitjs_location) {
+    $cvitjs_location = libraries_get_path('cvitjs') . DIRECTORY_SEPARATOR;
+  }
+
+  $cvit_conf_path = $cvitjs_location . 'cvit.conf';
+
+  return $cvit_conf_path;
+}

+ 2 - 0
blast_ui.info

@@ -4,3 +4,5 @@ configure = admin/tripal/extension/tripal_blast/blast_ui
 project = tripal_blast
 project = tripal_blast
 package = Tripal Extensions
 package = Tripal Extensions
 core = 7.x
 core = 7.x
+
+dependencies[] = libraries

+ 54 - 4
blast_ui.install

@@ -17,6 +17,34 @@ function blast_ui_install() {
    tripal_create_files_dir('tripal_blast');
    tripal_create_files_dir('tripal_blast');
 }
 }
 
 
+/**
+ * Implements hook_uninstall().
+ */
+function blast_ui_uninstall() {
+  // Remove all nodes of type blastdb
+  
+  $query = new EntityFieldQuery();
+  $query->entityCondition('entity_type', 'node')
+    // Restrict to BLASTDB nodes.
+    ->entityCondition('bundle', 'blastdb')
+    // Restrict to Published nodes.
+    ->propertyCondition('status', 1)
+    // Restrict to nodes the current user has permission to view.
+    ->addTag('node_access');
+  $entities = $query->execute();
+
+  // Get all BlastDB nodes and delete them
+  $nodes = node_load_multiple(array_keys($entities['node']));
+  foreach ($nodes as $node) {
+    print "Delete node " . $node->title . "\n";
+    $nrs = node_revision_list($node);
+    foreach ($nrs as $nr) {
+      node_revision_delete($nr->vid);
+    }
+    node_delete($node->nid);
+  }
+}
+
 /**
 /**
  * Implements hook_schema().
  * Implements hook_schema().
  * Create the blastdb database table for storing addditional info related to blastdb nodes.
  * Create the blastdb database table for storing addditional info related to blastdb nodes.
@@ -24,9 +52,8 @@ function blast_ui_install() {
  * NOTE: This hook is called via Drupal magic during the installation process and no longer
  * NOTE: This hook is called via Drupal magic during the installation process and no longer
  * needs to be called explicitly in hook_install().
  * needs to be called explicitly in hook_install().
  */
  */
-function blast_ui_schema(){
-
-  // A table ot keep extra information related to blastdb nodes.
+function blast_ui_schema() {
+  // A table to keep extra information related to blastdb nodes.
   $schema['blastdb'] = array(
   $schema['blastdb'] = array(
     'description' => t('The base table for blastdb node'),
     'description' => t('The base table for blastdb node'),
     'fields' => array(
     'fields' => array(
@@ -69,6 +96,12 @@ function blast_ui_schema(){
         'not null' => true,
         'not null' => true,
         'default' => 'link'
         'default' => 'link'
       ),
       ),
+      'cvitjs_enabled' => array(
+        'description' => t('Indicate if CViTjs should be used to display hits on a whole genome'),
+        'type' => 'int',
+        'not null' => false,
+        'default' => 0
+      ),
     ),
     ),
     
     
     'indexes' => array(
     'indexes' => array(
@@ -247,4 +280,21 @@ function blast_ui_update_7103() {
   // First create the tables.
   // First create the tables.
   db_create_table('blastjob', $schema['blastjob']);
   db_create_table('blastjob', $schema['blastjob']);
   
   
-}
+}
+
+
+/**
+ * Add fields to blastp table for CViTjs support.
+ */
+function blast_ui_update_7104() {
+  db_add_field(
+    'blastdb',
+    'cvitjs_enabled',
+    array(
+      'description' => t('Indicate if CViTjs should be used to display hits on a whole genome'),
+      'type' => 'int',
+      'not null' => false,
+      'default' => 0
+    )
+  );
+}

+ 20 - 4
blast_ui.module

@@ -168,14 +168,14 @@ function blast_ui_theme() {
     'variables' => array('hsps' => NULL),
     'variables' => array('hsps' => NULL),
     'path' => "$path/theme",
     'path' => "$path/theme",
   );
   );
-  
+
   // Lists the recent blast jobs for a given user/session.
   // Lists the recent blast jobs for a given user/session.
   $items['blast_recent_jobs'] = array(
   $items['blast_recent_jobs'] = array(
     'template' => 'blast_recent_jobs',
     'template' => 'blast_recent_jobs',
     'variables' => array('programs' => NULL),
     'variables' => array('programs' => NULL),
     'path' => "$path/theme",
     'path' => "$path/theme",
   );
   );
-  
+
   // Module Help
   // Module Help
   $items['blast_help'] = array(
   $items['blast_help'] = array(
     'template' => 'blast_help',
     'template' => 'blast_help',
@@ -210,6 +210,22 @@ function blast_ui_help($path, $arg) {
   }
   }
 }
 }
 
 
+/**
+ * Implements hook_libraries_info().
+ */
+function blast_ui_libraries_info() {
+
+  // Tell the libraries API about CViTjs
+  $libraries['cvitjs'] = array(
+    'name' => 'CViTjs',
+    'vendor url' => 'https://github.com/awilkey/cvitjs',
+    'version' => '0.0.1',
+    'download url' => 'https://github.com/awilkey/cvitjs/archive/master.zip',
+  );
+
+  return $libraries;
+}
+
 /**
 /**
  * Facilitate presenting the result of the blast search
  * Facilitate presenting the result of the blast search
  *
  *
@@ -259,11 +275,11 @@ function show_blast_output($job_string) {
  * Enable web services API
  * Enable web services API
  *
  *
  * @param $owner
  * @param $owner
- *   
+ *
  * @param $api
  * @param $api
  *
  *
  * @return $result
  * @return $result
- *  
+ *
  */
  */
 function blast_ui_ctools_plugin_api($owner, $api) {
 function blast_ui_ctools_plugin_api($owner, $api) {
   if ($owner == 'services' && $api == 'services') {
   if ($owner == 'services' && $api == 'services') {

+ 143 - 3
includes/blast_ui.admin.inc

@@ -31,7 +31,7 @@ function blast_ui_admin_form($form, $form_state) {
 
 
    $form['general']['eVal']= array(
    $form['general']['eVal']= array(
     '#type' => 'textfield',
     '#type' => 'textfield',
-    '#title' => t('Default e-Value (Expected Threshold)'),
+    '#title' => t('Default e-value (Expected Threshold)'),
     '#description' => t('Expected number of chance matches in a random model. This number should be give in a decimal format.'),
     '#description' => t('Expected number of chance matches in a random model. This number should be give in a decimal format.'),
      '#default_value' => variable_get('eVal', 0.001),
      '#default_value' => variable_get('eVal', 0.001),
     //'#default_value' => variable_get('blast_threads', 1),
     //'#default_value' => variable_get('blast_threads', 1),
@@ -151,6 +151,87 @@ KRSLEEGLKTTGEGLDWGVLFGFGPGLTIETVVLRSVAI';
     '#default_value' => variable_get('blast_ui_max_results_displayed', 500)
     '#default_value' => variable_get('blast_ui_max_results_displayed', 500)
   );
   );
 
 
+  // CVITJS
+  $cvitjs_enabled = variable_get('blast_ui_cvitjs_enabled', FALSE);
+  $description = 'The JavaScript program CViTjs enables users to see BLAST hits on an '
+               . 'entire genome assembly. See the help tab for information on how to '
+               . 'download and set up CViTjs.';
+
+  $form['cvitjs'] = array(
+    '#type' => 'fieldset',
+    '#collapsible' => true,
+    '#collapsed' => !$cvitjs_enabled,
+    '#title' => 'Enable and configure genome visualization',
+    '#description' => $description,
+  );
+
+  $absolute_cvitjs_data_path = DRUPAL_ROOT . '/sites/all/libraries/cvitjs/data';
+  $description = '<div class ="messages warning">CViTjs is only applicable for genome BLAST targets. After it is '
+               . 'enabled here, CViTjs will need to be enabled for each applicable BLAST '
+              . 'target node.</div>'
+              . '<div class="messages status"><strong>CViTjs Data Location: '.$absolute_cvitjs_data_path.'</strong>'
+              . '<br />The GFF3 and Genome Target-specific CViTjs configuration files should be located '
+              . 'at the above system path. Feel free to organize this directory further. '
+              . 'See the "Help" tab for more information.</div>';
+  $form['cvitjs']['explanation'] = array(
+    '#markup' => t($description),
+  );
+
+
+  $form['cvitjs']['cvitjs_enabled'] = array(
+    '#type' => 'checkbox',
+    '#title' => 'Enable CViTjs',
+    '#description' => 'When checked, CViTjs will be enabled.',
+    '#default_value' => $cvitjs_enabled,
+  );
+
+  // Get CViTjs confuration text, if possible.
+  if (!$default_value = blast_ui_get_cvit_conf_text()) {
+    $default_value = 'Unable to get CViTjs configuration information. '
+                   . 'You will need to enable CViTjs and set and save the '
+                   . 'path to CViTjs before you can edit the CViTjs configuration text.';
+    $disabled = true;
+  }
+  else {
+    $disabled = false;
+  }
+
+  $description = 'This is the contents of the file that defines data directories and '
+               . 'backbone GFF files for each genome assembly target. It is named '
+               . 'cvit.conf and is in the root directory for the CViTjs javascript code. '
+               . 'This is NOT the config file that is used to build the display for each '
+               . 'individual genome. See the help tab for more information about '
+               . 'configuration files.';
+  $form['cvitjs']['cvitjs_config'] = array(
+    '#type' => 'textarea',
+    '#title' => 'CViTjs configuration (empty until CViTjs path is saved)',
+    '#description' => $description,
+    '#default_value' => $default_value,
+    '#rows' => 10,
+    '#disabled' => $disabled,
+  );
+
+//eksc:
+  // WARNING
+  $description = 'This permits display of a temporary warning message at the top of the
+                  BLAST input form. Text can include HTML tags. Remember to remove the
+                  message when it is no longer relevant.';
+  $form['warning'] = array(
+    '#type' => 'fieldset',
+    '#collapsible' => true,
+    '#collapsed' => true,
+    '#title' => 'Show warning text',
+    '#description' => $description,
+  );
+    $form['warning']['warning_text'] = array(
+    '#type' => 'textarea',
+    '#title' => 'Text to be displayed',
+    '#description' => $description,
+    '#default_value' => variable_get('blast_ui_warning_text', ''),
+    '#rows' => 10,
+  );
+
+
   // SUBMIT
   // SUBMIT
   $form['submit'] = array(
   $form['submit'] = array(
     '#type' => 'submit',
     '#type' => 'submit',
@@ -176,6 +257,46 @@ function blast_ui_admin_form_validate($form, &$form_state) {
     }
     }
   }
   }
 
 
+  // Check path to CViTjs executable and make sure cvit.conf is writable
+  if ($form_state['values']['cvitjs_enabled']) {
+    $cvit_path = blast_ui_get_cvit_conf();
+    if (!$cvit_path || !file_exists($cvit_path)) {
+      $msg = "The CViTjs configuration file, cvit.conf, does not exist at the path given ("
+           . $form_state['values']['cvitjs_location']
+           . "). Please check your path. "
+           . "If you have not yet downloaded CViTjs, see the help tab for more information.";
+      form_set_error('cvitjs_location', t($msg));
+    }
+
+    if (!is_writable($cvit_path)) {
+      $msg = "The file $cvit_path is not writable by this page. "
+           . "Please enable write access for apache then try saving these settings again.";
+      form_set_error('cvitjs_location', t($msg));
+    }
+  }
+
+  // Empty contents of cvitjs_config textarea if it is disabled
+  if ($form['cvitjs']['cvitjs_config']['#disabled']) {
+    $form_state['values']['cvitjs_config'] = '';
+  }
+
+  // Check CViTjs configuration text
+  if ($form_state['values']['cvitjs_config']
+        && !preg_match('/\[general\]\s*\ndata_default =.*/m',
+                       $form_state['values']['cvitjs_config'])) {
+    $msg = "The CViTjs configuration text looks incorrect. "
+         . "It should contain a [general] section. "
+         . "See the help tab for more information.";
+    form_set_error('cvitjs_config', t($msg));
+  }
+  if ($form_state['values']['cvitjs_config']
+        && !preg_match('/\[.*\]\s*\nconf = .*\ndefaultData =.*/m',
+                       $form_state['values']['cvitjs_config'])) {
+    $msg = "The CViTjs configuration text looks incorrect. "
+         . "It should contain one section for each genome target. "
+         . "See the help tab for more information.";
+    form_set_error('cvitjs_config', t($msg));
+  }
 }
 }
 
 
 /**
 /**
@@ -196,7 +317,26 @@ function blast_ui_admin_form_submit($form, $form_state) {
   // Example sequence
   // Example sequence
   variable_set('blast_ui_nucleotide_example_sequence', $form_state['values']['nucleotide_example']);
   variable_set('blast_ui_nucleotide_example_sequence', $form_state['values']['nucleotide_example']);
   variable_set('blast_ui_protein_example_sequence', $form_state['values']['protein_example']);
   variable_set('blast_ui_protein_example_sequence', $form_state['values']['protein_example']);
-  
-/**: added by safetybrake*/
+
+  // Protect against large result sets
   variable_set('blast_ui_max_results_displayed', $form_state['values']['max_results_displayed']);
   variable_set('blast_ui_max_results_displayed', $form_state['values']['max_results_displayed']);
+
+  // Whole genome visualization - CViTjs
+  variable_set('blast_ui_cvitjs_enabled', $form_state['values']['cvitjs_enabled']);
+  if ($form_state['values']['cvitjs_enabled'] && $form_state['values']['cvitjs_config']) {
+    // Need absolute path to conf file to write
+    $cvit_conf_path = getcwd() . DIRECTORY_SEPARATOR
+                    . blast_ui_get_cvit_conf($form_state['values']['cvitjs_location']);
+    if ($fh = fopen($cvit_conf_path, 'w')) {
+      fwrite($fh, $form_state['values']['cvitjs_config']);
+      fclose($fh);
+    }
+    else {
+      drupal_set_message("Unable to open CViTjs conf file for writing: <pre>" . print_r(error_get_last(),true) . "</pre>");
+    }
+  }
+
+//eksc:
+  // Warning text
+  variable_set('blast_ui_warning_text', $form_state['values']['warning_text']);
 }
 }

+ 0 - 3
includes/blast_ui.form_advanced_options.inc

@@ -643,8 +643,6 @@ function _get_default_values($options, $program) {
     ? $options['evalue'] : variable_get('eVal', 0.001);
     ? $options['evalue'] : variable_get('eVal', 0.001);
   $word_size = (isset($options['word_size']))
   $word_size = (isset($options['word_size']))
     ? $options['word_size'] : 11;
     ? $options['word_size'] : 11;
-//  $qRange = (isset($options['culling_limit']))
-//    ? $options['culling_limit'] : variable_get('qRange', 0);
 
 
   // match/mismatch
   // match/mismatch
   $matchmiss = 0;
   $matchmiss = 0;
@@ -717,7 +715,6 @@ function _get_default_values($options, $program) {
     'evalue' => $evalue,
     'evalue' => $evalue,
     'matchmiss' => $matchmiss,
     'matchmiss' => $matchmiss,
     'gap' => $gap,
     'gap' => $gap,
-   // 'qRange' => $qRange,
     'matrix' => $matrix,
     'matrix' => $matrix,
   );
   );
 }//_get_default_values
 }//_get_default_values

+ 12 - 3
includes/blast_ui.form_per_program.inc

@@ -15,6 +15,15 @@
  */
  */
 function blast_ui_per_blast_program_form($form, $form_state) {
 function blast_ui_per_blast_program_form($form, $form_state) {
 
 
+  // Add a warning, if need be (to be used for temporary messages like down-for-maintanence)
+  if ($warning = variable_get('blast_ui_warning_text', '')) {
+    $form['warning'] = array(
+      '#markup' => t($warning),
+      '#prefix' => '<div class="messages warning">',
+      '#suffix' => '</div>',
+    );
+  }
+
   // CSS support to the form
   // CSS support to the form
   $form['#attached']['css'] = array(
   $form['#attached']['css'] = array(
     drupal_get_path('module', 'blast_ui') . '/theme/css/form.css',
     drupal_get_path('module', 'blast_ui') . '/theme/css/form.css',
@@ -444,9 +453,9 @@ function blast_ui_per_blast_program_form_submit($form, &$form_state) {
       drupal_set_message(t('Unable to generate a BLAST database from your uploaded FASTA '
       drupal_set_message(t('Unable to generate a BLAST database from your uploaded FASTA '
                           .'sequence. Please check that your file is a valid FASTA file '
                           .'sequence. Please check that your file is a valid FASTA file '
                           .'and that if your sequence headers include pipes (i.e.: | ) '
                           .'and that if your sequence headers include pipes (i.e.: | ) '
-                          .' they adhere to ') 
-                          .l('NCBI standards.', 
-                             'http://www.ncbi.nlm.nih.gov/books/NBK21097/table/A632/?report=objectonly', 
+                          .' they adhere to ')
+                          .l('NCBI standards.',
+                             'http://www.ncbi.nlm.nih.gov/books/NBK21097/table/A632/?report=objectonly',
                              array('attributes' => array('target' => '_blank'))
                              array('attributes' => array('target' => '_blank'))
                           ),
                           ),
         'error'
         'error'

+ 19 - 2
includes/blast_ui.linkouts.inc

@@ -152,10 +152,27 @@ function tripal_blast_generate_linkout_link($url_prefix, $hit, $info, $options =
 
 
   if (isset($hit->{'linkout_id'})) {
   if (isset($hit->{'linkout_id'})) {
     $hit_url = $url_prefix . $hit->{'linkout_id'};
     $hit_url = $url_prefix . $hit->{'linkout_id'};
+    
+    // Split out the CGI params, if any
+    $params = array();
+    if (!$paramstr=strstr($hit_url, '?')) {
+      $url_prefix = $hit_url;
+    }
+    else {
+      $url_parts = preg_split("/\?/", $hit_url);
+      $url_prefix = $url_parts[0];
+      $param_list = preg_split("/\&/", $url_parts[1]);
+
+      foreach ($param_list as $param) {
+        $param_parts = preg_split("/=/", $param, 2);
+        $params[$param_parts[0]] = $param_parts[1];
+      }
+    }//URL contains CGI parameters
+    
     return l(
     return l(
       $hit->{'linkout_id'},
       $hit->{'linkout_id'},
-      $hit_url,
-      array('attributes' => array('target' => '_blank'))
+      $url_prefix,
+      array('attributes' => array('target' => '_blank'), 'query' => $params)
     );
     );
   }
   }
   else {
   else {

+ 80 - 20
includes/blast_ui.node.inc

@@ -67,7 +67,7 @@ function blastdb_form($node, &$form_state) {
   $form = array();
   $form = array();
 
 
   $form['#validate'] = array('blastdb_form_validate');
   $form['#validate'] = array('blastdb_form_validate');
-  
+
   $form['#attached']['css'] = array(
   $form['#attached']['css'] = array(
     drupal_get_path('module', 'blast_ui') . '/theme/css/form.css',
     drupal_get_path('module', 'blast_ui') . '/theme/css/form.css',
   );
   );
@@ -106,7 +106,7 @@ function blastdb_form($node, &$form_state) {
   $form['dbxref'] = array(
   $form['dbxref'] = array(
     '#type' => 'fieldset',
     '#type' => 'fieldset',
     '#title' => 'Link-outs',
     '#title' => 'Link-outs',
-    '#description' => 'These settings will be used to <em>transform the hit name into a 
+    '#description' => 'These settings will be used to <em>transform the hit name into a
       link to additional information</em>.',
       link to additional information</em>.',
     '#prefix' => '<div id="link-outs">',
     '#prefix' => '<div id="link-outs">',
     '#suffix' => '</div>',
     '#suffix' => '</div>',
@@ -122,16 +122,13 @@ function blastdb_form($node, &$form_state) {
   $form['dbxref']['dbxref_linkout_type'] = array(
   $form['dbxref']['dbxref_linkout_type'] = array(
     '#type' => 'radios',
     '#type' => 'radios',
     '#title' => 'Link-out Type',
     '#title' => 'Link-out Type',
-    '#description' => 'This determines how the URL to be linked to is formed. <strong>Make 
-      sure the database chosen supports this type of link</strong> (ie: the database 
+    '#description' => 'This determines how the URL to be linked to is formed. <strong>Make
+      sure the database chosen supports this type of link</strong> (ie: the database
       should point to a GBrowse instance if you choose GBrowse here).',
       should point to a GBrowse instance if you choose GBrowse here).',
     '#options' => $options,
     '#options' => $options,
     '#default_value' => $linkout_type,
     '#default_value' => $linkout_type,
-    '#ajax' => array(
-      'callback' => 'ajax_blast_ui_node_linkout_custom_callback',
-      'wrapper' => 'link-outs',
-    )
   );
   );
+
   // Add information about each format to the description.
   // Add information about each format to the description.
   if ($linkout_type) {
   if ($linkout_type) {
     $form['dbxref']['dbxref_linkout_type']['#description'] .= '
     $form['dbxref']['dbxref_linkout_type']['#description'] .= '
@@ -232,7 +229,43 @@ function blastdb_form($node, &$form_state) {
       '#default_value' => (isset($node->linkout->db_id->db_id)) ? $node->linkout->db_id->db_id : 0
       '#default_value' => (isset($node->linkout->db_id->db_id)) ? $node->linkout->db_id->db_id : 0
     );
     );
   }
   }
-    
+
+  // CViTjs settings, if enabled
+  if (variable_get('blast_ui_cvitjs_enabled', false)) {
+    $form['cvitjs'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Whole Genome Visualization',
+      '#description' => 'Settings for the display of BLAST hits on an entire genome assembly using CViTjs.',
+      '#prefix' => '<div id="cvitjs-settings">',
+      '#suffix' => '</div>',
+    );
+
+    $form['cvitjs']['cvitjs_enabled'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Show BLAST hits on the genome in the results page.'),
+      '#description' => t('Uses CViTjs to display BLAST hits on the entire genome'),
+      '#default_value' => (isset($node->cvitjs_enabled)) ? $node->cvitjs_enabled : false,
+    );
+    $cvitjs_msg_class = 'blastdb-extra-info';
+    $cvitjs_msg = 'Target Genome Configuration should be under <strong>[data.'.$node->db_name.']</strong> in the main cvit.conf.';
+
+    $conf_section = blast_ui_get_cvit_conf_text('data.'.$node->db_name);
+    if (!$conf_section) {
+      $cvitjs_msg_class .= ' messages warning';
+      $cvitjs_msg .= '<br /><br />There is no section for this genome target defined in the CViTjs
+        configuration file. <strong>No genome visualization will be shown until you define a
+        configuration section, "[data.'.$form_state['values']['db_name'].']", at '
+        .l('Admin > Tripal > Extensions > Tripal BLAST > BLAST UI', 'admin/tripal/extension/tripal_blast')
+        .'</strong>.';
+    }
+    else {
+      $cvitjs_msg .= '<br /><br /><strong>Current Configuration:</strong><pre>'.$conf_section.'</pre>';
+    }
+
+    $form['cvitjs']['cvitjs_enabled']['#description'] .= '<div class="'.$cvitjs_msg_class.'">'.$cvitjs_msg.'</p>';
+
+  }
+
   return $form;
   return $form;
 }
 }
 
 
@@ -267,6 +300,19 @@ function blastdb_form_validate($form, $form_state) {
       );
       );
     }
     }
   }
   }
+
+  // Check that there is a cvitjs section for the current
+  if ($form_state['values']['cvitjs_enabled']) {
+    $conf_section = blast_ui_get_cvit_conf_text('data.'.$form_state['values']['db_name']);
+    if (!$conf_section) {
+      drupal_set_message('There is no section for this genome target defined in the CViTjs
+        configuration file. <strong>No genome visualization will be shown until you define a
+        configuration section, "[data.'.$form_state['values']['db_name'].']", at '
+        .l('Admin > Tripal > Extensions > Tripal BLAST > BLAST UI', 'admin/tripal/extension/tripal_blast')
+        .'</strong>.',
+      'warning');
+    }
+  }
 }
 }
 
 
 /**
 /**
@@ -284,7 +330,7 @@ function blastdb_insert($node) {
       $regex = $node->dbxref_id_type;
       $regex = $node->dbxref_id_type;
     }
     }
   }
   }
-  
+
   $db_id = 0;
   $db_id = 0;
   if (isset($node->db_id)) {
   if (isset($node->db_id)) {
     $db_id = $node->db_id;
     $db_id = $node->db_id;
@@ -293,7 +339,11 @@ function blastdb_insert($node) {
   if (!$node->dbxref_linkout_type) {
   if (!$node->dbxref_linkout_type) {
     $node->dbxref_linkout_type = 'none';
     $node->dbxref_linkout_type = 'none';
   }
   }
-  
+
+  if (!isset($node->cvitjs_enabled)) {
+    $node->cvitjs_enabled = 0;
+  }
+
   // Actually insert the record.
   // Actually insert the record.
   db_insert('blastdb')->fields(array(
   db_insert('blastdb')->fields(array(
     'nid'                 => $node->nid,
     'nid'                 => $node->nid,
@@ -303,6 +353,7 @@ function blastdb_insert($node) {
     'dbxref_id_regex'     => $regex,
     'dbxref_id_regex'     => $regex,
     'dbxref_db_id'        => $db_id,
     'dbxref_db_id'        => $db_id,
     'dbxref_linkout_type' => $node->dbxref_linkout_type,
     'dbxref_linkout_type' => $node->dbxref_linkout_type,
+    'cvitjs_enabled'      => $node->cvitjs_enabled,
   ))->execute();
   ))->execute();
 }
 }
 
 
@@ -331,16 +382,20 @@ function blastdb_update($node) {
       $regex = $node->dbxref_id_type;
       $regex = $node->dbxref_id_type;
     }
     }
   }
   }
-  
+
   $db_id = 0;
   $db_id = 0;
   if (isset($node->db_id)) {
   if (isset($node->db_id)) {
     $db_id = $node->db_id;
     $db_id = $node->db_id;
   }
   }
 
 
+  if (!$node->cvitjs_enabled) {
+    $node->cvitjs_enabled = 0;
+  }
+
   if (!$node->dbxref_linkout_type) {
   if (!$node->dbxref_linkout_type) {
     $node->dbxref_linkout_type = 'none';
     $node->dbxref_linkout_type = 'none';
   }
   }
-  
+
   // Update the record.
   // Update the record.
   db_update('blastdb')->fields(array(
   db_update('blastdb')->fields(array(
     'name'                => $node->db_name,
     'name'                => $node->db_name,
@@ -349,6 +404,7 @@ function blastdb_update($node) {
     'dbxref_id_regex'     => $regex,
     'dbxref_id_regex'     => $regex,
     'dbxref_db_id'        => $db_id,
     'dbxref_db_id'        => $db_id,
     'dbxref_linkout_type' => $node->dbxref_linkout_type,
     'dbxref_linkout_type' => $node->dbxref_linkout_type,
+    'cvitjs_enabled'      => $node->cvitjs_enabled,
   ))->condition('nid', $node->nid)->execute();
   ))->condition('nid', $node->nid)->execute();
 }
 }
 
 
@@ -375,9 +431,9 @@ function blastdb_delete($node) {
 function blastdb_load($nodes) {
 function blastdb_load($nodes) {
 
 
   $sql = "
   $sql = "
-    SELECT nid, name, path, dbtype, dbxref_id_regex, dbxref_db_id, 
-           dbxref_linkout_type
-    FROM {blastdb} 
+    SELECT nid, name, path, dbtype, dbxref_id_regex, dbxref_db_id,
+           dbxref_linkout_type, cvitjs_enabled
+    FROM {blastdb}
     WHERE nid IN (:nids)";
     WHERE nid IN (:nids)";
   $result = db_query($sql, array(':nids' => array_keys($nodes)));
   $result = db_query($sql, array(':nids' => array_keys($nodes)));
 
 
@@ -389,6 +445,9 @@ function blastdb_load($nodes) {
     $nodes[$record->nid]->title = $record->name;
     $nodes[$record->nid]->title = $record->name;
     $nodes[$record->nid]->db_dbtype = $record->dbtype;
     $nodes[$record->nid]->db_dbtype = $record->dbtype;
 
 
+    // CViTjs status
+    $nodes[$record->nid]->cvitjs_enabled  = $record->cvitjs_enabled;
+
     // Determine the type of link-out chosen.
     // Determine the type of link-out chosen.
     $types = module_invoke_all('blast_linkout_info');
     $types = module_invoke_all('blast_linkout_info');
     $type = NULL;
     $type = NULL;
@@ -399,7 +458,7 @@ function blastdb_load($nodes) {
       tripal_report_error(
       tripal_report_error(
         'blast_ui',
         'blast_ui',
         TRIPAL_ERROR,
         TRIPAL_ERROR,
-        'Unable to find details on the type of link-out choosen (%type). Have you defined hook_blast_linkout_info()? Make sure to clear the cache once you do so.',       
+        'Unable to find details on the type of link-out choosen (%type). Have you defined hook_blast_linkout_info()? Make sure to clear the cache once you do so.',
         array('%type' => $record->dbxref_linkout_type)
         array('%type' => $record->dbxref_linkout_type)
       );
       );
     }
     }
@@ -419,7 +478,7 @@ function blastdb_load($nodes) {
       }
       }
     }
     }
 
 
-    // finally add information related to link-outs to the node.
+    // Add information related to link-outs to the node.
     if ($add_linkout) {
     if ($add_linkout) {
       $nodes[$record->nid]->linkout = new stdClass();
       $nodes[$record->nid]->linkout = new stdClass();
 
 
@@ -440,7 +499,7 @@ function blastdb_load($nodes) {
       }
       }
 
 
       // If the link-out type requires a db then provide one.
       // If the link-out type requires a db then provide one.
-      if ($type['require_db']) {
+      if (isset($type['require_db'])) {
         $nodes[$record->nid]->linkout->db_id = tripal_get_db(array('db_id' => $record->dbxref_db_id));
         $nodes[$record->nid]->linkout->db_id = tripal_get_db(array('db_id' => $record->dbxref_db_id));
       }
       }
       else {
       else {
@@ -452,7 +511,7 @@ function blastdb_load($nodes) {
       // Support complex link-outs.
       // Support complex link-outs.
       $nodes[$record->nid]->linkout->type = $record->dbxref_linkout_type;
       $nodes[$record->nid]->linkout->type = $record->dbxref_linkout_type;
       $nodes[$record->nid]->linkout->url_function = $type['process function'];
       $nodes[$record->nid]->linkout->url_function = $type['process function'];
-   
+
     }
     }
     // If there is no linkout then provide some defaults.
     // If there is no linkout then provide some defaults.
     else {
     else {
@@ -475,3 +534,4 @@ function blastdb_load($nodes) {
 function ajax_blast_ui_node_linkout_custom_callback($form, $form_state) {
 function ajax_blast_ui_node_linkout_custom_callback($form, $form_state) {
   return $form['dbxref'];
   return $form['dbxref'];
 }
 }
+

+ 109 - 33
theme/blast_help.tpl.php

@@ -17,6 +17,7 @@
 
 
 <p>
 <p>
   <a href="#setup">Setup</a> | <a href="#function">Functionality</a>
   <a href="#setup">Setup</a> | <a href="#function">Functionality</a>
+  | <a href="#protection">Large jobs | <a href="#genomeview">Genome visualization</a>
 </p>
 </p>
 
 
 <a name="setup"></a>
 <a name="setup"></a>
@@ -24,32 +25,32 @@
 <h3><b>Setup Instructions</b></h3>
 <h3><b>Setup Instructions</b></h3>
 <ol>
 <ol>
   <li>
   <li>
-    Install NCBI BLAST+ on your server (Tested with 2.2.26+). There is a 
-    <a href="https://launchpad.net/ubuntu/+source/ncbi-blast+">package available 
-    for Ubuntu</a> to ease installation. Optionally you can set the path to your 
+    Install NCBI BLAST+ on your server (Tested with 2.2.26+). There is a
+    <a href="https://launchpad.net/ubuntu/+source/ncbi-blast+">package available
+    for Ubuntu</a> to ease installation. Optionally you can set the path to your
     BLAST executable <a href="<?php print url('admin/tripal/extension/tripal_blast/blast_ui');?>">
     BLAST executable <a href="<?php print url('admin/tripal/extension/tripal_blast/blast_ui');?>">
     in the settings</a>.
     in the settings</a>.
   </li>
   </li>
   <li>
   <li>
-    Optionally, create Tripal External Database References to allow you to link 
-    the records in your BLAST database to further information. To do this simply 
-    go to <a href="<?php print url('admin/tripal/chado/tripal_db/add'); ?>" target="_blank">Tripal> 
-    Chado Modules > Databases > Add DB</a> and make sure to fill in the Database 
-    prefix which will be concatenated with the record IDs in your BLAST database 
-    to determine the link-out to additional information. Note that a regular 
-    expression can be used when creating the BLAST database to indicate what the 
+    Optionally, create Tripal External Database References to allow you to link
+    the records in your BLAST database to further information. To do this simply
+    go to <a href="<?php print url('admin/tripal/chado/tripal_db/add'); ?>" target="_blank">Tripal>
+    Chado Modules > Databases > Add DB</a> and make sure to fill in the Database
+    prefix which will be concatenated with the record IDs in your BLAST database
+    to determine the link-out to additional information. Note that a regular
+    expression can be used when creating the BLAST database to indicate what the
     ID is.
     ID is.
   </li>
   </li>
   <li>
   <li>
-    <a href="<?php print url('node/add/blastdb');?>">Create "Blast Database" 
-    nodes</a> for each dataset you want to make available for your users to BLAST 
-    against. BLAST databases should first be created using the command-line 
-    <code>makeblastdb</code> program with the <code>-parse_seqids</code> flag.  
+    <a href="<?php print url('node/add/blastdb');?>">Create "BLAST Database"
+    nodes</a> for each dataset you want to make available for your users to BLAST
+    against. BLAST databases should first be created using the command-line
+    <code>makeblastdb</code> program with the <code>-parse_seqids</code> flag.
   </li>
   </li>
   <li>
   <li>
-    It's recommended that you also install the <a href="http://drupal.org/project/tripal_daemon">Tripal Job Daemon</a> 
-    to manage BLAST jobs and ensure they are run soon after being submitted by the 
-    user. Without this additional module, administrators will have to execute the 
+    It's recommended that you also install the <a href="http://drupal.org/project/tripal_daemon">Tripal Job Daemon</a>
+    to manage BLAST jobs and ensure they are run soon after being submitted by the
+    user. Without this additional module, administrators will have to execute the
     tripal jobs either manually or through use of cron jobs.
     tripal jobs either manually or through use of cron jobs.
   </li>
   </li>
 </ol>
 </ol>
@@ -58,50 +59,125 @@
 &mdash;
 &mdash;
 <h3><b>Highlighted Functionality</b></h3>
 <h3><b>Highlighted Functionality</b></h3>
 <ul>
 <ul>
-  <li>Supports <a href="<?php print url('blast/nucleotide/nucleotide');?>">blastn</a>, 
-    <a href="<?php print url('blast/nucleotide/protein');?>">blastx</a>, 
-    <a href="<?php print url('blast/protein/protein');?>">blastp</a> and 
+  <li>Supports <a href="<?php print url('blast/nucleotide/nucleotide');?>">blastn</a>,
+    <a href="<?php print url('blast/nucleotide/protein');?>">blastx</a>,
+    <a href="<?php print url('blast/protein/protein');?>">blastp</a> and
     <a href="<?php print url('blast/protein/nucleotide');?>">tblastx</a> with separate forms depending upon the database/query type.
     <a href="<?php print url('blast/protein/nucleotide');?>">tblastx</a> with separate forms depending upon the database/query type.
   </li>
   </li>
   <li>
   <li>
-    Simple interface allowing users to paste or upload a query sequence and then 
-    select from available databases. Additionally, a FASTA file can be uploaded 
+    Simple interface allowing users to paste or upload a query sequence and then
+    select from available databases. Additionally, a FASTA file can be uploaded
     for use as a database to BLAST against (this functionality can be disabled).
     for use as a database to BLAST against (this functionality can be disabled).
   </li>
   </li>
   <li>
   <li>
-    Tabular Results listing with alignment information and multiple download 
+    Tabular Results listing with alignment information and multiple download
     formats (HTML, TSV, XML) available.
     formats (HTML, TSV, XML) available.
   </li>
   </li>
   <li>
   <li>
-    Completely integrated with <a href="<?php print url('admin/tripal/tripal_jobs');?>">Tripal Jobs</a> 
-    providing administrators with a way to track BLAST jobs and ensuring long 
+    Completely integrated with <a href="<?php print url('admin/tripal/tripal_jobs');?>">Tripal Jobs</a>
+    providing administrators with a way to track BLAST jobs and ensuring long
     running BLASTs will not cause page time-outs
     running BLASTs will not cause page time-outs
   </li>
   </li>
   <li>
   <li>
-    BLAST databases are made available to the module by 
-    <a href="<?php print url('node/add/blastdb');?>">creating Drupal Pages</a> 
-    describing them. This allows administrators to 
+    BLAST databases are made available to the module by
+    <a href="<?php print url('node/add/blastdb');?>">creating Drupal Pages</a>
+    describing them. This allows administrators to
     <a href="<?php print url('admin/structure/types/manage/blastdb/fields');?>">use the Drupal Field API to add any information they want to these pages</a>.
     <a href="<?php print url('admin/structure/types/manage/blastdb/fields');?>">use the Drupal Field API to add any information they want to these pages</a>.
   </li>
   </li>
   <li>
   <li>
-    BLAST database records can be linked to an external source with more 
+    BLAST database records can be linked to an external source with more
     information (ie: NCBI) per BLAST database.
     information (ie: NCBI) per BLAST database.
   </li>
   </li>
 </ul>
 </ul>
 
 
+<a name="protection"</a></a>
+&mdash;
 <h3><b>Protection Against Large Jobs</b></h3>
 <h3><b>Protection Against Large Jobs</b></h3>
-Depending on the size and nature of your target databases, you may wish to constrain use 
+Depending on the size and nature of your target databases, you may wish to constrain use
 of this module.
 of this module.
 <ol>
 <ol>
   <li>Limit the number of results displayed via admin page. The recommended number is 500.</li>
   <li>Limit the number of results displayed via admin page. The recommended number is 500.</li>
   <li>
   <li>
-    Limit the maximum upload file size in php settings. This is less useful because some 
+    Limit the maximum upload file size in php settings. This is less useful because some
     very large queries may be manageable, and others not.
     very large queries may be manageable, and others not.
   </li>
   </li>
   <li>
   <li>
-    Repeat-mask your targets, or provide repeat-masked versions. Note that some 
-    researchers may be looking for repeats, so this may limit the usefulness of the BLAST 
+    Repeat-mask your targets, or provide repeat-masked versions. Note that some
+    researchers may be looking for repeats, so this may limit the usefulness of the BLAST
     service.
     service.
   </li>
   </li>
 </ol>
 </ol>
 
 
+<a name="genomeview"></a>
+&mdash;
+<h3><b>Whole Genome Visualization</b></h3>
+This module can be configured to use
+<a href="https://github.com/LegumeFederation/cvitjs">CViTjs</a> to display BLAST hits on
+a genome image.
+
+<h4>CViTjs Setup</h4>
+<ol>
+  <li>
+    <a href="https://github.com/LegumeFederation/cvitjs">Download CViTjs</a> and copy
+    the code to your webserver. It needs to be placed in <code>[your drupal root]/sites/all/libraries</code>. To download, execute
+    the git command inside the <code>libraries/</code> directory:<br>
+    <code>git clone https://github.com/LegumeFederation/cvitjs.git</code>
+  </li>
+  <li>
+    CViTjs will have a config file in its root directory named cvit.conf. This file
+    provides information for whole genome visualization for each genome BLAST target.
+    <b>Make sure the config file can be edited by your web server.</b>
+  </li>
+  <li>
+    Enable CViTjs from the BLAST module administration page.
+  </li>
+  <li>
+    Edit the configuration file to define each genome target. These will look like:
+    <pre>
+[data.Cajanus cajan - genome]
+conf = data/cajca/cajca.conf
+defaultData = data/cajca/cajca.gff</pre>
+    Where:<br>
+    <ul>
+      <li>the section name, "data.Cajanus cajan - genome", consists of "data." followed
+          by the name of the BLAST target node,</li>
+      <li>the file "cajca.conf" is a cvit configuration file which describes how to draw the
+          chromosomes and BLAST hits on the <i>Cajanus cajan</i> genome,</li>
+      <li>and the file "cajca.gff" is a GFF3 file that describes the <i>Cajanus cajan</i>
+          chromosomes.</li>
+    </ul>
+    At the top of the configuration file there must be a [general] section that defines
+    the default data set. For example:
+    <pre>
+[general]
+data_default = data.Cajanus cajan - genome</pre>
+  </li>
+  <li>
+    Edit the nodes for each genome target (nodes of type "BLAST Database") and enable whole
+    genome visualization. Remember that the names listed in the CViTjs config file must
+    match the BLAST node name. In the example above, the BLAST database node for the
+    <i>Cajanus cajan</i> genome assembly is named "Cajanus cajan - genome"
+  </li>
+</ol>
+
+<h4>Notes</h4>
+<ul>
+<li>The .conf file for each genome can be modified to suit your needs and tastes. See the
+  sample configuration file, <code>data/test1/test1.conf</code>, and the CViTjs
+  <a href="https://github.com/LegumeFederation/cvitjs#using-cvitjs">documentation</a>.</li>
+<li>Each blast target CViTjs configuration file must define how to visualize blast hits or you will not see them.
+  <pre>[blast]
+feature = BLASTRESULT:match_part
+glyph   = position
+shape = rect
+color   = #FF00FF
+width = 5</pre></li>
+<li>You will have to put the target-specific conf and gff files (e.g. cajca.conf and
+  cjca.gff) on your web server, in the directory, <code>sites/all/libraries/cvitjs/data</code>. You may
+  choose to group files for each genome into subdirectories, for example,
+  <code>sites/all/libraries/cvitjs/data/cajca</code>.</li>
+<li>It is important to make sure that cvit.conf points to the correct data directory and the
+  correct .gff and .conf files for the genome in question. For more information about how to
+  create the .gff file, see the
+  <a href="https://github.com/LegumeFederation/cvitjs#how-to">documentation</a>.</li>
+</ul>

+ 22 - 12
theme/blast_report.tpl.php

@@ -62,7 +62,6 @@ $no_hits = TRUE;
 
 
 <div class="blast-report">
 <div class="blast-report">
 
 
-  <div class="blast-job-info">
     <!-- Provide Information to the user about their blast job -->
     <!-- Provide Information to the user about their blast job -->
     <div class="blast-job-info">
     <div class="blast-job-info">
       <div class="blast-download-info"><strong>Download</strong>:
       <div class="blast-download-info"><strong>Download</strong>:
@@ -72,13 +71,13 @@ $no_hits = TRUE;
         <a href="<?php print '../../' . $blast_job->files->result->gff; ?>">GFF3</a>
         <a href="<?php print '../../' . $blast_job->files->result->gff; ?>">GFF3</a>
       </div>
       </div>
       <br />
       <br />
-      <div class="blast-query-info"><strong>Query Information</strong>: 
+      <div class="blast-query-info"><strong>Query Information</strong>:
         <?php print $blast_job->files->query;?></div>
         <?php print $blast_job->files->query;?></div>
-      <div class="blast-target-info"><strong>Search Target</strong>: 
+      <div class="blast-target-info"><strong>Search Target</strong>:
         <?php print $blast_job->blastdb->db_name;?></div>
         <?php print $blast_job->blastdb->db_name;?></div>
-      <div class="blast-date-info"><strong>Submission Date</strong>: 
+      <div class="blast-date-info"><strong>Submission Date</strong>:
         <?php print format_date($blast_job->date_submitted, 'medium');?></div>
         <?php print format_date($blast_job->date_submitted, 'medium');?></div>
-      <div class="blast-cmd-info"><strong>BLAST Command executed</strong>: 
+      <div class="blast-cmd-info"><strong>BLAST Command executed</strong>:
         <?php print $blast_job->blast_cmd;?></div>
         <?php print $blast_job->blast_cmd;?></div>
       <br />
       <br />
       <div class="num-results">
       <div class="num-results">
@@ -86,12 +85,19 @@ $no_hits = TRUE;
       </div>
       </div>
     </div>
     </div>
 
 
-    <br />
-    <div class="num-results">
-      <strong>Number of Results</strong>: <?php print $num_results; ?>
-    </div>
+    <?php
+      if ($show_cvit_diagram) {
+    ?>
+      <!-- CViTjs image of BLAST hits, if enabled -->
+      <div class="cvitjs">
+        <div id="title-div"><h2>Whole Genome Visualization of BLAST hits</h2></div>
+        <div id="cvit-div"></div>
+      </div>
+
+    <?php
+      }
+    ?>
 
 
-  </div>
   <br />
   <br />
 
 
   <div class="report-table">
   <div class="report-table">
@@ -104,6 +110,8 @@ $no_hits = TRUE;
     if ($xml) {
     if ($xml) {
     ?>
     ?>
 
 
+    <h2>Resulting BLAST hits</h2>
+
     <p>The following table summarizes the results of your BLAST.
     <p>The following table summarizes the results of your BLAST.
     Click on a <em>triangle </em> on the left to see the alignment and a visualization of the hit,
     Click on a <em>triangle </em> on the left to see the alignment and a visualization of the hit,
     and click the <em>target name </em> to get more information about the target hit.</p>
     and click the <em>target name </em> to get more information about the target hit.</p>
@@ -149,8 +157,8 @@ $no_hits = TRUE;
               // If the id is of the form gnl|BL_ORD_ID|### then the parseids flag
               // If the id is of the form gnl|BL_ORD_ID|### then the parseids flag
               // to makeblastdb did a really poor job. In thhis case we want to use
               // to makeblastdb did a really poor job. In thhis case we want to use
               // the def to provide the original FASTA header.
               // the def to provide the original FASTA header.
-              // @todo Deepak changed this to use just the hit_def; inquire as to why.
               $hit_name = (preg_match('/BL_ORD_ID/', $hit->{'Hit_id'})) ? $hit->{'Hit_def'} : $hit->{'Hit_id'};
               $hit_name = (preg_match('/BL_ORD_ID/', $hit->{'Hit_id'})) ? $hit->{'Hit_def'} : $hit->{'Hit_id'};
+
               // Used for the hit visualization to ensure the name isn't truncated.
               // Used for the hit visualization to ensure the name isn't truncated.
               $hit_name_short = (preg_match('/^([^\s]+)/', $hit_name, $matches)) ? $matches[1] : $hit_name;
               $hit_name_short = (preg_match('/^([^\s]+)/', $hit_name, $matches)) ? $matches[1] : $hit_name;
 
 
@@ -212,7 +220,7 @@ $no_hits = TRUE;
                               ';';
                               ';';
                 $Hsp_bit_score .=   $hsp_xml->{'Hsp_bit-score'} .';';
                 $Hsp_bit_score .=   $hsp_xml->{'Hsp_bit-score'} .';';
               }
               }
-              
+
               // Finally record the range.
               // Finally record the range.
               // @todo figure out why we arbitrarily subtract 50,000 here...
               // @todo figure out why we arbitrarily subtract 50,000 here...
               // @more removing the 50,000 and using track start/end appears to cause no change...
               // @more removing the 50,000 and using track start/end appears to cause no change...
@@ -251,6 +259,7 @@ $no_hits = TRUE;
                 // First extract the linkout text using the regex provided through
                 // First extract the linkout text using the regex provided through
                 // the Tripal blast database node.
                 // the Tripal blast database node.
                 if (preg_match($linkout_regex, $hit_name, $linkout_match)) {
                 if (preg_match($linkout_regex, $hit_name, $linkout_match)) {
+
                   $hit->{'linkout_id'} = $linkout_match[1];
                   $hit->{'linkout_id'} = $linkout_match[1];
                   $hit->{'hit_name'} = $hit_name;
                   $hit->{'hit_name'} = $hit_name;
 
 
@@ -266,6 +275,7 @@ $no_hits = TRUE;
                       'e-value'    => $evalue,
                       'e-value'    => $evalue,
                       'HSPs'       => $HSPs,
                       'HSPs'       => $HSPs,
                       'Target'     => $blast_job->blastdb->db_name,
                       'Target'     => $blast_job->blastdb->db_name,
+                      'RegEx'      => $linkout_regex,
                     )
                     )
                   );
                   );
                 }
                 }

+ 29 - 2
theme/blast_ui.theme.inc

@@ -30,6 +30,33 @@ function blast_ui_preprocess_show_blast_report(&$vars) {
       $vars['blast_job']->blast_cmd .= ' -' . $key. ' ' . $value ;
       $vars['blast_job']->blast_cmd .= ' -' . $key. ' ' . $value ;
   }
   }
 
 
+  // CViTjs
+  $vars['show_cvit_diagram'] = FALSE;
+  if (variable_get('blast_ui_cvitjs_enabled', false)
+    && isset($vars['blast_job']->blastdb->cvitjs_enabled)
+    && $vars['blast_job']->blastdb->cvitjs_enabled == '1') {
+
+    // Set a clean var so we don't have to do this long check again ;-).
+    $vars['show_cvit_diagram'] = TRUE;
+
+    // Add the CSS/JS.
+    $cvitjs_lib_path = libraries_get_path('cvitjs') . DIRECTORY_SEPARATOR;
+    drupal_add_css($cvitjs_lib_path . 'js/lib/bootstrap_embed/css/bootstrap.min.css',array('preprocess'=>FALSE));
+    drupal_add_css($cvitjs_lib_path . 'js/lib/hopscotch/css/hopscotch.min.css',array('preprocess'=>FALSE));
+    drupal_add_css($cvitjs_lib_path . 'css/cvit.css',array('preprocess'=> FALSE));
+    drupal_add_js($cvitjs_lib_path . 'js/lib/require/require.js',array('group'=>'JS_LIBRARY','type'=>'file'));
+    drupal_add_js($cvitjs_lib_path . 'js/lib/require/blast_ui-config.js',array('group'=>'JS_THEME'));
+
+    // Add the JS settings.
+    global $base_url;
+    drupal_add_js(array('blast_ui'=> array(
+            'dataset' => $vars['blast_job']->blastdb->db_name,
+            'gff' => $base_url . '/' . $vars['blast_job']->files->result->gff
+      )),
+      'setting'
+    );
+  }
+
   // Determine the URL of the blast form.
   // Determine the URL of the blast form.
   $vars['blast_form_url'] = 'blast/nucleotide/nucleotide';
   $vars['blast_form_url'] = 'blast/nucleotide/nucleotide';
   switch($vars['blast_job']->program) {
   switch($vars['blast_job']->program) {
@@ -129,7 +156,7 @@ function blast_ui_reveal_secret($secret) {
   // Check that the job_id exists if it is an integer.
   // Check that the job_id exists if it is an integer.
   if (is_numeric($job_id)) {
   if (is_numeric($job_id)) {
 
 
-    $exists = db_query('SELECT job_id FROM {tripal_jobs} WHERE job_id=:id', 
+    $exists = db_query('SELECT job_id FROM {tripal_jobs} WHERE job_id=:id',
                        array(':id' => $job_id))->fetchField();
                        array(':id' => $job_id))->fetchField();
     if (!$exists) {
     if (!$exists) {
       tripal_report_error(
       tripal_report_error(
@@ -148,7 +175,7 @@ function blast_ui_reveal_secret($secret) {
 
 
     $job_id = base64_decode($secret);
     $job_id = base64_decode($secret);
     if (is_numeric($job_id)) {
     if (is_numeric($job_id)) {
-      $exists = db_query('SELECT job_id FROM {tripal_jobs} WHERE job_id=:id', 
+      $exists = db_query('SELECT job_id FROM {tripal_jobs} WHERE job_id=:id',
                          array(':id' => $job_id))->fetchField();
                          array(':id' => $job_id))->fetchField();
       if (!$exists) {
       if (!$exists) {
         tripal_report_error(
         tripal_report_error(

+ 23 - 0
theme/css/blast_report.css

@@ -68,3 +68,26 @@ table#blast_report div.alignment-row{
 table#blast_report div.alignment-subrow{
 table#blast_report div.alignment-subrow{
   padding-bottom: 15px;
   padding-bottom: 15px;
 }
 }
+
+/* CViTjs support */
+.blast-top:after {
+   clear: both;
+   display: table;
+   content: "";
+}
+.blast-job-info {
+  float: left;
+  margin-bottom: 25px;
+}
+.cvitjs {
+  float: left;
+  margin-top: 10px;
+  margin-bottom: 25px;
+}
+.cvitjs #cvit-div{
+  padding: 5px 0 0 0;
+}
+.report-table {
+  clear: both;
+  position: static;
+}

+ 2 - 2
theme/css/form.css

@@ -2,10 +2,10 @@ div.center {
   margin-left: auto;
   margin-left: auto;
   margin-right: auto;
   margin-right: auto;
   width:70%
   width:70%
-} 
+}
 
 
 .blastdb-extra-info {
 .blastdb-extra-info {
   padding: 10px;
   padding: 10px;
   border: 1px solid #be7;
   border: 1px solid #be7;
   background-color: #f8fff0;
   background-color: #f8fff0;
-}
+}

+ 23 - 0
theme/node--blastdb.tpl.php

@@ -105,8 +105,31 @@
       <tr><th>RegEx</th><td><?php print $node->linkout->regex; ?></td></tr>
       <tr><th>RegEx</th><td><?php print $node->linkout->regex; ?></td></tr>
       <tr><th>Link-out Type</th><td><?php print $node->linkout->type; ?></td></tr>
       <tr><th>Link-out Type</th><td><?php print $node->linkout->type; ?></td></tr>
 <?php } ?>
 <?php } ?>
+      <tr><th>Whole Genome Viewer</th><td><?php print ($node->cvitjs_enabled) ? 'Enabled' : 'Disabled'; ?></td></tr>
     </table>
     </table>
 
 
+
+    <?php
+      if ($node->cvitjs_enabled) {
+
+        print '<h3>Whole Genome Viewer</h3>';
+
+        $conf_section = blast_ui_get_cvit_conf_text('data.'.$node->db_name);
+
+        if (!$conf_section) {
+
+          print '<div class="messages warning">There is no section for this genome target defined in the CViTjs
+            configuration file. <strong>No genome visualization will be shown until you define a
+            configuration section, "[data.'.$form_state['values']['db_name'].']", at '
+            .l('Admin > Tripal > Extensions > Tripal BLAST > BLAST UI', 'admin/tripal/extension/tripal_blast')
+            .'</strong>.</div>';
+        }
+        else {
+          print '<h4>Configuration</h4>'
+            . '<pre>'.$conf_section.'</pre>';
+        }
+    }
+    ?>
     <?php
     <?php
       // Add in any remaining content
       // Add in any remaining content
       print render($content);
       print render($content);