myexperiment-hackers
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[myexperiment-hackers] [2855] branches/datasets: Made Data Sets a contri


From: noreply
Subject: [myexperiment-hackers] [2855] branches/datasets: Made Data Sets a contributable.
Date: Thu, 1 Dec 2011 07:56:07 -0500 (EST)

Revision
2855
Author
fbacall
Date
2011-12-01 07:56:06 -0500 (Thu, 01 Dec 2011)

Log Message

Made Data Sets a contributable. Allowed them to have policies and be downloaded in packs

Modified Paths

Added Paths

Diff

Modified: branches/datasets/app/controllers/data_sets_controller.rb (2854 => 2855)


--- branches/datasets/app/controllers/data_sets_controller.rb	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/app/controllers/data_sets_controller.rb	2011-12-01 12:56:06 UTC (rev 2855)
@@ -5,16 +5,17 @@
 
 class DataSetsController < ApplicationController
 
+  before_filter :fetch_data_set, :except => [:create, :new, :index]
   before_filter :fetch_workflow
-  before_filter :fetch_data_set, :except => [:create, :new, :index]
   before_filter :auth
-  before_filter :fetch_data_sets, : [:index, :show]
+  before_filter :fetch_and_auth_data_sets, : [:index, :show]
+  before_filter :set_sharing_mode_variables, : [:new, :create, :edit, :update]
 
   def create
     @data_set = @workflow.data_sets.build(params[:data_set])
     @data_set.contributor = current_user
 
-    if @data_set.save
+    if @data_set.save && updated_policy?
       respond_to do |format|
         format.html { redirect_to workflow_data_set_url(@workflow, @data_set) }
       end
@@ -26,7 +27,7 @@
   end
 
   def update
-    if @data_set.update_attributes(params[:data_set])
+    if @data_set.update_attributes(params[:data_set]) && updated_policy?
       respond_to do |format|
         format.html { redirect_to workflow_data_set_url(@workflow, @data_set) }
       end
@@ -82,29 +83,76 @@
   private
 
   def fetch_workflow
-    @workflow = Workflow.find(params[:workflow_id])
+    @workflow = @data_set ? @data_set.workflow : Workflow.find(params[:workflow_id])
   end
 
   def fetch_data_set
     @data_set = DataSet.find(params[:id])
   end
 
-  def fetch_data_sets
-    @data_sets = @workflow.data_sets
+  def fetch_and_auth_data_sets
+    @data_sets = (@workflow.data_sets.select { |d| Authorization.is_authorized?("show", nil, d, current_user)})
   end
 
+  # You can view data sets if you can download the workflow
+  # You can create new data sets if you can edit the workflow
   def auth
-    unless Authorization.is_authorized?(action_name, nil, @workflow, current_user)
+    puts action_name
+    if action_name == "index"
+      unless Authorization.is_authorized?("download", nil, @workflow, current_user)
+        flash[:error] = "You are not authorized to this workflow's data sets."
 
-      action = ""
-      action = "" if action_name == "show"
-      action = "" == "new" ? "create new data sets for this workflow" : "#{action} this data set")
+        respond_to do |format|
+          format.html { redirect_to workflows_url }
+        end
+      end
+    elsif action_name == "new" || action_name == "create"
+      unless Authorization.is_authorized?("edit", nil, @workflow, current_user)
+        flash[:error] = "You are not authorized to create new data sets for this workflow."
 
-      flash[:error] = "You are not authorized to #{action}."
+        respond_to do |format|
+          format.html { redirect_to workflow_data_sets_url(@workflow) }
+        end
+      end
+    elsif action_name == "show"
+      unless Authorization.is_authorized?("show", nil, @data_set, current_user)
+        flash[:error] = "You are not authorized to view this data set."
 
-      respond_to do |format|
-        format.html { redirect_to workflow_url(@workflow) }
+        respond_to do |format|
+          format.html { redirect_to workflow_data_sets_url(@workflow) }
+        end
       end
+    else
+      unless Authorization.is_authorized?(action_name, nil, @data_set, current_user)
+        flash[:error] = "You are not authorized to #{action_name} this data set."
+
+        respond_to do |format|
+          format.html { redirect_to workflow_data_set_url(@workflow, @data_set) }
+        end
+      end
     end
   end
+
+  def updated_policy?
+    policy_err_msg = update_policy(@data_set, params)
+    if !policy_err_msg.blank?
+      @data_set.errors.add_to_base(policy_err_msg)
+      return false
+    end
+    true
+  end
+
+  def set_sharing_mode_variables
+    case action_name
+      when "new"
+        @sharing_mode  = 0
+        @updating_mode = 6
+      when "create", "update"
+        @sharing_mode  = params[:sharing][:class_id].to_i if params[:sharing]
+        @updating_mode = params[:updating][:class_id].to_i if params[:updating]
+      when "show", "edit"
+        @sharing_mode  = @data_set.contribution.policy.share_mode
+        @updating_mode = @data_set.contribution.policy.update_mode
+    end
+  end
 end

Modified: branches/datasets/app/helpers/application_helper.rb (2854 => 2855)


--- branches/datasets/app/helpers/application_helper.rb	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/app/helpers/application_helper.rb	2011-12-01 12:56:06 UTC (rev 2855)
@@ -361,39 +361,16 @@
       return nil
     end
   end
-  
+
   def contributable(contributableid, contributabletype, link=true, thumb=false)
     case contributabletype.to_s
-    when "Blob"
-      if b = Blob.find(:first, :conditions => ["id = ?", contributableid])
-        if (b.title)
-          if (b.title.length > 0)
-            name = h(b.title)
-          else 
-            name = h(b.local_name)
-          end
-        else
-          name = h(b.local_name)
-        end
-        
-        return link ? link_to(name, file_url(b)) : name
-      else
-        return nil
-      end
-    when "Pack"
-      if p = Pack.find(:first, :conditions => ["id = ?", contributableid])
-        return link ? link_to(h(p.title), pack_url(p)) : h(p.title)
-      else
-        return nil
-      end
-    when "Blog"
-      if b = Blog.find(:first, :conditions => ["id = ?", contributableid])
-        name = h(b.title)
-        
-        return link ? link_to(name, blog_url(b)) : name
-      else
-        return nil
-      end
+    when "Blob", "Pack", "Blog", "DataSet"
+      resource = eval(contributabletype).find_by_id(contributableid)
+      name = h(resource.label)
+      contributabletype = "File" if contributabletype == "Blob"
+      # todo: Rails 2: refactor this, or just remove and use polymorphic_path
+      url = "" + "_url(resource)")
+      return link ? link_to(name, url) : name
     when "Workflow"
       if w = Workflow.find(:first, :conditions => ["id = ?", contributableid])
         name = h(w.title)

Modified: branches/datasets/app/models/data_set.rb (2854 => 2855)


--- branches/datasets/app/models/data_set.rb	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/app/models/data_set.rb	2011-12-01 12:56:06 UTC (rev 2855)
@@ -9,6 +9,7 @@
   include ZipInMemory
 
   acts_as_site_entity
+  acts_as_contributable
 
   validates_presence_of :title, :workflow, :contributor
 
@@ -92,11 +93,11 @@
     "    Text: #{stats["input"][:text]}\r\n" +
     "    Files: #{stats["input"][:files]}" +
       (stats["input"][:hidden] > 0 ?
-        " (#{stats["input"][:hidden]} files were omitted due to insufficient privileges)\r\n" : "\r\n") +
+        " (#{stats["input"][:hidden]} files were omitted due to insufficient access privileges)\r\n" : "\r\n") +
     "  Outputs:\r\n" +
     "    Text: #{stats["output"][:text]}\r\n" +
     "    Files: #{stats["output"][:files]}" +
       (stats["output"][:hidden] > 0 ?
-        " (#{stats["output"][:hidden]} files were omitted due to insufficient privileges)\r\n" : "\r\n")
+        " (#{stats["output"][:hidden]} files were omitted due to insufficient access privileges)\r\n" : "\r\n")
   end
 end

Modified: branches/datasets/app/models/pack.rb (2854 => 2855)


--- branches/datasets/app/models/pack.rb	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/app/models/pack.rb	2011-12-01 12:56:06 UTC (rev 2855)
@@ -84,407 +84,487 @@
     # "#{Conf.base_uri}/packs/#{id}/download/pack_#{id}.zip"
     return(Pack.archive_folder + "/" + archive_file(no_timestamp))
   end
-  
-  
-  def create_zip(user, list_images_hash)
-    
-    # VARIABLE DECLARATIONS
-    
-    # item counters
-    self_downloaded_items_cnt = 0
-    self_downloaded_workflow_cnt = 0
-    self_downloaded_file_cnt = 0
-    self_view_only_workflow_cnt = 0
-    self_view_only_file_cnt = 0
-    self_pack_cnt = 0 # pack are always view-only (as download of nested packs is not supported)
-    self_hidden_items_cnt = 0 # items with no viewing permissions
-    self_internal_items_cnt = self.contributable_entries.length 
-    self_external_items_cnt = self.remote_entries.length
-    self_total_items_cnt = self_internal_items_cnt + self_external_items_cnt
-    
-    # pack description containers
-    pack_data_html = ""
-    pack_details_html = ""
-    internal_items_downloaded_html = ""
-    internal_items_viewing_only_html = ""
-    internal_items_packs_html = ""
-    internal_items_hidden_html = ""
-    external_items_html = "" # are always in 'not downloaded' section - as these are just links
-    
-    pack_data_txt = ""
-    internal_items_downloaded_txt = ""
-    internal_items_viewing_only_txt = ""
-    internal_items_packs_txt = ""
-    internal_items_hidden_txt = ""
-    external_items_txt = ""  # are always in 'not downloaded' section - as these are just links
-    
-    
-    # ========= PACK DESCRIPTION ===========
-    #
-    # plain-TEXT
-    # generate pack description data (which will be put into a text file in the archive)
-    pack_data_txt = "********** Snapshot of the Pack: #{self.title} **********\n\n"
-    pack_data_txt += "Downloaded from #{Conf.sitename}\n"
-    pack_data_txt += "Snapshot generated at " + Time.now.strftime("%H:%M:%S on %A, %d %B %Y") + "\n\n\n\n"
-    pack_data_txt += "========== Pack Details ==========\n\n"
-    pack_data_txt += "Title: #{self.title}"
-    pack_data_txt += "\nItems: #{self_total_items_cnt}"
-    pack_data_txt += "\nLocation: #{location_string("pack", self.id)}"
-    pack_data_txt += "\nCreated by: " + uploader_string(self.contributor_type, self.contributor_id, false, false)
-    pack_data_txt += "\nCreated at: " + self.created_at.strftime("%H:%M:%S on %A, %d %B %Y")
-    pack_data_txt += "\nLast updated at: " + self.updated_at.strftime("%H:%M:%S on %A, %d %B %Y")
-    pack_data_txt += "\n\nDescription: " + (self.description.nil? || self.description.empty? ? "none" : "\n\n" + prepare_description(self.description) )
-    #
-    # HTML
-    cgi = CGI.new("html4")
-    pack_details_html = cgi.div("class" => "pack_metadata") do
-      cgi.div("class" => "pack_details") do
-        cgi.h2 {"Pack Details"} +
-        cgi.table() {
-          cgi.tr() { cgi.td{"Title"} + cgi.td{cgi.a(location_string("pack", self.id)){self.title}} } +
-          cgi.tr() { cgi.td{"Items"} + cgi.td{self_total_items_cnt} } +
-          cgi.tr() { cgi.td{"Created by"} + cgi.td{uploader_string(self.contributor_type, self.contributor_id, true, false)} } +
-          cgi.tr() { cgi.td{"Created at"} + cgi.td{self.created_at.strftime("%H:%M:%S on %A, %d %B %Y")} } +
-          cgi.tr() { cgi.td{"Last updated at"} + cgi.td{self.updated_at.strftime("%H:%M:%S on %A, %d %B %Y")} }
-        }
-      end +
-      cgi.div("class" => "pack_description") do
-        cgi.h3 {"Description"} +
-        cgi.p { (self.description_html.nil? || self.description_html.empty? ? "none" : self.description_html ) }
-      end
+
+  # A generic method to get an appropriate filename for the given contributable item
+  def item_filename(item)
+    filename = nil
+    if item.kind_of?(Workflow)
+      filename = item.filename
+    elsif item.kind_of?(Workflow::Version)
+      filename = item.workflow.filename(item.version)
+    elsif item.kind_of?(Blob)
+      filename = item.local_name
+    elsif item.kind_of?(DataSet)
+      filename = item.archive_file_name
+    elsif item.kind_of?(Pack)
+      filename = item.archive_file_path
+    else
+      raise "Don't know how to generate file names for #{item.class.name.pluralize}"
     end
-    
-    
-    # ========= CREATE THE ZIP FILE & WRITE ALL PACK ENTRIES INTO IT ===========
-    # (also collect metadata about all the entries in plain text and HTML formats)
-    
+    filename
+  end
+
+  # A generic method to get the data of the given contributable item
+  def item_data(item, user)
+    data = ""
+    if [Workflow, Workflow::Version, Blob].include?(item.class)
+      data = ""
+    elsif item.kind_of?(DataSet)
+      data = ""
+    else
+      raise "Don't know how to get the contents of #{item.class.name.pluralize}"
+    end
+    data
+  end
+
+  def create_zip(user, list_images_hash)
+    # item statistics, used to generate the html and text metadata files
+    stats = {}
+    stats[:downloaded] = {}
+    stats[:view_only]  = {}
+    stats[:hidden] = []
+
     # check if the temp folder for storing packs exists
     FileUtils.mkdir(Pack.archive_folder) if not File.exists?(Pack.archive_folder)
-    
+
     # check to see if the file exists already, and if it does, delete it
     # (regular _expression_ needed to take care of timestamps)
     FileUtils.rm Dir.glob(archive_file_path(true).gsub(/[\[\]]/, "?")), :force => true
 
     # create the zip file
+
     zipfile = Zip::ZipFile.open(archive_file_path, Zip::ZipFile::CREATE)
-      # now add all pack items to the zip archive
-      item = nil
-    
-      # will keep a list of all filenames that are put into the archive (to delete temp files later)
-      zip_filenames = []
-      
-      # will help to save filesystem calls on checking whether the folder for workflows/files was already created
-      workflows_folder_created = false
-      files_folder_created = false
-           
-      # start with internal items
-      self.contributable_entries.each { |item_entry|
-        # the first thing to do with each item is to check if download is allowed
-        item_contribution = Contribution.find(:first, :conditions => ["contributable_type = ? AND contributable_id = ?", item_entry.contributable_type, item_entry.contributable_id])
-        
-        # check if the item to which this entry points to still exists
-        if item_contribution.nil?
-          self_hidden_items_cnt += 1
-          internal_items_hidden_html += generate_hidden_item_data("html", cgi, item_entry, true)
-          internal_items_hidden_txt  += generate_hidden_item_data("text", nil, item_entry, true)
-          next # skips all further processing and moves on to the next item
+
+    # will keep a list of all filenames that are put into the archive (to delete temp files later)
+    zip_filenames = []
+
+    # Add contributables to the zip file
+    self.contributable_entries.each do |entry|
+      item = entry.contributable
+      item_type = item.class.name
+
+      if entry.contributable_version
+        item = item.find_version(entry.contributable_version)
+      end
+
+      if item_type != "Pack" && Authorization.is_authorized?('download', nil, item, user)
+        stats[:downloaded][item_type] ||= []
+        stats[:downloaded][item_type] << entry
+        zip_filenames << item_filename(item)
+        #zipfile.add_data("#{item_type.underscore.pluralize}/#{item_filename(item)}", item_data(item, user))
+        zipfile.get_output_stream("#{item_type.model_alias.underscore.pluralize}/#{item_filename(item)}") { |stream| stream.write(item_data(item, user))}
+      elsif Authorization.is_authorized?('view', nil, item, user)
+        stats[:view_only][item_type] ||= []
+        stats[:view_only][item_type] << entry
+      else
+        stats[:hidden] << entry
+      end
+    end
+
+    zip_filenames << "_Pack Info.txt"
+    zipfile.get_output_stream("_Pack Info.txt") { |stream| stream.write(text_metadata(stats)) }
+    zip_filenames << "index.html"
+    zipfile.get_output_stream("index.html") { |stream| stream.write(html_metadata(stats)) }
+    zipfile.add("index.css", "./public/stylesheets/pack-snapshot.css")
+    zipfile.mkdir("_images") # LIST BULLET IMAGES
+    zipfile.add("_images/workflow.png", list_images_hash["workflow"])
+    zipfile.add("_images/file.png", list_images_hash["file"])
+    zipfile.add("_images/pack.png", list_images_hash["pack"])
+    zipfile.add("_images/link.png", list_images_hash["link"])
+    zipfile.add("_images/denied.png", list_images_hash["denied"])
+
+   zipfile.close() # finalize the archive file
+
+   # set read permissions on the zip file
+   File.chmod(0644, archive_file_path)
+
+   # remove any temporary files that were created while creating the zip file
+   # (these are created in the same place, where the zip file is stored)
+   zip_filenames.each do |temp_file|
+     FileUtils.rm Dir.glob(Pack.archive_folder + "/" + "#{temp_file}.*"), :force => true # 'force' option makes sure that exceptions are never raised
+   end
+
+
+  end
+
+  def text_metadata(stats)
+    total_items = self.contributable_entries.length + self.remote_entries.length
+    total_downloaded = stats[:downloaded].values.flatten.size
+
+    pack_entries = stats[:view_only]["Pack"] || {}
+
+    # Ignore packs as part of "view only" set, as they're treated differently
+    view_only = {}
+    stats[:view_only].each {|key, value| view_only[key] = value unless key == "Pack"}
+
+    text = "********** Snapshot of the Pack: #{self.title} **********\n\n"
+    text << "Downloaded from #{Conf.sitename}\n"
+    text << "Snapshot generated at " + Time.now.strftime("%H:%M:%S on %A, %d %B %Y") + "\n\n\n\n"
+    text << "========== Pack Details ==========\n\n"
+    text << "Title: #{self.title}"
+    text << "\nItems: #{total_items}"
+    text << "\nLocation: #{location_string("pack", self.id)}"
+    text << "\nCreated by: " + uploader_string(self.contributor_type, self.contributor_id, false, false)
+    text << "\nCreated at: " + self.created_at.strftime("%H:%M:%S on %A, %d %B %Y")
+    text << "\nLast updated at: " + self.updated_at.strftime("%H:%M:%S on %A, %d %B %Y")
+    text << "\n\nDescription: " + (self.description.nil? || self.description.empty? ? "none" : "\n\n" + prepare_description(self.description) )
+    text << "\n\n\n\n========== Summary of Pack Items (#{total_items}) ==========\n\n"
+    text << "Total number of items in the pack: #{total_items}\n\n"
+    text << "Downloaded items: #{total_downloaded}\n"
+    stats[:downloaded].each do |type, entries|
+      text << "     |-  #{type.model_alias.titleize.pluralize}: #{entries.size}\n"
+    end
+    text << "\n"
+    text << "Not downloaded items: #{total_items - total_downloaded}\n"
+    text << "     |- Packs         : #{pack_entries.size} (download of nested packs is not supported)\n"
+    text << "     |- External items: #{self.remote_entries.size}\n"
+
+    text << "     |- Items that can be viewed, but not downloaded: #{view_only.values.flatten.size} (view-only permissions)\n"
+    view_only.each do |type, entries|
+      text << "         |- #{type.model_alias.titleize.pluralize}: #{entries.size}\n"
+    end
+    text << "     |- Items that cannot be viewed : #{stats[:hidden].size}\n\n"
+
+
+    # ====== Summary ======
+    text << "\n\n========== Downloaded Items (#{total_downloaded}) ==========\n\n"
+    if total_downloaded == 0
+      text << "    // No items were downloaded //\n\n"
+    else
+      stats[:downloaded].each do |type, entries|
+        entries.each do |entry|
+          text << entry_text_metadata(type, entry, true)
         end
-        
-        download_allowed = Authorization.is_authorized?('download', nil, item_contribution, user)
-        viewing_allowed = download_allowed ? true : Authorization.is_authorized?('view', nil, item_contribution, user)
-        
-        
-        case item_entry.contributable_type.downcase
-          when "workflow"
-            # if 'contributable_version' in pack entry says 'NULL' - means the latest version that is available;
-            # otherwise choose a version specified by 'contributable_version'
-            item = Workflow.find(item_entry.contributable_id)
-            is_current_version = true
-            if item_entry.contributable_version
-              wf_version = item_entry.contributable_version
-              is_current_version = false if (wf_version != item.versions.length)
-            else
-              wf_version = item.current_version # (and 'is_current_version' already reflects this by default)
+      end
+    end
+
+
+    text << "\n\n========== Not Downloaded Items (#{total_items - total_downloaded}) ==========\n"
+    # Packs
+    text << "\n\n=== Packs within this pack (#{pack_entries.size}) ===\n\n"
+    if pack_entries.size == 0
+      text << "    // None //\n\n"
+    else
+      pack_entries.each do |entry|
+        text << entry_text_metadata("Pack", entry, false)
+      end
+    end
+
+    # External
+    text << "\n\n=== External items (#{self.remote_entries.size}) ===\n\n"
+    if self.remote_entries.size == 0
+      text << "    // None //\n\n"
+    else
+      self.remote_entries.each do |remote_item|
+        text << "+ #{remote_item.title} - #{remote_item.uri}\n"
+        text << "  (alternate link: #{remote_item.alternate_uri.nil? || remote_item.alternate_uri.blank? ? "none" : remote_item.alternate_uri})\n"
+        text << "  | Added to pack by: #{uploader_string("user", remote_item.user_id, false, false)};" +
+                             " added on (#{remote_item.created_at.strftime('%d/%m/%Y @ %H:%M:%S')})\n"
+        text << "  | #{item_comment_string(remote_item, false)}\n\n"
+      end
+    end
+
+    # View only
+    text << "\n\n=== Items that can be viewed, but not downloaded (#{view_only.values.flatten.size}) ===\n\n"
+    if view_only.values.flatten.size == 0
+      text << "    // None //\n\n"
+    else
+      view_only.each do |type, entries|
+        entries.each do |entry|
+          text << entry_text_metadata(type, entry, false)
+        end
+      end
+    end
+
+    # Hidden
+    text << "\n\n=== Items that cannot be viewed (#{stats[:hidden].size}) ===\n\n"
+    if stats[:hidden].size == 0
+      text << "    // None //\n\n"
+    else
+      stats[:hidden].each do |entry|
+        item = entry.contributable
+        text << (item.nil? ?
+                  "- The item this entry points to is not available. It may have been deleted.\n" :
+                  "- You don't have permissions to view this item\n"
+                     )
+        text << "  | Added to pack by: " + uploader_string("user", entry.user_id, false, false) + "; added on (#{entry.created_at.strftime('%d/%m/%Y @ %H:%M:%S')})\n"
+
+      end
+    end
+
+    text
+  end
+
+  def entry_text_metadata(type, entry, downloaded)
+    item = entry.contributable
+    original_item = item
+    if entry.contributable_version
+      item = item.find_version(entry.contributable_version)
+    end
+
+    visible_type = type.model_alias # Turns "Blob" into "File", etc.
+
+    text = "+ #{visible_type.titleize}: #{item.label}"
+    text << " (local copy: #{visible_type.underscore.pluralize}/#{item_filename(item)})" if downloaded
+    text << "\n"
+    text << "  Location: #{location_string(type.downcase, item.id, entry.contributable_version)}\n"
+    text << "  Type: #{original_item.type_display_name}\n" if type == "Workflow"
+    if entry.contributable_version
+      text << "  Originally uploaded by: #{uploader_string(original_item.contributor_type, original_item.contributor_id, false, false)}\n"
+      text << "  Version: #{entry.contributable_version} (created on: #{item.created_at.strftime("%d/%m/%Y")}, last edited on: #{item.updated_at.strftime("%d/%m/%Y")})\n"
+      text << "  Version uploaded by: #{uploader_string(item.contributor_type, item.contributor_id, false, false)}\n"
+    else
+      text << "  Uploaded by: #{uploader_string(item.contributor_type, item.contributor_id, false, false)}\n"
+    end
+    text << "  | Added to pack by: #{uploader_string("user", entry.user_id, false, false)};" +
+                         " added on (#{entry.created_at.strftime('%d/%m/%Y @ %H:%M:%S')})\n"
+    text << "  | #{item_comment_string(entry, false)}\n\n"
+    text
+  end
+
+  def html_metadata(stats)
+    total_items = self.contributable_entries.size + self.remote_entries.size
+    total_downloaded = stats[:downloaded].values.flatten.size
+
+    pack_entries = stats[:view_only]["Pack"] || {}
+
+    # Ignore packs as part of "view only" set, as they're treated differently
+    view_only = {}
+    stats[:view_only].each {|key, value| view_only[key] = value unless key == "Pack"}
+
+
+    cgi = CGI.new("html4")
+    html = cgi.html {
+      cgi.head{
+        cgi.title{"Snapshot of the Pack: #{self.title} (from #{Conf.sitename})"} +
+        cgi.link("href" => "index.css", "media" => "screen", "rel" => "Stylesheet", "type" => "text/css")
+      } +
+      cgi.body{
+        cgi.div("class" => "provider_info") do
+          cgi.h1{"Snapshot of the Pack: #{cgi.a(location_string("pack", self.id)){self.title}}"} +
+          cgi.p{
+            cgi.i{"Downloaded from #{cgi.a(Conf.base_uri){Conf.sitename}} website<br/><br/>" +
+            "Snapshot generated at " + Time.now.strftime("%H:%M:%S on %A, %d %B %Y")}
+          }
+        end +
+        cgi.div("class" => "pack_metadata") do
+          cgi.div("class" => "pack_details") do
+            cgi.h2 {"Pack Details"} +
+            cgi.table() {
+              cgi.tr() { cgi.td{"Title"} + cgi.td{cgi.a(location_string("pack", self.id)){self.title}} } +
+              cgi.tr() { cgi.td{"Items"} + cgi.td{total_items} } +
+              cgi.tr() { cgi.td{"Created by"} + cgi.td{uploader_string(self.contributor_type, self.contributor_id, true, false)} } +
+              cgi.tr() { cgi.td{"Created at"} + cgi.td{self.created_at.strftime("%H:%M:%S on %A, %d %B %Y")} } +
+              cgi.tr() { cgi.td{"Last updated at"} + cgi.td{self.updated_at.strftime("%H:%M:%S on %A, %d %B %Y")} }
+            }
+            end +
+            cgi.div("class" => "pack_description") do
+              cgi.h3 {"Description"} +
+              cgi.p { (self.description_html.nil? || self.description_html.empty? ? "none" : self.description_html ) }
             end
-            
-            # need to check if we have to obtain another (not the latest) version of the workflow
-            # (still have to keep pointer to the latest version, which is in 'item' at the moment -
-            #  in case if the required version will not be found, that will help to display the right 'item missing' message) 
-            required_item_version = (is_current_version ? item : item.find_version(wf_version)) 
-            
-            # 'required_item_version' now points to the right version of workflow -> add its data to ZIP & create description in HTML and text formats;
-            # (just checking that that version was really found - if not, add nothing to archive & display right error message)
-            if required_item_version
-              if download_allowed
-                self_downloaded_items_cnt += 1
-                self_downloaded_workflow_cnt += 1
-                zip_filenames << item.filename(wf_version)
-                
-                unless workflows_folder_created
-                  zipfile.mkdir("workflows")
-                  workflows_folder_created = true
+        end +
+
+        cgi.br +
+        cgi.div("class" => "pack_items_summary") do
+          cgi.h2{"Summary of Pack Items (#{total_items})"} +
+          cgi.ul {
+            cgi.li{cgi.a("#downloaded_items"){"Downloaded items:"} + " #{total_downloaded}" +
+              cgi.table{
+                stats[:downloaded].map do |type, entries|
+                  cgi.tr{ cgi.td{"#{type.model_alias.titleize.pluralize}:"} + cgi.td{entries.size} }
                 end
-                zipfile.get_output_stream( "workflows/" + item.filename(wf_version)) { |stream| stream.write(required_item_version.content_blob.data)}
-                
-                internal_items_downloaded_html += generate_workflow_data("html", cgi, item_entry, item, required_item_version, wf_version, true)
-                internal_items_downloaded_txt  += generate_workflow_data("text", nil, item_entry, item, required_item_version, wf_version, true)
-              elsif viewing_allowed
-                # only viewing is allowed, but can't download - metadata still displayed in full
-                self_view_only_workflow_cnt += 1
-                internal_items_viewing_only_html += generate_workflow_data("html", cgi, item_entry, item, required_item_version, wf_version, false)
-                internal_items_viewing_only_txt  += generate_workflow_data("text", nil, item_entry, item, required_item_version, wf_version, false)
+              }
+            } +
+            cgi.li{cgi.a("#not_downloaded_items"){"Not downloaded items:"} + " #{total_items - total_downloaded}" +
+              cgi.table{
+                cgi.tr{ cgi.td{cgi.a("#packs"){"Packs within this pack:"}} + cgi.td{pack_entries.size} } +
+                cgi.tr{ cgi.td{cgi.a("#external_items"){"External items:"}} + cgi.td{self.remote_entries.size} } +
+                cgi.tr{ cgi.td{cgi.a("#viewing_only_items"){"Items that can be viewed, but not downloaded:"}} +
+                        cgi.td{"#{view_only.values.flatten.size}; (" +
+                               view_only.map do |type, entries|
+                                 "#{entries.size} #{type.model_alias.titleize.downcase.pluralize}"
+                               end.join(",") +
+                               ")"} } +
+                cgi.tr{ cgi.td{cgi.a("#hidden_items"){"Items that cannot be viewed:"}} + cgi.td{stats[:hidden].size} }
+              }
+            }
+          }
+        end +
+        cgi.br +
+
+        # ====== Summary ======
+        cgi.div("class" => "pack_items") do
+
+          cgi.div("class" => "pack_downloaded_items") do
+            cgi.a("name" => "#downloaded_items") +
+            cgi.h2{"Downloaded Items (#{total_downloaded})"} +
+            if total_downloaded == 0
+              none_text_html_message("No items were downloaded")
+            else
+              cgi.ul {
+                stats[:downloaded].map do |type, entries|
+                  entries.map do |entry|
+                    entry_html_metadata(type, entry, cgi, true)
+                  end
+                end.flatten
+              }
+            end
+          end +
+
+          cgi.br +
+
+          # Packs
+          cgi.div("class" => "pack_not_downloaded_items") do
+            cgi.a("name" => "#not_downloaded_items") +
+            cgi.h2{"Not Downloaded Items (#{total_items - total_downloaded})"} +
+
+            cgi.div("class" => "pack_pack_items") do
+              cgi.a("name" => "#packs") +
+              cgi.h3{"Packs within this pack (#{pack_entries.size})"} +
+              if pack_entries.size == 0
+                none_text_html_message("None")
               else
-                # neither download, nor viewing allowed - display 'hidden item' and who/when added it to the pack
-                self_hidden_items_cnt += 1
-                internal_items_hidden_html += generate_hidden_item_data("html", cgi, item_entry)
-                internal_items_hidden_txt  += generate_hidden_item_data("text", nil, item_entry)
+                cgi.ul {
+                  pack_entries.map do |entry|
+                    entry_html_metadata("Pack", entry, cgi, false)
+                  end
+                }
               end
-              
-            # ELSE - version was not found; display error  
-            else
-              if viewing_allowed
-                # only viewing is allowed, but can't download - some metadata still displayed, subject to availability (the WF version is missing!!) 
-                self_view_only_workflow_cnt += 1
-                internal_items_viewing_only_txt += "+ Workflow: #{item.title} ( !! a specific version of this workflow that the pack entry points to was not found !! )\n"
-                internal_items_viewing_only_txt += "  Version: #{wf_version}\n"
-                internal_items_viewing_only_txt += "  #{item_comment_string(item_entry, false)}\n\n"
-                
-                internal_items_viewing_only_html += cgi.li("class" => "denied"){ 
-                  cgi.div("class" => "workflow_item") do
-                    cgi.div("class" => "item_data") do
-                      "<b>Workflow: </b>" + item.title + " (<font style='color: red;'> a specific version of the workflow that the pack enty point to was not found </font>)<br/>" +
-                      "Version: #{wf_version}"
-                    end
+            end +
+
+            # External
+            cgi.div("class" => "pack_external_items") do
+              cgi.a("name" => "#external_items") +
+              cgi.h3{"External items (#{self.remote_entries.size})"} +
+              if self.remote_entries.size == 0
+                none_text_html_message("None")
+              else
+                cgi.ul {
+                  self.remote_entries.map do |entry|
+                    cgi.li("class" => "link"){
+                      cgi.div("class" => "external_item") do
+                        cgi.div("class" => "item_data") do
+                          "#{entry.title} - " + cgi.a(entry.uri){entry.uri} +
+                          "<br/>Alternate link: " +
+                          if entry.alternate_uri.nil? || entry.alternate_uri.blank?
+                            "<span class='none_text'>none</span>"
+                          else
+                            cgi.a(entry.alternate_uri){entry.alternate_uri}
+                          end + "<br/>"
+                        end +
+                        cgi.div("class" => "item_metadata") do
+                          "Added to pack by: " + uploader_string("user", entry.user_id, true, false) +
+                          " (#{entry.created_at.strftime('%d/%m/%Y @ %H:%M:%S')})<br/>" +
+                          item_comment_string(entry, true)
+                        end
+                      end
+                    }
                   end
                 }
+              end
+            end +
+
+            # View only
+            cgi.div("class" => "pack_viewing_only_items") do
+              cgi.a("name" => "#viewing_only_items") +
+              cgi.h3{"Items that can be viewed, but not downloaded (#{view_only.values.flatten.size})"} +
+              if view_only.values.flatten.size == 0
+                none_text_html_message("None")
               else
-                # neither download, nor viewing allowed - display 'hidden item' and who/when added it to the pack
-                self_hidden_items_cnt += 1
-                internal_items_hidden_html += generate_hidden_item_data("html", cgi, item_entry)
-                internal_items_hidden_txt  += generate_hidden_item_data("text", nil, item_entry)
+                cgi.ul {
+                  view_only.map do |type, entries|
+                    entries.map do |entry|
+                      entry_html_metadata(type, entry, cgi, false)
+                    end
+                  end.flatten
+                }
               end
-            end
+            end +
 
-          when "blob"
-            item = Blob.find(item_entry.contributable_id)
-            
-            if download_allowed
-              self_downloaded_items_cnt += 1
-              self_downloaded_file_cnt += 1
-              zip_filenames << item.local_name
-              
-              unless files_folder_created
-                zipfile.mkdir("files")
-                files_folder_created = true
+            # Hidden
+            cgi.div("class" => "pack_hidden_items") do
+              cgi.a("name" => "#hidden_items") +
+              cgi.h3{"Items that cannot be viewed (#{stats[:hidden].size})"} +
+              if stats[:hidden].size == 0
+                none_text_html_message("None")
+              else
+                cgi.ul {
+                  stats[:hidden].map do |entry|
+                    item = entry.contributable
+                    cgi.li("class" => "denied"){
+                      cgi.div("class" => "hidden_item") do
+                        cgi.div("class" => "item_data") do
+                          (item.nil? ?
+                           "<b>The item this entry points to is not available. It may have been deleted.</b>" :
+                           "<b>You don't have permissions to view this item</b><br/>"
+                          )
+                        end +
+                        cgi.div("class" => "item_metadata") do
+                          "Added to pack by: " + uploader_string("user", entry.user_id, true, false) + " (#{entry.created_at.strftime('%d/%m/%Y @ %H:%M:%S')})<br/>"
+                        end
+                      end
+                    }
+                  end
+                }
               end
-              zipfile.get_output_stream("files/" + item.local_name) { |stream| stream.write(ContentBlob.find(item.content_blob_id).data) }
-              
-              internal_items_downloaded_html += generate_file_data("html", cgi, item_entry, item, true)
-              internal_items_downloaded_txt  += generate_file_data("text", nil, item_entry, item, true)
-            elsif viewing_allowed
-              # only viewing is allowed, but can't download - metadata still displayed in full
-              self_view_only_file_cnt += 1
-              internal_items_viewing_only_html += generate_file_data("html", cgi, item_entry, item, false)
-              internal_items_viewing_only_txt  += generate_file_data("text", nil, item_entry, item, false)
-            else
-              # neither download, nor viewing allowed - display 'hidden item' and who/when added it to the pack
-              self_hidden_items_cnt += 1
-              internal_items_hidden_html += generate_hidden_item_data("html", cgi, item_entry)
-              internal_items_hidden_txt  += generate_hidden_item_data("text", nil, item_entry)
             end
-            
-          when "pack"
-            item = Pack.find(item_entry.contributable_id)
-            
-            # download of nested packs is not supported anyway, so just check if viewing is allowed
-            # ('download_allowed' will be passed through, however, to display a separate download link for the pack, if allowed) 
-            if viewing_allowed
-              self_pack_cnt += 1
-              # download_allowed value was not checked before (as for workflows and files), so pass variable here, not particular value
-              internal_items_packs_html += generate_pack_data("html", cgi, item_entry, item, download_allowed)
-              internal_items_packs_txt  += generate_pack_data("text", nil, item_entry, item, download_allowed)
-            else
-              self_hidden_items_cnt += 1
-              internal_items_hidden_html += generate_hidden_item_data("html", cgi, item_entry)
-              internal_items_hidden_txt  += generate_hidden_item_data("text", nil, item_entry)
-            end
-            
+          end
+
         end
-        
-        # finished processing current item, carry on with the next one
       }
-      
-      
-      # continue with external items
-      # NOTE: there are no viewing/download restrictions on any of the external items
-      self.remote_entries.each { |remote_item|
-        external_items_html += generate_external_item_data("html", cgi, remote_item)
-        external_items_txt  += generate_external_item_data("text", nil, remote_item)
-      }
-      
-      
-      # ASSEMBLE ALL PARTS OF TEXT AND HTML DESCRIPTION FILES
-      #
-      # TEXT
-      #
-      # assemble pack data & item data together; also add date & 'downloaded from'
-      pack_data_txt += "\n\n\n\n========== Summary of Pack Items (#{self_total_items_cnt.to_s}) ==========\n\n"
-      pack_data_txt += "Total number of items in the pack: " + self_total_items_cnt.to_s + "\n\n"
-      pack_data_txt += "Downloaded items: #{self_downloaded_items_cnt}\n"
-      pack_data_txt += "     |-  Workflows: #{self_downloaded_workflow_cnt}\n"
-      pack_data_txt += "     |-  Files    : #{self_downloaded_file_cnt}\n\n"
-      pack_data_txt += "Not downloaded items: #{self_total_items_cnt - self_downloaded_items_cnt}\n"
-      pack_data_txt += "     |- Packs         : #{self_pack_cnt} (download of nested packs is not supported)\n"
-      pack_data_txt += "     |- External items: #{self_external_items_cnt}\n"
-      pack_data_txt += "     |- Items that can be viewed, but not downloaded: #{self_view_only_workflow_cnt + self_view_only_file_cnt} (view-only permissions)\n"
-      pack_data_txt += "         |- Workflows : #{self_view_only_workflow_cnt}\n"
-      pack_data_txt += "         |- Files     : #{self_view_only_file_cnt}\n"
-      pack_data_txt += "     |- Items that cannot be viewed : #{self_hidden_items_cnt}\n"
-      
-      pack_data_txt += "\n\n\n========== Downloaded Items (#{self_downloaded_items_cnt}) ==========\n\n" + (self_downloaded_items_cnt == 0 ? "    // No items were downloaded //\n\n" : internal_items_downloaded_txt)
-      pack_data_txt += "\n\n========== Not Downloaded Items (#{self_total_items_cnt - self_downloaded_items_cnt}) ==========\n"
-      pack_data_txt += "\n\n=== Packs within this pack (#{self_pack_cnt}) ===\n\n" + (self_pack_cnt == 0 ? "    // None //\n\n" : internal_items_packs_txt)
-      pack_data_txt += "\n\n=== External items (#{self_external_items_cnt}) ===\n\n" + (self_external_items_cnt == 0 ? "    // None //\n\n" : external_items_txt)
-      pack_data_txt += "\n\n=== Items that can be viewed, but not downloaded (#{self_view_only_workflow_cnt + self_view_only_file_cnt}) ===\n\n" + (internal_items_viewing_only_txt.blank? ? "    // None //\n\n" : internal_items_viewing_only_txt)
-      pack_data_txt += "\n\n=== Items that cannot be viewed (#{self_hidden_items_cnt}) ===\n\n" + (self_hidden_items_cnt == 0 ? "    // None //\n\n" : internal_items_hidden_txt)
-      #
-      # HTML
-      pack_data_html = cgi.html{
-        cgi.head{ 
-          cgi.title{"Snapshot of the Pack: #{self.title} (from #{Conf.sitename})"} +
-          cgi.link("href" => "index.css", "media" => "screen", "rel" => "Stylesheet", "type" => "text/css")
-        } +
-        cgi.body{
-          cgi.div("class" => "provider_info") do
-            cgi.h1{"Snapshot of the Pack: #{cgi.a(location_string("pack", self.id)){self.title}}"} +
-            cgi.p{
-              cgi.i{"Downloaded from #{cgi.a(Conf.base_uri){Conf.sitename}} website<br/><br/>" +
-              "Snapshot generated at " + Time.now.strftime("%H:%M:%S on %A, %d %B %Y")} 
-            }
+    }
+
+    CGI::pretty(html)
+  end
+
+  def entry_html_metadata(type, entry, cgi, downloaded)
+    item = entry.contributable
+    original_item = item
+    if entry.contributable_version
+      item = item.find_version(entry.contributable_version)
+    end
+
+    visible_type = type.model_alias # Turns "Blob" into "File", etc.
+
+    cgi.li("class" => "#{visible_type.downcase}") do
+      cgi.div("class" => "#{visible_type.downcase}_item") do
+        cgi.div("class" => "item_data") do
+          "<b>#{visible_type.titleize}: </b>" +
+          if entry.contributable_version
+            cgi.a(location_string(type.downcase, item.id, entry.contributable_version)){item.label}
+          else
+            cgi.a(location_string(type.downcase, item.id)){item.label}
           end +
-          pack_details_html +
-          
-          cgi.br +
-          cgi.div("class" => "pack_items_summary") do
-            cgi.h2{"Summary of Pack Items (#{self_total_items_cnt})"} +
-            cgi.ul {
-              cgi.li{cgi.a("#downloaded_items"){"Downloaded items:"} + " #{self_downloaded_items_cnt}" +
-                cgi.table{
-                  cgi.tr{ cgi.td{"Workflows:"} + cgi.td{self_downloaded_workflow_cnt} } +
-                  cgi.tr{ cgi.td{"Files:"} + cgi.td{self_downloaded_file_cnt} }
-                }
-              } +
-              cgi.li{cgi.a("#not_downloaded_items"){"Not downloaded items:"} + " #{self_total_items_cnt - self_downloaded_items_cnt}" +
-                cgi.table{
-                  cgi.tr{ cgi.td{cgi.a("#packs"){"Packs within this pack:"}} + cgi.td{self_pack_cnt} } +
-                  cgi.tr{ cgi.td{cgi.a("#external_items"){"External items:"}} + cgi.td{self_external_items_cnt} } +
-                  cgi.tr{ cgi.td{cgi.a("#viewing_only_items"){"Items that can be viewed, but not downloaded:"}} + cgi.td{"#{self_view_only_workflow_cnt + self_view_only_file_cnt}; ( #{self_view_only_workflow_cnt} workflows, #{self_view_only_file_cnt} files )"} } +
-                  cgi.tr{ cgi.td{cgi.a("#hidden_items"){"Items that cannot be viewed:"}} + cgi.td{self_hidden_items_cnt} }
-                }
-              }
-            }
+
+          if type == "Workflow"
+            "Type: <span class='workflow_type'>" + original_item.type_display_name() + "</span><br/>"
+          else
+            ""
           end +
-          cgi.br +
-          cgi.div("class" => "pack_items") do
-            
-            cgi.div("class" => "pack_downloaded_items") do
-              cgi.a("name" => "#downloaded_items") +
-              cgi.h2{"Downloaded Items (#{self_downloaded_items_cnt})"} +
-              (internal_items_downloaded_html.blank? ?
-               none_text_html_message("No items were downloaded") :
-               cgi.ul { internal_items_downloaded_html }
-              )
-            end +
-            
-            cgi.br +
-            cgi.div("class" => "pack_not_downloaded_items") do
-              cgi.a("name" => "#not_downloaded_items") +
-              cgi.h2{"Not Downloaded Items (#{self_total_items_cnt - self_downloaded_items_cnt})"} +
-              
-              cgi.div("class" => "pack_pack_items") do
-                cgi.a("name" => "#packs") +
-                cgi.h3{"Packs within this pack (#{self_pack_cnt})"} +
-                (internal_items_packs_html.blank? ?
-                 none_text_html_message("None") :
-                 cgi.ul{ internal_items_packs_html }
-                )
-              end +
-              
-              cgi.div("class" => "pack_external_items") do
-                cgi.a("name" => "#external_items") +
-                cgi.h3{"External items (#{self_external_items_cnt})"} +
-                (external_items_html.blank? ?
-                 none_text_html_message("None") :
-                 cgi.ul { external_items_html }
-                )
-              end +
-              
-              cgi.div("class" => "pack_viewing_only_items") do
-                cgi.a("name" => "#viewing_only_items") +
-                cgi.h3{"Items that can be viewed, but not downloaded (#{self_view_only_workflow_cnt + self_view_only_file_cnt})"} +
-                (internal_items_viewing_only_html.blank? ?
-                 none_text_html_message("None") : 
-                 cgi.ul{ internal_items_viewing_only_html }
-                )
-              end +
-              
-              cgi.div("class" => "pack_hidden_items") do
-                cgi.a("name" => "#hidden_items") +
-                cgi.h3{"Items that cannot be viewed (#{self_hidden_items_cnt})"} +
-                (internal_items_hidden_html.blank? ?
-                 none_text_html_message("None") :
-                 cgi.ul { internal_items_hidden_html }
-                )
-              end
-            end
-            
-          end
-        }
-      }
-      
-      # STORE DESCRIPTION DATA IN RELEVANT FILES
-      #
-      # TEXT
-      # put pack data into temporary file & add it to archive; then delete
-      info = Tempfile.new("pack_#{self.id}.tmp")
-      info.write(pack_data_txt)
-      info.close()
-      zipfile.add("_Pack Info.txt", info.path)
-      #
-      # HTML
-      index = Tempfile.new("index.html.tmp")
-      index.write(CGI::pretty(pack_data_html))
-      index.close()
-      zipfile.add("index.html", index.path)
-      #
-      # CSS
-      zipfile.add("index.css", "./public/stylesheets/pack-snapshot.css")
-      #
-      # LIST BULLET IMAGES
-      zipfile.mkdir("_images")
-      zipfile.add("_images/workflow.png", list_images_hash["workflow"])
-      zipfile.add("_images/file.png", list_images_hash["file"])
-      zipfile.add("_images/pack.png", list_images_hash["pack"])
-      zipfile.add("_images/link.png", list_images_hash["link"])
-      zipfile.add("_images/denied.png", list_images_hash["denied"])
-      
-   
-   # finalize the archive file
-   zipfile.close()
 
-   # set read permissions on the zip file
-   File.chmod(0644, archive_file_path)
-   
-   # remove any temporary files that were created while creating the zip file
-   # (these are created in the same place, where the zip file is stored)
-   zip_filenames.each do |temp_file|
-     FileUtils.rm Dir.glob(Pack.archive_folder + "/" + "#{temp_file}.*"), :force => true # 'force' option makes sure that exceptions are never raised
-   end
-   
-    
+          if downloaded
+            "&nbsp;&nbsp;&nbsp;[ " + cgi.a("./#{visible_type.underscore.pluralize}/#{item_filename(item)}"){"open local copy"} + " ]<br/>"
+          else
+            "<br/>"
+          end +
+
+          if entry.contributable_version
+            "Originally uploaded by: #{uploader_string(original_item.contributor_type, original_item.contributor_id, true, false)}<br/>" +
+            "Version: #{entry.contributable_version} (created on: #{item.created_at.strftime("%d/%m/%Y")}, last edited on: #{item.updated_at.strftime("%d/%m/%Y")}<br/>"+
+            "Version uploaded by: #{uploader_string(item.contributor_type, item.contributor_id, true, false)}<br/>"
+          else
+            "Uploaded by: #{uploader_string(item.contributor_type, item.contributor_id, true, false)}<br/>"
+          end
+        end +
+        cgi.div("class" => "item_metadata") do
+          "Added to pack by: " + uploader_string("user", entry.user_id, true, false) + " (#{entry.created_at.strftime('%d/%m/%Y @ %H:%M:%S')})<br/>" +
+          item_comment_string(entry, true)
+        end
+      end
+    end
   end
-  
-  
+
+
   # Resolves the link provided... identifies what internal entry type it corresponds to and creates the appropriate entry object (BUT DOES NOT SAVE IT)...
   # - if the link points to something internally on this site it will attempt to find that item and then create a new pack_contributable_entry for it (in the event that it doesn't find the item it will treat the URI as an external one and create a pack_remote_entry).
   # - if the URI is clearly not referring to this site, it will create a pack_remote_entry.
@@ -503,109 +583,84 @@
     errors_here = Pack.new.errors
     type = nil
     entry = nil
-    
     is_remote = false
-    
+
     begin
-      
       uri = URI.parse(link)
-      
-      if uri.absolute?
-        if is_internal_uri?(uri, host_name, host_port)
-          # Attempt to initialise a pack_contributable_entry
-          
-          expr = /^\/(workflows|files|packs)\/(\d+)$/   # e.g: "\workflows\45"
-          if uri.path =~ expr
-            arr = uri.path.scan(expr)
-            c_type, id = arr[0][0], arr[0][1]
-            
-            # Try to find the contributable item being pointed at
-            case c_type.downcase
-            when 'workflows'
-              contributable = Workflow.find(:first, :conditions => ["id = ?", id])
-            when 'files'
-              contributable = Blob.find(:first, :conditions => ["id = ?", id])
-            when 'packs'
-              contributable = Pack.find(:first, :conditions => ["id = ?", id])
-            else
-              contributable = nil
-            end
-            
-            if contributable
-              entry = PackContributableEntry.new
-              entry.contributable = contributable
-              
-              type = 'contributable'
-              
-              # check if the 'contributable' is a pack, then that it's not the same pack,
-              # to which we are trying to add something at the moment
-              if c_type.downcase == 'packs' && contributable.id == self.id
-                errors_here.add_to_base('Cannot add the pack to itself')
-              end
-              
-              # Check if version was specified in the uri
-              unless uri.query.blank?
-                expr2 = /version=(\d+)/
-                if uri.query =~ expr2
-                  entry.contributable_version = uri.query.scan(expr2)[0][0] 
-                end
-              end
-            else
-              errors_here.add_to_base('The item the link points to does not exist.')
-            end
-          else
-            # Treat as a remote entry
-            is_remote = true
+
+      if uri.relative? || (uri.absolute? && is_internal_uri?(uri, host_name, host_port))
+        # Attempt to initialise a pack_contributable_entry
+        contributable = nil
+
+        # Use Rails' routing to figure out the URL
+        resource = ActionController::Routing::Routes.recognize_path(uri.path, :method => :get)
+        model_name = resource[:controller].classify.model_alias
+
+        if Conf.contributable_models.include?(model_name) && resource[:action] == "show"
+          contributable = eval(model_name).find_by_id(resource[:id])
+        else
+          is_remote = true # Treat as a remote entry
+        end
+
+        if contributable && errors_here.empty?
+          entry = PackContributableEntry.new
+          entry.contributable = contributable
+
+          type = 'contributable'
+
+          # check if the 'contributable' is a pack, then that it's not the same pack,
+          # to which we are trying to add something at the moment
+          if contributable == self.id
+            errors_here.add_to_base('Cannot add the pack to itself')
           end
-          
+
+          # Check if version was specified in the uri
+          entry.contributable_version = resource[:version]
         else
-          # Treat as a remote entry
-          is_remote = true
+          errors_here.add_to_base('The item the link points to does not exist.')
         end
       else
-        errors_here.add_to_base('Please provide a valid link.')  
+        is_remote = true # Treat as a remote entry
       end
-      
+
       if is_remote
         entry = PackRemoteEntry.new(:title => "Link", :uri => link)
         type = 'remote'
       end
-      
+
       if entry
         entry.pack = self
         entry.user = current_user
       end
-      
+
     rescue URI::InvalidURIError
       errors_here.add_to_base('Really struggled to parse this link. Please could you check if it is valid.')
     end
-    
+
     return [errors_here, type, entry]
   end
-  
-  
-  
-  # Checks if the uri provided points to something internally to the host site. 
+
+  # Checks if the uri provided points to something internally to the host site.
   # Note: assumes that the host site runs on HTTP.
   def is_internal_uri?(uri, host_name, host_port)
-    return ((uri.scheme == "http") && (uri.host == host_name) && (uri.port.to_s == host_port)) 
+    return ((uri.scheme == "http") && (uri.host == host_name) && (uri.port.to_s == host_port))
   end
-  
-  
+
+
   # strips out all unnecessary html, preserving special characters and new lines
   def prepare_description(description)
     # replace all the new line equivalents in html with \n's
     desc = description.gsub(/<br\/?>/, "\n")
     desc = desc.gsub("<p></p>", "\n")
-    
+
     # strip out all the rest of html tags
     desc = desc.gsub(/<\/?[^>]*>/,  "")
     desc = desc.gsub("&nbsp;", "")
-    
+
     # decode all special character symbols back into text
     return CGI::unescapeHTML(desc)
   end
-  
+
   def contributables
     contributable_entries.map do |e| e.contributable end
   end
@@ -643,22 +698,22 @@
   def statistics_for_rest_api
     APIStatistics.statistics(self)
   end
- 
+
   protected
-  
+
   # produces html string containing the required messaged, enclosed within left-padded P tag, belonging to 'none_text' class
-  # (with a vertical spacing below the message as well) 
+  # (with a vertical spacing below the message as well)
   def none_text_html_message(msg)
     return "<p style='margin-left: 2em; margin-bottom: 1.5em;' class='none_text'>#{msg}</p>"
   end
-  
-  
+
+
   # just a helper method for packing items into a zip:
   # displays '(Uploader: X' string for each item, where X is a link to myExperiment account of that user
   # (link created only when 'html_required' is set to 'true')
   def uploader_string(contributor_type, contributor_id, html_required, show_leading_text=true)
     res = show_leading_text ? "Uploader: " : ""
-    
+
     case contributor_type.downcase
       when "user"
         user = User.find(contributor_id)
@@ -677,12 +732,12 @@
       else
         res += "unknown (contributor_type: #{contributor_type}; contributor_id: #{contributor_id})"
     end
-    
+
     return res
   end
-  
-  
-  # a helper to return the link to a resource based on type, id & version 
+
+
+  # a helper to return the link to a resource based on type, id & version
   # (version is NIL by default - means no version, or the latest one)
   def location_string(type, id, version=nil)
     link = Conf.base_uri
@@ -690,245 +745,30 @@
       when "workflow"; link += "/workflows/"
       when "blob";     link += "/files/"
       when "pack";     link += "/packs/"
+      when "dataset";  link += "/data_sets/"
       else;            return( link += "/home" )
     end
-    
+
     link += id.to_s
-    link += "?version=#{version}" if version
-    
+    link += "/versions/#{version}" if version
+
     return link
   end
-  
-  
+
+
   # a helper to print out comments for some item
   def item_comment_string(item, html_required)
     return "" if item.nil?
-    
+
     if item.comment.nil? || item.comment.blank?
       return "Comment: " + (html_required ? "<span class='none_text'>none</span>" : "none")
     else
       return "Comment: " + (html_required ? "<div class='comment_text'>#{white_list(simple_format(item.comment))}</div>" : ("\n  |   " + item.comment.gsub(/\n/, "\n  |   ")))
     end
   end
-  
-  
+
   # *********************************************************************
-  
-  # A helper method used to generate metadata for all the pack item types;
-  # see below for explanation on the input parameters.
-  def generate_item_metadata(format, cgi, item_entry)
-    item_metadata = ""
-    
-    case format.downcase
-      when "html"
-        item_metadata += cgi.div("class" => "item_metadata") do
-          "Added to pack by: " + uploader_string("user", item_entry.user_id, true, false) + " (#{item_entry.created_at.strftime('%d/%m/%Y @ %H:%M:%S')})<br/>" +
-          item_comment_string(item_entry, true)
-        end
-      when "text"
-        item_metadata += "  | Added to pack by: " + uploader_string("user", item_entry.user_id, false, false) + "; added on (#{item_entry.created_at.strftime('%d/%m/%Y @ %H:%M:%S')})\n"
-        item_metadata += "  | " + item_comment_string(item_entry, false) + "\n\n"
-      else
-        return "ERROR" 
-    end
-    
-    return item_metadata
-  end
-  
-  
-  
-  # Helper methods that follow have the same (or very similar set of input parameters) and are used for the same purpose -
-  # to generate a description of an appropriate type (hence multiple methods - one for each type) of pack items in the
-  # required format.
-  #
-  # Input parameters:
-  # 1) format - "html|text";
-  # 2) cgi - instance of a CGI class set to generate HTML code;
-  # 3) item_entry - entry in 'pack_contributable_entries' or 'pack_remote_entries' table for current item;
-  # 4) item - for contributable entries (workflows, files, packs), this is the actual item object;
-  # 5) required_item_versio - this is the pointer to a required version of the object (used only for versioned contributables - only workflows for now);
-  # 6) wf_version - number representing the required version of the object (used only for versioned contributables - only workflows for now);
-  # 7) download_allowed - boolean parameter specifying whether current user can download this item or not;
-  #
-  # Return value:
-  # a string with text/html description of the item (given that it can/can't be downloaded by current user)
-  
-  
-  # for workflows in the pack
-  def generate_workflow_data(format, cgi, item_entry, item, required_item_version, wf_version, download_allowed)
-    workflow_data = ""
-    workflow_metadata = generate_item_metadata(format, cgi, item_entry)
-    
-    case format.downcase
-      when "html"
-        workflow_data += cgi.li("class" => "workflow"){
-          cgi.div("class" => "workflow_item") do
-            cgi.div("class" => "item_data") do
-              "<b>Workflow: </b>" + cgi.a(location_string(item_entry.contributable_type, item.id, wf_version)){required_item_version.title} + 
-              (download_allowed ? ("&nbsp;&nbsp;&nbsp;[ " + cgi.a("./workflows/" + item.filename(wf_version)){"open local copy"} + " ]") : "") + "<br/>" +
-              "Type: <span class='workflow_type'>" + item.type_display_name() + "</span><br/>" +
-              "Originally uploaded by: " + uploader_string(item.contribution.contributor_type, item.contribution.contributor_id, true, false) + "<br/>" +
-              "Version: #{wf_version} (created on: #{required_item_version.created_at.strftime("%d/%m/%Y")}, last edited on: #{required_item_version.updated_at.strftime("%d/%m/%Y")})<br/>" +
-              "Version uploaded by: " + uploader_string(required_item_version.contributor_type, required_item_version.contributor_id, true, false) + "<br/>"
-            end +
-            workflow_metadata 
-          end
-        }
-        
-      when "text"
-        workflow_data += "+ Workflow: #{item.title}"
-        if download_allowed
-          workflow_data += " (local copy: workflows/#{item.filename(wf_version)})"
-        end
-        workflow_data += "\n  Type: " + item.type_display_name()
-        workflow_data += "\n  Location: " + location_string(item_entry.contributable_type, item.id, wf_version)
-        workflow_data += "\n  Originally uploaded by: " + uploader_string(item.contribution.contributor_type, item.contribution.contributor_id, false, false)
-        workflow_data += "\n  Version: #{wf_version} (created on: #{required_item_version.created_at.strftime("%d/%m/%Y")}, last edited on: #{required_item_version.updated_at.strftime("%d/%m/%Y")})"
-        workflow_data += "\n  Version uploaded by: " + uploader_string(required_item_version.contributor_type, required_item_version.contributor_id, false, false) + "\n"
-        workflow_data += workflow_metadata
-      else
-        return "ERROR"
-    end
-    
-    return workflow_data
-  end
-  
-  
-  # for files (aka 'blobs') in the pack
-  def generate_file_data(format, cgi, item_entry, item, download_allowed)
-    file_data = ""
-    file_metadata = generate_item_metadata(format, cgi, item_entry)
-    
-    case format.downcase
-      when "html"
-        file_data += cgi.li("class" => "file"){ 
-          cgi.div("class" => "file_item") do
-            cgi.div("class" => "item_data") do
-              "<b>File: </b>" + cgi.a(location_string(item_entry.contributable_type, item.id)){item.title} + 
-              (download_allowed ? ("&nbsp;&nbsp;&nbsp;[ " + cgi.a("./files/" + item.local_name){"open local copy"} + " ]") : "") + "<br/>" +
-              uploader_string(item.contributor_type, item.contributor_id, true) + "<br/>"
-            end +
-            file_metadata
-          end
-        }
-      
-      when "text"
-        file_data += "+ File: #{item.title}"
-        if download_allowed
-          file_data += " (local copy: files/#{item.local_name})"
-        end
-        file_data += "\n  Location: " + location_string(item_entry.contributable_type, item.id)
-        file_data += "\n  " + uploader_string(item.contributor_type, item.contributor_id, false) + "\n"
-        file_data += file_metadata
-        
-      else
-        return "ERROR"
-    end
-    
-    return file_data
-  end
 
-
-  # for packs that are contained within the pack
-  def generate_pack_data(format, cgi, item_entry, item, download_allowed)
-    pack_data = ""
-    pack_metadata = generate_item_metadata(format, cgi, item_entry)
-    
-    case format.downcase
-      when "html"
-        pack_data += cgi.li("class" => "pack"){
-          cgi.div("class" => "pack_item") do
-            cgi.div("class" => "item_data") do
-              "<b>Pack: </b>" + cgi.a(location_string(item_entry.contributable_type, item.id)){item.title} + 
-              (download_allowed ? ("&nbsp;&nbsp;&nbsp;[ #{cgi.a(location_string(item_entry.contributable_type, item.id) + '/download'){'click here to download this pack separately'}} ]") : "") + "<br/>" +
-              uploader_string(item.contributor_type, item.contributor_id, true) + "<br/>"
-            end +
-            pack_metadata
-          end
-        }
-      
-      when "text"
-        pack_data += "- Pack: #{item.title}"
-        pack_data += "\n  Location: " + location_string(item_entry.contributable_type, item.id)
-        if download_allowed
-          pack_data += "\n  Download link: " + location_string(item_entry.contributable_type, item.id) + "/download ( --download of nested packs is not supported, however using this link it can be downloaded separately-- )"
-        end
-        pack_data += "\n  " + uploader_string(item.contributor_type, item.contributor_id, false) + "\n"
-        pack_data += pack_metadata
-      
-      else
-        return "ERROR"
-    end
-    
-    return pack_data
-  end
-  
-  
-  # for external pack items (i.e. links that are external to myExperiment) 
-  def generate_external_item_data(format, cgi, item_entry)
-    item_data = ""
-    item_metadata = generate_item_metadata(format, cgi, item_entry)
-    
-    case format.downcase
-      when "html"
-        item_data += cgi.li("class" => "link"){
-          cgi.div("class" => "external_item") do
-            cgi.div("class" => "item_data") do
-              "#{item_entry.title} - " + cgi.a(item_entry.uri){item_entry.uri} +
-              "<br/>Alternate link: " + (item_entry.alternate_uri.nil? || item_entry.alternate_uri.blank? ? "<span class='none_text'>none</span>" : cgi.a(item_entry.alternate_uri){item_entry.alternate_uri}) + "<br/>"
-            end +
-            item_metadata
-          end
-        }
-      
-      when "text"
-        item_data += "+ #{item_entry.title} - #{item_entry.uri}\n"
-        item_data += "  (alternate link: #{item_entry.alternate_uri.nil? || item_entry.alternate_uri.blank? ? "none" : item_entry.alternate_uri})\n"
-        item_data += item_metadata
-      
-      else
-        return "ERROR"
-    end
-  
-    return item_data
-  end
-  
-  
-  # for pack items that don't have neither download, nor view permissions
-  def generate_hidden_item_data(format, cgi, item_entry, item_doesnt_exist=false)
-    item_data = ""
-    
-    case format.downcase
-      when "html"
-        item_data += cgi.li("class" => "denied"){
-          cgi.div("class" => "hidden_item") do
-            cgi.div("class" => "item_data") do
-              (item_doesnt_exist ?
-               "<b>The item this entry points to is not available. It may have been deleted.</b>" :
-               "<b>You don't have permissions to view this item</b><br/>"
-              )
-            end +
-            cgi.div("class" => "item_metadata") do
-              "Added to pack by: " + uploader_string("user", item_entry.user_id, true, false) + " (#{item_entry.created_at.strftime('%d/%m/%Y @ %H:%M:%S')})<br/>"
-            end
-          end
-        }
-        
-      when "text"
-        item_data += (item_doesnt_exist ? 
-                      "- The item this entry points to is not available. It may have been deleted.\n" :
-                      "- You don't have permissions to view this item\n"
-                     )
-        item_data += "  | Added to pack by: " + uploader_string("user", item_entry.user_id, false, false) + "; added on (#{item_entry.created_at.strftime('%d/%m/%Y @ %H:%M:%S')})"
-      
-      else
-        return "ERROR"
-        
-    end
-    
-    return item_data
-  end
-  
   def rank
 
     # initial boost depends on viewings count
@@ -936,10 +776,10 @@
 
     # Take curation events into account
     boost += CurationEvent.curation_score(CurationEvent.find_all_by_object_type_and_object_id('Pack', id))
-    
+
     # penalty for no description
     boost -= 20 if description.nil? || description.empty?
-    
+
     boost
   end
 

Modified: branches/datasets/app/models/user.rb (2854 => 2855)


--- branches/datasets/app/models/user.rb	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/app/models/user.rb	2011-12-01 12:56:06 UTC (rev 2855)
@@ -271,6 +271,13 @@
   acts_as_site_entity
 
   acts_as_contributor
+
+  # todo: fix this hack!
+  # hides contributions that shouldn't show up in various places. (asset manager, user profile)
+  alias_method :all_contributions, :contributions
+  def contributions
+    self.all_contributions.select {|c| c.contributable_type != "DataSet"}
+  end
   
   has_many :blobs, :as => :contributor, :dependent => :destroy
   has_many :blogs, :as => :contributor, :dependent => :destroy

Modified: branches/datasets/app/models/workflow.rb (2854 => 2855)


--- branches/datasets/app/models/workflow.rb	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/app/models/workflow.rb	2011-12-01 12:56:06 UTC (rev 2855)
@@ -69,6 +69,8 @@
 
     validates_presence_of :content_blob
     validates_presence_of :content_type
+
+    acts_as_site_entity :owner_text => 'Version Uploader'
     
     # :dependent => :destroy is not supported in belongs_to in rails 1.2.6
     after_destroy { |wv| wv.content_blob.destroy if wv.content_blob }

Modified: branches/datasets/app/views/data_sets/_form.rhtml (2854 => 2855)


--- branches/datasets/app/views/data_sets/_form.rhtml	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/app/views/data_sets/_form.rhtml	2011-12-01 12:56:06 UTC (rev 2855)
@@ -21,9 +21,13 @@
                  @workflow.versions.reverse.map {|w| ["#{w.version.to_s} address@hidden(w.version)}", w.version.to_s] } %>
   </p>
 
-
   <%= f.hidden_field :category, :value => "example_data" %>
 
+  <p>
+    <%= render :partial => "contributions/sharing_form",  :locals => { :edit => address@hidden, :contributable => @data_set,
+                                                                       :update_perms => true } %>
+  </p>
+
   <%= submit_tag (@data_set.id.nil? ? "Create" : "Update") %>
   or
   <%= link_to "Cancel", (@data_set.id.nil? ? workflow_data_sets_path(@workflow) :

Added: branches/datasets/app/views/data_sets/_table.rhtml (0 => 2855)


--- branches/datasets/app/views/data_sets/_table.rhtml	                        (rev 0)
+++ branches/datasets/app/views/data_sets/_table.rhtml	2011-12-01 12:56:06 UTC (rev 2855)
@@ -0,0 +1,23 @@
+<table class="data_set_list">
+  <tbody>
+  <tr>
+    <th>Title</th><th>Creator</th><th>Date Created</th><th>Workflow Version</th>
+  </tr>
+  <% collection.sort_by(&:workflow_version).reverse_each do |data_set| %>
+    <tr class="data_set <%= "old_version" if data_set.workflow_version < data_set.workflow.current_version-%>">
+      <td style="width: 25em">
+        <%= link_to data_set.title, workflow_data_set_path(data_set.workflow, data_set) %>
+      </td>
+      <td>
+        <%= link_to data_set.contributor.name, user_path(data_set.contributor) %>
+      </td>
+      <td>
+        <%= time_ago_in_words(data_set.created_at) %> ago
+      </td>
+      <td>
+        <%= "#{data_set.workflow_version} #{data_set.workflow.describe_version(data_set.workflow_version)}" %>
+      </td>
+    </tr>
+  <% end %>
+  </tbody>
+</table>
\ No newline at end of file

Modified: branches/datasets/app/views/data_sets/index.rhtml (2854 => 2855)


--- branches/datasets/app/views/data_sets/index.rhtml	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/app/views/data_sets/index.rhtml	2011-12-01 12:56:06 UTC (rev 2855)
@@ -8,27 +8,8 @@
 
 <h1>Data Sets</h1>
 
-<% if @workflow.data_sets.empty? %>
-  <span class="none_text">No data sets provided.</span>
+<% if @data_sets.empty? %>
+  <span class="none_text">Either no data sets have been provided, or you lack sufficient privileges to view them.</span>
 <% else %>
-  <table class="data_set_list">
-  <tbody>
-    <tr>
-      <th>Title</th><th>Date Created</th><th>Workflow Version</th>
-    </tr>
-    <% @workflow.data_sets.sort_by(&:workflow_version).reverse_each do |data_set| %>
-      <tr class="data_set <%= "old_version" if data_set.workflow_version < @workflow.current_version-%>">
-        <td style="width: 25em">
-          <%= link_to data_set.title, workflow_data_set_path(@workflow, data_set) %>
-        </td>
-        <td>
-          <%= time_ago_in_words(data_set.created_at) %> ago
-        </td>
-        <td>
-          <%= "#{data_set.workflow_version} address@hidden(data_set.workflow_version)}" %>
-        </td>
-      </tr>
-    <% end %>
-  </tbody>
-  </table>
+  <%= render :partial => 'table', :locals => {:collection => @data_sets} %>
 <% end %>
\ No newline at end of file

Modified: branches/datasets/app/views/packs/_entry_item.rhtml (2854 => 2855)


--- branches/datasets/app/views/packs/_entry_item.rhtml	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/app/views/packs/_entry_item.rhtml	2011-12-01 12:56:06 UTC (rev 2855)
@@ -5,8 +5,8 @@
 			<% if Authorization.is_authorized?("show", nil, item_entry.contributable, current_user) -%>
 				<p style="text-align: center;">
 					<b>
-						<%= visible_name(item_entry.contributable_type) -%>:
-						<%= link_to_function contributable_name(item_entry.contributable_id, item_entry.contributable_type) + expand_image, visual_effect(:toggle_blind, "contributable_listing", :duration => 0.3) %>
+            <%= visible_name(item_entry.contributable_type.titleize) -%>:
+						<%= link_to_function item_entry.contributable.label + expand_image, visual_effect(:toggle_blind, "contributable_listing", :duration => 0.3) %>
 					</b>
 					<% unless item_entry.contributable_version.blank? %>
 						<b>Version:</b>
@@ -15,7 +15,7 @@
 				</p>
 				<div id="contributable_listing" style="display: <%= show_full ? 'block' : 'none' -%>;">
 					<br/>
-					<%= render :partial => "#{item_entry.contributable_type.pluralize.downcase}/table", :locals => { :collection => [ item_entry.contributable ] } %>
+					<%= render :partial => "#{item_entry.contributable_type.pluralize.underscore}/table", :locals => { :collection => [ item_entry.contributable ] } %>
 				</div>
 			<% else -%>
 				<p class="denied_text" style="text-align: center;">

Modified: branches/datasets/app/views/packs/_items.rhtml (2854 => 2855)


--- branches/datasets/app/views/packs/_items.rhtml	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/app/views/packs/_items.rhtml	2011-12-01 12:56:06 UTC (rev 2855)
@@ -35,7 +35,7 @@
 									
 									<% if show -%>
 									
-										<b><%= visible_name(e.contributable_type) -%>:</b>
+										<b><%= visible_name(e.contributable_type.titleize) -%>:</b>
 										<% unless e.contributable_version.blank? -%>
 											<% # HACK: only workflows are versioned at the moment -%>
 											<%= versioned_workflow_link e.contributable_id, e.contributable_version, false -%>

Modified: branches/datasets/config/environment.rb (2854 => 2855)


--- branches/datasets/config/environment.rb	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/config/environment.rb	2011-12-01 12:56:06 UTC (rev 2855)
@@ -76,6 +76,10 @@
 
 require 'lib/conf'
 
+# Extensions to core ruby/rails functionality
+
+require 'lib/core_extensions'
+
 # SMTP configuration
 
 require 'smtp_tls'

Modified: branches/datasets/config/routes.rb (2854 => 2855)


--- branches/datasets/config/routes.rb	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/config/routes.rb	2011-12-01 12:56:06 UTC (rev 2855)
@@ -128,12 +128,17 @@
     workflow.resources :data_sets, :member => { :download => :get }
   end
 
+  # Data Sets
   #todo: Rails 2. Nest this in the above and use :shallow => true
   map.resources :data_items, :controller => "data_items",
                 :path_prefix => "/data_sets/:data_set_id",
                 :name_prefix => "data_set_"
 
+  # So we can access a data set on its own (without needing workflow id)
+  #  Useful for polymorphic_urls
+  map.data_set 'data_sets/:id', :controller => 'data_sets', :action ="" 'show'
 
+
   # workflow redirect for linked data model
   map.workflow_version           '/workflows/:id/versions/:version',         :conditions => { :method => :get }, :controller => 'workflows', :action ="" 'show'
   map.formatted_workflow_version '/workflows/:id/versions/:version.:format', :conditions => { :method => :get }, :controller => 'workflows', :action ="" 'show'

Modified: branches/datasets/db/schema.rb (2854 => 2855)


--- branches/datasets/db/schema.rb	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/db/schema.rb	2011-12-01 12:56:06 UTC (rev 2855)
@@ -39,17 +39,17 @@
   end
 
   create_table "blobs", :force => true do |t|
+    t.column "local_name",       :string
     t.column "contributor_id",   :integer
-    t.column "contributor_type", :string
-    t.column "local_name",       :string
+    t.column "body_html",        :text
     t.column "created_at",       :datetime
-    t.column "updated_at",       :datetime
+    t.column "body",             :text
     t.column "title",            :string
-    t.column "body",             :text
-    t.column "body_html",        :text
     t.column "content_blob_id",  :integer
+    t.column "updated_at",       :datetime
+    t.column "license_id",       :integer
     t.column "content_type_id",  :integer
-    t.column "license_id",       :integer
+    t.column "contributor_type", :string
   end
 
   create_table "blog_posts", :force => true do |t|
@@ -128,12 +128,12 @@
   end
 
   create_table "concepts", :force => true do |t|
+    t.column "vocabulary_id",    :integer
+    t.column "created_at",       :datetime
+    t.column "description_html", :text
     t.column "updated_at",       :datetime
-    t.column "description_html", :text
     t.column "phrase",           :string
     t.column "description",      :text
-    t.column "vocabulary_id",    :integer
-    t.column "created_at",       :datetime
   end
 
   create_table "content_blobs", :force => true do |t|
@@ -141,34 +141,34 @@
   end
 
   create_table "content_types", :force => true do |t|
+    t.column "created_at",       :datetime
+    t.column "category",         :string
+    t.column "description_html", :text
+    t.column "title",            :string
+    t.column "updated_at",       :datetime
+    t.column "mime_type",        :string
     t.column "user_id",          :integer
-    t.column "title",            :string
     t.column "description",      :text
-    t.column "description_html", :text
-    t.column "mime_type",        :string
-    t.column "created_at",       :datetime
-    t.column "updated_at",       :datetime
-    t.column "category",         :string
   end
 
   create_table "contributions", :force => true do |t|
+    t.column "label",                :string
+    t.column "rating",               :float
+    t.column "contributable_type",   :string
     t.column "contributor_id",       :integer
-    t.column "contributor_type",     :string
-    t.column "contributable_id",     :integer
-    t.column "contributable_type",   :string
+    t.column "layout",               :string
+    t.column "created_at",           :datetime
     t.column "policy_id",            :integer
-    t.column "created_at",           :datetime
     t.column "updated_at",           :datetime
-    t.column "downloads_count",      :integer,  :default => 0
-    t.column "viewings_count",       :integer,  :default => 0
-    t.column "rating",               :float
+    t.column "license_id",           :integer
     t.column "rank",                 :float
     t.column "content_type_id",      :integer
-    t.column "license_id",           :integer
     t.column "site_downloads_count", :integer,  :default => 0
+    t.column "viewings_count",       :integer,  :default => 0
+    t.column "contributor_type",     :string
+    t.column "downloads_count",      :integer,  :default => 0
     t.column "site_viewings_count",  :integer,  :default => 0
-    t.column "label",                :string
-    t.column "layout",               :string
+    t.column "contributable_id",     :integer
   end
 
   add_index "contributions", ["contributable_id", "contributable_type"], :name => "index_contributions_on_contributable_id_and_contributable_type"
@@ -200,20 +200,20 @@
     t.column "created_at",       :datetime
     t.column "title",            :text
     t.column "updated_at",       :datetime
+    t.column "description",      :text
     t.column "workflow_id",      :integer
-    t.column "description",      :text
     t.column "workflow_version", :integer
     t.column "contributor_id",   :integer
     t.column "contributor_type", :string
   end
 
   create_table "downloads", :force => true do |t|
-    t.column "contribution_id",    :integer
+    t.column "kind",               :string
+    t.column "created_at",         :datetime
+    t.column "accessed_from_site", :boolean,  :default => false
     t.column "user_id",            :integer
-    t.column "created_at",         :datetime
     t.column "user_agent",         :string
-    t.column "accessed_from_site", :boolean,  :default => false
-    t.column "kind",               :string
+    t.column "contribution_id",    :integer
   end
 
   add_index "downloads", ["contribution_id"], :name => "index_downloads_on_contribution_id"
@@ -286,11 +286,11 @@
   end
 
   create_table "labels", :force => true do |t|
-    t.column "concept_id",    :integer
+    t.column "vocabulary_id", :integer
     t.column "language",      :string
     t.column "text",          :string
-    t.column "vocabulary_id", :integer
     t.column "label_type",    :string
+    t.column "concept_id",    :integer
   end
 
   create_table "license_attributes", :force => true do |t|
@@ -385,46 +385,46 @@
   add_index "oauth_tokens", ["token"], :name => "index_oauth_tokens_on_token", :unique => true
 
   create_table "ontologies", :force => true do |t|
+    t.column "created_at",       :datetime
+    t.column "description_html", :text
+    t.column "uri",              :string
     t.column "prefix",           :string
+    t.column "title",            :string
     t.column "updated_at",       :datetime
-    t.column "uri",              :string
-    t.column "title",            :string
-    t.column "description_html", :text
+    t.column "user_id",          :integer
     t.column "description",      :text
-    t.column "user_id",          :integer
-    t.column "created_at",       :datetime
   end
 
   create_table "pack_contributable_entries", :force => true do |t|
-    t.column "pack_id",               :integer,  :null => false
-    t.column "contributable_id",      :integer,  :null => false
+    t.column "comment",               :text
+    t.column "contributable_type",    :string
     t.column "contributable_version", :integer
-    t.column "contributable_type",    :string
-    t.column "comment",               :text
-    t.column "user_id",               :integer,  :null => false
     t.column "created_at",            :datetime
     t.column "updated_at",            :datetime
+    t.column "pack_id",               :integer,  :null => false
+    t.column "user_id",               :integer,  :null => false
+    t.column "contributable_id",      :integer,  :null => false
   end
 
   create_table "pack_remote_entries", :force => true do |t|
-    t.column "pack_id",       :integer,  :null => false
-    t.column "title",         :string
-    t.column "uri",           :string
-    t.column "alternate_uri", :string
     t.column "comment",       :text
-    t.column "user_id",       :integer,  :null => false
     t.column "created_at",    :datetime
+    t.column "uri",           :string
+    t.column "title",         :string
     t.column "updated_at",    :datetime
+    t.column "pack_id",       :integer,  :null => false
+    t.column "user_id",       :integer,  :null => false
+    t.column "alternate_uri", :string
   end
 
   create_table "packs", :force => true do |t|
     t.column "contributor_id",   :integer
-    t.column "contributor_type", :string
+    t.column "created_at",       :datetime
+    t.column "description_html", :text
     t.column "title",            :string
+    t.column "updated_at",       :datetime
     t.column "description",      :text
-    t.column "description_html", :text
-    t.column "created_at",       :datetime
-    t.column "updated_at",       :datetime
+    t.column "contributor_type", :string
   end
 
   create_table "pending_invitations", :force => true do |t|
@@ -462,32 +462,32 @@
   end
 
   create_table "policies", :force => true do |t|
+    t.column "name",             :string
     t.column "contributor_id",   :integer
-    t.column "contributor_type", :string
-    t.column "name",             :string
     t.column "created_at",       :datetime
     t.column "updated_at",       :datetime
+    t.column "update_mode",      :integer
     t.column "share_mode",       :integer
-    t.column "update_mode",      :integer
+    t.column "contributor_type", :string
+    t.column "public_view",      :boolean,  :default => false
     t.column "public_download",  :boolean,  :default => false
-    t.column "public_view",      :boolean,  :default => false
   end
 
   create_table "predicates", :force => true do |t|
+    t.column "created_at",       :datetime
+    t.column "description_html", :text
+    t.column "title",            :string
+    t.column "ontology_id",      :integer
     t.column "updated_at",       :datetime
-    t.column "title",            :string
-    t.column "description_html", :text
     t.column "phrase",           :string
-    t.column "ontology_id",      :integer
+    t.column "equivalent_to",    :text
     t.column "description",      :text
-    t.column "equivalent_to",    :text
-    t.column "created_at",       :datetime
   end
 
   create_table "previews", :force => true do |t|
+    t.column "created_at",    :datetime
     t.column "svg_blob_id",   :integer
     t.column "image_blob_id", :integer
-    t.column "created_at",    :datetime
   end
 
   create_table "profiles", :force => true do |t|
@@ -521,15 +521,15 @@
   add_index "ratings", ["user_id"], :name => "index_ratings_on_user_id"
 
   create_table "relationships", :force => true do |t|
+    t.column "context_id",   :integer
+    t.column "created_at",   :datetime
+    t.column "context_type", :string
     t.column "objekt_type",  :string
     t.column "objekt_id",    :integer
+    t.column "subject_id",   :integer
+    t.column "predicate_id", :integer
     t.column "subject_type", :string
-    t.column "subject_id",   :integer
     t.column "user_id",      :integer
-    t.column "created_at",   :datetime
-    t.column "context_id",   :integer
-    t.column "predicate_id", :integer
-    t.column "context_type", :string
   end
 
   create_table "remote_workflows", :force => true do |t|
@@ -552,84 +552,84 @@
   add_index "reviews", ["user_id"], :name => "index_reviews_on_user_id"
 
   create_table "service_categories", :force => true do |t|
+    t.column "label",        :string
     t.column "uri",          :string
+    t.column "retrieved_at", :datetime
+    t.column "created_at",   :datetime
     t.column "updated_at",   :datetime
     t.column "service_id",   :integer
-    t.column "label",        :string
-    t.column "retrieved_at", :datetime
-    t.column "created_at",   :datetime
   end
 
   create_table "service_deployments", :force => true do |t|
+    t.column "service_provider_id",  :integer
     t.column "iso3166_country_code", :string
     t.column "city",                 :string
     t.column "submitter_label",      :string
     t.column "uri",                  :string
-    t.column "updated_at",           :datetime
+    t.column "retrieved_at",         :datetime
+    t.column "created_at",           :datetime
     t.column "submitter_uri",        :string
     t.column "country",              :string
+    t.column "updated_at",           :datetime
     t.column "service_id",           :integer
+    t.column "flag_url",             :string
     t.column "created",              :datetime
-    t.column "service_provider_id",  :integer
-    t.column "flag_url",             :string
     t.column "endpoint",             :string
-    t.column "retrieved_at",         :datetime
-    t.column "created_at",           :datetime
   end
 
   create_table "service_providers", :force => true do |t|
     t.column "name",         :string
     t.column "uri",          :string
+    t.column "retrieved_at", :datetime
+    t.column "created_at",   :datetime
     t.column "updated_at",   :datetime
     t.column "description",  :text
     t.column "created",      :datetime
-    t.column "retrieved_at", :datetime
-    t.column "created_at",   :datetime
   end
 
   create_table "service_tags", :force => true do |t|
+    t.column "label",        :string
     t.column "uri",          :string
+    t.column "retrieved_at", :datetime
+    t.column "created_at",   :datetime
     t.column "updated_at",   :datetime
     t.column "service_id",   :integer
-    t.column "label",        :string
-    t.column "retrieved_at", :datetime
-    t.column "created_at",   :datetime
   end
 
   create_table "service_types", :force => true do |t|
-    t.column "updated_at",   :datetime
-    t.column "service_id",   :integer
     t.column "label",        :string
     t.column "retrieved_at", :datetime
     t.column "created_at",   :datetime
+    t.column "updated_at",   :datetime
+    t.column "service_id",   :integer
   end
 
   create_table "services", :force => true do |t|
-    t.column "documentation_uri",        :string
     t.column "iso3166_country_code",     :string
     t.column "city",                     :string
     t.column "name",                     :string
-    t.column "provider_uri",             :string
+    t.column "contributor_id",           :integer
     t.column "submitter_label",          :string
     t.column "uri",                      :string
-    t.column "updated_at",               :datetime
+    t.column "retrieved_at",             :datetime
+    t.column "created_at",               :datetime
     t.column "monitor_symbol_url",       :string
-    t.column "monitor_last_checked",     :datetime
-    t.column "monitor_label",            :string
     t.column "country",                  :string
     t.column "submitter_uri",            :string
+    t.column "updated_at",               :datetime
+    t.column "monitor_message",          :text
+    t.column "monitor_label",            :string
+    t.column "monitor_last_checked",     :datetime
     t.column "monitor_small_symbol_url", :string
-    t.column "monitor_message",          :text
     t.column "description",              :text
+    t.column "flag_url",                 :string
     t.column "wsdl",                     :string
-    t.column "created",                  :datetime
+    t.column "provider_label",           :string
     t.column "contributor_type",         :string
-    t.column "contributor_id",           :integer
-    t.column "flag_url",                 :string
+    t.column "documentation_uri",        :string
     t.column "endpoint",                 :string
-    t.column "provider_label",           :string
-    t.column "retrieved_at",             :datetime
-    t.column "created_at",               :datetime
+    t.column "provider_uri",             :string
+    t.column "created",                  :datetime
   end
 
   create_table "sessions", :force => true do |t|
@@ -685,10 +685,10 @@
   end
 
   create_table "topic_feedbacks", :force => true do |t|
+    t.column "submit_dt", :datetime
+    t.column "user_id",   :integer
     t.column "score",     :integer
     t.column "topic_id",  :integer
-    t.column "submit_dt", :datetime
-    t.column "user_id",   :integer
   end
 
   create_table "topic_runs", :force => true do |t|
@@ -697,16 +697,16 @@
   end
 
   create_table "topic_tag_map", :force => true do |t|
-    t.column "topic_id",     :integer
     t.column "display_flag", :boolean
     t.column "tag_id",       :integer
+    t.column "topic_id",     :integer
     t.column "probability",  :float
   end
 
   create_table "topic_workflow_map", :force => true do |t|
-    t.column "topic_id",     :integer
     t.column "display_flag", :boolean
     t.column "workflow_id",  :integer
+    t.column "topic_id",     :integer
     t.column "probability",  :float
   end
 
@@ -717,12 +717,12 @@
   end
 
   create_table "user_reports", :force => true do |t|
+    t.column "report",       :text
+    t.column "created_at",   :datetime
+    t.column "subject_id",   :integer
     t.column "subject_type", :string
+    t.column "user_id",      :integer
     t.column "content",      :text
-    t.column "subject_id",   :integer
-    t.column "user_id",      :integer
-    t.column "report",       :text
-    t.column "created_at",   :datetime
   end
 
   create_table "users", :force => true do |t|
@@ -758,14 +758,14 @@
   add_index "viewings", ["contribution_id"], :name => "index_viewings_on_contribution_id"
 
   create_table "vocabularies", :force => true do |t|
+    t.column "created_at",       :datetime
+    t.column "description_html", :text
+    t.column "uri",              :string
+    t.column "prefix",           :string
+    t.column "title",            :string
+    t.column "updated_at",       :datetime
     t.column "user_id",          :integer
-    t.column "title",            :string
     t.column "description",      :text
-    t.column "description_html", :text
-    t.column "created_at",       :datetime
-    t.column "updated_at",       :datetime
-    t.column "prefix",           :string
-    t.column "uri",              :string
   end
 
   create_table "workflow_ports", :force => true do |t|
@@ -778,52 +778,52 @@
   create_table "workflow_processors", :force => true do |t|
     t.column "name",           :string
     t.column "wsdl_operation", :string
+    t.column "workflow_id",    :integer
     t.column "wsdl",           :string
-    t.column "workflow_id",    :integer
   end
 
   create_table "workflow_versions", :force => true do |t|
-    t.column "workflow_id",       :integer
-    t.column "version",           :integer
     t.column "contributor_id",    :integer
-    t.column "contributor_type",  :string
+    t.column "revision_comments", :text
+    t.column "created_at",        :datetime
+    t.column "body_html",         :text
+    t.column "body",              :text
     t.column "title",             :string
-    t.column "unique_name",       :string
-    t.column "body",              :text
-    t.column "body_html",         :text
-    t.column "created_at",        :datetime
+    t.column "content_blob_id",   :integer
+    t.column "license",           :string
     t.column "updated_at",        :datetime
-    t.column "image",             :string
+    t.column "last_edited_by",    :string
     t.column "svg",               :string
-    t.column "revision_comments", :text
-    t.column "content_blob_id",   :integer
-    t.column "file_ext",          :string
-    t.column "last_edited_by",    :string
+    t.column "unique_name",       :string
     t.column "content_type_id",   :integer
-    t.column "license",           :string
+    t.column "version",           :integer
+    t.column "workflow_id",       :integer
+    t.column "contributor_type",  :string
     t.column "preview_id",        :integer
+    t.column "image",             :string
+    t.column "file_ext",          :string
   end
 
   add_index "workflow_versions", ["workflow_id"], :name => "index_workflow_versions_on_workflow_id"
 
   create_table "workflows", :force => true do |t|
     t.column "contributor_id",   :integer
-    t.column "contributor_type", :string
-    t.column "image",            :string
-    t.column "svg",              :string
+    t.column "created_at",       :datetime
+    t.column "body_html",        :text
+    t.column "body",             :text
     t.column "title",            :string
-    t.column "unique_name",      :string
-    t.column "body",             :text
-    t.column "body_html",        :text
-    t.column "created_at",       :datetime
+    t.column "content_blob_id",  :integer
     t.column "updated_at",       :datetime
-    t.column "current_version",  :integer
-    t.column "content_blob_id",  :integer
-    t.column "file_ext",         :string
     t.column "last_edited_by",   :string
+    t.column "svg",              :string
+    t.column "license_id",       :integer
+    t.column "unique_name",      :string
     t.column "content_type_id",  :integer
-    t.column "license_id",       :integer
+    t.column "current_version",  :integer
+    t.column "contributor_type", :string
     t.column "preview_id",       :integer
+    t.column "image",            :string
+    t.column "file_ext",         :string
   end
 
 end

Modified: branches/datasets/lib/authorization.rb (2854 => 2855)


--- branches/datasets/lib/authorization.rb	2011-11-29 14:14:37 UTC (rev 2854)
+++ branches/datasets/lib/authorization.rb	2011-12-01 12:56:06 UTC (rev 2855)
@@ -322,7 +322,7 @@
     #
     # this is required to get "policy_id" for policy-based aurhorized objects (like workflows / blobs / packs / contributions)
     # and to get objects themself for other object types (networks, experiments, jobs, tavernaenactors, runners)
-    if (thing_contribution.nil? && ["Workflow", "Blog", "Blob", "Pack", "Ontology", "Contribution"].include?(thing_type)) || 
+    if (thing_contribution.nil? && (Conf.contributable_models + ["Ontology", "Contribution"]).include?(thing_type)) ||
        (thing_instance.nil? && ["Network", "Comment", "Bookmark", "Experiment", "Job", "TavernaEnactor", "Runner", "Picture", "ClientApplication", "Predicate", "Relationship"].include?(thing_type))
       
       found_thing = find_thing(thing_type, thing_id)
@@ -332,7 +332,7 @@
         logger.error("UNEXPECTED ERROR - Couldn't find object to be authorized:(#{thing_type}, #{thing_id}); action: #{action_name}; user: #{user_id}")
         return false
       else
-        if ["Workflow", "Blog", "Blob", "Pack", "Contribution"].include?(thing_type)
+        if (["Contribution"] + Conf.contributable_models).include?(thing_type)
           # "contribution" are only found for these three types of object (and the contributions themself),
           # for all the rest - use instances
           thing_contribution = found_thing
@@ -348,7 +348,7 @@
     is_authorized = false
     
     case thing_type
-      when "Workflow", "Blog", "Blob", "Pack", "Contribution"
+      when "Contribution", *Conf.contributable_models
         unless user_id.nil?
           # access is authorized and no further checks required in two cases:
           # ** user is the owner of the "thing"
@@ -628,7 +628,7 @@
     
     begin
       case thing_type
-        when "Workflow", "Blog", "Blob", "Pack"
+        when *Conf.contributable_models
           # "find_by_sql" works faster itself PLUS only a subset of all fields is selected;
           # this is the most frequent query to be executed, hence needs to be optimised
           found_instance = Contribution.find_by_sql "SELECT contributor_id, contributor_type, policy_id FROM contributions WHERE contributable_id=#{thing_id} AND contributable_type='#{thing_type}'"

Added: branches/datasets/lib/core_extensions.rb (0 => 2855)


--- branches/datasets/lib/core_extensions.rb	                        (rev 0)
+++ branches/datasets/lib/core_extensions.rb	2011-12-01 12:56:06 UTC (rev 2855)
@@ -0,0 +1,11 @@
+# myExperiment: lib/core_extensions.rb
+#
+# Copyright (c) 2011 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+# An easy way to get the "aliased" name for a resource.
+class String
+  def model_alias
+    Conf.model_aliases.index(self) || self
+  end
+end

reply via email to

[Prev in Thread] Current Thread [Next in Thread]