Browse Source

Merge branch '7.x-3.x' of https://github.com/tripal/tripal into 7.x-3.x

bradfordcondon 7 years ago
parent
commit
c938620a09

+ 2 - 0
tripal/api/tripal.entities.api.inc

@@ -534,6 +534,8 @@ function tripal_create_bundle_fields($bundle, $term) {
     // If the field already exists then skip it.
     $field = field_info_field($details['field_name']);
     if ($field) {
+      tripal_set_message(t("Could not create new field: %field because it already exists.",
+        array('%field' =>  $details['field_name'])), TRIPAL_WARNING);
       continue;
     }
 

+ 1 - 1
tripal/api/tripal.importer.api.inc

@@ -100,7 +100,7 @@ function tripal_run_importer($import_id, TripalJob $job = NULL) {
 
     $loader = TripalImporter::byID($import_id);
     $loader->setJob($job);
-    $loader->prepareFile();
+    $loader->prepareFiles();
     $loader->run();
     $loader->cleanFile();
 

+ 102 - 70
tripal/includes/TripalImporter.inc

@@ -97,6 +97,14 @@ class TripalImporter {
    */
   public static $argument_list = array();
 
+
+  /**
+   * Indicates how many files are allowed to be uploaded.  By default this is
+   * set to allow only one file.  Change to any positive number. A value of
+   * zero indicates an unlimited number of uploaded files are allowed.
+   */
+  public static $cardinality = 1;
+
   // --------------------------------------------------------------------------
   //                  PRIVATE MEMBERS -- DO NOT EDIT or OVERRIDE
   // --------------------------------------------------------------------------
@@ -259,32 +267,54 @@ class TripalImporter {
       // and the file.
       $arguments = array(
         'run_args' => $run_args,
-        'file' => array(
-          'file_path' => '',
-          'file_local' => '',
-          'file_remote' => '',
-          'fid' => NULL,
-        ),
+        'files' => array(),
       );
 
       // Get the file argument.
       $has_file = 0;
       if (array_key_exists('file_local', $file_details)) {
-        $arguments['file']['file_local'] = $file_details['file_local'];
-        $arguments['file']['file_path'] = $file_details['file_local'];
+        $arguments['files'][] = array(
+          'file_local' => $file_details['file_local'],
+          'file_path' => $file_details['file_local']
+        );
         $has_file++;
       }
       if (array_key_exists('file_remote', $file_details)) {
-        $arguments['file']['file_remote'] = $file_details['file_remote'];
+        $arguments['files'][] = array(
+          'file_remote' => $file_details['file_remote']
+        );
         $has_file++;
       }
       if (array_key_exists('fid', $file_details)) {
-        $fid = $file_details['fid'];
-        $file = file_load($fid);
-        $arguments['file']['file_path'] =  base_path() . drupal_realpath($file->uri);
-        $arguments['file']['fid'] = $fid;
-        $values['fid'] = $fid;
-        $has_file++;
+        $values['fid'] = $file_details['fid'];
+        // Handle multiple file uploads.
+        if (preg_match('/\|/', $file_details['fid'])) {
+          $fids = explode('|', $file_details['fid']);
+          foreach ($fids as $fid) {
+            $file = file_load($fid);
+            $arguments['files'][] = array(
+              'file_path' => base_path() . drupal_realpath($file->uri),
+              'fid' => $fid
+            );
+            $has_file++;
+          }
+        }
+        // Handle a single file.
+        else {
+          $fid = $file_details['fid'];
+          $file = file_load($fid);
+          $arguments['files'][] = array(
+            'file_path' => base_path() . drupal_realpath($file->uri),
+            'fid' => $fid
+          );
+          $has_file++;
+
+          // For backwards compatibility add the old 'file' element.
+          $arguments['file'] = array(
+            'file_path' => base_path() . drupal_realpath($file->uri),
+            'fid' => $fid
+          );
+        }
       }
 
       // Validate the $file_details argument.
@@ -375,7 +405,7 @@ class TripalImporter {
    * This function must be run prior to the run() function to ensure that
    * the import file is ready to go.
    */
-  public function prepareFile() {
+  public function prepareFiles() {
     $class = get_called_class();
 
     // If no file is required then just indicate that all is good to go.
@@ -385,63 +415,65 @@ class TripalImporter {
     }
 
     try {
-      if (!empty($this->arguments['file']['file_remote'])) {
-        $file_remote = $this->arguments['file']['file_remote'];
-        $this->logMessage('Download file: !file_remote...', array('!file_remote' => $file_remote));
-
-        // If this file is compressed then keepthe .gz extension so we can
-        // uncompress it.
-        $ext = '';
-        if (preg_match('/^(.*?)\.gz$/', $this->arguments['file']['file_remote'])) {
-          $ext = '.gz';
-        }
-        // Create a temporary file.
-        $temp = tempnam("temporary://", 'import_') . $ext;
-        $this->logMessage("Saving as: !file", array('!file' => $temp));
-
-        $url_fh = fopen($file_remote, "r");
-        $tmp_fh = fopen($temp, "w");
-        if (!$url_fh) {
-          throw new Exception(t("Unable to download the remote file at %url. Could a firewall be blocking outgoing connections?",
-              array('%url', $file_remote)));
+      for($i = 0; $i < count($this->arguments['file']); $i++) {
+        if (!empty($this->arguments['file'][$i]['file_remote'])) {
+          $file_remote = $this->arguments['file'][$i]['file_remote'];
+          $this->logMessage('Download file: !file_remote...', array('!file_remote' => $file_remote));
+
+          // If this file is compressed then keepthe .gz extension so we can
+          // uncompress it.
+          $ext = '';
+          if (preg_match('/^(.*?)\.gz$/', $this->arguments['file']['file_remote'])) {
+            $ext = '.gz';
+          }
+          // Create a temporary file.
+          $temp = tempnam("temporary://", 'import_') . $ext;
+          $this->logMessage("Saving as: !file", array('!file' => $temp));
+
+          $url_fh = fopen($file_remote, "r");
+          $tmp_fh = fopen($temp, "w");
+          if (!$url_fh) {
+            throw new Exception(t("Unable to download the remote file at %url. Could a firewall be blocking outgoing connections?",
+                array('%url', $file_remote)));
+          }
+
+          // Write the contents of the remote file to the temp file.
+          while (!feof($url_fh)) {
+            fwrite($tmp_fh, fread($url_fh, 255), 255);
+          }
+          // Set the path to the file for the importer to use.
+          $this->arguments['file']['file_path'] = $temp;
+          $this->is_prepared = TRUE;
         }
 
-        // Write the contents of the remote file to the temp file.
-        while (!feof($url_fh)) {
-          fwrite($tmp_fh, fread($url_fh, 255), 255);
+        // Is this file compressed?  If so, then uncompress it
+        $matches = array();
+        if (preg_match('/^(.*?)\.gz$/', $this->arguments['file']['file_path'], $matches)) {
+          $this->logMessage("Uncompressing: !file", array('!file' => $this->arguments['file']['file_path']));
+          $buffer_size = 4096;
+          $new_file_path = $matches[1];
+          $gzfile = gzopen($this->arguments['file']['file_path'], 'rb');
+          $out_file = fopen($new_file_path, 'wb');
+          if (!$out_file) {
+            throw new Exception("Cannot uncompress file: new temporary file, '$new_file_path', cannot be created.");
+          }
+
+          // Keep repeating until the end of the input file
+          while (!gzeof($gzfile)) {
+            // Read buffer-size bytes
+            // Both fwrite and gzread and binary-safe
+            fwrite($out_file, gzread($gzfile, $buffer_size));
+          }
+
+          // Files are done, close files
+          fclose($out_file);
+          gzclose($gzfile);
+
+          // Now remove the .gz file and reset the file_path to the new
+          // uncompressed version.
+          unlink($this->arguments['file'][$i]['file_path']);
+          $this->arguments['file'][$i]['file_path'] = $new_file_path;
         }
-        // Set the path to the file for the importer to use.
-        $this->arguments['file']['file_path'] = $temp;
-        $this->is_prepared = TRUE;
-      }
-
-      // Is this file compressed?  If so, then uncompress it
-      $matches = array();
-      if (preg_match('/^(.*?)\.gz$/', $this->arguments['file']['file_path'], $matches)) {
-        $this->logMessage("Uncompressing: !file", array('!file' => $this->arguments['file']['file_path']));
-        $buffer_size = 4096;
-        $new_file_path = $matches[1];
-        $gzfile = gzopen($this->arguments['file']['file_path'], 'rb');
-        $out_file = fopen($new_file_path, 'wb');
-        if (!$out_file) {
-          throw new Exception("Cannot uncompress file: new temporary file, '$new_file_path', cannot be created.");
-        }
-
-        // Keep repeating until the end of the input file
-        while (!gzeof($gzfile)) {
-          // Read buffer-size bytes
-          // Both fwrite and gzread and binary-safe
-          fwrite($out_file, gzread($gzfile, $buffer_size));
-        }
-
-        // Files are done, close files
-        fclose($out_file);
-        gzclose($gzfile);
-
-        // Now remove the .gz file and reset the file_path to the new
-        // uncompressed version.
-        unlink($this->arguments['file']['file_path']);
-        $this->arguments['file']['file_path'] = $new_file_path;
       }
     }
     catch (Exception $e){

+ 2 - 1
tripal/includes/tripal.importer.inc

@@ -35,6 +35,8 @@ function tripal_get_importer_form($form, &$form_state, $class) {
         'then the status will quickly update to "Complete".',
       '#usage_type' => 'tripal_importer',
       '#usage_id' => 0,
+      '#allowed_types' => $class::$file_types,
+      '#cardinality' => $class::$cardinality,
     );
   }
 
@@ -77,7 +79,6 @@ function tripal_get_importer_form($form, &$form_state, $class) {
     );
   }
 
-
   $importer = new $class();
   $element = array();
   $form['class_elements'] = $importer->form($element, $form_state);

+ 2 - 2
tripal/includes/tripal.upload.inc

@@ -47,7 +47,7 @@ function tripal_file_upload($type, $filename, $action = NULL, $chunk = 0) {
       tripal_file_upload_put($filename, $chunk, $user_dir);
       break;
     case 'check':
-      tripal_file_upload_check($filename, $chunk, $user_dir);
+      tripal_file_upload_verify($filename, $chunk, $user_dir);
       break;
     case 'merge':
       tripal_file_upload_merge($filename, $type, $user_dir);
@@ -171,7 +171,7 @@ function tripal_file_upload_merge($filename, $type, $user_dir) {
  *   returns a JSON array with a status, message and the
  *   current chunk.
  */
-function tripal_file_upload_check($filename, $chunk, $user_dir) {
+function tripal_file_upload_verify($filename, $chunk, $user_dir) {
 
   $chunk_size = $_GET['chunk_size'];
 

BIN
tripal/theme/images/ajax-loader.gif


+ 8 - 8
tripal/theme/js/TripalUploadFile.js

@@ -285,7 +285,7 @@
         var parent = self.options['parent'];
         var tname = self.options['tname'];
         var category = self.options['category'];
-        parent.setTarget(this.file_id, tname, category);
+        parent.setTarget(tname);
       }
       else if (this.status == 'paused') {
         $("<span>", {
@@ -297,7 +297,7 @@
       if (this.status == 'uploading' || this.status == 'checking' || this.status == 'merging') {
         $("<img>", {
            'src': tripal_path + '/theme/images/ajax-loader.gif',
-           'class' : 'tripal-chunked-file-progress-throbber',
+           'class' : 'tripal-uploader-chunked-file-progress-throbber',
          }).appendTo('#' + progress_id);
       }
       
@@ -362,10 +362,10 @@
         var index = self.options['index'];
         var tname = self.options['tname'];
         var category = self.options['category'];
-        parent.removeFile(category, index);
+        parent.removeFile(tname, category, index);
         parent.updateTable(category);
         // Unset the parent's target field.
-        parent.setTarget(null, tname, category);
+        parent.setTarget(tname);
         self.cancel();
       })
     }
@@ -384,22 +384,22 @@
       if (this.status == 'uploading') {
         $('#' + progress_id).html('');
         $("<span>", {
-          'class': 'tripal-chunked-file-progress-label',
+          'class': 'tripal-uploader-chunked-file-progress-label',
           'text': size_transferred,
         }).appendTo($("<div>", {
           'id': progress_id + '-bar',
-          'class': 'tripal-chunked-file-progress',
+          'class': 'tripal-uploader-chunked-file-progress',
           'width': progress + '%'
         }).appendTo($("<div>", {
           'id': progress_id + '-box',
-          'class': 'tripal-chunked-file-progress',
+          'class': 'tripal-uploader-chunked-file-progress',
         }).appendTo('#' + progress_id)));
 
       }
       if (this.status == 'uploading' || this.status == 'checking' || this.status == 'merging') {
         $("<img>", {
            'src': tripal_path + '/theme/images/ajax-loader.gif',
-           'class' : 'tripal-chunked-file-progress-throbber',
+           'class' : 'tripal-uploader-chunked-file-progress-throbber',
          }).appendTo('#' + progress_id);
       }
       

+ 175 - 113
tripal/theme/js/TripalUploader.js

@@ -127,6 +127,28 @@
       var url = options['url'];
       var self = this;
       
+      // Make sure the file type is allowed
+      if (this.tables[tname]['allowed_types']) {
+        var allowed_types = this.tables[tname]['allowed_types'];
+        var matches = file.name.match(/^.*\.(.+)$/);
+        if (!matches) {
+          alert('Please provide a file with a valid extension.');
+          return null;
+        }
+        var type = matches[1];
+        var j;
+        var found = false;
+        for (j = 0; j < allowed_types.length; j++) {
+          if (allowed_types[j] == type) {
+            found = true;
+          }
+        }
+        if (!found) {
+          alert('Please provide a file with a valid extension. The following are allowed: ' + allowed_types.join(','));
+          return null;
+        }
+      }
+      
       if (!(category in this.files)) {
         this.files[category] = {}
       }      
@@ -140,6 +162,7 @@
         'links' : category + '-links-' + i,
         'module' : this.tables[tname]['module']
       }
+      
       var guf = new TripalUploadFile(file, options)
       this.files[category][i] = guf;
       return guf
@@ -147,12 +170,13 @@
     /**
      * 
      */
-    this.removeFile = function(category, i) {
+    this.removeFile = function(tname, category, i) {
       if (category in this.files) {
         if (i in this.files[category]) {
           delete this.files[category][i];
         }
       }
+      this.setTarget(tname);
     }
     /**
      * 
@@ -234,7 +258,7 @@
      */
     this.getFileButton = function(tname, category, i) {
       var button_name = tname + '--' + category + '-upload-' + i;
-      var element = '<input id="' + button_name + '" class="tripal-chunked-file-upload" type="file">';
+      var element = '<input id="' + button_name + '" class="tripal-chunked-file-upload" type="file" ready="false">';
       
       return {
         'name' : button_name,
@@ -432,21 +456,52 @@
      * @param $category
      *   The name of the category to which the file belongs.
      */
-    this.setTarget = function(file_id, tname, category) {
-      var files  = this.getCategoryFiles(category);
+    this.setTarget = function(tname) {
+      var categories = this.tables[tname]['category'];
+      var num_categories = categories.length;
       var cardinality = this.tables[tname]['cardinality'];
       var target_id = this.tables[tname]['target_id'];
-      var num_files = this.getNumFiles(category);
-         
+      
       if (target_id) {
-        // Always set the first file_id.
-        var fids = files[0].file_id;
-        // Iterate through any other files and add them with a '|' delemiter.
-        var i;
-        for (i = 1; i < num_files; i++) {
-          fids = fids + "|" + files[i].file_id;
-        } 
-        $('#' + target_id).val(fids);
+        var fids = '';
+        var c;
+
+        // Iterate through the file categories.
+        for (c = 0; c < num_categories; c++) {
+          var files  = this.getCategoryFiles(categories[c]);
+          var num_files = this.getNumFiles(categories[c]);
+          var i;
+          
+          // Deal with one category.
+          if (num_categories == 1) {
+            if (num_files > 0) {
+              // Always set the first file_id.
+              fids = files[0].file_id;
+            }
+          }
+          // Deal with multiple categories.
+          else {
+            // When we have more than one category then we need to 
+            // separate the categories with a comma. So, this must happen
+            // after every category except the first.
+            if (c == 0) {
+              if (num_files > 0) {
+                fids = fids + files[0].file_id;
+              }
+            }
+            else {
+              fids = fids + ',';
+              if (num_files > 0) {
+                fids = fids + files[0].file_id;
+              }
+            }
+          }
+          // Iterate through any other files and add them with a '|' delemiter.
+          for (i = 1; i < num_files; i++) {
+            fids = fids + "|" + files[i].file_id;
+          } 
+          $('#' + target_id).val(fids);
+        }
       }
     }
 
@@ -454,74 +509,76 @@
      * A table for paired data (e.g. RNA-seq).
      */
     this.updatePairedTable = function(tname, categories) {
-        var i = 0;
-        var table_id = this.tables[tname]['table_id'];
-        var cardinality = this.tables[tname]['cardinality'];
+      var i = 0;
+      var table_id = this.tables[tname]['table_id'];
+      var cardinality = this.tables[tname]['cardinality'];
 
-        var category1 = categories[0];
-        var category2 = categories[1];
+      var category1 = categories[0];
+      var category2 = categories[1];
 
-        var paired_content = '';   
-        var category1_files = this.getCategoryFiles(category1);
-        var category2_files = this.getCategoryFiles(category2);    
-        var max_paired1 = this.getMaxIndex(category1);
-        var max_paired2 = this.getMaxIndex(category2);
-        
-        var button1 = null;
-        var button2 = null;
+      var paired_content = '';   
+      var category1_files = this.getCategoryFiles(category1);
+      var category2_files = this.getCategoryFiles(category2);    
+      var max_paired1 = this.getMaxIndex(category1);
+      var max_paired2 = this.getMaxIndex(category2);
+      
+      var buttons = []
+      var button1 = null;
+      var button2 = null;
 
-        // Bulid the rows for the paired sample files table.
-        var has_file = false;
-        for (i = 0; i <= Math.max(max_paired1, max_paired2); i++) {
-          button1 = this.getFileButton(tname, category1, i);
-          button2 = this.getFileButton(tname, category2, i);
+      // Build the rows for the paired sample files table.
+      var has_file = false;
+      for (i = 0; i <= Math.max(max_paired1, max_paired2); i++) {
+        button1 = this.getFileButton(tname, category1, i);
+        button2 = this.getFileButton(tname, category2, i);
 
-          var trclass = 'odd';
-          if (i % 2 == 0) {
-            trclass = 'even';
-          }
-          paired_content +=  '<tr class="' + trclass + '">';
-          if (i in category1_files) {
-            paired_content += '<td>' + category1_files[i].getFileName() + '</td>';
-            paired_content += '<td>' + category1_files[i].getFileSize(true)  + '</td>';
-            paired_content += '<td>' + category1_files[i].getProgressBar() + '</td>';
-            paired_content += '<td>' + category1_files[i].getLinks() + '</td>';
-            has_file = true;
-          }
-          else {
-            paired_content += '<td colspan="4">' + button1['element'] + '</td>';
-          }
-          if (i in category2_files) {
-            paired_content += '<td>' + category2_files[i].getFileName() + '</td>';
-            paired_content += '<td>' + category2_files[i].getFileSize(true) + '</td>';
-            paired_content += '<td>' + category2_files[i].getProgressBar() + '</td>';
-            paired_content += '<td nowrap>' + category2_files[i].getLinks() + '</td>';
-            has_file = true;
-          }
-          else {
-            paired_content += '<td colspan="4">' + button2['element'] + '</td>';
-          }
-          paired_content +=  '</tr>';
+        var trclass = 'odd';
+        if (i % 2 == 0) {
+          trclass = 'even';
         }
-
-        // Create a new empty row of buttons if we have files.
-        if (has_file) {
-          // Only add a new row if we haven't reached our cardinality limit.
-          if (!cardinality || cardinality == 0 || cardinality < max_index) {
-            button1 = this.getFileButton(tname, category1, i);
-            button2 = this.getFileButton(tname, category2, i);
-            paired_content += '<tr class="odd"><td colspan="4">' + button1['element'] + 
-              '</td><td colspan="4">' + button2['element'] + '</td></tr>'
-          }
+        paired_content +=  '<tr class="' + trclass + '">';
+        if (i in category1_files) {
+          paired_content += '<td>' + category1_files[i].getFileName() + '</td>';
+          paired_content += '<td>' + category1_files[i].getFileSize(true)  + '</td>';
+          paired_content += '<td>' + category1_files[i].getProgressBar() + '</td>';
+          paired_content += '<td>' + category1_files[i].getLinks() + '</td>';
+          has_file = true;
         }
-
-        $(table_id + ' > tbody').html(paired_content);
-        if (button1) {
-          this.enableFileButton(button1['name']);
+        else {
+          paired_content += '<td colspan="4">' + button1['element'] + '</td>';
+          buttons.push(button1);
         }
-        if (button2) {
-          this.enableFileButton(button2['name']);
+        if (i in category2_files) {
+          paired_content += '<td>' + category2_files[i].getFileName() + '</td>';
+          paired_content += '<td>' + category2_files[i].getFileSize(true) + '</td>';
+          paired_content += '<td>' + category2_files[i].getProgressBar() + '</td>';
+          paired_content += '<td nowrap>' + category2_files[i].getLinks() + '</td>';
+          has_file = true;
+        }
+        else {
+          paired_content += '<td colspan="4">' + button2['element'] + '</td>';
+          buttons.push(button2);
         }
+        paired_content +=  '</tr>';
+      }
+
+      // Create a new empty row of buttons if we have files.
+      if (has_file) {
+        // Only add a new row if we haven't reached our cardinality limit.
+        if (!cardinality || cardinality == 0 || cardinality < max_paired1) {
+          button1 = this.getFileButton(tname, category1, i);
+          button2 = this.getFileButton(tname, category2, i);
+          buttons.push(button1);
+          buttons.push(button2);
+          paired_content += '<tr class="odd"><td colspan="4">' + button1['element'] + 
+            '</td><td colspan="4">' + button2['element'] + '</td></tr>'
+        }
+      }
+
+      $(table_id + ' > tbody').html(paired_content);
+      for (i = 0; i < buttons.length; i++) {
+        this.enableFileButton(buttons[i]['name']);
+      }
     }
 
     /**
@@ -530,51 +587,56 @@
      * The button is added by the updateUploadTable
      */
     this.enableFileButton = function(button_name) {
-        // When the button provided by the TripalUploader class is clicked
-        // then we need to add the files to the object.  We must have this
-        // function so that we can set the proper URL
-        var self = this;
+     
+      // If the button already exists then it's already setup so just
+      // return.
+      if($('#' + button_name).attr('ready') == 'true') {
+        return;
+      }
 
-        var func_name = ($.isFunction($.fn.live)) ? 'live' : 'on';
-        $('#' + button_name)[func_name]('change', function(e) {
-          var id = this.id;
-          
-          // Get the HTML5 list of files to upload.
-          var hfiles = e.target.files;
 
-          // Let the TripalUploader object parse the button ID to give us
-          // the proper category name and index.
-          var button = self.parseButtonID(id);
-          var tname = button['tname'];
-          var category = button['category'];
-          var index = button['index'];
+      // When the button provided by the TripalUploader class is clicked
+      // then we need to add the files to the object.  We must have this
+      // function so that we can set the proper URL
+      var self = this;
+
+      var func_name = ($.isFunction($.fn.live)) ? 'live' : 'on';
+      $('#' + button_name)[func_name]('change', function(e) {
+        var id = this.id;
+        
+        // Get the HTML5 list of files to upload.
+        var hfiles = e.target.files;
+
+        // Let the TripalUploader object parse the button ID to give us
+        // the proper category name and index.
+        var button = self.parseButtonID(id);
+        var tname = button['tname'];
+        var category = button['category'];
+        var index = button['index'];
 
-          // Add the file(s) to the uploader object.
-          for (var i = 0; i < hfiles.length; i++) {
-            var f = hfiles[i];
-//            if (!f.name.match('^.*\.fastq$')){
-//               alert('Only FastQ files are allowed.');
-//               continue;
-//            }
-            var options = {
-              // Files are managed by tables.
-              'tname' : tname,
-              // Files can be categorized to seprate them from other files.
-              'category': category,
-              // The index is the numeric index of the file. Files are ordered
-              // by their index. The file with an index of 0 is always ordered first.
-              'i': index,
-              // The URL at the remote server where the file will uploaded. 
-              'url' : baseurl + '/tripal/upload/' + category,
+        // Add the file(s) to the uploader object.
+        for (var i = 0; i < hfiles.length; i++) {
+          var f = hfiles[i];
+          var options = {
+            // Files are managed by tables.
+            'tname' : tname,
+            // Files can be categorized to seprate them from other files.
+            'category': category,
+            // The index is the numeric index of the file. Files are ordered
+            // by their index. The file with an index of 0 is always ordered first.
+            'i': index,
+            // The URL at the remote server where the file will uploaded. 
+            'url' : baseurl + '/tripal/upload/' + category,
             };
             self.addFile(f, options);
-
+ 
             // We need to update the upload table and the progress. The
-            // information for which table to update is in the self.tables
-            // array.
-            self.updateTable(category);
-          }
-        });
+          // information for which table to update is in the self.tables
+          // array.
+          self.updateTable(category);
+        }
+      });
+      $('#' + button_name).attr('ready', 'true');
     }
   };
 

+ 17 - 11
tripal/theme/js/tripal.file.js

@@ -1,25 +1,31 @@
-(function($) {
+(function ($) {
   Drupal.behaviors.TripalFile = {
     attach: function (context, settings) {
-
       // Initialize the TripalUploader object.
       var tripal_files = new TripalUploader();
-      
+
       // All tables that belong to the html5-file form element should
       // be enabled for uploading.
-      $(".tripal-html5-file-upload-table-key").each(function(index) {
-        
+      $('.tripal-html5-file-upload-table-key').each(function (index) {
+        // If we already attached functionality to the field, skip it
+        if ($(this).data('tripal.file')) {
+          return;
+        }
+
+        // Set the field status
+        $(this).data('tripal.file', true);
+
         // The settings for this uploader are provided in a custom variable
         // specific to the table. We can get the variable name by piecing
         // together parts of the table ID.
-        var id = $(this).val();
-        var details = id.split("-");
-        var settings_var_name = "Drupal.settings.uploader_" + details[0] + '_' + details[1] + "_" + details[2];
-        var settings = eval(settings_var_name);
+        var id                = $(this).val();
+        var details           = id.split('-');
+        var settings_var_name = 'Drupal.settings.uploader_' + details[0] + '_' + details[1] + '_' + details[2];
+        var settings          = eval(settings_var_name);
 
         // Initialize the table for uploads.
         tripal_files.addUploadTable(details[0] + '-' + details[1], settings);
       });
     }
-  }
-}) (jQuery);
+  };
+})(jQuery);

+ 40 - 37
tripal/tripal.drush.inc

@@ -19,32 +19,6 @@
  *
  * @ingroup tripal_drush
  */
-function tripal_drush_help($command) {
-  switch ($command) {
-
-    // TRIPAL JOBS
-    case 'trp-run-jobs':
-      return dt('Launches pending jobs waiting in the queue.');
-      break;
-    case 'trp-rerun-job':
-      return dt('Rerun a job in the queue.');
-      break;
-    case 'trp-get-currjob':
-      return dt('Returns details about the currently running tripal job including percent complete.');
-      break;
-    // Placeholders for unimplmeneted jobs
-    case 'trp-show-job':
-      break;
-    case 'trp-revert-jobs':
-      break;
-    case 'trp-cancel-job':
-      break;
-    case 'trp-list-jobs':
-      break;
-
-  }
-}
-
 /**
  * Registers a drush command and constructs the full help for that command.
  *
@@ -186,6 +160,39 @@ function drush_tripal_trp_run_jobs() {
   }
 }
 
+
+/**
+ * Executes jobs in the Tripal Jobs Queue.
+ *
+ * Executed when 'drush trp-run-job' is called.
+ *
+ * @ingroup tripal_drush
+ */
+function drush_tripal_trp_run_jobs_install($username) {
+  $parallel = drush_get_option('parallel');
+  $job_id   = drush_get_option('job_id');
+  $max_jobs = drush_get_option('max_jobs', -1);
+  $single   = drush_get_option('single', 0);
+
+  drush_tripal_set_user($username);
+
+  drush_print("\n" . date('Y-m-d H:i:s'));
+  if ($parallel) {
+    drush_print("Tripal Job Launcher (in parallel)");
+    if ($max_jobs !== -1) drush_print("Maximum number of jobs is " . $max_jobs);
+    drush_print("Running as user '$username'");
+    drush_print("-------------------");
+    tripal_launch_job($parallel, $job_id, $max_jobs, $single);
+  }
+  else {
+    drush_print("Tripal Job Launcher");
+    drush_print("Running as user '$username'");
+    drush_print("-------------------");
+    tripal_launch_job(0, $job_id, $max_jobs, $single);
+  }
+}
+
+
 /**
  * Executes jobs in the Tripal Jobs Queue.
  *
@@ -254,13 +261,13 @@ function drush_tripal_trp_get_currjob() {
   foreach ($jobs as $job) {
     $job_pid = $job->pid;
     $output = "Name: " . $job->job_name . "\n" .
-        "Submitted: " . date(DATE_RFC822, $job->submit_date) . "\n" .
-        "Started: " . date(DATE_RFC822, $job->start_time) . "\n" .
-        "Module: " . $job->modulename . "\n" .
-        "Callback: " . $job->callback . "\n" .
-        "Process ID: " . $job->pid . "\n" .
-        "Progress: " . $job->progress . "%\n".
-        "Current Date: " . date('Y-m-d H:i:s') . "\n";
+      "Submitted: " . date(DATE_RFC822, $job->submit_date) . "\n" .
+      "Started: " . date(DATE_RFC822, $job->start_time) . "\n" .
+      "Module: " . $job->modulename . "\n" .
+      "Callback: " . $job->callback . "\n" .
+      "Process ID: " . $job->pid . "\n" .
+      "Progress: " . $job->progress . "%\n".
+      "Current Date: " . date('Y-m-d H:i:s') . "\n";
     drush_print(date('Y-m-d H:i:s'));
     drush_print($output);
   }
@@ -271,7 +278,3 @@ function drush_tripal_trp_get_currjob() {
   //log to the command line with an OK status
   drush_log('Running tripal-current-job', 'ok');
 }
-
-
-
-

+ 21 - 3
tripal/tripal.install

@@ -331,10 +331,9 @@ function tripal_tripal_import_schema() {
         'not null' => TRUE,
       ),
       'fid' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
+        'type' => 'text',
         'not null' => FALSE,
-        'description' => 'The file ID of the file to import. This only applies if the file was uploaded (i.e. not already on the server) and is mangaged by Drupal.'
+        'description' => 'The file IDs of the to import. This only applies if the file was uploaded (i.e. not already on the server) and is mangaged by Drupal. Multiple fids are separated using a | character.'
       ),
       'arguments' => array(
         'type' => 'text',
@@ -917,4 +916,23 @@ function tripal_update_7304() {
     $error = $e->getMessage();
     throw new DrupalUpdateException('Could not perform update: '. $error);
   }
+}
+
+/**
+ * Adjusts the tripal_import table to support multiple file uploads.
+ */
+function tripal_update_7305() {
+  $transaction = db_transaction();
+  try {
+    db_change_field('tripal_import', 'fid', 'fid', array(
+      'type' => 'text',
+      'not null' => FALSE,
+      'description' => 'The file IDs of the to import. This only applies if the file was uploaded (i.e. not already on the server) and is mangaged by Drupal. Multiple fids are separated using a | character.'
+    ));
+  }
+  catch (\PDOException $e) {
+    $transaction->rollback();
+    $error = $e->getMessage();
+    throw new DrupalUpdateException('Could not perform update: '. $error);
+  }
 }

+ 32 - 8
tripal/tripal.module

@@ -982,13 +982,28 @@ function tripal_html5_file_process($element, $form_state, $complete_form) {
   $name = preg_replace('/[^\w]/', '_', $name);
   $allowed_types = array_key_exists('#allowed_types', $element) ? $element['#allowed_types'] : array();
   $cardinality = array_key_exists('#cardinality', $element) ? $element['#cardinality'] : 1;
-
-  $headers = array(
-    array('data' => 'File'),
-    array('data' => 'Size', 'width' => '10%'),
-    array('data' => 'Upload Progress', 'width' => '20%'),
-    array('data' => 'Action', 'width' => '10%')
-  );
+  $paired = array_key_exists('#paired', $element) ? $element['#paired'] : FALSE;
+
+  if ($paired) {
+    $headers = array(
+      array('data' => 'File #1'),
+      array('data' => 'Size', 'width' => '10%'),
+      array('data' => 'Upload Progress', 'width' => '20%'),
+      array('data' => 'Action', 'width' => '10%'),
+      array('data' => 'File #2'),
+      array('data' => 'Size', 'width' => '10%'),
+      array('data' => 'Upload Progress', 'width' => '20%'),
+      array('data' => 'Action', 'width' => '10%')
+    );
+  }
+  else {
+    $headers = array(
+      array('data' => 'File'),
+      array('data' => 'Size', 'width' => '10%'),
+      array('data' => 'Upload Progress', 'width' => '20%'),
+      array('data' => 'Action', 'width' => '10%'),
+    );
+  }
   $rows = array();
   $table_vars = array(
     'header'      => $headers,
@@ -1032,10 +1047,19 @@ function tripal_html5_file_process($element, $form_state, $complete_form) {
     )
   );
 
+  if (!$paired) {
+    $categories = array($element['#usage_id'] . '-' . $element['#usage_type']);
+  }
+  else {
+    $categories = array(
+      $element['#usage_id'] . '-' . $element['#usage_type'] . '-f',
+      $element['#usage_id'] . '-' . $element['#usage_type'] . '-r',
+    );
+  }
   $uploader_settings = array(
     'table_id' => '#tripal-html5-file-upload-table-' . $type,
     'submit_id' => '#tripal-html5-file-upload-submit-' . $type,
-    'category' => array($element['#usage_id'] . '-' . $element['#usage_type']),
+    'category' => $categories,
     'cardinality' => $cardinality,
     'target_id' => 'tripal-html5-upload-fid-' . $type,
     'module' => $module,

+ 1 - 1
tripal_chado/includes/TripalImporter/FASTAImporter.inc

@@ -324,7 +324,7 @@ class FASTAImporter extends TripalImporter {
   public function run() {
 
     $arguments = $this->arguments['run_args'];
-    $file_path = $this->arguments['file']['file_path'];
+    $file_path = $this->arguments['files'][0]['file_path'];
 
     $organism_id = $arguments['organism_id'];
     $type = $arguments['seqtype'];

+ 3 - 3
tripal_chado/includes/TripalImporter/GFF3Importer.inc

@@ -23,14 +23,14 @@ class GFF3Importer extends TripalImporter {
   /**
    * An array containing the extensions of allowed file types.
    */
-  public static $file_types = array('gff3');
+  public static $file_types = array('gff', 'gff3');
 
 
   /**
    * Provides information to the user about the file upload.  Typically this
    * may include a description of the file types allowed.
    */
-  public static $upload_description = 'Please provide the GFF3 file. The file must have a .gff3 extension.';
+  public static $upload_description = 'Please provide the GFF3 file.';
 
   /**
    * The title that should appear above the upload button.
@@ -297,7 +297,7 @@ class GFF3Importer extends TripalImporter {
   public function run() {
 
     $arguments = $this->arguments['run_args'];
-    $file_path = $this->arguments['file']['file_path'];
+    $file_path = $this->arguments['files'][0]['file_path'];
 
     $organism_id = $arguments['organism_id'];
     $analysis_id = $arguments['analysis_id'];

+ 17 - 0
tripal_chado/includes/setup/tripal_chado.setup.inc

@@ -62,6 +62,23 @@ function tripal_chado_prepare_form_submit($form, $form_state) {
    }
 }
 
+/**
+ * Submit function for the tripal_chado_prepare_form().
+ *
+ * @param $form
+ * @param $form_state
+ */
+function tripal_chado_prepare_drush_submit() {
+  $args = array();
+  $includes = array(
+    module_load_include('inc', 'tripal_chado', 'includes/setup/tripal_chado.setup'),
+  );
+  tripal_add_job('Prepare Chado', 'tripal_chado',
+    'tripal_chado_prepare_chado', $args,
+    1, 10, $includes);
+
+}
+
 /**
  *
  */

+ 11 - 0
tripal_chado/includes/tripal_chado.install.inc

@@ -186,6 +186,17 @@ function tripal_chado_load_form_submit($form, &$form_state) {
     'tripal_chado_install_chado', $args, $user->uid, 10, $includes);
 }
 
+/**
+ * Submit Load Chado Schema Form
+ *
+ * @ingroup tripal_chado
+ */
+function tripal_chado_load_drush_submit($version) {
+  $args = array($version);
+  $includes = array(module_load_include('inc', 'tripal_chado', 'includes/tripal_chado.install'));
+  tripal_add_job($version, 'tripal_chado',
+    'tripal_chado_install_chado', $args, 1, 10, $includes);
+}
 /**
  * Install Chado Schema
  *