Bläddra i källkod

Added user interface for collections, and FASTA downloader now works

Stephen Ficklin 7 år sedan
förälder
incheckning
c38e664d71

+ 38 - 2
tripal/api/tripal.collections.api.inc

@@ -16,10 +16,27 @@
  *   An instance of a TripalEntityCollection class or FALSE on failure.
  */
 function tripal_create_collection($details) {
+  global $user;
+
   try {
     $collection = new TripalEntityCollection();
-    $collection_id = $collection->create($details);
-    drupal_set_message(t("Collection '%name' created.", array('%name' => $details['collection_name'])));
+    $collection->create($details);
+    $collection_id = $collection->getCollectionID();
+
+    // Add the job to write the collection download files.
+    $args = array($collection_id);
+    tripal_add_job('Create data collection files for ' . $user->name, 'tripal',
+        'tripal_create_collection_files', $args, $user->uid, 10, array());
+
+
+    drupal_set_message(t("Collection '%name' created with %num_recs record(s).  Downloadble files will be available shortly.  Check the !view for status and download links.",
+      array(
+        '%name' => $details['collection_name'],
+        '%num_recs' => count($details['ids']),
+        '!view' => l('data collections page', 'user/' . $user->uid . '/data-collections'),
+      ))
+    );
+
   }
   catch (Exception $e) {
     drupal_set_message(t("Failed to create the collection '%name': " . $e->getMessage(), array('%name' =>  $details['collection_name'])), 'error');
@@ -80,4 +97,23 @@ function tripal_get_collection($values) {
     drupal_set_message(t("Failed to load the collection with id '%id': " . $e->getMessage(), array('%id' =>  $collection_id)), 'error');
     return FALSE;
   }
+}
+
+/**
+ * Generates the downloadable files for a Collection
+ *
+ * @param TripalEntityCollection $collection
+ */
+function tripal_create_collection_files($collection_id, TripalJob $job = NULL) {
+   if($job) {
+     $job->setProgress(0);
+   }
+
+   $collection = new TripalEntityCollection();
+   $collection->load($collection_id);
+   $collection->writeAll();
+
+   if ($job) {
+     $job->setProgress(100);
+   }
 }

+ 196 - 23
tripal/includes/TripalEntityCollection.inc

@@ -6,6 +6,12 @@ class TripalEntityCollection {
    * The name of the bundle (i.e. content type) to which the entities belong.
    */
   protected $bundle_name = '';
+
+  /**
+   * The collection ID
+   */
+  protected $collection_id = NULL;
+
   /**
    * The name of this collection.
    */
@@ -32,6 +38,11 @@ class TripalEntityCollection {
    */
   protected $downloaders = array();
 
+  /**
+   * The description for this collection.
+   */
+  protected $description = '';
+
   /**
    * Constructs a new instance of the TripalEntityCollection class.
    */
@@ -39,6 +50,44 @@ class TripalEntityCollection {
 
   }
 
+  /**
+   * Deletes the current collection
+   */
+  public function delete() {
+
+    if (!$this->collection_id) {
+      throw new Exception('This data collection object has not yet been loaded. Cannot delete.');
+    }
+
+    try {
+      db_delete('tripal_collection')
+        ->condition('collection_id', $this->collection_id)
+        ->execute();
+
+      // Remove any files that may have been created
+      foreach ($this->downloaders as $class_name => $label) {
+        tripal_load_include_downloader_class($class_name);
+        $outfile = $this->getOutfile($class_name);
+        $downloader = new $class_name($this->bundle_name, $this->ids, $this->fields,
+            $outfile, $this->getUserID());
+        $downloader->delete();
+      }
+
+      // Reset the class to defaults.
+      $this->collection_id = NULL;
+      $this->bundle_name = '';
+      $this->collection_name = '';
+      $this->create_date = '';
+      $this->description = '';
+      $this->fields = array();
+      $this->ids = array();
+
+    }
+    catch (Exception $e) {
+      throw new Exception('Cannot delete collection: ' .  $e->getMessage());
+    }
+  }
+
   /**
    * Loads an existing collection using a collection ID.
    *
@@ -48,6 +97,7 @@ class TripalEntityCollection {
    * @throws Exception
    */
   public function load($collection_id) {
+
     // Make sure we have a numeric job_id.
     if (!$collection_id or !is_numeric($collection_id)) {
       throw new Exception("You must provide the collection_id to load the collection.");
@@ -70,31 +120,36 @@ class TripalEntityCollection {
     $this->user = user_load($collection->uid);
     $this->ids = unserialize($collection->ids);
     $this->fields = unserialize($collection->fields);
+    $this->description = $collection->description;
+    $this->collection_id = $collection->collection_id;
 
     // Iterate through the fields and find out what download formats are
     // supported for this basket.
     foreach ($this->fields as $field_id) {
       $field = field_info_field_by_id($field_id);
+      if (!$field) {
+        continue;
+      }
       $field_name = $field['field_name'];
       $field_type = $field['type'];
       $field_module = $field['module'];
       $instance = field_info_instance('TripalEntity', $field_name, $this->bundle_name);
-      $settings = $instance['settings'];
       $downloaders = array();
-      if (array_key_exists('download_formatters', $settings)) {
-        foreach ($settings['download_formatters'] as $class_name) {
-          if (!in_array($class_name, $settings['download_formatters'])) {
-            tripal_load_include_downloader_class($class_name);
-            $this->downloaders[$class_name] = $class_name::$label;
-          }
-          // For backwards compatibility for fields that don't have
-          // the download_formatters we'll set tab-delimeted and CSV
-          // downloaders.
-          else {
-            tripal_load_include_downloader_class('TripalTabDownloader');
-            $this->downloaders[$class_name] = $class_name::$label;
-            tripal_load_include_downloader_class('TripalCSVDownloader');
-            $this->downloaders[$class_name] = $class_name::$label;
+
+      // All fields should support the Tab and CSV downloaders.
+      tripal_load_include_downloader_class('TripalTabDownloader');
+      $this->downloaders['TripalTabDownloader'] = TripalTabDownloader::$label;
+      tripal_load_include_downloader_class('TripalCSVDownloader');
+      $this->downloaders['TripalCSVDownloader'] = TripalCSVDownloader::$label;
+
+      if (tripal_load_include_field_class($field_type)) {
+        $settings = $field_type::$default_instance_settings;
+        if (array_key_exists('download_formatters', $settings)) {
+          foreach ($settings['download_formatters'] as $class_name) {
+            if (!array_key_exists($class_name, $this->downloaders)) {
+              tripal_load_include_downloader_class($class_name);
+              $this->downloaders[$class_name] = $class_name::$label;
+            }
           }
         }
       }
@@ -229,6 +284,26 @@ class TripalEntityCollection {
     return $this->collection_name;
   }
 
+  /**
+   * Retrieves the collection ID.
+   *
+   * @return
+   *   A numeric ID for this collection.
+   */
+  public function getCollectionID(){
+    return $this->collection_id;
+  }
+
+  /**
+   * Retreives the collection description
+   *
+   * @return
+   *   A string containing the description of the collection.
+   */
+  public function getDescription() {
+    return $this->description;
+  }
+
   /**
    * Retrieves the user object of the user that owns the collection
    *
@@ -253,12 +328,18 @@ class TripalEntityCollection {
   }
 
   /**
-   * Writes the collection to a file.
+   * Retrieves the output filename for the desired formatter.
    *
-   * @param formatter
-   *   The name of the formatter class to use (e.g. TripalTabDownloader).
+   * @param $formatter
+   *   The class name of the formatter to use.  The formatter must
+   *   be compatible with the data collection.
+   *
+   * @throws Exception
    */
-  public function write($formatter) {
+  public function getOutfile($formatter) {
+    if(!$this->isFormatterCompatible($formatter)) {
+      throw new Exception(t('The formatter, "%formatter", is not compatible with this data collection.', array('%formatter' => $formatter)));
+    }
 
     if (!tripal_load_include_downloader_class($formatter)) {
       throw new Exception(t('Cannot find the formatter named "@formatter".', array('@formatter', $formatter)));
@@ -268,6 +349,95 @@ class TripalEntityCollection {
     $create_date = $this->getCreateDate(FALSE);
     $outfile = preg_replace('/[^\w]/', '_', ucwords($this->collection_name)) . '_collection' . '_' . $create_date . '.' . $extension;
 
+    return $outfile;
+  }
+
+  /**
+   * Indicates if the given formatter is compatible with the data collection.
+   *
+   * @param $formatter
+   *   The class name of the formatter to check.
+   * @return boolean
+   *   TRUE if the formatter is compatible, FALSE otherwise.
+   */
+  public function isFormatterCompatible($formatter) {
+    foreach ($this->downloaders as $class_name => $label) {
+      if ($class_name == $formatter) {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+
+  /**
+   * Writes the collection to all file downloadable formats.
+   *
+   * @throws Exception
+   */
+  public function writeAll() {
+    foreach ($this->downloaders as $class_name => $lable) {
+      $this->write($class_name);
+    }
+  }
+
+  /**
+   * Retrieves the URL for the downloadable file.
+   *
+   * @param $formatter
+   *   The name of the class
+   */
+  public function getOutfileURL($formatter) {
+    $outfile = $this->getOutfilePath($formatter);
+    return file_create_url($outfile);
+  }
+
+  /**
+   * Retrieves the path for the downloadable file.
+   *
+   * The path is in the Drupal URI format.
+   *
+   * @param $formatter
+   *   The name of the class
+   */
+  public function getOutfilePath($formatter) {
+    if(!$this->isFormatterCompatible($formatter)) {
+      throw new Exception(t('The formatter, "@formatter", is not compatible with this data collection.', array('@formatter' => $formatter)));
+
+    }
+
+    if (!tripal_load_include_downloader_class($formatter)) {
+      throw new Exception(t('Cannot find the formatter named "@formatter".', array('@formatter', $formatter)));
+    }
+
+    $outfile = $this->getOutfile($formatter);
+
+    $downloader = new $formatter($this->bundle_name, $this->ids, $this->fields, $outfile, $this->user->uid);
+
+    return $downloader->getURL();
+  }
+
+  /**
+   * Writes the collection to a file.
+   *
+   * @param formatter
+   *   The name of the formatter class to use (e.g. TripalTabDownloader). The
+   *   formatter must be compatible with the data collection.
+   *
+   * @throws Exception
+   */
+  public function write($formatter) {
+
+    if(!$this->isFormatterCompatible($formatter)) {
+      throw new Exception(t('The formatter, "@formatter", is not compatible with this data collection.', array('@formatter' => $formatter)));
+
+    }
+
+    if (!tripal_load_include_downloader_class($formatter)) {
+      throw new Exception(t('Cannot find the formatter named "@formatter".', array('@formatter', $formatter)));
+    }
+
+    $outfile = $this->getOutfile($formatter);
+
     // Filter out fields that aren't supported by the formatter.
     $supported_fields = array();
     foreach ($this->fields as $field_id) {
@@ -284,10 +454,13 @@ class TripalEntityCollection {
       // field and if so then add it to our list of supported fields.
       $field = field_info_field_by_id($field_id);
       $field_name = $field['field_name'];
-      $instance = field_info_instance('TripalEntity', $field_name, $this->bundle_name);
-      if (array_key_exists('download_formatters', $instance['settings'])) {
-        if (in_array($formatter, $instance['settings']['download_formatters'])) {
-          $supported_fields[] = $field_id;
+      $field_type = $field['type'];
+      if (tripal_load_include_field_class($field_type)) {
+        $settings = $field_type::$default_instance_settings;
+        if (array_key_exists('download_formatters', $settings)) {
+          if (in_array($formatter, $settings['download_formatters'])) {
+            $supported_fields[] = $field_id;
+          }
         }
       }
     }

+ 11 - 2
tripal/includes/TripalFieldDownloaders/TripalCSVDownloader.inc

@@ -4,7 +4,7 @@ class TripalCSVDownloader extends TripalFieldDownloader {
   /**
    * Sets the label shown to the user describing this formatter.
    */
-  static public $label = 'CSV (comma separated)';
+  static public $label = 'CSV';
 
   /**
    * Indicates the default extension for the outputfile.
@@ -20,6 +20,10 @@ class TripalCSVDownloader extends TripalFieldDownloader {
       $field = field_info_field_by_id($field_id);
       $field_name = $field['field_name'];
 
+      if (!property_exists($entity, $field_name)) {
+        continue;
+      }
+
       // If we only have one element then this is good.
       if (count($entity->{$field_name}['und']) == 1) {
         $value = $entity->{$field_name}['und'][0]['value'];
@@ -28,7 +32,12 @@ class TripalCSVDownloader extends TripalFieldDownloader {
           $row[] = '"' . $value . '"';
         }
         else {
-          $row[] = '';
+          if (array_key_exists('rdfs:label', $entity->{$field_name}['und'][0]['value'])) {
+            $row[] = strip_tags($entity->{$field_name}['und'][0]['value']['rdfs:label']);
+          }
+          else {
+            $row[] = '';
+          }
           // TODO: What to do with fields that are arrays?
         }
       }

+ 0 - 28
tripal/includes/TripalFieldDownloaders/TripalFASTADownloader.inc

@@ -1,28 +0,0 @@
-<?php
-
-class TripalFASTADownloader extends TripalFieldDownloader {
-
-  /**
-   * Sets the label shown to the user describing this formatter.
-   */
-  static public $label = 'FASTA';
-
-  /**
-   * Indicates the default extension for the outputfile.
-   */
-  static public $default_extension = 'fasta';
-
-  /**
-   * @see TripalFieldDownloader::format()
-   */
-  protected function formatEntity($entity) {
-
-  }
-
-  /**
-   * @see TripalFieldDownloader::getHeader()
-   */
-  protected function getHeader() {
-
-  }
-}

+ 19 - 9
tripal/includes/TripalFieldDownloaders/TripalFieldDownloader.inc

@@ -49,11 +49,14 @@ abstract class TripalFieldDownloader {
    *   a path.
    */
   public function __construct($bundle_name, $ids, $fields = array(),
-      $outfile_name = '', $uid) {
+      $outfile_name, $uid) {
 
     $user = user_load($uid);
     if (!$user) {
-      throw new ErrorException(t("The provided user ID does not reference a real user: '@uid'.", array('@uid' => $uid)));
+      throw new Exception(t("The provided user ID does not reference a real user: '@uid'.", array('@uid' => $uid)));
+    }
+    if (!$outfile_name) {
+      throw new Exception("Please provide an outputfilename");
     }
 
     $this->bundle_name = $bundle_name;
@@ -73,10 +76,6 @@ abstract class TripalFieldDownloader {
       return;
     }
 
-    if (!$outfile_name) {
-      $outfile_name = unqiueid();
-    }
-
     $this->outfile = $user_dir. '/' . $outfile_name;
   }
 
@@ -84,7 +83,16 @@ abstract class TripalFieldDownloader {
    * Retrieves the URL for the downloadable file.
    */
   public function getURL() {
-     return $this0>outfile;
+     return $this->outfile;
+  }
+
+  /**
+   * Removes the downloadable file.
+   */
+  public function delete() {
+    if (file_exists($this->outfile)) {
+      unlink($this->outfile);
+    }
   }
 
   /**
@@ -97,8 +105,10 @@ abstract class TripalFieldDownloader {
     }
 
     $headers = $this->getHeader();
-    foreach ($headers as $line) {
-      fwrite($fh, $line . "\r\n");
+    if ($headers) {
+      foreach ($headers as $line) {
+        fwrite($fh, $line . "\r\n");
+      }
     }
 
     foreach ($this->entity_ids as $entity_id) {

+ 107 - 0
tripal/includes/TripalFieldDownloaders/TripalNucFASTADownloader.inc

@@ -0,0 +1,107 @@
+<?php
+
+class TripalNucFASTADownloader extends TripalFieldDownloader {
+
+  /**
+   * Sets the label shown to the user describing this formatter.
+   */
+  static public $label = 'Nucleotide FASTA';
+
+  /**
+   * Indicates the default extension for the outputfile.
+   */
+  static public $default_extension = 'fna';
+
+  /**
+   * @see TripalFieldDownloader::format()
+   */
+  protected function formatEntity($entity) {
+    $lines = array();
+
+    // Get the list of all fields that have been attached to the entity
+    $instances = field_info_instances('TripalEntity', $entity->bundle);
+    $available_fields = array();
+    foreach ($instances as $field_name => $instance) {
+      if ($instance['field_name'] == 'entity_id') {
+        continue;
+      }
+      $available_fields[$instance['field_name']] = $instance;
+    }
+
+    foreach ($this->fields as $field_id) {
+      $field = field_info_field_by_id($field_id);
+      $field_name = $field['field_name'];
+
+      if (!property_exists($entity, $field_name)) {
+        continue;
+      }
+
+      // If we only have one element then this is good.
+      if (count($entity->{$field_name}['und']) == 1) {
+        $value = $entity->{$field_name}['und'][0]['value'];
+        // If the single element is not an array then this is good.
+        if (!is_array($value)) {
+
+          // We need to make sure we have some fields for the definition line.
+          // those may or may not have been included, so we should add them.
+          $defline = '>';
+          $found_identifier = FALSE;
+          if (property_exists($entity, 'data__identifier')) {
+            $found_identifier = TRUE;
+            $defline .= $entity->{'data__identifier'}['und'][0]['value'] . ' ';
+          }
+          if (property_exists($entity, 'schema__name')) {
+            $found_identifier = TRUE;
+            $defline .= $entity->{'schema__name'}['und'][0]['value'] . ' ';
+          }
+          if (property_exists($entity, 'data__accession')) {
+            $found_identifier = TRUE;
+            $defline .= $entity->{'data__accession'}['und'][0]['value'] . ' ';
+          }
+          if (!$found_identifier) {
+            $defline .= "Unknown feature identifier. Please add a name field to the data collection";
+          }
+          // Add to the defnition line values from any single valued fields.
+          foreach ($available_fields as $fname => $instance) {
+            if (count($entity->{$fname}['und']) == 1) {
+              if (!is_array($entity->{$fname}['und'][0]['value'])) {
+                // Skip the identifier fields and the residues fields.
+                if (!in_array($fname, array('data__identifier',
+                    'schema__name', 'data__protein_sequence', $field_name))) {
+                  $defline .= $instance['label'] . ': ' . $entity->{$fname}['und'][0]['value'] . '; ';
+                }
+              }
+              else {
+                if (array_key_exists('rdfs:label', $entity->{$fname}['und'][0]['value'])) {
+                  $defline .= $instance['label'] . ': ' . strip_tags($entity->{$fname}['und'][0]['value']['rdfs:label']) . '; ';
+                }
+              }
+            }
+          }
+          $defline = rtrim($defline, '; ');
+
+          // Now add the residues.
+          $lines[] = $defline;
+          $residues = explode('|', wordwrap($value, 50, "|", TRUE));
+          foreach ($residues as $line) {
+            $lines[] = $line;
+          }
+        }
+        else {
+          // TODO: What to do with fields that are arrays?
+        }
+      }
+      else {
+        // TODO: What to do with fields that have multiple values?
+      }
+    }
+    return $lines;
+  }
+
+  /**
+   * @see TripalFieldDownloader::getHeader()
+   */
+  protected function getHeader() {
+
+  }
+}

+ 106 - 0
tripal/includes/TripalFieldDownloaders/TripalProteinFastaDownloader.inc

@@ -0,0 +1,106 @@
+<?php
+class TripalProteinFASTADownloader extends TripalFieldDownloader {
+
+  /**
+   * Sets the label shown to the user describing this formatter.
+   */
+  static public $label = 'Protein FASTA';
+
+  /**
+   * Indicates the default extension for the outputfile.
+   */
+  static public $default_extension = 'faa';
+
+  /**
+   * @see TripalFieldDownloader::format()
+   */
+  protected function formatEntity($entity) {
+    $lines = array();
+
+    // Get the list of all fields that have been attached to the entity
+    $instances = field_info_instances('TripalEntity', $entity->bundle);
+    $available_fields = array();
+    foreach ($instances as $field_name => $instance) {
+      if ($instance['field_name'] == 'entity_id') {
+        continue;
+      }
+      $available_fields[$instance['field_name']] = $instance;
+    }
+
+    foreach ($this->fields as $field_id) {
+      $field = field_info_field_by_id($field_id);
+      $field_name = $field['field_name'];
+
+      if (!property_exists($entity, $field_name)) {
+        continue;
+      }
+
+      // If we only have one element then this is good.
+      if (count($entity->{$field_name}['und']) == 1) {
+        $value = $entity->{$field_name}['und'][0]['value'];
+        // If the single element is not an array then this is good.
+        if (!is_array($value)) {
+
+          // We need to make sure we have some fields for the definition line.
+          // those may or may not have been included, so we should add them.
+          $defline = '>';
+          $found_identifier = FALSE;
+          if (property_exists($entity, 'data__identifier')) {
+            $found_identifier = TRUE;
+            $defline .= $entity->{'data__identifier'}['und'][0]['value'] . ' ';
+          }
+          if (property_exists($entity, 'schema__name')) {
+            $found_identifier = TRUE;
+            $defline .= $entity->{'schema__name'}['und'][0]['value'] . ' ';
+          }
+          if (property_exists($entity, 'data__accession')) {
+            $found_identifier = TRUE;
+            $defline .= $entity->{'data__accession'}['und'][0]['value'] . ' ';
+          }
+          if (!$found_identifier) {
+            $defline .= "Unknown feature identifier. Please add a name field to the data collection";
+          }
+          // Add to the defnition line values from any single valued fields.
+          foreach ($available_fields as $fname => $instance) {
+            if (count($entity->{$fname}['und']) == 1) {
+              if (!is_array($entity->{$fname}['und'][0]['value'])) {
+                // Skip the identifier fields and the residues fields.
+                if (!in_array($fname, array('data__identifier',
+                  'schema__name', 'data__sequence', $field_name))) {
+                  $defline .= $instance['label'] . ': ' . $entity->{$fname}['und'][0]['value'] . '; ';
+                }
+              }
+              else {
+                if (array_key_exists('rdfs:label', $entity->{$fname}['und'][0]['value'])) {
+                  $defline .= $instance['label'] . ': ' . strip_tags($entity->{$fname}['und'][0]['value']['rdfs:label']) . '; ';
+                }
+              }
+            }
+          }
+          $defline = rtrim($defline, '; ');
+
+          // Now add the residues.
+          $lines[] = $defline;
+          $residues = explode('|', wordwrap($value, 50, "|", TRUE));
+          foreach ($residues as $line) {
+            $lines[] = $line;
+          }
+        }
+        else {
+          // TODO: What to do with fields that are arrays?
+        }
+      }
+      else {
+        // TODO: What to do with fields that have multiple values?
+      }
+    }
+    return $lines;
+  }
+
+  /**
+   * @see TripalFieldDownloader::getHeader()
+   */
+  protected function getHeader() {
+
+  }
+}

+ 10 - 1
tripal/includes/TripalFieldDownloaders/TripalTabDownloader.inc

@@ -20,6 +20,10 @@ class TripalTabDownloader extends TripalFieldDownloader {
       $field = field_info_field_by_id($field_id);
       $field_name = $field['field_name'];
 
+      if (!property_exists($entity, $field_name)) {
+        continue;
+      }
+
       // If we only have one element then this is good.
       if (count($entity->{$field_name}['und']) == 1) {
         $value = $entity->{$field_name}['und'][0]['value'];
@@ -28,7 +32,12 @@ class TripalTabDownloader extends TripalFieldDownloader {
           $row[] = $value;
         }
         else {
-          $row[] = '';
+          if (array_key_exists('rdfs:label', $entity->{$field_name}['und'][0]['value'])) {
+            $row[] = strip_tags($entity->{$field_name}['und'][0]['value']['rdfs:label']);
+          }
+          else {
+            $row[] = '';
+          }
           // TODO: What to do with fields that are arrays?
         }
       }

+ 114 - 0
tripal/includes/tripal.collections.inc

@@ -0,0 +1,114 @@
+<?php
+
+
+/**
+ *
+ */
+function tripal_user_collections_page() {
+  global $user;
+
+  $headers = array('Name', 'Description', 'Create Date', 'Downloads Formats', 'Actions');
+  $rows = array();
+
+  $collections = db_select('tripal_collection', 'tc')
+    ->fields('tc', array('collection_id'))
+    ->condition('uid', $user->uid)
+    ->orderBy('tc.collection_name')
+    ->execute();
+
+  while ($collection_id = $collections->fetchField()) {
+    $collection = new TripalEntityCollection();
+    $collection->load($collection_id);
+
+    $downloads = array();
+    $formatters = $collection->getDownloadFormatters();
+    foreach ($formatters as $class_name => $label) {
+      $outfile = $collection->getOutfilePath($class_name);
+      $outfileURL = file_create_url($outfile);
+      if (file_exists($outfile)) {
+        $downloads[] = l($label, $outfileURL);
+      }
+      else {
+        $downloads[] = 'Waiting on ' . $label . '...';
+      }
+    }
+    $download_list = theme_item_list(array(
+      'items' => $downloads,
+      'title' => '',
+      'type' => 'ul',
+      'attributes' => array(),
+    ));
+
+    $rows[] = array(
+      'data' => array(
+        $collection->getName(),
+        $collection->getDescription(),
+        $collection->getCreateDate(),
+        $download_list,
+        l('Delete', 'user/' . $user->uid . '/data-collections/' . $collection_id . '/delete'),
+      ),
+    );
+  }
+
+  $content['instructions'] = array(
+    '#type' => 'markup',
+    '#markup' => '<p>' .  t('Data collections allow you to store sets of data
+       for download or later use by other tools on this site.  Typically data
+       collections are created using search tools.  The following data
+       collections are available to you.  Some files take time to generate
+       before they can be downloaded.') . '</p>',
+  );
+  $content['files_table'] = array(
+    '#type' => 'item',
+    '#title' => 'Data Collections',
+    '#markup' => theme_table(array(
+      'header' => $headers,
+      'rows' => $rows,
+      'attributes' => array(),
+      'caption' => '',
+      'colgroups' => array(),
+      'sticky' => TRUE,
+      'empty' => t('You currently have no data collections.')
+    )),
+  );
+
+  return $content;
+}
+
+function tripal_user_collections_delete_form($form, &$form_state,  $uid, $collection_id) {
+  $form_state['collection_id'] = $collection_id;
+  $form['#submit'][] = 'tripal_user_collections_delete_form_submit';
+
+  $collection  = new TripalEntityCollection();
+  $collection->load($collection_id);
+
+  $form = confirm_form($form,
+      t('Click the delete button below to confirm deletion of the collection titled: %title',
+          array('%title' => $collection->getName())), 'user/' . $uid . '/data-collections',
+      '<p>' .t('This action cannot be undone.') .'</p>', t('Delete'), t('Cancel'), 'confirm');
+
+  return $form;
+}
+/**
+ * Deletes a user's collection.
+ *
+ * @param $collection_id
+ *   The ID of the collection to delete.
+ */
+function tripal_user_collections_delete_form_submit($form, &$form_state) {
+  global $user;
+  $collection_id = $form_state['collection_id'];
+  $collection  = new TripalEntityCollection();
+  $collection->load($collection_id);
+
+  if ($collection->getUserID() == $user->uid) {
+    try {
+      $collection->delete();
+      drupal_set_message('The data collection has been deleted.');
+    }
+    catch (Exception $e) {
+      drupal_set_message('There was a problem deleting the data collection please contact the site to report the error.', 'error');
+    }
+  }
+  drupal_goto('user/' . $user->uid . '/data-collections');
+}

+ 1 - 1
tripal/tripal.install

@@ -342,7 +342,7 @@ function tripal_tripal_collection_schema() {
       ),
       'fields' => array(
         'type' => 'text',
-        'size' => normal,
+        'size' => 'normal',
         'not null' => TRUE,
         'description' => 'An array of numeric field IDs.'
       ),

+ 45 - 0
tripal/tripal.module

@@ -331,9 +331,54 @@ function tripal_menu() {
     }
   }
 
+  $items['user/%/data-collections'] = array (
+    'title' => 'Data Collections',
+    'description' => 'Your list of saved data collections',
+    'page callback' => 'tripal_user_collections_page',
+    'access callback' => 'tripal_access_user_data',
+    'access arguments' => array(1),
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'includes/tripal.collections.inc',
+    'file path' => drupal_get_path('module', 'tripal'),
+    'weight' => 10,
+  );
+
+  $items['user/%/data-collections/%/delete'] = array (
+    'title' => 'Delete a Collections',
+    'description' => 'Deletes a data collection.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('tripal_user_collections_delete_form', 1, 3),
+    'access callback' => 'tripal_access_user_data',
+    'access arguments' => array(1),
+    'type' => MENU_CALLBACK,
+    'file' => 'includes/tripal.collections.inc',
+    'file path' => drupal_get_path('module', 'tripal'),
+    'weight' => 10,
+  );
   return $items;
 }
 
+/**
+ * Access callback for accessing a user's Tripal-added private data.
+ *
+ * The User account being accessed must match the ID of the current user. This
+ * function can be used to check access for any type of user-specfic data
+ * added by any Tripal module.
+ *
+ * @param  $uid
+ *   The UID of the user's account to access.
+ * @return boolean
+ */
+function tripal_access_user_data($uid) {
+  global $user;
+
+  drupal_debug($uid);
+  if ($uid == $user->uid) {
+    return TRUE;
+  }
+  return FALSE;
+}
+
 /**
  * Implements hook_permission().
  */

+ 6 - 1
tripal/tripal.views_default.inc

@@ -63,7 +63,7 @@ function tripal_bundle_default_views(&$views) {
     $handler->display->display_options['query']['type'] = 'views_query';
     $handler->display->display_options['exposed_form']['type'] = 'basic';
     $handler->display->display_options['pager']['type'] = 'full';
-    $handler->display->display_options['pager']['options']['items_per_page'] = '25';
+    $handler->display->display_options['pager']['options']['items_per_page'] = '10';
     $handler->display->display_options['pager']['options']['offset'] = '0';
     $handler->display->display_options['pager']['options']['id'] = '0';
     $handler->display->display_options['pager']['options']['quantity'] = '9';
@@ -208,6 +208,11 @@ function tripal_bundle_default_views(&$views) {
       $handler->display->display_options['sorts']['priority']['field'] = 'taxrank__species';
     }
 
+    /* Footer: Global: Tripal Content Data Collections */
+    $handler->display->display_options['footer']['tripal_area_collections']['id'] = 'tripal_area_collections';
+    $handler->display->display_options['footer']['tripal_area_collections']['table'] = 'views';
+    $handler->display->display_options['footer']['tripal_area_collections']['field'] = 'tripal_area_collections';
+
     // No results behavior: Global: Text area.
     $handler->display->display_options['empty']['text']['id'] = 'text';
     $handler->display->display_options['empty']['text']['table'] = 'views';

+ 47 - 17
tripal/views_handlers/tripal_views_handler_area_collections.inc

@@ -48,9 +48,17 @@ function tripal_views_handler_area_collections_form($form, $form_state, $view, $
      save data.  You can place your search results into a data collection for
      download or use with other tools on this site that support data collections.'),
   );
-  $form['save_collection']['bundle_name'] = array(
+  $form['save_collection']['bundle'] = array(
     '#type' => 'value',
-    '#value' => $bundle->name,
+    '#value' => $bundle,
+  );
+  $form['save_collection']['view'] = array(
+    '#type' => 'value',
+    '#value' => unserialize(serialize($view))
+  );
+  $form['save_collection']['query'] = array(
+    '#type' => 'value',
+    '#value' => unserialize(serialize($query->query))
   );
   $form['save_collection']['summary'] = array(
    '#type' => 'item',
@@ -72,48 +80,70 @@ function tripal_views_handler_area_collections_form($form, $form_state, $view, $
   );
 
   // Get the list of fields in this view.
-  $fields = $view->field;
-  $field_ids = array();
-  foreach ($fields as $field_name => $handler) {
-    if ($field_name == 'entity_id') {
+  $fields = field_info_instances('TripalEntity', $bundle->name);
+  foreach ($fields as $field_name => $instance) {
+    if ($instance['field_name'] == 'entity_id') {
       continue;
     }
-    $field = field_info_field($field_name);
-    $instance = field_info_instance('TripalEntity', $field_name, $bundle->name);
-    $field_ids[$field['id']] = $instance['label'];
+    $field_ids[$instance['field_id']] = $instance['label'];
   }
   $form['save_collection']['field_ids'] = array(
     '#type' => 'checkboxes',
     '#title' => t('Field Selection'),
-    '#description' => t('Please select the fields to include in this data collection. If you do not select any fields then all fields will be included.'),
+    '#description' => t('Please select the fields to include in this data
+      collection. Not all of these fields will appear in the search results
+      above but they are available for this content type. Also, not all will
+      be compatible with every download file format. If you do not select any
+      fields then all fields will be included.'),
     '#options' => $field_ids,
   );
 
   $form['save_collection']['button'] = array(
     '#type' => 'submit',
     '#value' => 'Save Data Collection',
-    '#name' => 'save_collection'
+    '#name' => 'save_collection',
+    '#ajax' => array(
+      'callback' => "tripal_views_handler_area_collections_form_ajax",
+      'wrapper' => 'tripal-views-handler-area-collections',
+      'effect'   => 'fade',
+      'method'   => 'replace',
+      'prevent'  => 'click'
+    ),
   );
-
+  $form['#prefix'] = '<div id="tripal-views-handler-area-collections">';
+  $form['#suffix'] = '</div>';
   return $form;
 }
 
-
+/**
+ *
+ */
+function tripal_views_handler_area_collections_form_ajax($form, $form_state) {
+  return $form;
+}
 /**
  *
  */
 function tripal_views_handler_area_collections_form_submit($form, $form_state) {
   global $user;
 
-  $bundle_name = $form_state['values']['bundle_name'];
+  $bundle = $form_state['values']['bundle'];
+  $view = $form_state['values']['view'];
+  $query = $form_state['values']['query'];
   $collection_name = $form_state['values']['collection_name'];
   $description = $form_state['values']['collection_desc'];
-  $field_ids = $form_state['values']['field_ids'];
+  $field_ids = array_key_exists('field_ids', $form_state['values']) ? $form_state['values']['field_ids'] : array();
   $uid = $user->uid;
+  $bundle_name = $bundle->name;
 
+  // Get the entity Ids that match results
+  $query->range['length'] = $view->total_rows;
+  $results = $query->execute();
   $entities = array();
-
-  tripal_create_collection(array(
+  foreach ($results['TripalEntity'] as $entity) {
+    $entities[] = $entity->id;
+  }
+  $collection = tripal_create_collection(array(
     'uid'  => $uid,
     'collection_name' => $collection_name,
     'bundle_name' => $bundle_name,

+ 7 - 0
tripal_chado/includes/TripalFields/data__protein_sequence/data__protein_sequence.inc

@@ -35,6 +35,13 @@ class data__protein_sequence extends ChadoField {
     // type. This will create form elements when editing the field instance
     // to allow the site admin to change the term settings above.
     'term_fixed' => FALSE,
+    // Indicates the download formats for this field.  The list must be the
+    // name of a child class of the TripalFieldDownloader.
+    'download_formatters' => array(
+      'TripalTabDownloader',
+      'TripalCSVDownloader',
+      'TripalProteinFASTADownloader',
+    ),
   );
 
   // The default widget for this field.

+ 7 - 0
tripal_chado/includes/TripalFields/data__sequence/data__sequence.inc

@@ -35,6 +35,13 @@ class data__sequence extends ChadoField {
     // type. This will create form elements when editing the field instance
     // to allow the site admin to change the term settings above.
     'term_fixed' => FALSE,
+    // Indicates the download formats for this field.  The list must be the
+    // name of a child class of the TripalFieldDownloader.
+    'download_formatters' => array(
+      'TripalTabDownloader',
+      'TripalCSVDownloader',
+      'TripalNucFASTADownloader',
+    ),
   );
 
   // The default widget for this field.