myexperiment-hackers
[Top][All Lists]
Advanced

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

[myexperiment-hackers] [2606] branches/biocat: updated biocat branch wit


From: noreply
Subject: [myexperiment-hackers] [2606] branches/biocat: updated biocat branch with svn merge -r 2351: 2605 svn+ssh://address@hidden/var/svn/myexperiment/trunk
Date: Wed, 15 Jun 2011 11:27:15 -0400 (EDT)

Revision
2606
Author
dgc
Date
2011-06-15 11:27:14 -0400 (Wed, 15 Jun 2011)

Log Message

updated biocat branch with svn merge -r 2351:2605 svn+ssh://address@hidden/var/svn/myexperiment/trunk

Modified Paths

Added Paths

Removed Paths

Diff

Modified: branches/biocat/Rakefile (2605 => 2606)


--- branches/biocat/Rakefile	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/Rakefile	2011-06-15 15:27:14 UTC (rev 2606)
@@ -20,6 +20,71 @@
   Service.rebuild_solr_index
 end
 
+desc 'Refresh contribution caches'
+task "myexp:refresh:contributions" do
+  require File.dirname(__FILE__) + '/config/environment'
+
+  all_viewings = Viewing.find(:all, :conditions => "accessed_from_site = 1").group_by do |v| v.contribution_id end
+  all_downloads = Download.find(:all, :conditions => "accessed_from_site = 1").group_by do |v| v.contribution_id end
+
+  Contribution.find(:all).each do |c|
+    c.contributable.update_contribution_rank
+    c.contributable.update_contribution_rating
+    c.contributable.update_contribution_cache
+
+    ActiveRecord::Base.record_timestamps = false
+
+    c.reload
+    c.update_attribute(:created_at, c.contributable.created_at)
+    c.update_attribute(:updated_at, c.contributable.updated_at)
+
+    c.update_attribute(:site_viewings_count,  all_viewings[c.id]  ? all_viewings[c.id].length  : 0)
+    c.update_attribute(:site_downloads_count, all_downloads[c.id] ? all_downloads[c.id].length : 0)
+
+    ActiveRecord::Base.record_timestamps = true
+  end
+end
+
+desc 'Create a myExperiment data backup'
+task "myexp:backup:create" do
+  require File.dirname(__FILE__) + '/config/environment'
+  Maintenance::Backup.create
+end
+
+desc 'Restore from a myExperiment data backup'
+task "myexp:backup:restore" do
+  require File.dirname(__FILE__) + '/config/environment'
+  Maintenance::Backup.restore
+end
+
+desc 'Load a SKOS concept schema'
+task "myexp:skos:load" do
+  require File.dirname(__FILE__) + '/config/environment'
+
+  file_name = ENV['FILE']
+
+  if file_name.nil?
+    puts "Missing file name."
+    return
+  end
+
+  LoadVocabulary::load_skos(YAML::load_file(file_name))
+end
+
+desc 'Load an OWL ontology'
+task "myexp:ontology:load" do
+  require File.dirname(__FILE__) + '/config/environment'
+
+  file_name = ENV['FILE']
+
+  if file_name.nil?
+    puts "Missing file name."
+    return
+  end
+
+  LoadVocabulary::load_ontology(YAML::load_file(file_name))
+end
+
 desc 'Refresh workflow metadata'
 task "myexp:refresh:workflows" do
   require File.dirname(__FILE__) + '/config/environment'

Modified: branches/biocat/app/controllers/announcements_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/announcements_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/announcements_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -22,6 +22,24 @@
 
   def show
     @announcement = Announcement.find(params[:id])
+
+    respond_to do |format|
+      format.html {
+
+        @lod_nir  = announcement_url(@announcement)
+        @lod_html = formatted_announcement_url(:id => @announcement.id, :format => 'html')
+        @lod_rdf  = formatted_announcement_url(:id => @announcement.id, :format => 'rdf')
+        @lod_xml  = formatted_announcement_url(:id => @announcement.id, :format => 'xml')
+        #
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} announcements address@hidden
+        }
+      end
+    end
   end
 
   def new

Modified: branches/biocat/app/controllers/api_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/api_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/api_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -24,7 +24,7 @@
       credentials = Base64.decode64(auth.sub(/^Basic /, '')).split(':')
       user = User.authenticate(credentials[0], credentials[1])
 
-      return rest_response(401) if user.nil?
+      return rest_response(401, :reason => "Failed to authenticate") if user.nil?
 
     end
 
@@ -68,14 +68,14 @@
         permission_found = true if permission.for == requested_permission
       end
 
-      return rest_response(403) if permission_found == false
+      return rest_response(403, :reason => "OAuth token does not grant sufficient permission for this action") if permission_found == false
     end  
 
     case rules['Type']
-      when 'index'; doc = rest_index_request(params[:uri], rules, user, request.query_parameters)
-      when 'crud';  doc = rest_crud_request(params[:uri], rules, user, request.query_parameters)
-      when 'call';  doc = rest_call_request(params[:uri], rules, user, request.query_parameters)
-      else;         return rest_response(400)
+      when 'index'; doc = rest_index_request(params[:uri], params[:format], rules, user, request.query_parameters)
+      when 'crud';  doc = rest_crud_request(params[:uri], params[:format], rules, user, request.query_parameters)
+      when 'call';  doc = rest_call_request(params[:uri], params[:format], rules, user, request.query_parameters)
+      else;         return rest_response(500, :reason => "Unknown REST table type")
     end
   end
 end

Modified: branches/biocat/app/controllers/application.rb (2605 => 2606)


--- branches/biocat/app/controllers/application.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/application.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -358,5 +358,760 @@
     end
     
   end
+ 
+  # helper function to determine the context of a polymorphic nested resource
+
+  def extract_resource_context(params)
+    (Conf.contributor_models + Conf.contributable_models).each do |model_name|
+      id_param = "#{Conf.to_visible(model_name.underscore)}_id"
+      return Object.const_get(model_name).find_by_id(params[id_param]) if params[id_param]
+    end
+
+    nil
+  end 
+
+  def deep_clone(ob)
+    case ob.class.name
+    when "Array"
+      ob.map do |x| deep_clone(x) end
+    when "Hash"
+      hash = {}
+      ob.each do |k, v| hash[deep_clone(k)] = deep_clone(v) end
+      hash
+    when "Symbol"
+      ob
+    else
+      ob.clone
+    end
+  end
+
+  # Pivot code
   
+  def pivot_options
+    {
+      :order => 
+      [
+        {
+          :option => 'rank',
+          :label  => 'Rank',
+          :order  => 'contributions.rank DESC'
+        },
+        
+        {
+          :option => 'title',
+          :label  => 'Title',
+          :order  => 'contributions.label, contributions.rank DESC'
+        },
+
+        {
+          :option => 'latest',
+          :label  => 'Latest',
+          :order  => 'contributions.created_at DESC, contributions.rank DESC'
+        },
+
+        {
+          :option => 'last_updated',
+          :label  => 'Last updated',
+          :order  => 'contributions.updated_at DESC, contributions.rank DESC'
+        },
+
+        {
+          :option => 'rating',
+          :label  => 'Community rating',
+          :order  => 'contributions.rating DESC, contributions.rank DESC'
+        },
+
+        {
+          :option => 'viewings',
+          :label  => 'Most viewed',
+          :order  => 'contributions.site_viewings_count DESC, contributions.rank DESC'
+        },
+
+        {
+          :option => 'downloads',
+          :label  => 'Most downloaded',
+          :order  => 'contributions.site_downloads_count DESC, contributions.rank DESC'
+        },
+
+        {
+          :option => 'type',
+          :label  => 'Type',
+          :joins  => [ :content_types ],
+          :order  => 'content_types.title, contributions.rank DESC'
+        },
+
+        {
+          :option => 'licence',
+          :label  => 'Licence',
+          :joins  => [ :licences ],
+          :order  => 'licenses.title, contributions.rank DESC'
+        }
+      ],
+
+      :num_options => ['10', '20', '25', '50', '100'],
+
+      :filters =>
+      [
+        {
+          :title        => 'category',
+          :query_option => 'CATEGORY',
+          :id_column    => :auth_type,
+          :label_column => :auth_type,
+          :visible_name => true
+        },
+
+        {
+          :title        => 'type',
+          :query_option => 'TYPE_ID',
+          :id_column    => 'content_types.id',
+          :label_column => 'content_types.title',
+          :joins        => [ :content_types ],
+          :not_null     => true
+        },
+
+        {
+          :title        => 'tag',
+          :query_option => 'TAG_ID',
+          :id_column    => 'tags.id',
+          :label_column => 'tags.name',
+          :joins        => [ :taggings, :tags ]
+        },
+
+        {
+          :title        => 'user',
+          :query_option => 'USER_ID',
+          :id_column    => 'users.id',
+          :label_column => 'users.name',
+          :joins        => [ :users ]
+        },
+
+        {
+          :title        => 'licence',
+          :query_option => 'LICENSE_ID',
+          :id_column    => 'licenses.id',
+          :label_column => 'licenses.unique_name',
+          :joins        => [ :licences ],
+          :not_null     => true
+        },
+
+        {
+          :title        => 'group',
+          :query_option => 'GROUP_ID',
+          :id_column    => 'networks.id',
+          :label_column => 'networks.title',
+          :joins        => [ :networks ]
+        },
+
+        {
+          :title        => 'wsdl',
+          :query_option => 'WSDL_ENDPOINT',
+          :id_column    => 'workflow_processors.wsdl',
+          :label_column => 'workflow_processors.wsdl',
+          :joins        => [ :workflow_processors ],
+          :not_null     => true
+        },
+
+        {
+          :title        => 'curation',
+          :query_option => 'CURATION_EVENT',
+          :id_column    => 'curation_events.category',
+          :label_column => 'curation_events.category',
+          :joins        => [ :curation_events ],
+          :capitalize   => true
+        },
+      ],
+
+      :joins =>
+      {
+        :content_types       => "LEFT OUTER JOIN content_types ON contributions.content_type_id = content_types.id",
+        :licences            => "LEFT OUTER JOIN licenses ON contributions.license_id = licenses.id",
+        :users               => "INNER JOIN users ON contributions.contributor_type = 'User' AND contributions.contributor_id = users.id",
+        :taggings            => "LEFT OUTER JOIN taggings ON AUTH_TYPE = taggings.taggable_type AND AUTH_ID = taggings.taggable_id",
+        :tags                => "INNER JOIN tags ON taggings.tag_id = tags.id",
+        :networks            => "INNER JOIN networks ON permissions.contributor_type = 'Network' AND permissions.contributor_id = networks.id",
+        :credits             => "INNER JOIN creditations ON creditations.creditable_type = AUTH_TYPE AND creditations.creditable_id = AUTH_ID",
+        :curation_events     => "INNER JOIN curation_events ON curation_events.object_type = AUTH_TYPE AND curation_events.object_id = AUTH_ID",
+        :workflow_processors => "INNER JOIN workflow_processors ON AUTH_TYPE = 'Workflow' AND workflow_processors.workflow_id = AUTH_ID",
+        :search              => "RIGHT OUTER JOIN search_results ON search_results.result_type = AUTH_TYPE AND search_results.result_id = AUTH_ID"
+      }
+    }
+  end
+
+  TOKEN_UNKNOWN         = 0x0000
+  TOKEN_AND             = 0x0001
+  TOKEN_OR              = 0x0002
+  TOKEN_WORD            = 0x0003
+  TOKEN_OPEN            = 0x0004
+  TOKEN_CLOSE           = 0x0005
+  TOKEN_STRING          = 0x0006
+  TOKEN_EOS             = 0x00ff
+
+  NUM_TOKENS            = 6
+
+  STATE_INITIAL         = 0x0000
+  STATE_EXPECT_OPEN     = 0x0100
+  STATE_EXPECT_STR      = 0x0200
+  STATE_EXPECT_EXPR_END = 0x0300
+  STATE_EXPECT_END      = 0x0400
+  STATE_COMPLETE        = 0x0500
+
+  def parse_filter_expression(expr)
+
+    def unescape_string(str)
+      str.match(/^"(.*)"$/)[1].gsub(/\\"/, '"')
+    end
+
+    state  = STATE_INITIAL
+    data   = ""
+
+    begin
+
+      tokens = expr.match(/^
+
+          \s* (\sAND\s)         | # AND operator
+          \s* (\sOR\s)          | # OR operator
+          \s* (\w+)             | # a non-keyword word
+          \s* (\()              | # an open paranthesis
+          \s* (\))              | # a close paranthesis
+          \s* ("(\\.|[^\\"])*")   # double quoted string with backslash escapes
+
+          /ix)
+
+      if tokens.nil?
+        token = TOKEN_UNKNOWN
+      else
+        (1..NUM_TOKENS).each do |i|
+          token = i if tokens[i]
+        end
+      end
+
+      if token == TOKEN_UNKNOWN
+        token = TOKEN_EOS if expr.strip.empty?
+      end
+
+      case state | token
+        when STATE_INITIAL         | TOKEN_WORD   : state = STATE_EXPECT_OPEN     ; data << { :name => tokens[0], :expr => [] }
+        when STATE_EXPECT_OPEN     | TOKEN_OPEN   : state = STATE_EXPECT_STR
+        when STATE_EXPECT_STR      | TOKEN_STRING : state = STATE_EXPECT_EXPR_END ; data.last[:expr] << tokens[0] 
+        when STATE_EXPECT_EXPR_END | TOKEN_AND    : state = STATE_EXPECT_STR      ; data.last[:expr] << :and 
+        when STATE_EXPECT_EXPR_END | TOKEN_OR     : state = STATE_EXPECT_STR      ; data.last[:expr] << :or 
+        when STATE_EXPECT_EXPR_END | TOKEN_CLOSE  : state = STATE_EXPECT_END
+        when STATE_EXPECT_END      | TOKEN_AND    : state = STATE_INITIAL         ; data << :and 
+        when STATE_EXPECT_END      | TOKEN_OR     : state = STATE_INITIAL         ; data << :or 
+        when STATE_EXPECT_END      | TOKEN_EOS    : state = STATE_COMPLETE
+
+        else raise "Error parsing query _expression_"
+      end
+
+      expr = tokens.post_match unless state == STATE_COMPLETE
+
+    end while state != STATE_COMPLETE
+
+    # validate and reduce expressions to current capabilities
+
+    valid_filters = pivot_options[:filters].map do |f| f[:query_option] end
+
+    data.each do |category|
+      case category
+      when :or
+        raise "Unsupported query _expression_"
+      when :and
+        # Fine
+      else
+        raise "Unknown filter category" unless valid_filters.include?(category[:name])
+
+        counts = { :and => 0, :or => 0 }
+
+        category[:expr].each do |bit|
+          counts[bit] = counts[bit] + 1 if bit.class == Symbol
+        end
+
+        raise "Unsupported query _expression_" if counts[:and] > 0 && counts[:or] > 0
+
+        # haven't implemented 'and' within a particular filter yet
+        raise "Unsupported query _expression_" if counts[:and] > 0
+
+        if category[:expr].length == 1
+          category[:expr] = { :terms => [unescape_string(category[:expr].first)] }
+        else
+          category[:expr] = {
+            :operator => category[:expr][1],
+            :terms    => category[:expr].select do |t|
+              t.class == String
+            end.map do |t|
+              unescape_string(t)
+            end
+          }
+        end
+      end
+    end
+
+    data
+  end
+
+  def contributions_list(klass = nil, params = nil, user = nil, opts = {})
+
+    def escape_sql(str)
+      str.gsub(/\\/, '\&\&').gsub(/'/, "''")
+    end
+
+    def build_url(params, opts, expr, parts, extra = {})
+
+      query = {}
+
+      if parts.include?(:filter)
+        bits = []
+        pivot_options[:filters].each do |filter|
+          if !opts[:lock_filter] || opts[:lock_filter][filter[:query_option]].nil?
+            if find_filter(expr, filter[:query_option])
+              bits << filter[:query_option] + "(\"" + find_filter(expr, filter[:query_option])[:expr][:terms].map do |t| t.gsub(/"/, '\"') end.join("\" OR \"") + "\")"
+            end
+          end
+        end
+
+        if bits.length > 0
+          query["filter"] = bits.join(" AND ")
+        end
+      end
+
+      query["query"]        = params[:query]        if params[:query]
+      query["order"]        = params[:order]        if parts.include?(:order)
+      query["filter_query"] = params[:filter_query] if parts.include?(:filter_query)
+
+      query.merge!(extra)
+
+      query
+    end
+
+    def comparison(lhs, rhs)
+      if rhs.length == 1
+        "#{lhs} = '#{escape_sql(rhs.first)}'"
+      else
+        "#{lhs} IN ('#{rhs.map do |bit| escape_sql(bit) end.join("', '")}')"
+      end
+    end
+
+    def create_search_results_table(search_query, models)
+
+      solr_results = models.first.multi_solr_search(search_query,
+          :models         => models,
+          :results_format => :ids,
+          :limit          => Conf.max_search_size)
+
+      conn =  ActiveRecord::Base.connection
+
+      conn.execute("CREATE TEMPORARY TABLE search_results (id INT AUTO_INCREMENT UNIQUE KEY, result_type VARCHAR(255), result_id INT)")
+
+      # This next part converts the search results to SQL values
+      #
+      # from:  { "id" => "Workflow:4" }, { "id" => "Pack:6" }, ...
+      # to:    "(NULL, 'Workflow', '4'), (NULL, 'Pack', '6'), ..."
+
+      if solr_results.results.length > 0
+        insert_part = solr_results.results.map do |result|
+          "(NULL, " + result["id"].split(":").map do |bit|
+            "'#{bit}'"
+          end.join(", ") + ")"
+        end.join(", ")
+ 
+        conn.execute("INSERT INTO search_results VALUES #{insert_part}")
+      end
+    end
+
+    def drop_search_results_table
+      ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS search_results")
+    end
+
+    def calculate_having_clause(filter, opts)
+
+      having_bits = []
+
+      pivot_options[:filters].each do |f|
+        if f != filter
+#         if opts[:filters][f[:query_option]] && opts[:filters]["and_#{f[:query_option]}"] == "yes"
+#           having_bits << "(GROUP_CONCAT(DISTINCT #{f[:id_column]} ORDER BY #{f[:id_column]}) = '#{escape_sql(opts[:filters][f[:query_option]])}')"
+#         end
+        end
+      end
+
+      return nil if having_bits.empty?
+
+      "HAVING " + having_bits.join(" OR ")
+    end
+
+    def column(column, opts)
+      if column == :auth_type
+        if opts[:auth_type]
+          opts[:auth_type]
+        else
+          "contributions.contributable_type"
+        end
+      else
+        column
+      end
+    end
+
+    def calculate_filter(params, filter, user, opts = {})
+
+      # apply all the joins and conditions except for the current filter
+
+      joins      = []
+      conditions = []
+
+      pivot_options[:filters].each do |other_filter|
+        if filter_list = find_filter(opts[:filters], other_filter[:query_option])
+          unless opts[:inhibit_other_conditions]
+            conditions << comparison(column(other_filter[:id_column], opts), filter_list[:expr][:terms]) unless other_filter == filter
+          end
+          joins += other_filter[:joins] if other_filter[:joins]
+        end
+      end
+
+      filter_id_column    = column(filter[:id_column],    opts)
+      filter_label_column = column(filter[:label_column], opts)
+
+      joins += filter[:joins] if filter[:joins]
+      conditions << "#{filter_id_column} IS NOT NULL" if filter[:not_null]
+
+      unless opts[:inhibit_filter_query]
+        if params[:filter_query]
+          conditions << "(#{filter_label_column} LIKE '%#{escape_sql(params[:filter_query])}%')"
+        end
+      end
+
+      joins.push(:search) if params[:query] && !opts[:arbitrary_models]
+
+      current = find_filter(opts[:filters], filter[:query_option]) ? find_filter(opts[:filters], filter[:query_option])[:expr][:terms] : []
+
+      if opts[:ids].nil?
+        limit = 10
+      else
+        conditions << "(#{filter_id_column} IN ('#{opts[:ids].map do |id| escape_sql(id) end.join("','")}'))"
+        limit = nil
+      end
+
+      conditions = conditions.length.zero? ? nil : conditions.join(" AND ")
+
+      if opts[:auth_type] && opts[:auth_id]
+        count_expr = "COUNT(DISTINCT #{opts[:auth_type]}, #{opts[:auth_id]})"
+      else
+        count_expr = "COUNT(DISTINCT contributions.contributable_type, contributions.contributable_id)"
+      end
+
+      objects = Authorization.authorised_index(params[:query] && opts[:arbitrary_models] ? SearchResult : Contribution,
+          :all,
+          :include_permissions => true,
+          :select => "#{filter_id_column} AS filter_id, #{filter_label_column} AS filter_label, #{count_expr} AS filter_count",
+          :arbitrary_models => opts[:arbitrary_models],
+          :auth_type => opts[:auth_type],
+          :auth_id => opts[:auth_id],
+          :joins => merge_joins(joins, :auth_type => opts[:auth_type], :auth_id => opts[:auth_id]),
+          :conditions => conditions,
+          :group => "#{filter_id_column} #{calculate_having_clause(filter, opts)}",
+          :limit => limit,
+          :order => "#{count_expr} DESC, #{filter_label_column}",
+          :authorised_user => user)
+      
+      objects = objects.select do |x| !x[:filter_id].nil? end
+
+      objects = objects.map do |object|
+
+        value = object.filter_id.to_s
+        selected = current.include?(value)
+
+        label_expr = deep_clone(opts[:filters])
+        label_expr -= [find_filter(label_expr, filter[:query_option])] if find_filter(label_expr, filter[:query_option])
+
+        unless selected && current.length == 1
+          label_expr << { :name => filter[:query_option], :expr => { :terms => [value] } }
+        end
+
+        checkbox_expr = deep_clone(opts[:filters])
+
+        if expr_filter = find_filter(checkbox_expr, filter[:query_option])
+
+          if selected
+            expr_filter[:expr][:terms] -= [value]
+          else
+            expr_filter[:expr][:terms] += [value]
+          end
+
+          checkbox_expr -= [expr_filter] if expr_filter[:expr][:terms].empty?
+
+        else
+          checkbox_expr << { :name => filter[:query_option], :expr => { :terms => [value] } }
+        end
+
+        label_uri = build_url(params, opts, label_expr, [:filter, :order], "page" => nil)
+
+        checkbox_uri = build_url(params, opts, checkbox_expr, [:filter, :order], "page" => nil)
+
+        label = object.filter_label.clone
+        label = visible_name(label) if filter[:visible_name]
+        label = label.capitalize    if filter[:capitalize]
+
+        plain_label = object.filter_label
+
+        if params[:filter_query]
+          label.sub!(Regexp.new("(#{params[:filter_query]})", Regexp::IGNORECASE), '<b>\1</b>')
+        end
+
+        {
+          :object       => object,
+          :value        => value,
+          :label        => label,
+          :plain_label  => plain_label,
+          :count        => object.filter_count,
+          :checkbox_uri => checkbox_uri,
+          :label_uri    => label_uri,
+          :selected     => selected
+        }
+      end
+
+      [current, objects]
+    end
+
+    def calculate_filters(params, opts, user)
+
+      # produce the filter list
+
+      filters = pivot_options[:filters].clone
+      cancel_filter_query_url = nil
+
+      filters.each do |filter|
+
+        # calculate the top n items of the list
+
+        filter[:current], filter[:objects] = calculate_filter(params, filter, user, opts)
+
+        # calculate which active filters are missing (because they weren't in the
+        # top part of the list or have a count of zero)
+
+        missing_filter_ids = filter[:current] - filter[:objects].map do |ob| ob[:value] end
+
+        if missing_filter_ids.length > 0
+          filter[:objects] += calculate_filter(params, filter, user, opts.merge(:ids => missing_filter_ids))[1]
+        end
+
+        # calculate which active filters are still missing (because they have a
+        # count of zero)
+
+        missing_filter_ids = filter[:current] - filter[:objects].map do |ob| ob[:value] end
+        
+        if missing_filter_ids.length > 0
+          zero_list = calculate_filter(params, filter, user, opts.merge(:ids => missing_filter_ids, :inhibit_other_conditions => true))[1]
+
+          zero_list.each do |x| x[:count] = 0 end
+
+          zero_list.sort! do |a, b| a[:label] <=> b[:label] end
+
+          filter[:objects] += zero_list
+        end
+      end
+
+      [filters, cancel_filter_query_url]
+    end
+
+    def find_filter(filters, name)
+      filters.find do |f|
+        f[:name] == name
+      end
+    end
+
+    def merge_joins(joins, opts = {})
+
+      opts[:auth_type] ||= 'contributions.contributable_type'
+      opts[:auth_id]   ||= 'contributions.contributable_id'
+
+      if joins.length.zero?
+        nil
+      else
+        joins.uniq.map do |j|
+          text = pivot_options[:joins][j]
+          text.gsub!(/AUTH_TYPE/, opts[:auth_type])
+          text.gsub!(/AUTH_ID/,   opts[:auth_id])
+          text
+        end.join(" ")
+      end
+    end
+
+    joins      = []
+    conditions = []
+
+    # parse the filter _expression_ if provided.  convert filter _expression_ to
+    # the old format.  this will need to be replaced eventually
+
+    opts[:filters] ||= []
+    
+    include_reset_url = opts[:filters].length > 0
+
+    # filter out top level logic operators for now
+
+    opts[:filters] = opts[:filters].select do |bit|
+      bit.class == Hash
+    end
+
+    # apply locked filters
+
+    if opts[:lock_filter]
+      opts[:lock_filter].each do |filter, value|
+        opts[:filters] << { :name => filter, :expr => { :terms => [value] } }
+      end
+    end
+
+    # perform search if requested
+
+    group_by = "contributions.contributable_type, contributions.contributable_id"
+
+    if params["query"]
+      drop_search_results_table
+      create_search_results_table(params["query"], [Workflow, Blob, Pack, User, Network])
+      joins.push(:search) unless opts[:arbitrary_models]
+    end
+
+    if opts[:arbitrary_models] && params[:query]
+      klass = SearchResult
+      contribution_records = false
+      auth_type = "search_results.result_type"
+      auth_id   = "search_results.result_id"
+      group_by  = "search_results.result_type, search_results.result_id"
+    else
+      contribution_records = true
+    end
+
+    # determine joins, conditions and order for the main results
+
+    pivot_options[:filters].each do |filter|
+      if filter_list = find_filter(opts[:filters], filter[:query_option])
+        conditions << comparison(column(filter[:id_column], opts.merge( { :auth_type => auth_type, :auth_id => auth_id } )), filter_list[:expr][:terms])
+        joins += filter[:joins] if filter[:joins]
+      end
+    end
+
+    order_options = pivot_options[:order].find do |x|
+      x[:option] == params[:order]
+    end
+
+    order_options ||= pivot_options[:order].first
+
+    joins += order_options[:joins] if order_options[:joins]
+
+    having_bits = []
+
+#   pivot_options[:filters].each do |filter|
+#     if params["and_#{filter[:query_option]}"]
+#       having_bits << "GROUP_CONCAT(DISTINCT #{filter[:id_column]} ORDER BY #{filter[:id_column]}) = \"#{escape_sql(opts[:filters][filter[:query_option]])}\""
+#     end
+#   end
+
+    having_clause = ""
+
+    if having_bits.length > 0
+      having_clause = "HAVING #{having_bits.join(' AND ')}"
+    end
+
+    # perform the results query
+
+    results = Authorization.authorised_index(klass,
+        :all,
+        :authorised_user => user,
+        :include_permissions => true,
+        :contribution_records => contribution_records,
+        :arbitrary_models => opts[:arbitrary_models],
+        :auth_type => auth_type,
+        :auth_id => auth_id,
+        :page => { :size => params["num"] ? params["num"].to_i : nil, :current => params["page"] },
+        :joins => merge_joins(joins, :auth_type => auth_type, :auth_id => auth_id),
+        :conditions => conditions.length.zero? ? nil : conditions.join(" AND "),
+        :group => "#{group_by} #{having_clause}",
+        :order => order_options[:order])
+
+    # produce a query hash to match the current filters
+
+    opts[:filter_params] = {}
+
+    pivot_options[:filters].each do |filter|
+      if params[filter[:query_option]]
+        next if opts[:lock_filter] && opts[:lock_filter][filter[:query_option]]
+        opts[:filter_params][filter[:query_option]] = params[filter[:query_option]]
+      end
+    end
+
+    # produce the filter list
+
+    opts_for_filter_query = opts.merge( { :auth_type => auth_type,
+        :auth_id => auth_id, :group_by => group_by } )
+
+    filters, cancel_filter_query_url = calculate_filters(params, opts_for_filter_query, user)
+
+    # produce the summary.  If a filter query is specified, then we need to
+    # recalculate the filters without the query to get all of them.
+
+    if params[:filter_query]
+      filters2 = calculate_filters(params, opts_for_filter_query.merge( { :inhibit_filter_query => true } ), user)[0]
+    else
+      filters2 = filters
+    end
+
+    summary = ""
+
+    filters2.select do |filter|
+
+      next if opts[:lock_filter] && opts[:lock_filter][filter[:query_option]]
+
+      selected = filter[:objects].select do |x| x[:selected] end
+      current  = selected.map do |x| x[:value] end
+
+      if selected.length > 0
+        selected_labels = selected.map do |x|
+
+          expr = deep_clone(opts[:filters])
+
+          f = find_filter(expr, filter[:query_option])
+  
+          expr -= f[:expr][:terms] -= [x[:value]]
+          expr -= [f] if f[:expr][:terms].empty?
+
+          x[:plain_label] + ' <a href="" + url_for(build_url(params, opts, expr,
+          [:filter, :filter_query, :order])) +
+            '">' + " <img src='' /></a>"
+
+        end
+
+        bits = selected_labels.map do |label| label end.join(" <i>or</i> ")
+
+        summary << '<span class="filter-in-use"><b>' + filter[:title].capitalize + "</b>: " + bits + "</span> "
+      end
+    end
+
+    if params[:filter_query]
+      cancel_filter_query_url = build_url(params, opts, opts[:filters], [:filter, :order])
+    end
+
+    if include_reset_url
+      reset_filters_url = build_url(params, opts, opts[:filters], [:order])
+    end
+
+    # remove filters that do not help in narrowing down the result set
+
+    filters = filters.select do |filter|
+      if filter[:objects].empty?
+        false
+      elsif opts[:lock_filter] && opts[:lock_filter][filter[:query_option]]
+        false
+      else
+        true
+      end
+    end
+
+    {
+      :results                 => results,
+      :filters                 => filters,
+      :reset_filters_url       => reset_filters_url,
+      :cancel_filter_query_url => cancel_filter_query_url,
+      :filter_query_url        => build_url(params, opts, opts[:filters], [:filter]),
+      :summary                 => summary
+    }
+  end
 end
+

Modified: branches/biocat/app/controllers/blobs_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/blobs_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/blobs_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -4,10 +4,12 @@
 # See license.txt for details.
 
 class BlobsController < ApplicationController
-  before_filter :login_required, :except => [:index, :show, :download, :named_download, :statistics, :search, :all]
+
+  include ApplicationHelper
+
+  before_filter :login_required, :except => [:index, :show, :download, :named_download, :statistics, :search]
   
-  before_filter :find_blobs, : [:all]
-  before_filter :find_blob_auth, :except => [:search, :index, :new, :create, :all]
+  before_filter :find_blob_auth, :except => [:search, :index, :new, :create]
   
   before_filter :initiliase_empty_objects_for_new_pages, : [:new, :create]
   before_filter :set_sharing_mode_variables, : [:show, :new, :create, :edit, :update]
@@ -53,17 +55,26 @@
   # GET /files
   def index
     respond_to do |format|
-      format.html # index.rhtml
+      format.html {
+        @pivot_options = pivot_options
+
+        begin
+          expr = parse_filter_expression(params["filter"]) if params["filter"]
+        rescue Exception => ex
+          puts "ex = #{ex.inspect}"
+          flash.now[:error] = "Problem with query _expression_: #{ex}"
+          expr = nil
+        end
+
+        @pivot = contributions_list(Contribution, params, current_user,
+            :lock_filter => { 'CATEGORY' => 'Blob' },
+            :filters     => expr)
+
+        # index.rhtml
+      }
     end
   end
   
-  # GET /files/all
-  def all
-    respond_to do |format|
-      format.html # all.rhtml
-    end
-  end
-  
   # GET /files/1
   def show
     if allow_statistics_logging(@blob)
@@ -71,7 +82,21 @@
     end
     
     respond_to do |format|
-      format.html # show.rhtml
+      format.html {
+
+        @lod_nir  = file_url(@blob)
+        @lod_html = formatted_file_url(:id => @blob.id, :format => 'html')
+        @lod_rdf  = formatted_file_url(:id => @blob.id, :format => 'rdf')
+        @lod_xml  = formatted_file_url(:id => @blob.id, :format => 'xml')
+
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} files address@hidden
+        }
+      end
     end
   end
   
@@ -85,7 +110,7 @@
   
   # POST /blobs
   def create
-    
+
     # don't create new blob if no file has been selected
     if params[:blob][:data].size == 0
       respond_to do |format|
@@ -99,6 +124,8 @@
       params[:blob].delete('data')
 
       params[:blob][:contributor_type], params[:blob][:contributor_id] = "User", current_user.id
+
+      params[:blob][:license_id] = nil if params[:blob][:license_id] && params[:blob][:license_id] == "0"
    
       @blob = Blob.new(params[:blob])
       @blob.content_blob = ContentBlob.new(:data ="" data)
@@ -153,6 +180,8 @@
       end
     end
     
+    params[:blob][:license_id] = nil if params[:blob][:license_id] && params[:blob][:license_id] == "0"
+
     # 'Data' (ie: the actual file) cannot be updated!
     params[:blob].delete('data') if params[:blob][:data]
     
@@ -192,45 +221,6 @@
     end
   end
   
-  # POST /files/1;comment
-  def comment 
-    text = params[:comment][:comment]
-    ajaxy = true
-    
-    if text.nil? or (text.length == 0)
-      text = params[:comment_0_comment_editor]
-      ajaxy = false
-    end
-
-    if text and text.length > 0
-      comment = Comment.create(:user => current_user, :comment => text)
-      @blob.comments << comment
-    end
-    
-    respond_to do |format|
-      if ajaxy
-        format.html { render :partial => "comments/comments", :locals => { :commentable => @blob } }
-      else
-        format.html { redirect_to file_url(@blob) }
-      end
-    end
-  end
-  
-  # DELETE /files/1;comment_delete
-  def comment_delete
-    if params[:comment_id]
-      comment = Comment.find(params[:comment_id].to_i)
-      # security checks:
-      if comment.user_id == current_user.id and comment.commentable_type.downcase == 'blob' and comment.commentable_id == @blob.id
-        comment.destroy
-      end
-    end
-    
-    respond_to do |format|
-      format.html { render :partial => "comments/comments", :locals => { :commentable => @blob } }
-    end
-  end
-  
   # POST /files/1;rate
   def rate
     if @blob.contributor_type == 'User' and @blob.contributor_id == current_user.id
@@ -238,7 +228,7 @@
     else
       Rating.delete_all(["rateable_type = ? AND rateable_id = ? AND user_id = ?", @blob.class.to_s, @blob.id, current_user.id])
       
-      @blob.ratings << Rating.create(:user => current_user, :rating => params[:rating])
+      Rating.create(:rateable => @blob, :user => current_user, :rating => params[:rating])
       
       respond_to do |format|
         format.html { 
@@ -295,19 +285,6 @@
   
   protected
   
-  def find_blobs
-    found = Blob.find(:all, 
-                       :order => "updated_at DESC, created_at DESC",
-                       :page => { :size => 20, 
-                       :current => params[:page] })
-    
-    found.each do |blob|
-      blob.content_blob.data = "" unless Authorization.is_authorized?("download", nil, blob, current_user)
-    end
-    
-    @blobs = found
-  end
-  
   def find_blob_auth
     begin
       blob = Blob.find(params[:id])

Copied: branches/biocat/app/controllers/comments_controller.rb (from rev 2605, trunk/app/controllers/comments_controller.rb) (0 => 2606)


--- branches/biocat/app/controllers/comments_controller.rb	                        (rev 0)
+++ branches/biocat/app/controllers/comments_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,84 @@
+# myExperiment: app/controllers/comments_controller.rb
+#
+# Copyright (c) 2010 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class CommentsController < ApplicationController
+  
+  before_filter :find_resource_context, : [ :create, :index, :timeline ]
+  before_filter :find_resource, : [ :destroy ]
+
+  # GET /:context_type/:context_id/comments
+  def index
+    if params[:start] && params[:end]
+      begin
+        @comments = Comment.find(:all, :conditions => [ "commentable_id = ? AND commentable_type = ? AND created_at > ? AND created_at < ?", @context.id, @context.class.name, params[:start].to_time, params[:end].to_time ] )
+      rescue TypeError
+      end
+    end
+    @comments ||= []
+    respond_to do |format|
+      format.json { render :partial => 'comments/timeline_json', :layout => false }
+    end
+  end
+
+  # POST /:context_type/:context_id/comments
+  def create 
+    text = params[:comment][:comment]
+    ajaxy = true
+    
+    if text.nil? or (text.length == 0)
+      text = params[:comment_0_comment_editor]
+      ajaxy = false
+    end
+
+    if text and text.length > 0
+      comment = Comment.create(:user => current_user, :comment => text)
+      @context.comments << comment
+    end
+    
+    respond_to do |format|
+      if ajaxy
+        format.html { render :partial => "comments/comments", :locals => { :commentable => @context } }
+      else
+        format.html { redirect_to rest_resource_uri(@context) }
+      end
+    end
+  end
+  
+  # DELETE /:context_type/:context_id/comments/:id
+  def destroy
+    @comment.destroy
+    
+    respond_to do |format|
+      format.html { render :partial => "comments/comments", :locals => { :commentable => @context } }
+    end
+  end
+
+  # GET /:context_type/:context_id/comments/timeline
+  def timeline
+    respond_to do |format|
+      format.html # timeline.rhtml
+    end
+  end
+
+  private
+
+  def find_resource
+
+    @context = extract_resource_context(params)
+    @comment = Comment.find_by_id(params[:id])
+
+    return false if @comment.nil? || @context.nil? || @comment.commentable != @context
+    return false if Authorization.is_authorized?('view', nil, @context, current_user) == false
+  end
+
+  def find_resource_context
+
+    @context = extract_resource_context(params)
+
+    return false if @context.nil?
+    return false if Authorization.is_authorized?('view', nil, @context, current_user) == false
+  end
+end
+

Copied: branches/biocat/app/controllers/content_controller.rb (from rev 2605, trunk/app/controllers/content_controller.rb) (0 => 2606)


--- branches/biocat/app/controllers/content_controller.rb	                        (rev 0)
+++ branches/biocat/app/controllers/content_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,36 @@
+# myExperiment: app/controllers/content_controller.rb
+#
+# Copyright (c) 2010 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class ContentController < ApplicationController
+
+  include ApplicationHelper
+
+  def index
+    respond_to do |format|
+      format.html do
+        @pivot_options = pivot_options
+
+        begin
+          expr = parse_filter_expression(params["filter"]) if params["filter"]
+        rescue Exception => ex
+          puts "ex = #{ex.inspect}"
+          flash.now[:error] = "Problem with query _expression_: #{ex}"
+          expr = nil
+        end
+
+        @pivot = contributions_list(Contribution, params, current_user,
+            :filters => expr, :arbitrary_models => true)
+
+        # index.rhtml
+      end
+#     format.rss do
+#       address@hidden = Workflow.find(:all, :order => "updated_at DESC") # list all (if required)
+#       render :action ="" 'index.rxml', :layout => false
+#     end
+    end
+  end
+
+end
+

Copied: branches/biocat/app/controllers/content_types_controller.rb (from rev 2605, trunk/app/controllers/content_types_controller.rb) (0 => 2606)


--- branches/biocat/app/controllers/content_types_controller.rb	                        (rev 0)
+++ branches/biocat/app/controllers/content_types_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,90 @@
+# myExperiment: app/controllers/content_types_controller.rb
+#
+# Copyright (c) 2007 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class ContentTypesController < ApplicationController
+
+  before_filter :find_content_type, : [ :show, :edit, :update ]
+
+  # GET /content_types
+  def index
+
+    params[:num] = 25 unless params[:num]
+
+    @content_types = ContentType.find(:all, :order => 'title ASC',
+        :page => { :size => params[:num], :current => params[:page] })
+  end
+
+  # GET /content_types/1
+  def show
+    @workflow_count = Workflow.count(:conditions => ['content_type_id = ?', @content_type.id])
+    @blob_count = Blob.count(:conditions => ['content_type_id = ?', @content_type.id])
+
+    @total_count = @workflow_count + @blob_count
+
+    respond_to do |format|
+      format.html {
+
+        @lod_nir  = content_type_url(@content_type)
+        @lod_html = formatted_content_type_url(:id => @content_type.id, :format => 'html')
+        @lod_rdf  = formatted_content_type_url(:id => @content_type.id, :format => 'rdf')
+        @lod_xml  = formatted_content_type_url(:id => @content_type.id, :format => 'xml')
+
+        # show.rhtml
+      }
+
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} content_types address@hidden
+        }
+      end
+    end
+  end
+
+  # GET /content_types/1
+  def edit
+  end
+
+  # PUT /content_types/1
+  def update
+
+    if !Authorization.check(:action ="" 'edit', :object => @content_type, :user => current_user)
+      error("You do not have the authorisation to edit.", "is unauthorised")
+      return
+    end
+
+    @content_type.title       = params[:content_type][:title]
+    @content_type.description = params[:content_type][:description]
+
+    if @content_type.valid?
+      @content_type.save
+      redirect_to :action ="" 'show'
+    else
+      flash[:error] = "Failed to update Content Type."
+      render :action ="" :edit
+    end
+  end
+
+  private
+
+  def find_content_type
+    @content_type = ContentType.find_by_id(params[:id])
+
+    if @content_type.nil?
+      error("Content type not found", "is invalid")
+      return false
+    end
+  end
+
+  def error(notice, message, attr=:id)
+    flash[:error] = notice
+     (err = ContentType.new.errors).add(attr, message)
+    
+    respond_to do |format|
+      format.html { redirect_to content_types_url }
+    end
+  end
+end
+

Modified: branches/biocat/app/controllers/experiments_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/experiments_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/experiments_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -18,7 +18,21 @@
 
   def show
     respond_to do |format|
-      format.html # show.rhtml
+      format.html {
+        
+        @lod_nir  = experiment_url(@experiment)
+        @lod_html = formatted_experiment_url(:id => @experiment.id, :format => 'html')
+        @lod_rdf  = formatted_experiment_url(:id => @experiment.id, :format => 'rdf')
+        @lod_xml  = formatted_experiment_url(:id => @experiment.id, :format => 'xml')
+        
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} experiments address@hidden
+        }
+      end
     end
   end
 

Modified: branches/biocat/app/controllers/group_announcements_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/group_announcements_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/group_announcements_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -28,7 +28,21 @@
   # GET /group_announcements/1
   def show
     respond_to do |format|
-      format.html # show.rhtml
+      format.html {
+
+        @lod_nir  = group_announcement_url(:id => @announcement.id, :group_id => @announcement.network_id)
+        @lod_html = formatted_group_announcement_url(:id => @announcement.id, :group_id => @announcement.network_id, :format => 'html')
+        @lod_rdf  = formatted_group_announcement_url(:id => @announcement.id, :group_id => @announcement.network_id, :format => 'rdf')
+        @lod_xml  = formatted_group_announcement_url(:id => @announcement.id, :group_id => @announcement.network_id, :format => 'xml')
+
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} group_announcements address@hidden
+        }
+      end
     end
   end
 
@@ -95,7 +109,7 @@
 
   
   def check_admin
-    unless @group.owner?(current_user.id)
+    unless @group.administrator?(current_user.id)
       error("Only group administrators are allowed to create new announcements")
       return false
     end
@@ -134,7 +148,7 @@
           end
         when "edit","update","destroy"
           # only owner of the group can destroy the announcement
-          unless @group.owner?(current_user.id)
+          unless ((@announcement.user == current_user) || (@group.owner?(current_user.id)))
             not_auth = true;
             raise ActiveRecord::RecordNotFound, "You don't have permissions to perform this action"
           end

Modified: branches/biocat/app/controllers/jobs_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/jobs_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/jobs_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -38,7 +38,21 @@
     @job.refresh_status!
 
     respond_to do |format|
-      format.html # show.rhtml
+      format.html {
+        
+        @lod_nir  = experiment_job_url(:id => @job.id, :experiment_id => @experiment.id)
+        @lod_html = formatted_experiment_job_url(:id => @job.id, :experiment_id => @experiment.id, :format => 'html')
+        @lod_rdf  = formatted_experiment_job_url(:id => @job.id, :experiment_id => @experiment.id, :format => 'rdf')
+        @lod_xml  = formatted_experiment_job_url(:id => @job.id, :experiment_id => @experiment.id, :format => 'xml')
+        
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} jobs address@hidden
+        }
+      end
     end
   end
 

Modified: branches/biocat/app/controllers/licenses_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/licenses_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/licenses_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -11,6 +11,24 @@
 
   def show
     @license = License.find(params[:id])
+
+    respond_to do |format|
+      format.html {
+
+        @lod_nir  = license_url(@license)
+        @lod_html = formatted_license_url(:id => @license.id, :format => 'html')
+        @lod_rdf  = formatted_license_url(:id => @license.id, :format => 'rdf')
+        @lod_xml  = formatted_license_url(:id => @license.id, :format => 'xml')
+
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} licenses address@hidden
+        }
+      end
+    end
   end
 
   def new
@@ -69,7 +87,7 @@
   end
   
   def update_license_info
-      license = License.find(params[:license_id])
+      license = License.find_by_id(params[:license_id])
       render :partial => "licenses/view", :locals => { :license => license }
   end
   

Copied: branches/biocat/app/controllers/linked_data_controller.rb (from rev 2605, trunk/app/controllers/linked_data_controller.rb) (0 => 2606)


--- branches/biocat/app/controllers/linked_data_controller.rb	                        (rev 0)
+++ branches/biocat/app/controllers/linked_data_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,200 @@
+# myExperiment: app/controllers/linked_data_controller.rb
+#
+# Copyright (c) 2007 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class LinkedDataController < ApplicationController
+
+  before_filter :check_contributable_for_view_permission, : [ :attributions, :citations, :comments, :credits, :local_pack_entries, :remote_pack_entries, :policies, :ratings ]
+
+  def attributions
+
+    attribution = Attribution.find_by_id(params[:attribution_id])
+
+    return not_found if attribution.nil?
+    return not_found if attribution.attributable != @contributable
+
+    respond_to do |format|
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} attributions #{attribution.id}`
+        }
+      end
+    end
+  end
+
+  def citations
+
+    citation = Citation.find_by_id(params[:citation_id])
+
+    return not_found if citation.nil?
+    return not_found if citation.workflow != @contributable
+
+    respond_to do |format|
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} citations #{citation.id}`
+        }
+      end
+    end
+  end
+
+  def comments
+
+    comment = Comment.find_by_id(params[:comment_id])
+
+    return not_found if comment.nil?
+    return not_found if comment.commentable != @contributable
+
+    respond_to do |format|
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} comments #{comment.id}`
+        }
+      end
+    end
+  end
+
+  def credits
+
+    credit = Creditation.find_by_id(params[:credit_id])
+
+    return not_found if credit.nil?
+    return not_found if credit.creditable != @contributable
+
+    respond_to do |format|
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} creditations #{credit.id}`
+        }
+      end
+    end
+  end
+
+  def favourites
+
+    user      = User.find_by_id(params[:user_id])
+    favourite = Bookmark.find_by_id(params[:favourite_id])
+
+    return not_found if user.nil?
+    return not_found if favourite.nil?
+    return not_found if favourite.user != user
+
+    respond_to do |format|
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} favourites #{favourite.id}`
+        }
+      end
+    end
+  end
+
+  def local_pack_entries
+
+    local_pack_entry = PackContributableEntry.find_by_id(params[:local_pack_entry_id])
+
+    return not_found if local_pack_entry.nil?
+    return not_found if local_pack_entry.pack != @contributable
+
+    respond_to do |format|
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} local_pack_entries #{local_pack_entry.id}`
+        }
+      end
+    end
+  end
+
+  def remote_pack_entries
+
+    remote_pack_entry = PackRemoteEntry.find_by_id(params[:remote_pack_entry_id])
+
+    return not_found if remote_pack_entry.nil?
+    return not_found if remote_pack_entry.pack != @contributable
+
+    respond_to do |format|
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} remote_pack_entries #{remote_pack_entry.id}`
+        }
+      end
+    end
+  end
+
+  def policies
+
+    policy = Policy.find_by_id(params[:policy_id])
+
+    return not_found if policy.nil?
+    return not_found if policy.contributions.include?(@contributable.contribution) == false
+
+    respond_to do |format|
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} policies #{policy.id}`
+        }
+      end
+    end
+  end
+
+  def ratings
+
+    rating = Rating.find_by_id(params[:rating_id])
+
+    return not_found if rating.nil?
+    return not_found if rating.rateable != @contributable
+
+    respond_to do |format|
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} ratings #{rating.id}`
+        }
+      end
+    end
+  end
+
+  def taggings
+
+    tag     = Tag.find_by_id(params[:tag_id])
+    tagging = Tagging.find_by_id(params[:tagging_id])
+
+    return not_found if tag.nil?
+    return not_found if tagging.nil?
+    return not_found if tagging.tag != tag
+    return not_auth  if Authorization.is_authorized?('view', nil, tagging.taggable, current_user) == false
+
+    respond_to do |format|
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} taggings #{tagging.id}`
+        }
+      end
+    end
+  end
+
+  private
+
+  def check_contributable_for_view_permission
+
+    @contributable = case params[:contributable_type]
+      when 'workflows'; Workflow.find_by_id(params[:contributable_id])
+      when 'files';     Blob.find_by_id(params[:contributable_id])
+      when 'packs';     Pack.find_by_id(params[:contributable_id])
+    end
+
+    return not_found if @contributable.nil?
+    return not_auth  if Authorization.is_authorized?('view', nil, @contributable, current_user) == false
+  end
+
+  def not_found
+    render(:inline => 'Not Found', :status => "404 Not Found")
+    false
+  end
+
+  def not_auth
+    response.headers['WWW-Authenticate'] = "Basic realm=\"#{Conf.sitename}\""
+    render(:inline => 'Not Found', :status => "401 Unauthorized")
+    false
+  end
+end
+

Modified: branches/biocat/app/controllers/memberships_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/memberships_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/memberships_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -22,22 +22,63 @@
     group = Network.find(@membership.network_id)
     invite = @membership.is_invite?
       
-    from_id = invite ? @membership.user_id : group.user_id
-    to_id = invite ? group.user_id : @membership.user_id
-    subject = User.find(from_id).name + " has accepted your membership " + (invite ? "invite" : "request") + " for '" + group.title + "' group" 
-    body = "<strong><i>Personal message from " + (invite ? "user" : "group admin") + ":</i></strong><hr/>"
+    personal_message = "NONE"
     if params[:accept_msg] && !params[:accept_msg].blank?
-      body += ae_some_html(params[:accept_msg])
+      personal_message = ae_some_html(params[:accept_msg])
+    end
+
+    # the messages will appear as 'deleted-by-sender', because the owner of the account effectively didn't send it,
+    # so there is no reason for showing this message in their 'sent messages' folder
+
+    if invite
+      from = User.find(@membership.user_id)
+
+      subject = "Invitation to '" + group.title + "' accepted"
+      body = render_to_string :locals => { :from => from, :group => group, :msg => personal_message },
+                              :inline => <<EOM
+<%= name(from) %> has accepted an invitation to join <%= title(group) %> group.
+<br/>
+<br/>
+<strong><i>Personal message from user:</i></strong><hr/>
+<%= msg %>
+<hr/>
+EOM
+
+      group.administrators(true).each {|to| 
+        send_action_message(from.id, to.id, subject, body)
+      }
     else
-      body += "NONE"
+      from = current_user
+
+      subject = "Membership to '" + group.title + "' accepted"
+      body = render_to_string :locals => { :from => from, :group => group, :msg => personal_message },
+                              :inline => <<EOM
+<%= name(from) %> has accepted your request to join <%= title(group) %> group.
+<br/>
+<br/>
+<strong><i>Personal message from <%= name(from) %>:</i></strong><hr/>
+<%= msg %>
+<hr/>
+EOM
+
+      send_action_message(from.id, @membership.user_id, subject, body)
+
+      subject = "Membership to '" + group.title + "' accepted"
+      body = render_to_string :locals => { :from => from, :other => @membership.user_id, :group => group, :msg => personal_message },
+                              :inline => <<EOM
+<%= name(from) %> has accepted a request by <%= name(other) %> to join <%= title(group) %>  group.
+<br/>
+<br/>
+<strong><i>Personal message from <%= name(from) %> to user:</i></strong><hr/>
+<%= msg %>
+<hr/>
+EOM
+
+      group.administrators(true).select{|admin| admin.id != from.id}.each {|to|
+        send_action_message(from.id, to.id, subject, body)
+        }
     end
-    body += "<hr/>"
-      
-    # the message will appear as 'deleted-by-sender', because the owner of the account effectively didn't send it,
-    # so there is no reason for showing this message in their 'sent messages' folder
-    message = Message.new( :from => from_id, :to => to_id, :subject => subject, :body => body, :reply_id => nil, :read_at => nil, :deleted_by_sender => true )
-    message.save
-        
+
     respond_to do |format|
       if @membership.accept!
         flash[:notice] = 'Membership was successfully accepted.'
@@ -60,7 +101,21 @@
   # GET /memberships/1
   def show
     respond_to do |format|
-      format.html # show.rhtml
+      format.html {
+
+        @lod_nir  = user_membership_url(:id => @membership, :user_id => @membership.user_id)
+        @lod_html = formatted_user_membership_url(:id => @membership.id, :user_id => @membership.user_id, :format => 'html')
+        @lod_rdf  = formatted_user_membership_url(:id => @membership.id, :user_id => @membership.user_id, :format => 'rdf')
+        @lod_xml  = formatted_user_membership_url(:id => @membership.id, :user_id => @membership.user_id, :format => 'xml')
+
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} memberships address@hidden
+        }
+      end
     end
   end
 
@@ -161,57 +216,110 @@
   # DELETE /memberships/1
   def destroy
     network_id = @membership.network_id
+    from = current_user
     
     # a notification message will be delivered to the the requestor anyway;
     # it may contain a personal note, if any was supplied
     group = Network.find(network_id)
     invite = @membership.is_invite?
     rejection = (@membership.network_established_at.nil? || @membership.user_established_at.nil?) ? true : false
+
+    personal_message = "NONE"
+    if params[:reject_msg]  && !params[:reject_msg].blank?
+      personal_message = ae_some_html(params[:reject_msg])
+    end
       
     # the same method ('destroy') works when membership is rejected
     # or removed after being accepted previously
     if rejection
       # if this is rejection, then just group admin can do this action, so
       # the message would go from group admin to the requesting-user
-      from_id = invite ? @membership.user_id : group.user_id
-      to_id = invite ? group.user_id : @membership.user_id
-      
-      subject = User.find(from_id).name + " has rejected your membership " + (invite ? "invite" : "request") + " for '" + group.title + "' group"
-      body = "<strong><i>Personal message from " + (invite ? "user" : "group admin") + ":</i></strong><hr/>"
-      
-      if params[:reject_msg]  && !params[:reject_msg].blank?
-        body += ae_some_html(params[:reject_msg])
+      if invite
+        subject =  "Invitation to '" + group.title + "' rejected"
+        body = render_to_string :locals => { :from => from, :group => group, :msg => personal_message },
+                                :inline => <<EOM
+<%= name(from) %> has rejected an invitation to join <%= title(group) %> group.
+<br/>
+<br/>
+<strong><i>Personal message from <%= name(from) %> to user:</i></strong><hr/>
+<%= msg %>
+<hr/>
+EOM
+
+        group.administrators(true).each {|to| 
+          send_action_message(from.id, to.id, subject, body)
+        }
       else
-        body += "NONE"
+        to_id = @membership.user_id
+
+        subject = "Membership to '" + group.title + "' rejected"
+        body = render_to_string :locals => { :from => from, :group => group, :msg => personal_message },
+                                :inline => <<EOM
+<%= name(from) %> has rejected your request to join <%= title(group) %> group.
+<br/>
+<br/>
+<strong><i>Personal message from <%= name(from) %> to user:</i></strong><hr/>
+<%= msg %>
+<hr/>
+EOM
+
+        send_action_message(from.id, to_id, subject, body)
+
+        subject =  "Membership to '" + group.title + "' rejected"
+        body = render_to_string :locals => { :from => from, :other => @membership.user_id, :group => group, :msg => personal_message },
+                                :inline => <<EOM
+<%= name(from) %> has rejected the request by <%= name(other) %> to join <%= title(group) %> group."
+<br/>
+<strong><i>Personal message from <%= name(from) %> to user:</i></strong><hr/>
+<%= msg %>
+<hr/>
+EOM
+
+        group.administrators(true).select{|admin| admin.id != from.id}.each {|to|
+          send_action_message(from.id, to.id, subject, body)
+        }
       end
-      body += "<hr/>"
+      
     else
       # membership was cancelled, so the message goes from the current user
       # (who can be either admin or a former group member) to the 'other side' of the membership;
       # NB! subject and body should change accordingly!
-      from_id = current_user.id
-      from_user = User.find(from_id)
-      to_id = (@membership.network.owner.id == from_id ? @membership.user_id : @membership.network.owner.id)
       
-      if from_id == @membership.user_id
-        subject = from_user.name + " has left the '" + group.title + "' group that you manage"
-        body = "User: <a href=''>#{from_user.name}</a>" +
-               "<br/><br/>If you want to contact this user directly, just reply to this message."
+      if current_user.id == @membership.user_id
+        subject = from.name + " has left the '" + group.title + "' group"
+        body = render_to_string :locals => { :from => from, :group => group, :msg => personal_message },
+                                :inline => <<EOM
+User <%= name(from) %> has left <%= title(group) %> group.
+<br/>
+<br/>
+If you want to contact this user directly, just reply to this message.
+EOM
+
+        group.administrators(true).each {|to| 
+          send_action_message(from.id, to.id, subject, body)
+        }
       else  
-        subject = from_user.name + " has removed you from '" + group.title + "' group"
-        body = "Group: <a href=''networks', :action ="" 'show', :id => @membership.network_id}'>address@hidden</a>" + 
-               "<br/>Admin: <a href=''>address@hidden</a>" +
-               "<br/><br/>If you want to contact the administrator of the group directly, just reply to this message."
+        subject = "You have been removed from '" + group.title + "' group"
+        body = render_to_string :locals => { :from => from, :group => group, :msg => personal_message },
+                                :inline => <<EOM
+<%= name(from) %> has removed you from <%= title(group) %> group.
+<br/>
+<br/>
+If you want to contact the administrator directly, just reply to this message.
+EOM
+
+        send_action_message(from.id, @membership.user_id, subject, body)
+
+        subject = "User removed from '" + group.title + "' group"
+        body = render_to_string :locals => { :from => from, :other => @membership.user_id, :group => group, :msg => personal_message },
+                                :inline => "<%= name(from) %> has removed <%= name(other) %> from <%= title(group) %> group."
+
+        group.administrators(true).select{|admin| admin.id != current_user.id}.each {|to|
+          send_action_message(from.id, to.id, subject, body)
+        }
       end
     end
-    
       
-    # the message will appear as 'deleted-by-sender', because the owner of the account effectively didn't send it,
-    # so there is no reason for showing this message in their 'sent messages' folder
-    message = Message.new( :from => from_id, :to => to_id, :subject => subject, :body => body, :reply_id => nil, :read_at => nil, :deleted_by_sender => true )
-    message.save
-    
-    
     @membership.destroy
 
     respond_to do |format|
@@ -288,14 +396,14 @@
               not_auth = true;
             end
           elsif @membership.network_established_at == nil
-            unless @membership.network.owner.id == current_user.id && params[:user_id].to_i == @membership.network.owner.id
+            unless @membership.network.administrator?(current_user.id) # TODO: CHECK WHY?! && params[:user_id].to_i == @membership.network.owner.id
               not_auth = true;
             end
           end
-        when "show", "destroy"
+        when "show", "destroy", "update"
           # Only the owner of the network OR the person who the membership is for can view/delete memberships;
           # link - just user to whom the membership belongs
-          unless (address@hidden, @membership.user_id].include? current_user.id) && @membership.user_id == params[:user_id].to_i 
+          unless (@membership.network.administrator?(current_user.id) || @membership.user_id == current_user.id) && @membership.user_id == params[:user_id].to_i 
             not_auth = true
           end
         else
@@ -316,6 +424,11 @@
   end
   
 private
+
+  def send_action_message(from_id, to_id, subject, body)
+    message = Message.new(:from => from_id, :to => to_id, :subject => subject, :body => body, :reply_id => nil, :read_at => nil, :deleted_by_sender => true )
+    message.save
+  end
   
   def error(notice, message, attr=:id)
     flash[:error] = notice

Modified: branches/biocat/app/controllers/messages_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/messages_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/messages_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -66,7 +66,21 @@
         
       @message_folder = message_folder
       respond_to do |format|
-        format.html # show.rhtml
+        format.html {
+          
+        @lod_nir  = message_url(@message)
+        @lod_html = formatted_message_url(:id => @message.id, :format => 'html')
+        @lod_rdf  = formatted_message_url(:id => @message.id, :format => 'rdf')
+        @lod_xml  = formatted_message_url(:id => @message.id, :format => 'xml')
+          
+          # show.rhtml
+        }
+
+        if Conf.rdfgen_enable
+          format.rdf {
+            render :inline => `#{Conf.rdfgen_tool} messages address@hidden
+          }
+        end
       end  
     end
     

Modified: branches/biocat/app/controllers/networks_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/networks_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/networks_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -7,8 +7,9 @@
   before_filter :login_required, :except => [:index, :show, :search, :all]
   
   before_filter :find_networks, : [:all]
-  before_filter :find_network, : [:membership_request, :show, :comment, :comment_delete, :tag]
-  before_filter :find_network_auth, : [:invite, :membership_invite, :membership_invite_external, :edit, :update, :destroy]
+  before_filter :find_network, : [:membership_request, :show, :tag]
+  before_filter :find_network_auth_admin, : [:invite, :membership_invite, :membership_invite_external]
+  before_filter :find_network_auth_owner, : [:edit, :update, :destroy]
   
   # declare sweepers and which actions should invoke them
   cache_sweeper :network_sweeper, : [ :create, :update, :destroy, :membership_request, :membership_invite, :membership_invite_external ]
@@ -237,9 +238,115 @@
 
   # GET /networks/1
   def show
+
+    @item_sort_options = [
+      ["rank",          "Rank"],
+      ["most_recent",   "Most recent"],
+      ["title",         "Title"],
+      ["uploader",      "Uploader"],
+      ["last_updated",  "Last updated"],
+      ["rating",        "User rating"],
+      ["licence",       "Licence"],
+      ["content_type",  "Content Type"]
+    ]
+
     @shared_items = @network.shared_contributions
+
+    case params[:item_sort]
+
+      when "rank"; @shared_items.sort! do |a, b|
+        b.rank <=> a.rank
+      end
+
+      when "title"; @shared_items.sort! do |a, b|
+        a.contributable.label <=> b.contributable.label
+      end
+
+      when "most_recent"; @shared_items.sort! do |a, b|
+        b.contributable.created_at <=> a.contributable.created_at
+      end
+
+      when "uploader"; @shared_items.sort! do |a, b|
+        if a.contributor.label == b.contributor.label
+          b.rank <=> a.rank
+        else
+          a.contributor.label <=> b.contributor.label
+        end
+      end
+
+      when "last_updated"; @shared_items.sort! do |a, b|
+        b.contributable.updated_at <=> a.contributable.updated_at
+      end
+
+      when "rating"; @shared_items.sort! do |a, b|
+
+        a_rating = a.rating
+        b_rating = b.rating
+
+        if a_rating == b_rating
+          b.rank <=> a.rank
+        else
+          b.rating <=> a.rating
+        end
+      end
+
+      when "licence"; @shared_items.sort! do |a, b|
+
+        a_has_licence = a.contributable.respond_to?('license')
+        b_has_licence = b.contributable.respond_to?('license')
+
+        if (a_has_licence && b_has_licence)
+          if a.contributable.license == b.contributable.license
+            b.rank <=> a.rank
+          else
+            a.contributable.license.title <=> b.contributable.license.title
+          end
+        elsif (a_has_licence && !b_has_licence)
+          -1
+        elsif (!a_has_licence && b_has_licence)
+          1
+        else
+          b.rank <=> a.rank
+        end
+      end
+
+      when "content_type"; @shared_items.sort! do |a, b|
+
+        a_has_content_type = a.contributable.respond_to?('content_type')
+        b_has_content_type = b.contributable.respond_to?('content_type')
+
+        if (a_has_content_type && b_has_content_type)
+          if a.contributable.content_type == b.contributable.content_type
+            b.rank <=> a.rank
+          else
+            a.contributable.content_type.title <=> b.contributable.content_type.title
+          end
+        elsif (a_has_content_type && !b_has_content_type)
+          -1
+        elsif (!a_has_content_type && b_has_content_type)
+          1
+        else
+          b.rank <=> a.rank
+        end
+      end
+    end
+
     respond_to do |format|
-      format.html # show.rhtml
+      format.html {
+         
+        @lod_nir  = group_url(@network)
+        @lod_html = formatted_group_url(:id => @network.id, :format => 'html')
+        @lod_rdf  = formatted_group_url(:id => @network.id, :format => 'rdf')
+        @lod_xml  = formatted_group_url(:id => @network.id, :format => 'xml')
+         
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} groups address@hidden
+        }
+      end
     end
   end
 
@@ -294,46 +401,7 @@
       format.html { redirect_to groups_url }
     end
   end
-  
-  # POST /networks/1;comment
-  def comment
-    text = params[:comment][:comment]
-    ajaxy = true
-    
-    if text.nil? or (text.length == 0)
-      text = params[:comment_0_comment_editor]
-      ajaxy = false
-    end
-    
-    if text and text.length > 0
-      comment = Comment.create(:user => current_user, :comment => text)
-      @network.comments << comment
-    end
-    
-    respond_to do |format|
-      if ajaxy
-        format.html { render :partial => "comments/comments", :locals => { :commentable => @network } }
-      else
-        format.html { redirect_to group_url(@network) }
-      end
-    end
-  end
-  
-  # DELETE /networks/1;comment_delete
-  def comment_delete
-    if params[:comment_id]
-      comment = Comment.find(params[:comment_id].to_i)
-      # security checks:
-      if comment.user_id == current_user.id and comment.commentable_type.downcase == 'network' and comment.commentable_id == @network.id
-        comment.destroy
-      end
-    end
-    
-    respond_to do |format|
-      format.html { render :partial => "comments/comments", :locals => { :commentable => @network } }
-    end
-  end
-  
+ 
   # POST /networks/1;tag
   def tag
     @network.tags_user_id = current_user
@@ -373,14 +441,23 @@
     end 
   end
 
-  def find_network_auth
+  def find_network_auth_owner
     begin
       @network = Network.find(params[:id], :conditions => ["networks.user_id = ?", current_user.id], :include => [ :owner, :memberships ])
     rescue ActiveRecord::RecordNotFound
+      error("Group not found (id not authorized)", "is invalid (not group adminsitrator)")
+    end
+  end
+  
+  def find_network_auth_admin
+    begin
+      @network = Network.find(params[:id], :include => [ :owner, :memberships ])
+      raise unless @network.administrator?(current_user.id)
+    rescue ActiveRecord::RecordNotFound
       error("Group not found (id not authorized)", "is invalid (not owner)")
     end
   end
-  
+
 private
 
   def error(notice, message)

Modified: branches/biocat/app/controllers/packs_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/packs_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/packs_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -6,10 +6,9 @@
 class PacksController < ApplicationController
   include ApplicationHelper
   
-  before_filter :login_required, :except => [:index, :show, :all, :search, :items, :download, :statistics]
+  before_filter :login_required, :except => [:index, :show, :search, :items, :download, :statistics]
   
-  before_filter :find_packs, : [:all]
-  before_filter :find_pack_auth, :except => [:index, :new, :create, :all, :search]
+  before_filter :find_pack_auth, :except => [:index, :new, :create, :search]
   
   before_filter :set_sharing_mode_variables, : [:show, :new, :create, :edit, :update]
   
@@ -29,17 +28,26 @@
   # GET /packs
   def index
     respond_to do |format|
-      format.html # index.rhtml
+      format.html {
+        @pivot_options = pivot_options
+
+        begin
+          expr = parse_filter_expression(params["filter"]) if params["filter"]
+        rescue Exception => ex
+          puts "ex = #{ex.inspect}"
+          flash.now[:error] = "Problem with query _expression_: #{ex}"
+          expr = nil
+        end
+
+        @pivot = contributions_list(Contribution, params, current_user,
+            :lock_filter => { 'CATEGORY' => 'Pack' },
+            :filters     => expr)
+
+        # index.rhtml
+      }
     end
   end
   
-  # GET /packs/all
-  def all
-    respond_to do |format|
-      format.html # all.rhtml
-    end
-  end
-  
   # GET /packs/1
   def show
     if allow_statistics_logging(@pack)
@@ -47,7 +55,21 @@
     end
     
     respond_to do |format|
-      format.html # show.rhtml
+      format.html {
+        
+        @lod_nir  = pack_url(@pack)
+        @lod_html = formatted_pack_url(:id => @pack.id, :format => 'html')
+        @lod_rdf  = formatted_pack_url(:id => @pack.id, :format => 'rdf')
+        @lod_xml  = formatted_pack_url(:id => @pack.id, :format => 'xml')
+        
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} packs address@hidden
+        }
+      end
     end
   end
   
@@ -161,7 +183,7 @@
   
   # POST /packs/1;favourite
   def favourite
-    @pack.bookmarks << Bookmark.create(:user => current_user) unless @pack.bookmarked_by_user?(current_user)
+    @pack.bookmarks << Bookmark.create(:user => current_user, :bookmarkable => @pack) unless @pack.bookmarked_by_user?(current_user)
     
     respond_to do |format|
       flash[:notice] = "You have successfully added this item to your favourites."
@@ -184,45 +206,6 @@
     end
   end
   
-  # POST /packs/1;comment
-  def comment 
-    text = params[:comment][:comment]
-    ajaxy = true
-    
-    if text.nil? or (text.length == 0)
-      text = params[:comment_0_comment_editor]
-      ajaxy = false
-    end
-
-    if text and text.length > 0
-      comment = Comment.create(:user => current_user, :comment => text)
-      @pack.comments << comment
-    end
-    
-    respond_to do |format|
-      if ajaxy
-        format.html { render :partial => "comments/comments", :locals => { :commentable => @pack } }
-      else
-        format.html { redirect_to pack_url(@pack) }
-      end
-    end
-  end
-  
-  # DELETE /packs/1;comment_delete
-  def comment_delete
-    if params[:comment_id]
-      comment = Comment.find(params[:comment_id].to_i)
-      # security checks:
-      if comment.user_id == current_user.id and comment.commentable_type == 'Pack' and comment.commentable_id == @pack.id
-        comment.destroy
-      end
-    end
-    
-    respond_to do |format|
-      format.html { render :partial => "comments/comments", :locals => { :commentable => @pack } }
-    end
-  end
-  
   # POST /packs/1;tag
   def tag
     @pack.tags_user_id = current_user # acts_as_taggable_redux
@@ -422,13 +405,6 @@
     end
   end
   
-  def find_packs
-    @packs = Pack.find(:all, 
-                       :order => "title ASC",
-                       :page => { :size => 20, 
-                       :current => params[:page] })
-  end
-  
   def find_pack_auth
     begin
       pack = Pack.find(params[:id])

Modified: branches/biocat/app/controllers/pictures_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/pictures_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/pictures_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -6,6 +6,7 @@
 class PicturesController < ApplicationController
   before_filter :login_required, :except => [:index, :show]
   
+  before_filter :find_picture, : [:show]
   before_filter :find_pictures, : [:index]
   before_filter :find_picture_auth, : [:select, :edit, :update, :destroy]
   
@@ -36,21 +37,36 @@
   # GET /users/1/pictures/1
   # GET /pictures/1
   def show
-    size = params[:size] || "200x200"
-    size = size[0..-($1.length.to_i + 2)] if size =~ /[0-9]+x[0-9]+\.([a-z0-9]+)/ # trim file extension
+
+    matches = params[:size].match("([0-9]+)x([0-9]+).*") if params[:size]
+
+    default_size = 200
+    min_size     = 16
+    max_size     = 200
+
+    if matches
+
+      width  = matches[1].to_i
+      height = matches[2].to_i
+
+      if ((width < min_size) || (width > max_size) || (height < min_size) || (height > max_size))
+        width  = default_size
+        height = default_size
+      end
+
+    else
+      width  = 200
+      height = 200
+    end
     
-    id = params[:id].to_i
-
-    send_cached_data("public/pictures/show/#{size}/#{id}.jpg",
+    send_cached_data("public/pictures/show/#{width.to_i}x#{height.to_i}/#{params[:id].to_i}.jpg",
         :type => 'image/jpeg', :disposition => 'inline') {
 
-      find_picture
+      img = Magick::Image.from_blob(@picture.data).first
+      img = img.change_geometry("#{width}x#{height}>") do |c, r, i| i.resize(c, r) end
 
-      # resize and encode the picture
-      @picture.resize!(:size => size)
-      @picture.to_jpg!
-
-      @picture.data
+      img.format = "jpg"
+      img.to_blob
     }
 
   end
@@ -70,7 +86,7 @@
   # POST /users/1/pictures
   # POST /pictures
   def create
-    @picture = Picture.create(:data ="" params[:picture][:data], :user_id => current_user.id)
+    @picture = Picture.create(:data ="" params[:picture][:data].read, :user_id => current_user.id)
 
     respond_to do |format|
       if @picture.save

Copied: branches/biocat/app/controllers/relationships_controller.rb (from rev 2605, trunk/app/controllers/relationships_controller.rb) (0 => 2606)


--- branches/biocat/app/controllers/relationships_controller.rb	                        (rev 0)
+++ branches/biocat/app/controllers/relationships_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,84 @@
+# myExperiment: app/controllers/relationships_controller.rb
+#
+# Copyright (c) 2010 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class RelationshipsController < ApplicationController
+  
+  helper PacksHelper
+
+  before_filter :find_resource_context
+  before_filter :find_resource, :except => [ :edit_relationships, :create ]
+
+  # GET /:context_type/:context_id/edit_relationships
+  def edit_relationships
+
+    @predicates = Ontology.find(:all).map do |o| o.predicates end.flatten
+
+    @pack_entries = @context.contributable_entries + @context.remote_entries
+
+    @select_options = @pack_entries.map do |pe|
+      if pe.class == PackContributableEntry
+        [pe.contributable.label, "contributable:#{pe.id}"]
+      else
+        [pe.title, "remote:#{pe.id}"]
+      end
+    end
+  end
+
+  # POST /:context_type/:context_id/relationships
+  def create 
+
+    subject = @context.find_pack_item(params[:subject])
+    objekt  = @context.find_pack_item(params[:objekt])
+
+    prefix, title = params[:predicate].split(":")
+
+    predicate = Predicate.find(:first, :conditions =>
+        [' AND title = ?',
+           Ontology.find_by_prefix(prefix).id, title])
+
+    raise("Invalid form data") if subject.nil? || objekt.nil? || predicate.nil?
+
+    @relationship = Relationship.new(:context => @context, :predicate => predicate, :user => current_user)
+
+    @relationship.subject = subject
+    @relationship.objekt  = objekt
+
+    @relationship.save
+
+    redirect_to(:action ="" :edit_relationships)
+  end
+  
+  # DELETE /:context_type/:context_id/relationships/:id
+  def destroy
+
+   if Authorization.is_authorized?('destroy', nil, @relationship, current_user)
+      @relationship.destroy
+    end
+    
+    render :partial => "relationships/relationships",
+           :locals  => { :context => @context, :show_delete => true }
+  end
+
+  private
+
+  def find_resource
+
+    @context      = extract_resource_context(params)
+    @relationship = Relationship.find_by_id(params[:id])
+
+    return false if @relationship.nil? || @context.nil? || @relationship.context != @context
+    return false if Authorization.is_authorized?('view', nil, @context, current_user) == false
+  end
+
+  def find_resource_context
+
+    @context = extract_resource_context(params)
+
+    return false if @context.nil?
+    return false if Authorization.is_authorized?('view', nil, @context, current_user) == false
+  end
+
+end
+

Modified: branches/biocat/app/controllers/reviews_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/reviews_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/reviews_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -29,7 +29,21 @@
 
   def show
     respond_to do |format|
-      format.html # show.rhtml
+      format.html {
+        
+        @lod_nir  = review_url(:id => @review.id, :workflow_id => @reviewable.id)
+        @lod_html = formatted_workflow_review_url(:id => @review.id, :workflow_id => @reviewable.id, :format => 'html')
+        @lod_rdf  = formatted_workflow_review_url(:id => @review.id, :workflow_id => @reviewable.id, :format => 'rdf')
+        @lod_xml  = formatted_workflow_review_url(:id => @review.id, :workflow_id => @reviewable.id, :format => 'xml')
+        
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} reviews address@hidden
+        }
+      end
     end
   end
 
@@ -120,13 +134,15 @@
         @reviewable = workflow
       else
         if logged_in?
-          error("Workflow not found (id not authorized)", "is invalid (not authorized)", :workflow_id)
+          error("Workflow not found (id not authorized)", "is invalid (not authorized)")
+          return false
         else
           find_reviewable_auth if login_required
         end
       end
     rescue ActiveRecord::RecordNotFound
-      error("Workflow not found", "is invalid", :workflow_id)
+      error("Workflow not found", "is invalid")
+      return false
     end
   end
   
@@ -139,6 +155,7 @@
       @review = review
     else
       error("Review not found", "is invalid")
+      return false
     end
   end
   
@@ -147,17 +164,29 @@
       @review = review
     else
       error("Review not found or action not authorized", "is invalid (not authorized)")
+      return false
     end
   end
   
 private
 
-  def error(notice, message, attr=:id)
+  def error(notice, message, attr = nil)
     flash[:error] = notice
-    (err = Review.new.errors).add(attr, message)
+
+    workflow_id_attr = attr
+    workflow_id_attr = :id if workflow_id_attr.nil?
+
+    (err = Review.new.errors).add(workflow_id_attr, message)
     
     respond_to do |format|
-      format.html { redirect_to reviews_url(params[:workflow_id]) }
+      format.html {
+        if attr
+          redirect_to reviews_url(params[:workflow_id])
+        else
+          redirect_to workflows_url
+        end
+      }
     end
   end
 end
+

Modified: branches/biocat/app/controllers/runners_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/runners_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/runners_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -20,7 +20,21 @@
 
   def show
     respond_to do |format|
-      format.html # show.rhtml
+      format.html {
+
+        @lod_nir  = runner_url(@runner)
+        @lod_html = formatted_runner_url(:id => @runner.id, :format => 'html')
+        @lod_rdf  = formatted_runner_url(:id => @runner.id, :format => 'rdf')
+        @lod_xml  = formatted_runner_url(:id => @runner.id, :format => 'xml')
+
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} runners address@hidden
+        }
+      end
     end
   end
 

Modified: branches/biocat/app/controllers/search_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/search_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/search_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -4,6 +4,9 @@
 # See license.txt for details.
 
 class SearchController < ApplicationController
+
+  include ApplicationHelper
+
   def show
 
     if params[:query].nil? or params[:query] == ''
@@ -14,6 +17,8 @@
 
     @type = params[:type].to_s.downcase
     
+    @type = "all" if @type.nil? or @type == ""
+
     if !Conf.search_categories.include?(@type)
       error(@type)
       return false
@@ -168,63 +173,22 @@
   end
 
   def search_all
-    @query = params[:query] || ''
-    @query.strip!
 
-    @results = []
+    @query = params[:query]
 
-    if Conf.solr_enable && address@hidden
+    @pivot_options = pivot_options
 
-      categories = (Conf.search_categories - ['all']).map do |category|
-        if Conf.model_aliases.key?(category.camelize.singularize)
-          category = Conf.model_aliases[category.camelize.singularize].pluralize.underscore
-        end
-
-        category
-      end
-
-      models = categories.map do |category| eval(category.singularize.camelize) end
-
-      @total_count = 0
-
-      @infos = []
-
-      models.each do |model|
-
-        begin
-          model_results = model.find_by_solr(@query.downcase, :limit => 10)
-
-          results = model_results.results
-          count   = model_results.total
-        rescue
-          flash.now[:error] = "There was a problem with your search query."
-
-          results = []
-          count   = 0
-        end
-
-        search_type = model.name.downcase.pluralize
-
-        Conf.model_aliases.each do |k,v|
-          search_type = k.downcase.pluralize if model.name == v
-        end
-
-        if (results.length > 0)
-          @infos.push({
-            :search_type => search_type,
-            :model       => model,
-            :results     => results,
-            :total_count => count
-          })
-        end
-
-        @total_count += count
-      end
+    begin
+      expr = parse_filter_expression(params["filter"]) if params["filter"]
+    rescue Exception => ex
+      puts "ex = #{ex.inspect}"
+      flash.now[:error] = "Problem with query _expression_: #{ex}"
+      expr = nil
     end
 
-    respond_to do |format|
-      format.html # search.rhtml
-    end
+    @pivot = contributions_list(Contribution, params, current_user,
+        :filters => expr, :arbitrary_models => true)
+
   end
 
   def search_model

Modified: branches/biocat/app/controllers/tags_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/tags_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/tags_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -17,7 +17,21 @@
   
   def show
     respond_to do |format|
-      format.html # show.rhtml
+      format.html {
+
+        @lod_nir  = tag_url(@tag)
+        @lod_html = formatted_tag_url(:id => @tag.id, :format => 'html')
+        @lod_rdf  = formatted_tag_url(:id => @tag.id, :format => 'rdf')
+        @lod_xml  = formatted_tag_url(:id => @tag.id, :format => 'xml')
+
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} tags address@hidden
+        }
+      end
     end
   end
   

Modified: branches/biocat/app/controllers/userhistory_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/userhistory_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/userhistory_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -32,9 +32,22 @@
     
       rescue ActiveRecord::RecordNotFound
         error("User not found", "is invalid", :user_id)
+        return false
       end
     else
       @user = User.find(params[:id])
     end
   end
-end
\ No newline at end of file
+
+private
+
+  def error(notice, message, attr=:id)
+    flash[:error] = notice
+    (err = User.new.errors).add(attr, message)
+    
+    respond_to do |format|
+      format.html { redirect_to users_url }
+    end
+  end
+  
+end

Modified: branches/biocat/app/controllers/users_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/users_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/users_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -11,8 +11,8 @@
   before_filter :login_required, :except => [:index, :new, :create, :search, :all, :confirm_email, :forgot_password, :reset_password] + show_actions
   
   before_filter :find_users, : [:all]
-  before_filter :find_user, : show_actions
-  before_filter :find_user_auth, : [:edit, :update, :destroy]
+  before_filter :find_user, : [:destroy] + show_actions
+  before_filter :find_user_auth, : [:edit, :update]
   
   # declare sweepers and which actions should invoke them
   cache_sweeper :user_sweeper, : [ :create, :update, :destroy ]
@@ -39,6 +39,11 @@
   # GET /users/1
   def show
 
+    @lod_nir  = user_url(@user)
+    @lod_html = formatted_user_url(:id => @user.id, :format => 'html')
+    @lod_rdf  = formatted_user_url(:id => @user.id, :format => 'rdf')
+    @lod_xml  = formatted_user_url(:id => @user.id, :format => 'xml')
+
     @tab = "News" if @tab.nil?
 
     @user.salt = nil
@@ -46,6 +51,12 @@
     
     respond_to do |format|
       format.html # show.rhtml
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          render :inline => `#{Conf.rdfgen_tool} users address@hidden
+        }
+      end
     end
   end
 
@@ -135,10 +146,20 @@
 
     # check that captcha was entered correctly
 
-    if !captcha_valid?(params[:validation][:captcha_id], params[:validation][:captcha_validation])
-      flash.now[:error] = 'Verification text was not entered correctly - please try again.'
-      render :action ="" 'new'
-      return
+    unless RAILS_ENV == 'test'
+      if true
+        if !verify_recaptcha()
+          flash.now[:error] = 'Recaptcha text was not entered correctly - please try again.'
+          render :action ="" 'new'
+          return
+        end
+      else
+        if !captcha_valid?(params[:validation][:captcha_id], params[:validation][:captcha_validation])
+          flash.now[:error] = 'Verification text was not entered correctly - please try again.'
+          render :action ="" 'new'
+          return
+        end
+      end
     end
 
     if params[:user][:username] && params[:user][:password] && params[:user][:password_confirmation]
@@ -163,7 +184,7 @@
     respond_to do |format|
       if @user.save
         # DO NOT log in user yet, since account needs to be validated and activated first (through email).
-        Mailer.deliver_account_confirmation(@user, confirmation_hash(@user.unconfirmed_email), base_host)
+        @user.send_email_confirmation_email
         
         # If required, copy the email address to the Profile
         if params[:make_email_public]
@@ -209,7 +230,7 @@
         else
           # If a new email address was set, then need to send out a confirmation email
           if params[:user][:unconfirmed_email]
-            Mailer.deliver_update_email_address(@user, confirmation_hash(@user.unconfirmed_email), base_host)
+            @user.send_update_email_confirmation
             flash.now[:notice] = "We have sent an email to address@hidden with instructions on how to confirm this new email address"
           elsif params[:update_type]
             case params[:update_type]
@@ -232,20 +253,25 @@
 
   # DELETE /users/1
   def destroy
-    flash[:notice] = 'Please contact the administrator to have your account removed.'
-    redirect_to :action ="" :index
+
+    unless Authorization.check(:action ="" 'destroy', :object => @user, :user => current_user)
+      flash[:notice] = 'You do not have permission to delete this user.'
+      redirect_to :action ="" :index
+      return
+    end
     
-    address@hidden
+    @user.destroy
     
-    # the user MUST be logged in to destroy their account
+    # If the destroyed account belongs to the current user, then
     # it is important to log them out afterwards or they'll 
     # receive a nasty error message..
-    #session[:user_id] = nil
+
+    session[:user_id] = nil if @user == current_user
     
-    #respond_to do |format|
-    #  flash[:notice] = 'User was successfully destroyed'
-    #  format.html { redirect_to users_url }
-    #end
+    respond_to do |format|
+      flash[:notice] = 'User account was successfully deleted'
+      format.html { redirect_to(params[:return_to] ? "#{Conf.base_uri}#{params[:return_to]}" : users_url) }
+    end
   end
   
   # GET /users/confirm_email/:hash
@@ -261,7 +287,7 @@
     for user in @users
       unless user.unconfirmed_email.blank?
         # Check if hash matches user, in which case confirm the user's email
-        if confirmation_hash(user.unconfirmed_email) == params[:hash]
+        if user.email_confirmation_hash == params[:hash]
           confirmed = user.confirm_email!
           # BEGIN DEBUG
           logger.error("ERRORS!") unless user.errors.empty?
@@ -303,7 +329,7 @@
           user.reset_password_code_until = 1.day.from_now
           user.reset_password_code =  Digest::SHA1.hexdigest( "#{user.email}#{Time.now.to_s.split(//).sort_by {rand}.join}" )
           user.save!
-          Mailer.deliver_forgot_password(user, base_host)
+          Mailer.deliver_forgot_password(user)
           flash[:notice] = "Instructions on how to reset your password have been sent to #{user.email}"
           format.html { render :action ="" "forgot_password" }
         else
@@ -595,9 +621,5 @@
       format.html { redirect_to users_url }
     end
   end
-  
-  def confirmation_hash(string)
-    Digest::SHA1.hexdigest(string + Conf.secret_word)
-  end
-  
 end
+

Modified: branches/biocat/app/controllers/workflows_controller.rb (2605 => 2606)


--- branches/biocat/app/controllers/workflows_controller.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/controllers/workflows_controller.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -4,11 +4,13 @@
 # See license.txt for details.
 
 class WorkflowsController < ApplicationController
-  before_filter :login_required, :except => [:index, :show, :download, :named_download, :statistics, :launch, :search, :all]
+
+  include ApplicationHelper
+
+  before_filter :login_required, :except => [:index, :show, :download, :named_download, :galaxy_tool, :galaxy_tool_download, :statistics, :launch, :search]
   
-  before_filter :find_workflows, : [:all]
   before_filter :find_workflows_rss, : [:index]
-  before_filter :find_workflow_auth, :except => [:search, :index, :new, :create, :all]
+  before_filter :find_workflow_auth, :except => [:search, :index, :new, :create]
   
   before_filter :initiliase_empty_objects_for_new_pages, : [:new, :create, :new_version, :create_version]
   before_filter :set_sharing_mode_variables, : [:show, :new, :create, :edit, :update]
@@ -17,10 +19,10 @@
   before_filter :check_custom_workflow_type, : [:create, :create_version]
   
   before_filter :check_is_owner, : [:edit, :update]
-  
+
   # declare sweepers and which actions should invoke them
   cache_sweeper :workflow_sweeper, : [ :create, :create_version, :launch, :update, :update_version, :destroy_version, :destroy ]
-  cache_sweeper :download_viewing_sweeper, : [ :show, :download, :named_download, :launch ]
+  cache_sweeper :download_viewing_sweeper, : [ :show, :download, :named_download, :galaxy_tool, :galaxy_tool_download, :launch ]
   cache_sweeper :permission_sweeper, : [ :create, :update, :destroy ]
   cache_sweeper :bookmark_sweeper, : [ :destroy, :favourite, :favourite_delete ]
   cache_sweeper :tag_sweeper, : [ :create, :update, :tag, :destroy ]
@@ -62,56 +64,6 @@
     end
   end
   
-  # POST /workflows/1;comment
-  def comment
-    text = params[:comment][:comment]
-    ajaxy = true
-
-    if text.nil? or (text.length == 0)
-      text = params[:comment_0_comment_editor]
-      ajaxy = false
-    end
-    
-    if text and text.length > 0
-      comment = Comment.create(:user => current_user, :comment => text)
-      @workflow.comments << comment
-    end
-  
-    respond_to do |format|
-      if ajaxy
-        format.html { render :partial => "comments/comments", :locals => { :commentable => @workflow } }
-      else
-        format.html { redirect_to workflow_url(@workflow) }
-      end
-    end
-  end
-  
-  # DELETE /workflows/1;comment_delete
-  def comment_delete
-    if params[:comment_id]
-      comment = Comment.find(params[:comment_id].to_i)
-      comment.destroy if Authorization.check(:action ="" 'destroy', :object => comment, :user => current_user)
-    end
-    
-    respond_to do |format|
-      format.html { render :partial => "comments/comments", :locals => { :commentable => @workflow } }
-    end
-  end
-  
-  def comments_timeline
-    respond_to do |format|
-      format.html # comments_timeline.rhtml
-    end
-  end
-  
-  # For simile timeline
-  def comments
-    @comments = Comment.find(:all, :conditions => [ "commentable_id = ? AND commentable_type = ? AND created_at > ? AND created_at < ?", @workflow.id, 'Workflow', params[:start].to_time, params[:end].to_time ] )
-    respond_to do |format|
-      format.json { render :partial => 'comments/timeline_json', :layout => false }
-    end
-  end
-  
   # POST /workflows/1;rate
   def rate
     if @workflow.contribution.contributor_type == 'User' and @workflow.contribution.contributor_id == current_user.id
@@ -119,7 +71,7 @@
     else
       Rating.delete_all(["rateable_type = ? AND rateable_id = ? AND user_id = ?", @workflow.class.to_s, @workflow.id, current_user.id])
       
-      @workflow.ratings << Rating.create(:user => current_user, :rating => params[:rating])
+      Rating.create(:rateable => @workflow, :user => current_user, :rating => params[:rating])
       
       respond_to do |format|
         format.html { 
@@ -210,10 +162,57 @@
     end
   end
 
+  # GET /workflows/:id/versions/:version/galaxy_tool
+  def galaxy_tool
+  end
+
+  # GET /workflows/:id/versions/:version/galaxy_tool_download
+  def galaxy_tool_download
+
+    if params[:server].nil? || params[:server].empty?
+      flash.now[:error] = "You must provide the URL to a Taverna server."
+      render(:action ="" :galaxy_tool, :id => @workflow.id, :version => @viewing_version_number.to_s)
+      return
+    end
+
+    zip_file_name = "tmp/galaxy_tool.#{Process.pid}"
+
+    TavernaToGalaxy.generate(@workflow, @viewing_version_number, params[:server], zip_file_name)
+
+    zip_file = File.read(zip_file_name)
+    File.unlink(zip_file_name)
+
+    Download.create(:contribution => @workflow.contribution,
+        :user               => (logged_in? ? current_user : nil),
+        :user_agent         => request.env['HTTP_USER_AGENT'],
+        :accessed_from_site => accessed_from_website?(),
+        :kind               => 'Galaxy tool')
+
+    send_data(zip_file,
+        :filename => "address@hidden",
+        :type => 'application/zip',
+        :disposition => 'attachment')
+  end
+
   # GET /workflows
   def index
     respond_to do |format|
-      format.html # index.rhtml
+      format.html do
+        @pivot_options = pivot_options
+
+        begin
+          expr = parse_filter_expression(params["filter"]) if params["filter"]
+        rescue Exception => ex
+          puts "ex = #{ex.inspect}"
+          flash.now[:error] = "Problem with query _expression_: #{ex}"
+          expr = nil
+        end
+
+        @pivot = contributions_list(Contribution, params, current_user,
+            :lock_filter => { 'CATEGORY' => 'Workflow' },
+            :filters     => expr)
+
+      end
       format.rss do
         address@hidden = Workflow.find(:all, :order => "updated_at DESC") # list all (if required)
         render :action ="" 'index.rxml', :layout => false
@@ -221,16 +220,8 @@
     end
   end
   
-  # GET /workflows/all
-  def all
-    respond_to do |format|
-      format.html # all.rhtml
-    end
-  end
-
   # GET /workflows/1
   def show
-    logger.debug("DEBUG DEBUG DEBUG")
     if allow_statistics_logging(@viewing_version)
       @viewing = Viewing.create(:contribution => @workflow.contribution, :user => (logged_in? ? current_user : nil), :user_agent => request.env['HTTP_USER_AGENT'], :accessed_from_site => accessed_from_website?())
     end
@@ -244,7 +235,32 @@
     @similar_services_limit = 10
 
     respond_to do |format|
-      format.html # show.rhtml
+      format.html {
+
+        if params[:version]
+          @lod_nir  = workflow_version_url(:id => @workflow.id, :version => @viewing_version_number)
+          @lod_html = formatted_workflow_version_url(:id => @workflow.id, :version => @viewing_version_number, :format => 'html')
+          @lod_rdf  = formatted_workflow_version_url(:id => @workflow.id, :version => @viewing_version_number, :format => 'rdf')
+          @lod_xml  = formatted_workflow_version_url(:id => @workflow.id, :version => @viewing_version_number, :format => 'xml')
+        else
+          @lod_nir  = workflow_url(@workflow)
+          @lod_html = formatted_workflow_url(:id => @workflow.id, :format => 'html')
+          @lod_rdf  = formatted_workflow_url(:id => @workflow.id, :format => 'rdf')
+          @lod_xml  = formatted_workflow_url(:id => @workflow.id, :format => 'xml')
+        end
+
+        # show.rhtml
+      }
+
+      if Conf.rdfgen_enable
+        format.rdf {
+          if params[:version]
+            render :inline => `#{Conf.rdfgen_tool} workflows address@hidden versions/address@hidden
+          else
+            render :inline => `#{Conf.rdfgen_tool} workflows address@hidden
+          end
+        }
+      end
     end
   end
 
@@ -271,7 +287,7 @@
     @workflow = Workflow.new
     @workflow.contributor = current_user
     @workflow.last_edited_by = current_user.id
-    @workflow.license_id = params[:workflow][:license_id]
+    @workflow.license_id = params[:workflow][:license_id] == "0" ? nil : params[:workflow][:license_id]
     @workflow.content_blob = ContentBlob.new(:data ="" file.read)
     @workflow.file_ext = file.original_filename.split(".").last.downcase
     
@@ -332,7 +348,7 @@
         if policy_err_msg.blank?
         	flash[:notice] = 'Workflow was successfully created.'
           format.html {
-            if @workflow.get_tag_suggestions.length > 0
+            if (@workflow.get_tag_suggestions.length > 0 || (@workflow.body.nil? || @workflow.body == ''))
               redirect_to tag_suggestions_workflow_url(@workflow)
             else
               redirect_to workflow_url(@workflow)
@@ -435,7 +451,16 @@
 
         respond_to do |format|
           flash[:notice] = 'New workflow version successfully created.'
-          format.html { redirect_to workflow_url(@workflow) }
+          format.html {
+
+            @workflow.reload
+
+            if (@workflow.get_tag_suggestions.length > 0 || (@workflow.body.nil? || @workflow.body == ''))
+              redirect_to tag_suggestions_workflow_url(@workflow)
+            else
+              redirect_to workflow_url(@workflow)
+            end
+          }
         end
       else
         fail = true
@@ -471,6 +496,8 @@
       end
     end
     
+    params[:workflow][:license_id] = nil if params[:workflow][:license_id] && params[:workflow][:license_id] == "0"
+
     respond_to do |format|
       # Here we assume that no actual workflow metadata is being updated that affects workflow versions,
       # so we need to prevent the timestamping update of workflow version objects.
@@ -602,8 +629,13 @@
 
   def process_tag_suggestions
 
+    if params[:workflow] && params[:workflow][:body]
+      @workflow.body = params[:workflow][:body]
+      @workflow.save
+    end
+
     params[:tag_list].split(',').each do |tag|
-      @workflow.add_tag(tag, current_user)
+      @workflow.add_tag(tag.strip, current_user)
     end
 
     redirect_to(workflow_url(@workflow))
@@ -611,23 +643,10 @@
 
 protected
 
-  def find_workflows
-    found = Workflow.find(:all, 
-                          construct_options.merge({:page => { :size => 20, :current => params[:page] },
-                          :include => [ { :contribution => :policy }, :tags, :ratings ],
-                          :order => "workflows.updated_at DESC" }))
-    
-    found.each do |workflow|
-      workflow.content_blob.data = "" unless Authorization.is_authorized?('download', nil, workflow, current_user)
-    end
-    
-    @workflows = found
-  end
-  
   def find_workflows_rss
     # Only carry out if request is for RSS
     if params[:format] and params[:format].downcase == 'rss'
-      @rss_workflows = Authorization.authorised_index(:type => Workflow, :limit => 30, :order => 'updated_at DESC', :user => current_user)
+      @rss_workflows = Authorization.authorised_index(Workflow, :all, :limit => 30, :order => 'updated_at DESC', :authorised_user => current_user)
     end
   end
   

Modified: branches/biocat/app/helpers/application_helper.rb (2605 => 2606)


--- branches/biocat/app/helpers/application_helper.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/helpers/application_helper.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -394,7 +394,7 @@
       end
     when "Pack"
       if p = Pack.find(:first, :conditions => ["id = ?", contributableid])
-        return link ? link_to(p.title, pack_url(p)) : h(p.title)
+        return link ? link_to(h(p.title), pack_url(p)) : h(p.title)
       else
         return nil
       end
@@ -781,6 +781,10 @@
       return "famfamfam_silk/text_signature.png"
     when "home"
       return "famfamfam_silk/application_home.png"
+    when "make_group_admin"
+      return "famfamfam_silk/award_star_add.png"
+    when "remove_group_admin"
+      return "famfamfam_silk/award_star_delete.png"
     when "service"
       return "biocat_icon.png"
     else
@@ -837,7 +841,7 @@
   def workflows_for_attribution_form
     workflows = Workflow.find(:all, :select => 'workflows.id, workflows.title, users.name',
         :joins => 'LEFT OUTER JOIN users ON workflows.contributor_type = "User" AND workflows.contributor_id = users.id',
-        :order => 'workflows.title ASC')
+        :order => 'workflows.id ASC')
 
     workflows.select { |w| Authorization.is_authorized?('show', 'Workflow', w.id, current_user) }
   end
@@ -845,7 +849,7 @@
   def blobs_for_attribution_form
     blobs = Blob.find(:all, :select => 'blobs.id, blobs.title, users.name',
         :joins => 'LEFT OUTER JOIN users ON blobs.contributor_type = "User" AND blobs.contributor_id = users.id',
-        :order => 'blobs.title ASC')
+        :order => 'blobs.id ASC')
 
     blobs.select { |b| Authorization.is_authorized?('show', 'Blob', b.id, current_user) }
   end
@@ -976,7 +980,7 @@
   
   def friend_badge(user)
     if user and logged_in? and user.id != current_user.id
-      return image_tag("friend_badge.png", :class => 'badge') if (user.friend? current_user.id)
+      return image_tag("friend_badge.png", :class => 'badge') if (current_user.friend? user.id)
     else 
       return ''
     end
@@ -1003,7 +1007,7 @@
   end
   
   def tooltip_title_attrib(text, delay=200)
-    return "header=[] body=[#{text}] cssheader=[boxoverTooltipHeader] cssbody=[boxoverTooltipBody] delay=[#{delay}]"
+    return "header=[] body=[#{h(text)}] cssheader=[boxoverTooltipHeader] cssbody=[boxoverTooltipBody] delay=[#{delay}]"
   end
   
   # This method checks to see if the current user is allowed to approve a membership that is still pending approval
@@ -1012,7 +1016,7 @@
       if membership.user_established_at == nil
         return membership.user_id == current_user.id
       elsif membership.network_established_at == nil
-        return current_user.id == membership.network.owner.id
+        return membership.network.administrator?(current_user.id)
       end 
     else
       return false

Modified: branches/biocat/app/helpers/packs_helper.rb (2605 => 2606)


--- branches/biocat/app/helpers/packs_helper.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/helpers/packs_helper.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -78,4 +78,18 @@
     
     return tags
   end
+
+  def pack_entry_link(entry)
+
+    case entry.class.name
+    when "PackContributableEntry"
+      text = entry.contributable.label
+      href = ""
+    when "PackRemoteEntry"
+      text = entry.title
+      href = ""
+    end
+
+    link_to(text, href)
+  end
 end

Modified: branches/biocat/app/models/blob.rb (2605 => 2606)


--- branches/biocat/app/models/blob.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/blob.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -25,8 +25,8 @@
   acts_as_attributor
   acts_as_attributable
   
-  acts_as_solr(:fields => [:title, :local_name, :body, :kind, :uploader],
-               :boost => "search_boost",
+  acts_as_solr(:fields => [:title, :local_name, :body, :kind, :uploader, :tag_list],
+               :boost => "rank",
                :include => [ :comments ]) if Conf.solr_enable
 
   belongs_to :content_blob
@@ -37,7 +37,6 @@
   # :dependent => :destroy is not supported in belongs_to in rails 1.2.6
   after_destroy { |b| b.content_blob.destroy }
 
-  validates_presence_of :license_id
   validates_presence_of :content_blob
   validates_presence_of :content_type
 
@@ -51,7 +50,7 @@
 
   alias_method :kind, :type
 
-  def search_boost
+  def rank
 
     # initial boost depends on viewings count
     boost = contribution.viewings_count / 100
@@ -68,4 +67,8 @@
   def named_download_url
     "#{Conf.base_uri}/files/#{id}/download/#{local_name}"
   end
+
+  def statistics_for_rest_api
+    APIStatistics.statistics(self)
+  end
 end

Copied: branches/biocat/app/models/concept.rb (from rev 2605, trunk/app/models/concept.rb) (0 => 2606)


--- branches/biocat/app/models/concept.rb	                        (rev 0)
+++ branches/biocat/app/models/concept.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,17 @@
+# myExperiment: app/models/concept.rb
+#
+# Copyright (c) 2010 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class Concept < ActiveRecord::Base
+
+  acts_as_structured_data
+
+  format_attribute :description
+
+  def preferred_label
+    preferred_labels.first
+  end
+
+end
+

Modified: branches/biocat/app/models/contribution.rb (2605 => 2606)


--- branches/biocat/app/models/contribution.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/contribution.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -3,7 +3,9 @@
 # Copyright (c) 2007 University of Manchester and the University of Southampton.
 # See license.txt for details.
 
+
 class Contribution < ActiveRecord::Base
+
   belongs_to :contributor, :polymorphic => true
   belongs_to :contributable, :polymorphic => true
   belongs_to :policy
@@ -15,7 +17,7 @@
   has_many :viewings,
            :order => "created_at DESC",
            :dependent => :destroy
-           
+
   # returns the 'most downloaded' Contributions
   # (only takes into account downloads, that is internal usage)
   # the maximum number of results is set by #limit#
@@ -44,14 +46,14 @@
   
   # returns the 'most recent' Contributions
   # the maximum number of results is set by #limit#
-  def self.most_recent(limit=10, klass=nil)
-    Authorization.authorised_index(:type => klass ? Object.const_get(klass) : nil, :contribution_records => true, :limit => limit, :order => 'created_at DESC')
+  def self.most_recent(limit = 10, klass = 'Contribution')
+    Authorization.authorised_index(Object.const_get(klass), :all, :contribution_records => true, :limit => limit, :order => 'created_at DESC')
   end
   
   # returns the 'last updated' Contributions
   # the maximum number of results is set by #limit#
-  def self.last_updated(limit=10, klass=nil)
-    Authorization.authorised_index(:type => klass ? Object.const_get(klass) : nil, :contribution_records => true, :limit => limit, :order => 'updated_at DESC')
+  def self.last_updated(limit = 10, klass = 'Contribution')
+    Authorization.authorised_index(Object.const_get(klass), :all, :contribution_records => true, :limit => limit, :order => 'updated_at DESC')
   end
   
   # returns the 'most favourited' Contributions

Modified: branches/biocat/app/models/download.rb (2605 => 2606)


--- branches/biocat/app/models/download.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/download.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,12 +1,17 @@
 class Download < ActiveRecord::Base
-  belongs_to :contribution,
-             :counter_cache => true
+
+  belongs_to :contribution
+  belongs_to :user
              
-  belongs_to :user,
-             :counter_cache => true
-             
   validates_presence_of :contribution
   
+  after_save { |download|
+    Contribution.increment_counter(:downloads_count,      download.contribution.id)
+    Contribution.increment_counter(:site_downloads_count, download.contribution.id) if download.accessed_from_site
+
+    User.increment_counter(:downloads_count, download.user.id) if download.user
+  }
+
   # returns the 'most recent' Downloads #after# a given time
   # the maximum number of results is set by #limit#
   def self.most_recent(after=(Time.now - 3.hours), limit=10)

Modified: branches/biocat/app/models/invitation.rb (2605 => 2606)


--- branches/biocat/app/models/invitation.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/invitation.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -73,14 +73,14 @@
       # decide which action to make
       case type
         when "invite"
-          Mailer.deliver_invite_new_user(user, email_addr, msg_text, base_host)
+          Mailer.deliver_invite_new_user(user, email_addr, msg_text)
         when "group_invite"
           group = Network.find(id_of_group_to_join)
-          Mailer.deliver_group_invite_new_user(user, group, email_addr, msg_text, token, base_host)
+          Mailer.deliver_group_invite_new_user(user, group, email_addr, msg_text, token)
         when "friendship_invite"
-          Mailer.deliver_friendship_invite_new_user(user, email_addr, msg_text, token, base_host)
+          Mailer.deliver_friendship_invite_new_user(user, email_addr, msg_text, token)
       end
     }    
   end
   
-end
\ No newline at end of file
+end

Modified: branches/biocat/app/models/mailer.rb (2605 => 2606)


--- branches/biocat/app/models/mailer.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/mailer.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -12,30 +12,27 @@
          :content => content
   end
   
-  def account_confirmation(user, hash, base_url)
+  def account_confirmation(user, hash)
     recipients user.unconfirmed_email
     from Conf.notifications_email_address
     subject "Welcome to #{Conf.sitename}. Please activate your account."
 
     body :name => user.name, 
          :user => user,
-         :hash => hash, 
-         :base_url => base_url
+         :hash => hash
   end
   
-  def forgot_password(user, base_url)
+  def forgot_password(user)
     recipients user.email
     from Conf.notifications_email_address
     subject "#{Conf.sitename} - Reset Password Request"
 
     body :name => user.name, 
          :user => user,
-         :reset_code => user.reset_password_code, 
-         :base_url => base_url
-         
+         :reset_code => user.reset_password_code
   end
   
-  def update_email_address(user, hash, base_url)
+  def update_email_address(user, hash)
     recipients user.unconfirmed_email
     from Conf.notifications_email_address
     subject "#{Conf.sitename} - Update Email Address on Account"
@@ -43,22 +40,20 @@
     body :name => user.name, 
          :user => user,
          :hash => hash, 
-         :base_url => base_url,
          :email => user.unconfirmed_email
   end
   
-  def invite_new_user(user, email, msg_text, base_url)
+  def invite_new_user(user, email, msg_text)
     recipients email
     from user.name + "<" + Conf.notifications_email_address + ">"
     subject "Invitation to join #{Conf.sitename}"
 
     body :name => user.name, 
          :user_id => user.id, 
-         :message => msg_text,
-         :base_url => base_url
+         :message => msg_text
   end
 
-  def group_invite_new_user(user, group, email, msg_text, token, base_url)
+  def group_invite_new_user(user, group, email, msg_text, token)
     recipients email
     from user.name + "<" + Conf.notifications_email_address + ">"
     subject "Invitation to join group \"#{group.title}\" at #{Conf.sitename}"
@@ -69,11 +64,10 @@
          :group_title => group.title,
          :email => email,
          :message => msg_text,
-         :token => token,
-         :base_url => base_url
+         :token => token
   end
   
-  def friendship_invite_new_user(user, email, msg_text, token, base_url)
+  def friendship_invite_new_user(user, email, msg_text, token)
     recipients email
     from user.name + "<" + Conf.notifications_email_address + ">"
     subject "Invitation to become my friend on #{Conf.sitename}"
@@ -82,8 +76,7 @@
          :user_id => user.id,
          :email => email,
          :message => msg_text,
-         :token => token,
-         :base_url => base_url
+         :token => token
   end
 
 end

Modified: branches/biocat/app/models/membership.rb (2605 => 2606)


--- branches/biocat/app/models/membership.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/membership.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -10,9 +10,9 @@
 
   validates_presence_of :user_id, :network_id
 
-  validates_each :user_id do |model, attr, value|
-    model.errors.add attr, "already member" if model.network.member? value
-  end
+#  validates_each :user_id do |model, attr, value|
+#    model.errors.add attr, "already member" if model.network.member? value
+#  end
 
   def user_establish!
     if self.user_established_at.nil?

Modified: branches/biocat/app/models/network.rb (2605 => 2606)


--- branches/biocat/app/models/network.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/network.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -143,6 +143,27 @@
     return false
   end
   
+  def administrators(incl_owner=true)
+    explicit_administrators = User.find(:all,
+                                 :select     => "users.*",
+                                 :joins      => "JOIN memberships m on (users.id = m.user_id)",
+                                 :conditions => [ "m.network_id=? AND m.administrator AND m.user_established_at IS NOT NULL AND m.network_established_at IS NOT NULL", id ],
+                                 :order      => "GREATEST(m.user_established_at, m.network_established_at) DESC"
+                                )
+    return incl_owner ? ( [owner] + explicit_administrators ) : explicit_administrators
+  end
+
+  def administrator?(userid)
+    # the owner is automatically an adminsitrator of the network
+    return true if owner? userid
+    
+    administrators(false).each do |a|
+      return true if a.id.to_i == userid.to_i
+    end
+    
+    return false
+  end
+                          
   # Finds all the contributions that have been explicitly shared via Permissions
   def shared_contributions
     list = []

Copied: branches/biocat/app/models/ontology.rb (from rev 2605, trunk/app/models/ontology.rb) (0 => 2606)


--- branches/biocat/app/models/ontology.rb	                        (rev 0)
+++ branches/biocat/app/models/ontology.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,15 @@
+# myExperiment: app/models/ontology.rb
+#
+# Copyright (c) 2011 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class Ontology < ActiveRecord::Base
+
+  acts_as_structured_data
+
+  format_attribute(:description)
+
+  validates_presence_of(:uri, :title, :prefix)
+
+end
+

Modified: branches/biocat/app/models/pack.rb (2605 => 2606)


--- branches/biocat/app/models/pack.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/pack.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -22,12 +22,14 @@
   acts_as_rateable
   acts_as_taggable
 
+  acts_as_structured_data
+
   validates_presence_of :title
   
   format_attribute :description
   
   acts_as_solr(:fields => [ :title, :description, :contributor_name, :tag_list ],
-               :boost => "search_boost",
+               :boost => "rank",
                :include => [ :comments ]) if Conf.solr_enable
   
   has_many :contributable_entries,
@@ -608,6 +610,40 @@
     contributable_entries.map do |e| e.contributable end
   end
 
+  # This function takes a string, such as 'contributable:4' (which would return
+  # a PackContributableEntry with the id of 4) or 'remote:8' (which would
+  # return a PackRemoteEntry with an id of 8) and returns the appropriate pack
+  # item if this pack contains that item.
+
+  def find_pack_item(str)
+
+    thing_type, thing_id = str.split(":")
+
+    case thing_type
+      when 'contributable'
+        ob = PackContributableEntry.find(:first,
+            :conditions => ['id = ? AND pack_id = ?', thing_id, id])
+
+      when 'remote'
+        ob = PackRemoteEntry.find(:first,
+            :conditions => ['id = ? AND pack_id = ?', thing_id, id])
+    end
+  end
+
+  # This method determines which pack relationships refer to contributables
+  # that are not included as pack entries in this pack.  Such relationships
+  # might occur when deleting entries from a pack.
+
+  def dangling_relationships
+    relationships.select do |relationship|
+      relationship.subject.nil? || relationship.objekt.nil?
+    end
+  end
+
+  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
@@ -893,7 +929,7 @@
     return item_data
   end
   
-  def search_boost
+  def rank
 
     # initial boost depends on viewings count
     boost = contribution.viewings_count / 100

Modified: branches/biocat/app/models/picture.rb (2605 => 2606)


--- branches/biocat/app/models/picture.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/picture.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -3,8 +3,8 @@
 # Copyright (c) 2007 University of Manchester and the University of Southampton.
 # See license.txt for details.
 
-#class Picture < ActiveRecord::Base
-class Picture < FlexImage::Model
+class Picture < ActiveRecord::Base
+
   validates_associated :owner
   
   validates_presence_of :user_id, :data
@@ -32,12 +32,4 @@
   def selected?
     owner.profile.avatar? and owner.profile.picture.id.to_i == id.to_i
   end
-  
-  #file_column :data, :magick => {
-  #  :versions => {
-  #    :small    => { :size => "50x50!" }, 
-  #    :medium   => { :size => "100x100!" },
-  #    :large => { :size => "200x200!" }
-  #  }
-  #}
 end

Copied: branches/biocat/app/models/predicate.rb (from rev 2605, trunk/app/models/predicate.rb) (0 => 2606)


--- branches/biocat/app/models/predicate.rb	                        (rev 0)
+++ branches/biocat/app/models/predicate.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,15 @@
+# myExperiment: app/models/predicate.rb
+#
+# Copyright (c) 2011 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class Predicate < ActiveRecord::Base
+
+  acts_as_structured_data
+
+  format_attribute(:description)
+
+  validates_presence_of(:title)
+
+end
+

Modified: branches/biocat/app/models/rating.rb (2605 => 2606)


--- branches/biocat/app/models/rating.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/rating.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -6,6 +6,9 @@
 class Rating < ActiveRecord::Base
   belongs_to :rateable, :polymorphic => true
   
+  after_save    :update_contribution_rating
+  after_destroy :update_contribution_rating
+      
   # NOTE: Comments belong to a user
   belongs_to :user
   
@@ -17,4 +20,13 @@
       :order => "created_at DESC"
     )
   end
+
+  private
+
+  def update_contribution_rating
+    if rateable.contribution
+      rateable.update_contribution_rating
+    end
+  end
 end
+

Copied: branches/biocat/app/models/relationship.rb (from rev 2605, trunk/app/models/relationship.rb) (0 => 2606)


--- branches/biocat/app/models/relationship.rb	                        (rev 0)
+++ branches/biocat/app/models/relationship.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,13 @@
+# myExperiment: app/models/relationship.rb
+#
+# Copyright (c) 2010 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class Relationship < ActiveRecord::Base
+
+  acts_as_structured_data
+
+  validates_uniqueness_of :predicate_id, :scope => [:subject_id, :objekt_id]
+
+end
+

Copied: branches/biocat/app/models/search_result.rb (from rev 2605, trunk/app/models/search_result.rb) (0 => 2606)


--- branches/biocat/app/models/search_result.rb	                        (rev 0)
+++ branches/biocat/app/models/search_result.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,9 @@
+# myExperiment: app/models/search_result.rb
+#
+# Copyright (c) 2011 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class SearchResult < ActiveRecord::Base
+  belongs_to(:result, :polymorphic => true)
+end
+

Modified: branches/biocat/app/models/user.rb (2605 => 2606)


--- branches/biocat/app/models/user.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/user.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -432,16 +432,31 @@
            :order => "created_at DESC",
            :dependent => :destroy
            
-  def networks_membership_requests_pending
+  def networks_membership_requests_pending(include_group_admin=false)
     rtn = []
     
-    networks_owned.each do |n|
+    networks_admined(include_group_admin).each do |n|
       rtn.concat n.memberships_requested
     end
     
     return rtn
   end
   
+  def networks_admined(include_group_admin=false)
+    rtn = []
+
+    rtn.concat(networks_owned)
+
+    if include_group_admin
+      rtn.concat Network.find(:all,
+                   :select => "networks.*",
+                   :joins => "JOIN memberships m ON (networks.id = m.network_id)",
+                   :conditions => ["m.user_id=? AND m.user_established_at is NOT NULL AND m.network_established_at IS NOT NULL AND m.administrator", id])
+    end
+
+    return rtn
+  end
+                          
   def networks_membership_invites_pending
     rtn = []
     
@@ -570,7 +585,19 @@
     return [0,0] if result_set.empty?
     return [result_set[0]["avg_rating"], result_set[0]["rating_count"]]
   end
+
+  def send_email_confirmation_email
+    Mailer.deliver_account_confirmation(self, email_confirmation_hash)
+  end
   
+  def send_update_email_confirmation
+    Mailer.deliver_update_email_address(self, email_confirmation_hash)
+  end
+
+  def email_confirmation_hash
+    Digest::SHA1.hexdigest(unconfirmed_email + Conf.secret_word)
+  end
+
 protected
 
   # clean up emails and username before validation
@@ -678,4 +705,6 @@
   def self.clean_string(string)
     (string.downcase).gsub(" ","")
   end
+
 end
+

Modified: branches/biocat/app/models/viewing.rb (2605 => 2606)


--- branches/biocat/app/models/viewing.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/viewing.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,12 +1,17 @@
 class Viewing < ActiveRecord::Base
-  belongs_to :contribution,
-             :counter_cache => true
+
+  belongs_to :contribution
+  belongs_to :user
              
-  belongs_to :user,
-             :counter_cache => true
-             
   validates_presence_of :contribution
   
+  after_save { |viewing|
+    Contribution.increment_counter(:viewings_count,      viewing.contribution.id)
+    Contribution.increment_counter(:site_viewings_count, viewing.contribution.id) if viewing.accessed_from_site
+
+    User.increment_counter(:viewings_count, viewing.user.id) if viewing.user
+  }
+
   # returns the 'most recent' Viewings #after# a given time
   # the maximum number of results is set by #limit#
   def self.most_recent(after=(Time.now - 3.hours), limit=10)

Modified: branches/biocat/app/models/vocabulary.rb (2605 => 2606)


--- branches/biocat/app/models/vocabulary.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/vocabulary.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -5,12 +5,15 @@
 
 class Vocabulary < ActiveRecord::Base
 
+  acts_as_structured_data
+
   belongs_to :user
 
-  has_many :tags, :dependent => :destroy
-
   validates_presence_of :title
+  validates_presence_of :prefix
 
+  validates_uniqueness_of :prefix
+
   format_attribute :description
 end
 

Modified: branches/biocat/app/models/workflow.rb (2605 => 2606)


--- branches/biocat/app/models/workflow.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/models/workflow.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -79,12 +79,19 @@
     # This is required to keep the contribution's updated_at field accurate.
     after_save { |wv| wv.workflow.contribution.save if wv.workflow.contribution && wv.version != wv.workflow.current_version }
     
+    def components
+      if workflow.processor_class
+        workflow.processor_class.new(content_blob.data).get_components
+      else
+        XML::Node.new('components')
+      end
+    end
   end
   
   non_versioned_columns.push("license_id", "tag_list")
   
   acts_as_solr(:fields => [ :title, :body, :tag_list, :contributor_name, :kind, :get_all_search_terms ],
-               :boost => "search_boost",
+               :boost => "rank",
                :include => [ :comments ]) if Conf.solr_enable
 
   acts_as_runnable
@@ -96,7 +103,6 @@
   validates_presence_of :unique_name
   validates_uniqueness_of :unique_name
   
-  validates_presence_of :license_id
   validates_presence_of :content_blob
   validates_presence_of :content_type
 
@@ -283,7 +289,7 @@
 
   alias_method :kind, :type
 
-  def search_boost
+  def rank
 
     # initial boost depends on viewings count
     boost = contribution.viewings_count / 100
@@ -297,6 +303,14 @@
     boost
   end
 
+  def show_download_section?
+    if processor_class
+      processor_class.show_download_section?
+    else
+      true
+    end
+  end
+
   def delete_metadata
     if processor_class
       WorkflowProcessor.destroy_all(["workflow_id = ?", id])
@@ -339,4 +353,9 @@
 
     related_workflows.map do |result| result[0] end
   end
+
+  def statistics_for_rest_api
+    APIStatistics.statistics(self)
+  end
+
 end

Modified: branches/biocat/app/views/announcements/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/announcements/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/announcements/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -33,3 +33,6 @@
 		<%= @announcement.body_html %>
 	</div>
 </div>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Deleted: branches/biocat/app/views/blobs/_license_form.rhtml (2605 => 2606)


--- branches/biocat/app/views/blobs/_license_form.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/blobs/_license_form.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,46 +0,0 @@
-<% if params[:blob] && !params[:blob][:license_id].blank? %>
-	<% @license = License.find(params[:blob][:license_id]) %>
-<% elsif edit %>
-	<% @license = License.find(@blob.license_id) %>
-<% else %>
-    <% @license = License.find(2) %>
-<% end %>
-
-<div class="fold">
-    <div class="foldTitle">
-      <%= info_icon_with_tooltip("This section allows you to specify the <strong>rights</strong> that people have when they download and use this File, by setting the license. <br/><br/>By default, the license specifies that people are allowed to build on this File as long as they give the original author credit and share their resulting work under the same conditions.") %>
-      License/Rights
-			<% if edit %>
-				<hr />
-				<small>Current: <%= @license.title %> (<%= link_to h(@license.url), @license.url %>) </small>
-			<% else %>
-				<hr />
-				<small>Default: <%= @license.title %> (<%= link_to h(@license.url), @license.url %>)</small>
-			<% end %>
-    </div>
-    <div class="foldContent" style="display: none;">
-        <p class="box_infotext">
-            This section allows you to specify the <strong>rights</strong> that people have when they download and use this File, by setting the license.
-        </p>
-        <br />
-        <p>
-            <strong>What license do you want people to adhere to if they download and use this File?</strong>
-        </p>
-        <div style="padding-left: 1em;">
-          <%= select(:blob, :license_id, License.find(:all).collect {|l| [l.title, l.id] }, 
-	    { :selected => @license.id },
-	    {  : remote_function(:update => 'license_info',
-	         :url ="" {:controller => 'licenses', :action="" 'update_license_info' },
-	         :with => "'license_id=' + escape(value)")}) %>
-
-        </div>
-        <hr/>
-        <div id="license_info" style="padding: 0 20px;">
-          <%= render :partial => "licenses/view", :locals => { :license => @license } %>
-		</div>
-    </div>
-</div>
-                
-
-                
-                

Modified: branches/biocat/app/views/blobs/_table.rhtml (2605 => 2606)


--- branches/biocat/app/views/blobs/_table.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/blobs/_table.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -61,7 +61,11 @@
 							</p>
 						<% end %>
 						
-						<p style="font-size:85%;"><b>License: </b><% @license = License.find(blob.license_id) %><%= link_to h(@license.title), license_path(@license) %></p>
+            <% if blob.license_id.nil? %>
+              <p style="font-size:85%;"><b>License: </b>No license</p>
+            <% else %>
+              <p style="font-size:85%;"><b>License: </b><% @license = License.find(blob.license_id) %><%= link_to h(@license.title), license_path(@license) %></p>
+            <% end %>
 						
 						<div class="desc" style="font-size: 85%;">
 							<% if blob.body and blob.body.length > 0 %>
@@ -77,8 +81,8 @@
 						<p style="font-size: 85%;">
 							<a href="" file_path(blob) + '#ratings' -%>"><b>Rating: </b><%= number_with_precision(blob.rating, 1) %> / 5 (<%= pluralize blob.ratings_count, 'rating' %>)</a> |
 							<a href="" file_path(blob) + '#comments' -%>"><b>Comments: </b><%= blob.comments_count %></a> |
-							<b>Viewed:</b> <%= pluralize Viewing.total_site_viewings_count_for_contribution(blob.contribution.id), "time" %> |
-							<b>Downloaded:</b> <%= pluralize Download.total_site_downloads_count_for_contribution(blob.contribution.id), "time" %>
+							<b>Viewed:</b> <%=pluralize blob.contribution.site_viewings_count, "time" %> |
+				      <b>Downloaded:</b> <%=pluralize blob.contribution.site_downloads_count, "time" %>
 						</p>
 						
 						<% unless (tags = blob.tags).empty? %>

Deleted: branches/biocat/app/views/blobs/all.rhtml (2605 => 2606)


--- branches/biocat/app/views/blobs/all.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/blobs/all.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,9 +0,0 @@
-<% t "All" -%>
-
-<h2>All Files</h2>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @blobs } %>
-
-<%= render :partial => "blobs/table", :locals => { :collection => @blobs } %>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @blobs } %>
\ No newline at end of file

Modified: branches/biocat/app/views/blobs/edit.rhtml (2605 => 2606)


--- branches/biocat/app/views/blobs/edit.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/blobs/edit.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -31,8 +31,8 @@
   <%= render :partial => "contributions/credit_attribution_form", :locals => { :edit => true, :contributable => @blob } %>
 
   <% if @blob.owner?(current_user) %>
-  	<%= render :partial => "contributions/sharing_form",  :locals => { :edit => true, :contributable => @blob, :update_perms => true } %>
-	<%= render :partial => "blobs/license_form", :locals => { :edit => true } %>
+  	<%= render :partial => "contributions/sharing_form", :locals => { :edit => true, :contributable => @blob, :update_perms => true } %>
+    <%= render :partial => "contributions/license_form", :locals => { :object => :blob, :contributable => @blob, :edit => true } %>
   <% end %>
 
   <p>

Deleted: branches/biocat/app/views/blobs/index.rhtml (2605 => 2606)


--- branches/biocat/app/views/blobs/index.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/blobs/index.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,17 +0,0 @@
-<div class="box_standout" style="text-align: center; margin-bottom: 1em; font-weight: bold; line-height: 1.5em;">
-	<% Blob.count(:all, :group => 'content_type_id').sort{|x,y| y[1] <=> x[1]}.each do |arr| %>
-		  <span class="nowrap"><%= link_to((h(ContentType.find_by_id(arr[0]).title)),
-        search_files_path + "?query=kind:(#{ContentType.find_by_id(arr[0]).title})") %> (<%= arr[1] %>)</span>
-	<% end %>
-</div>
-
-<ul class="sectionIcons">
-	<li><%= icon "blob", new_file_path, nil, nil, "Upload New File" %></li>
-	<li><%= icon "view-all", all_files_path, nil, nil, "View All Files" %></li>
-</ul>
-
-<% cache(:controller => 'files', :action ="" 'all_tags') do -%>
-	<%= render :partial => "blobs/all_tags" %>
-<% end -%>
-
-<%= render :partial => "contributions/most_tabs", :locals => { :type => "Blob" } %>

Copied: branches/biocat/app/views/blobs/index.rhtml (from rev 2605, trunk/app/views/blobs/index.rhtml) (0 => 2606)


--- branches/biocat/app/views/blobs/index.rhtml	                        (rev 0)
+++ branches/biocat/app/views/blobs/index.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,4 @@
+<h1>Files</h1>
+
+<%= render :partial => "content/index" -%>
+

Modified: branches/biocat/app/views/blobs/new.rhtml (2605 => 2606)


--- branches/biocat/app/views/blobs/new.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/blobs/new.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -39,7 +39,7 @@
 
   <%= render :partial => "contributions/sharing_form",  :locals => { :edit => false, :contributable => @blob, :update_perms => true } %>
   
-  <%= render :partial => "blobs/license_form", :locals => { :edit => false } %>
+  <%= render :partial => "contributions/license_form", :locals => { :object => :blob, :contributable => @blob, :edit => false } %>
   
   <%= render :partial => 'contributions/terms_and_conditions' %>
 	

Modified: branches/biocat/app/views/blobs/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/blobs/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/blobs/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -174,3 +174,6 @@
 <div id="commentsBox">
 	<%= render :partial => "comments/comments", :locals => { :commentable => @blob } %>
 </div>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Modified: branches/biocat/app/views/comments/_comment.rhtml (2605 => 2606)


--- branches/biocat/app/views/comments/_comment.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/comments/_comment.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -21,7 +21,7 @@
 					[
 					<%= link_to_remote( "delete",
 							:update => "commentsBox", 
-							:url ="" { :controller => controller.controller_name.to_s, :action ="" "comment_delete", :comment_id => comment.id },
+							:url ="" rest_resource_uri(comment.commentable) + "/comments/#{comment.id}",
 							:method => :delete,
 							:complete => "new Effect.Highlight('commentsBox', { duration: 1.5 }); $('comment').value = '';",
 							:confirm => "Are you sure you want to delete this comment?" ) %>

Modified: branches/biocat/app/views/comments/_comments.rhtml (2605 => 2606)


--- branches/biocat/app/views/comments/_comments.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/comments/_comments.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,18 +1,14 @@
 <%= _javascript__include_tag :fckeditor %>
 
-<% url_to_timeline ||= false -%>
-
 <h2>
 	<%= icon "comments", nil, nil, { :style => "vertical-align: middle;" }, "" -%>
 	<span style="vertical-align: middle;">Comments</span>
 	<span class="count_text" style="vertical-align: middle;">(<%= commentable.comments.length %>)</span>
 </h2>
 
-<% if url_to_timeline -%>
-	<ul class="sectionIcons">
-		<li><%= icon('timeline', url_to_timeline, nil, nil, 'View Timeline')%></li>
-	</ul>
-<% end -%>
+<ul class="sectionIcons">
+  <li><%= icon('timeline', rest_resource_uri(commentable) + "/comments/timeline", nil, nil, 'View Timeline')%></li>
+</ul>
 
 <div class="commentsBox">
 	
@@ -45,7 +41,7 @@
 			<% # Hack for FCKEditor: -%>
 			<% @comment = Comment.new; @comment.id = 0 -%>
 			
-			<% form_remote_tag(:url ="" { :controller => controller.controller_name.to_s, :action ="" "comment" },
+			<% form_remote_tag(:url ="" rest_resource_uri(commentable) + "/comments",
 								 :before => fckeditor_before_js("comment", "comment"),
 							   :update => 'commentsBox', 
 							   :loading => "Element.show('addcomment_indicator')",

Deleted: branches/biocat/app/views/comments/_timeline.rhtml (2605 => 2606)


--- branches/biocat/app/views/comments/_timeline.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/comments/_timeline.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,37 +0,0 @@
-<%
-  comments_timeline = ::Simile::Timeline::Timeline.new(
-    :name        => "Comments",
-    :event_source => comments_url_for_timeline,
-    :event_band   => :days,
-    :bands       => {
-      :months => {
-        :trackGap       => '0.1',
-        :showEventText  => 'false',
-        :width          => '"25%"',
-        :intervalUnit   => 'Timeline.DateTime.MONTH',
-        :intervalPixels => '200'
-      },
-      :days => {
-        :trackGap       => '0.1',
-        :showEventText  => 'false',
-        :width          => '"25%"',
-        :intervalUnit   => 'Timeline.DateTime.DAY',
-        :intervalPixels => '100'
-      },
-      :hours => {
-        :width          => '"50%"',
-        :intervalUnit   => 'Timeline.DateTime.HOUR',
-        :intervalPixels => '20'
-      }
-    },
-    :bands_order => [ :months, :days, :hours ],
-    :synchronize => {
-      :months => :hours,
-      :days   => :hours
-    },
-    :highlight => [ :months, :days ]
-  )
-
-%>
-
-<%= simile_timeline(comments_timeline, { :class => 'default_timeline' } ) %>
\ No newline at end of file

Copied: branches/biocat/app/views/comments/timeline.rhtml (from rev 2605, trunk/app/views/comments/timeline.rhtml) (0 => 2606)


--- branches/biocat/app/views/comments/timeline.rhtml	                        (rev 0)
+++ branches/biocat/app/views/comments/timeline.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,37 @@
+<%
+  comments_timeline = ::Simile::Timeline::Timeline.new(
+    :name        => "Comments",
+    :event_source => rest_resource_uri(@context) + "/comments.json",
+    :event_band   => :days,
+    :bands       => {
+      :months => {
+        :trackGap       => '0.1',
+        :showEventText  => 'false',
+        :width          => '"25%"',
+        :intervalUnit   => 'Timeline.DateTime.MONTH',
+        :intervalPixels => '200'
+      },
+      :days => {
+        :trackGap       => '0.1',
+        :showEventText  => 'false',
+        :width          => '"25%"',
+        :intervalUnit   => 'Timeline.DateTime.DAY',
+        :intervalPixels => '100'
+      },
+      :hours => {
+        :width          => '"50%"',
+        :intervalUnit   => 'Timeline.DateTime.HOUR',
+        :intervalPixels => '20'
+      }
+    },
+    :bands_order => [ :months, :days, :hours ],
+    :synchronize => {
+      :months => :hours,
+      :days   => :hours
+    },
+    :highlight => [ :months, :days ]
+  )
+
+%>
+
+<%= simile_timeline(comments_timeline, { :class => 'default_timeline' } ) %>

Copied: branches/biocat/app/views/contributions/_alternative_formats.rhtml (from rev 2605, trunk/app/views/contributions/_alternative_formats.rhtml) (0 => 2606)


--- branches/biocat/app/views/contributions/_alternative_formats.rhtml	                        (rev 0)
+++ branches/biocat/app/views/contributions/_alternative_formats.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,13 @@
+<div class="box_standout" style="margin: 1.5em 3em; padding: 0.7em 1.5em">
+  <p style="float: right"><a href="" is this?</a></p>
+  <p style="float: left; font-size: 120%; font-weight: bold">Linked Data</p>
+  <div style="clear: both"></div>
+  <p><b>Non-Information Resource URI:</b> <%= link_to(@lod_nir, h(@lod_nir)) -%></p>
+  <br>
+  <p style="font-size: 110%; font-weight: bold">Alternative Formats</p>
+  <div style="width: 33%; float: left; text-align: center"><a href="" @lod_html -%>"><img src="" image_path("html_icon.png") -%>" alt="HTML" /></a></div>
+  <div style="width: 33%; float: left; text-align: center"><a href="" @lod_rdf -%>"><img src="" image_path("rdf_icon.png") -%>" alt="RDF" /></a></div>
+  <div style="width: 33%; float: left; text-align: center"><a href="" @lod_xml -%>"><img src="" image_path("xml_icon.png") -%>" alt="XML" /></a></div>
+  <div style="clear: both"></div>
+</div>
+

Modified: branches/biocat/app/views/contributions/_credit_attribution_form.rhtml (2605 => 2606)


--- branches/biocat/app/views/contributions/_credit_attribution_form.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/contributions/_credit_attribution_form.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -140,7 +140,7 @@
           <br />
           <select id="existingworkflows_dropdown" style="width: 280px;">
             <% workflows_for_attribution_form.each do |w| %>
-            	<option value="<%= w.id %>"><%= h(w.title) -%> (<%= h(w.name) -%>)</option>
+            	<option value="<%= w.id %>"><%=h "#{w.id}: #{w.title} (#{w.name})" -%></option>
             <% end %>
           </select>
           <br />
@@ -154,7 +154,7 @@
           <br />
           <select id="existingfiles_dropdown" style="width: 280px;">
             <% blobs_for_attribution_form.each do |b| %>
-            	<option value="<%= b.id %>"><%= h(b.title) -%> (<%= h(b.name) -%>)</option>
+            	<option value="<%= b.id %>"><%=h "#{b.id}: #{b.title} (#{b.name})" -%></option>
             <% end %>
           </select>
           <br />

Modified: branches/biocat/app/views/contributions/_in_packs_box.rhtml (2605 => 2606)


--- branches/biocat/app/views/contributions/_in_packs_box.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/contributions/_in_packs_box.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,7 +1,7 @@
 <% packs = Pack.packs_with_contributable(contributable) -%>
 <div class="contribution_section_box">
 	<p class="heading">
-		<%= info_icon_with_tooltip("All the Packs that this item (or versions of this item [if available]) is referred to in") -%>
+		<%= info_icon_with_tooltip("All the Packs that this item (or versions of this item [[if available]]) is referred to in") -%>
 		Featured In Packs  <span class="count_text">(<%= packs.length -%>)</span>
 		<a name="featured_in_packs"></a>
 	</p>

Modified: branches/biocat/app/views/contributions/_license_box.rhtml (2605 => 2606)


--- branches/biocat/app/views/contributions/_license_box.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/contributions/_license_box.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -7,44 +7,47 @@
 		<a name="license"></a>
 	</p>
 
+  <% if contributable.license_id %>
 
-<% dc_title = contributable.title %>
-<% dc_description = h(contributable.respond_to?('description_html') ? contributable.description_html : contributable.body_html) %>
-<% dc_date = contributable.created_at %>
-<% dc_creator = contributor(contributable.contribution.contributor_id, contributable.contribution.contributor_type, link=false)  %>
-<% dc_source = Conf.base_uri + request.request_uri %>
+    <% dc_title = contributable.title %>
+    <% dc_description = h(contributable.respond_to?('description_html') ? contributable.description_html : contributable.body_html) %>
+    <% dc_date = contributable.created_at %>
+    <% dc_creator = contributor(contributable.contribution.contributor_id, contributable.contribution.contributor_type, link=false)  %>
+    <% dc_source = Conf.base_uri + request.request_uri %>
 
 
-<% @license = License.find(contributable.license_id) %>
+    <% @license = License.find(contributable.license_id) %>
 
-<%=  "<!--
+    <%=  "<!--
 
-<rdf:RDF xmlns=\"http://creativecommons.org/ns#\"
-    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
-    xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">
-<Work rdf:about=\"\">
-   <dc:title>#{dc_title}</dc:title>
-   <dc:date>#{dc_date}</dc:date>
-   <dc:description>#{dc_description}</dc:description>
-   <dc:creator><Agent>
-      <dc:title>#{dc_creator}</dc:title>
-   </Agent></dc:creator>
-   <dc:rights><Agent>
-      <dc:title>#{dc_creator}</dc:title>
-   </Agent></dc:rights>
-   <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/Data\" />
-   <dc:source rdf:resource=\"#{dc_source}\"/>
-   <license rdf:resource=\"address@hidden" />
-</Work>
+    <rdf:RDF xmlns=\"http://creativecommons.org/ns#\"
+        xmlns:dc=\"http://purl.org/dc/elements/1.1/\"
+        xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">
+    <Work rdf:about=\"\">
+       <dc:title>#{dc_title}</dc:title>
+       <dc:date>#{dc_date}</dc:date>
+       <dc:description>#{dc_description}</dc:description>
+       <dc:creator><Agent>
+          <dc:title>#{dc_creator}</dc:title>
+       </Agent></dc:creator>
+       <dc:rights><Agent>
+          <dc:title>#{dc_creator}</dc:title>
+       </Agent></dc:rights>
+       <dc:type rdf:resource=\"http://purl.org/dc/dcmitype/Data\" />
+       <dc:source rdf:resource=\"#{dc_source}\"/>
+       <license rdf:resource=\"address@hidden" />
+    </Work>
 
-<License rdf:about=\"address@hidden">"%>
-<% @license.license_attributes.each do |attrib| %><%= "  <#{attrib.predicate} rdf:resource=\"#{attrib.uri}\" />" %><% end %>
-</License>
+    <License rdf:about=\"address@hidden">"%>
+    <% @license.license_attributes.each do |attrib| %><%= "  <#{attrib.predicate} rdf:resource=\"#{attrib.uri}\" />" %><% end %>
+    </License>
 
-</rdf:RDF>
+    </rdf:RDF>
 
--->
+    -->
 
+  <% end %>
+
   <div class="contribution_currentlicense">
     <p>
       <% if contributable.respond_to?('versions') %>
@@ -52,7 +55,11 @@
       <% else %>
         This <%= visible_name(contributable) -%> is
       <% end %>
-      licensed under: <br/><br/><%= license_icon_link(@license) %>
+      <% if contributable.license_id %>
+        licensed under: <br/><br/><%= license_icon_link(@license) %>
+      <% else %>
+        not licensed.
+      <% end %>
     </p>
   </div>
 	

Copied: branches/biocat/app/views/contributions/_license_form.rhtml (from rev 2605, trunk/app/views/contributions/_license_form.rhtml) (0 => 2606)


--- branches/biocat/app/views/contributions/_license_form.rhtml	                        (rev 0)
+++ branches/biocat/app/views/contributions/_license_form.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,51 @@
+<% if params[object] && !params[object][:license_id].blank? %>
+	<% @license = License.find(params[object][:license_id]) %>
+<% elsif edit %>
+	<% @license = License.find_by_id(contributable.license_id) %>
+<% else %>
+    <% @license = License.find(2) %>
+<% end %>
+
+<div class="fold">
+    <div class="foldTitle">
+      <%= info_icon_with_tooltip("This section allows you to specify the <strong>rights</strong> that people have when they download and use this File, by setting the license. <br/><br/>By default, the license specifies that people are allowed to build on this File as long as they give the original author credit and share their resulting work under the same conditions.") %>
+      License/Rights
+			<% if edit %>
+				<hr />
+        <% if @license %>
+          <small>Current: <%= @license.title %> (<%= link_to h(@license.url), @license.url %>) </small>
+        <% else %>
+          <small>Current: No license</small>
+        <% end %>
+			<% else %>
+				<hr />
+        <% if @license %>
+          <small>Default: <%= @license.title %> (<%= link_to h(@license.url), @license.url %>)</small>
+        <% else %>
+          <small>Default: No license</small>
+        <% end %>
+			<% end %>
+    </div>
+    <div class="foldContent" style="display: none;">
+        <p class="box_infotext">
+            This section allows you to specify the <strong>rights</strong> that people have when they download and use this File, by setting the license.
+        </p>
+        <br />
+        <p>
+            <strong>What license do you want people to adhere to if they download and use this File?</strong>
+        </p>
+        <div style="padding-left: 1em;">
+          <%= select(object, :license_id, [["No license", 0]] + License.find(:all).collect {|l| [l.title, l.id] },
+            { :selected => @license ? @license.id : 0 },
+            {  : remote_function(:update => 'license_info',
+                 :url ="" {:controller => 'licenses', :action="" 'update_license_info' },
+                 :with => "'license_id=' + escape(value)")}) %>
+
+        </div>
+        <hr/>
+        <div id="license_info" style="padding: 0 20px;">
+          <%= render :partial => "licenses/view", :locals => { :license => @license } %>
+		</div>
+    </div>
+</div>
+

Modified: branches/biocat/app/views/experiments/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/experiments/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/experiments/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -67,3 +67,6 @@
 		<% end -%>
 	</div>
 </div>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Modified: branches/biocat/app/views/gadgets/_asset_manager.rhtml (2605 => 2606)


--- branches/biocat/app/views/gadgets/_asset_manager.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/gadgets/_asset_manager.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,7 +1,6 @@
 <% gadget_characters_limit ||= 20 -%>
 
 <div class="myexperiment_gadget">
-  <b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
 
   <div class="header">
     <p>My Stuff</p>
@@ -72,6 +71,5 @@
     <% end %>
   
   </div>
-  
-  <b class="xbottom"><b class="xb5"></b><b class="xb6"></b><b class="xb7"></b><b class="xb1"></b></b>
+
 </div>

Modified: branches/biocat/app/views/gadgets/_most_popular_tags.rhtml (2605 => 2606)


--- branches/biocat/app/views/gadgets/_most_popular_tags.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/gadgets/_most_popular_tags.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,6 +1,5 @@
 <% limit ||= 25 -%>
 <div class="myexperiment_gadget">
-  <b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
 
   <div class="header">
     <p>
@@ -15,6 +14,5 @@
 		<%= tag_cloud(limit) %>
 	</div>
   </div>
-  
-  <b class="xbottom"><b class="xb5"></b><b class="xb6"></b><b class="xb7"></b><b class="xb1"></b></b>
-</div>
\ No newline at end of file
+
+</div>

Modified: branches/biocat/app/views/gadgets/_network_monitor.rhtml (2605 => 2606)


--- branches/biocat/app/views/gadgets/_network_monitor.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/gadgets/_network_monitor.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,5 +1,4 @@
 <div class="myexperiment_gadget">
-  <b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
 
   <div class="header">
     <p>
@@ -40,5 +39,4 @@
     <% end %>
   </div>
   
-  <b class="xbottom"><b class="xb5"></b><b class="xb6"></b><b class="xb7"></b><b class="xb1"></b></b>
-</div>
\ No newline at end of file
+</div>

Modified: branches/biocat/app/views/gadgets/_new_object.rhtml (2605 => 2606)


--- branches/biocat/app/views/gadgets/_new_object.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/gadgets/_new_object.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -9,7 +9,6 @@
 </script>
 
 <div class="myexperiment_gadget">
-  <b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
 
   <div class="header">
     <p>
@@ -33,5 +32,4 @@
     </div>
   </div>
   
-  <b class="xbottom"><b class="xb5"></b><b class="xb6"></b><b class="xb7"></b><b class="xb1"></b></b>
 </div>

Modified: branches/biocat/app/views/gadgets/_sign_in.rhtml (2605 => 2606)


--- branches/biocat/app/views/gadgets/_sign_in.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/gadgets/_sign_in.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,7 +1,6 @@
 <% unless logged_in? %>
 
 <div class="myexperiment_gadget">
-  <b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
     
   <div class="header">
     <p>Log in / Register</p>
@@ -56,7 +55,6 @@
 		</p>
   </div>
   
-  <b class="xbottom"><b class="xb5"></b><b class="xb6"></b><b class="xb7"></b><b class="xb1"></b></b>
 </div>
 
 <% end %>

Modified: branches/biocat/app/views/gadgets/_user_favourites.rhtml (2605 => 2606)


--- branches/biocat/app/views/gadgets/_user_favourites.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/gadgets/_user_favourites.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -2,7 +2,6 @@
 <% bookmarkables = current_user.bookmarks.collect {|b| b.bookmarkable} %>
 
 <div class="myexperiment_gadget">
-  <b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
 
   <div class="header">
     <p>
@@ -40,5 +39,4 @@
     </div>
 	</div>
   
-  <b class="xbottom"><b class="xb5"></b><b class="xb6"></b><b class="xb7"></b><b class="xb1"></b></b>
 </div>

Modified: branches/biocat/app/views/gadgets/_user_monitor.rhtml (2605 => 2606)


--- branches/biocat/app/views/gadgets/_user_monitor.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/gadgets/_user_monitor.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -3,115 +3,113 @@
 <% trunc_length = 22 -%>
 
 <div class="myexperiment_gadget">
-  <b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
 
-  <div class="header">
-      <center><%= render :partial => "users/avatar", :locals => { :user => current_user, :size => 60 } %></center>
-  </div>
-  
-  <div id="myexp_usermonitor_gadget_contents" class="contents">
-	<ul class="gadget_list">
-	  <li>
-	  	<%= icon "profile", user_url(current_user), nil, nil, " My Profile" %>
-			<span style="color: #666666; margin-left: 0.5em;">
-				<small>[ <%= link_to "edit", edit_user_profile_path(current_user) -%> ]</small>
-			</span>
-		</li>
-    <li><%= messages_link(current_user, "My Messages") %></li>
-    <li><%= memberships_link(current_user, "My Memberships") %></li>
-	  <li><%= icon "history", user_userhistory_url(current_user), nil, nil, "My History" %></li>
-	  <li><%= icon "news", home_url, nil, nil, "My News" %></li>
-  </ul>
-	
-	<% if admin? %>
-		<hr/>	
+	<div class="header">
+		<center><%= render :partial => "users/avatar", :locals => { :user => current_user, :size => 60 } %></center>
+	</div>
+
+	<div id="myexp_usermonitor_gadget_contents" class="contents">
 		<ul class="gadget_list">
-			<li><%= icon "announcement", announcements_url, nil, nil, "Manage Announcements" %></li>
+			<li>
+			<%= icon "profile", user_url(current_user), nil, nil, " My Profile" %>
+				<span style="color: #666666; margin-left: 0.5em;">
+					<small>[ <%= link_to "edit", edit_user_profile_path(current_user) -%> ]</small>
+				</span>
+			</li>
+			<li><%= messages_link(current_user, "My Messages") %></li>
+			<li><%= memberships_link(current_user, "My Memberships") %></li>
+			<li><%= icon "history", user_userhistory_url(current_user), nil, nil, "My History" %></li>
+			<li><%= icon "news", home_url, nil, nil, "My News" %></li>
 		</ul>
-	<% end %>
-	
-    <% unless current_user.messages_unread.empty? %>
-	  <hr/>
-      <div>
-        <div class="title">
-        	<%= pluralize(current_user.messages_unread.length, "new message") %>
+
+		<% if admin? %>
+			<hr/>
+			<ul class="gadget_list">
+				<li><%= icon "announcement", announcements_url, nil, nil, "Manage Announcements" %></li>
+			</ul>
+		<% end %>
+
+		<% unless current_user.messages_unread.empty? %>
+			<hr/>
+			<div>
+				<div class="title">
+					<%= pluralize(current_user.messages_unread.length, "new message") %>
 				</div>
-        <div class="content">
-          <ul class="gadget_list">
-            <% current_user.messages_unread.each do |message| %>
-              <li>
-             		<%= icon "message", message_path(message), nil, nil, "" %>
+				<div class="content">
+					<ul class="gadget_list">
+						<% current_user.messages_unread.each do |message| %>
+							<li>
+								<%= icon "message", message_path(message), nil, nil, "" %>
 								<b><%= link_to truncate(h(message.subject), trunc_length), message_path(message), :title => tooltip_title_attrib(h(message.subject)) -%></b>
 							</li>
-            <% end %>
-          </ul>
-        </div>
-      </div>
-    <% end %>
-    
-    <% unless current_user.friendships_pending.empty? %>
-    <hr/>
-	  <div>
-        <div class="title">
-        	<%= pluralize(current_user.friendships_pending.length, "new friendship request") %>
+						<% end %>
+					</ul>
 				</div>
-        <div class="content">
-          <ul class="gadget_list">
-            <% current_user.friendships_pending.each do |friendship| %>
-              <li>
-              	<%= icon "user", user_friendship_path(current_user, friendship), nil, nil, "" %>
-								<b><%= link_to truncate(h(friendship.user.name), trunc_length), user_friendship_path(current_user, friendship), :title => tooltip_title_attrib(h(friendship.user.name)) -%></b>
-							</li>
-            <% end %>
-          </ul>
-        </div>
-      </div>
-    <% end %>
-	
+			</div>
+		<% end %>
+
+		<% unless current_user.friendships_pending.empty? %>
+			<hr/>
+			<div>
+				<div class="title">
+					<%= pluralize(current_user.friendships_pending.length, "new friendship request") %>
+						</div>
+							<div class="content">
+								<ul class="gadget_list">
+									<% current_user.friendships_pending.each do |friendship| %>
+										<li>
+											<%= icon "user", user_friendship_path(current_user, friendship), nil, nil, "" %>
+											<b><%= link_to truncate(h(friendship.user.name), trunc_length), user_friendship_path(current_user, friendship), :title => tooltip_title_attrib(h(friendship.user.name)) -%></b>
+										</li>
+									<% end %>
+								</ul>
+							</div>
+						</div>
+					<% end %>
+
 		<% unless (membership_invites = current_user.memberships_invited).empty? %>
-    <hr/>
-	  <div>
-        <div class="title">
-        	<%= info_icon_with_tooltip("You have been invited to join groups") %>
-        	<%= pluralize(membership_invites.length, "new group invite") %>
+			<hr/>
+			<div>
+				<div class="title">
+					<%= info_icon_with_tooltip("You have been invited to join groups") %>
+					<%= pluralize(membership_invites.length, "new group invite") %>
 				</div>
-        <div class="content">
-          <ul class="gadget_list">
-            <% membership_invites.each do |membership| %>
-              <li>
-              	<%= icon "membership", membership_path(membership.user, membership), nil, nil, "" -%>
+				<div class="content">
+					<ul class="gadget_list">
+						<% membership_invites.each do |membership| %>
+							<li>
+								<%= icon "membership", membership_path(membership.user, membership), nil, nil, "" -%>
 								<b><%= link_to truncate("From #{h(membership.network.owner.name)}", trunc_length), membership_path(membership.user, membership), :title => tooltip_title_attrib("From #{h(membership.network.owner.name)}") -%></b>
 								<span style="margin-left: 20px; display: block;"><small>(for Group: <%= title membership.network_id %>)</small></span>
-						  </li>
-            <% end %>
-          </ul>
-        </div>
-      </div>
-    <% end %>
-	
-		<% unless (membership_requests = current_user.networks_membership_requests_pending).empty? %>
-    <hr/>
-	  <div>
-        <div class="title">
-        	<%= info_icon_with_tooltip("Users are requesting to join your groups") %>
-        	<%= pluralize(membership_requests.length, "new group request") %>
+							</li>
+						<% end %>
+					</ul>
 				</div>
-        <div class="content">
-          <ul class="gadget_list">
-            <% membership_requests.each do |membership| %>
-              <li>
-              	<%= icon "membership", membership_path(membership.user, membership), nil, nil, "" -%>
+			</div>
+		<% end %>
+
+		<% unless (membership_requests = current_user.networks_membership_requests_pending(true)).empty? %>
+			<hr/>
+			<div>
+				<div class="title">
+					<%= info_icon_with_tooltip("Users are requesting to join your groups") %>
+					<%= pluralize(membership_requests.length, "new group request") %>
+				</div>
+				<div class="content">
+					<ul class="gadget_list">
+						<% membership_requests.each do |membership| %>
+							<li>
+								<%= icon "membership", membership_path(membership.user, membership), nil, nil, "" -%>
 								<b><%= link_to truncate("From #{h(membership.user.name)}", trunc_length), membership_path(membership.user, membership), :title => tooltip_title_attrib("From #{h(membership.user.name)}") -%></b>
 								<span style="margin-left: 20px; display: block;"><small>(for Group: <%= title membership.network_id %>)</small></span>
-			  			</li>
-            <% end %>
-          </ul>
-        </div>
-      </div>
-    <% end %>
-  </div>
-  
-  <b class="xbottom"><b class="xb5"></b><b class="xb6"></b><b class="xb7"></b><b class="xb1"></b></b>
+							</li>
+						<% end %>
+					</ul>
+				</div>
+			</div>
+		<% end %>
+	</div>
+
 </div>
 
 <% end %>

Modified: branches/biocat/app/views/gadgets/_user_tags.rhtml (2605 => 2606)


--- branches/biocat/app/views/gadgets/_user_tags.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/gadgets/_user_tags.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,7 +1,6 @@
 <% tags = current_user.tags %>
 
 <div class="myexperiment_gadget">
-  <b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
 
   <div class="header">
     <p>
@@ -22,5 +21,4 @@
 		<% end %>
   </div>
   
-  <b class="xbottom"><b class="xb5"></b><b class="xb6"></b><b class="xb7"></b><b class="xb1"></b></b>
 </div>

Modified: branches/biocat/app/views/group_announcements/_table.rhtml (2605 => 2606)


--- branches/biocat/app/views/group_announcements/_table.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/group_announcements/_table.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -30,7 +30,7 @@
 					</td>
 			    <td class="actions"  style="width: 80px;">
 		      	<%= icon "show", group_announcement_path(group, announcement), nil, nil, "View" %>
-						<% if (current_user != 0) && (group.owner?(current_user.id)) %>
+						<% if (current_user != 0) && ((announcement.user==current_user) || (group.owner?(current_user.id))) %>
 							<%= icon('edit', edit_group_announcement_path(group, announcement), nil, nil, 'Edit') %>
 							<%= icon("destroy", group_announcement_path(group, announcement), "Delete", :confirm => "Are you sure you want to delete this announcement?", :method => :delete) %>
 						<% end %>

Modified: branches/biocat/app/views/group_announcements/index.rhtml (2605 => 2606)


--- branches/biocat/app/views/group_announcements/index.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/group_announcements/index.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -5,7 +5,7 @@
 <% end %>
 
 <h1>
-	<%= feed_icon_tag "Group address@hidden Announcements", formatted_group_announcements_path(@group, :rss) %>
+	<%= feed_icon_tag "Group #{h(@group.title)} Announcements", formatted_group_announcements_path(@group, :rss) %>
 	<%= @group.announcements_in_public_mode_for_user(current_user) ? "Public " : "All " -%> Group Announcements (<%= @announcements.length %>)
 	<br/>
 	<span style="font-size: 77%;">for group: <%= link_to_function h(@group.title) + expand_image, visual_effect(:toggle_blind, "group_box", :duration => 0.3) -%></span>

Modified: branches/biocat/app/views/group_announcements/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/group_announcements/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/group_announcements/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -46,3 +46,6 @@
 		<%= @announcement.body_html %>
 	</div>
 </div>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Modified: branches/biocat/app/views/home/_latest_groups.rhtml (2605 => 2606)


--- branches/biocat/app/views/home/_latest_groups.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/home/_latest_groups.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -12,7 +12,7 @@
 						<% if Authorization.is_authorized?("show", nil, g, current_user) %>
 							<li>
 								<%= icon('network', nil, nil, 'group icon', '') %>
-								<b><%= link_to g.title, group_path(g) %></b> 
+								<b><%= link_to h(g.title), group_path(g) %></b> 
 								by <b><%= contributor(g.user_id, 'User') %></b>
 								(<%= timeago_fuzzy g.created_at %>)
 							</li>

Modified: branches/biocat/app/views/jobs/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/jobs/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/jobs/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -348,3 +348,6 @@
 </div>
 
 <br/>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Modified: branches/biocat/app/views/layouts/_footer.rhtml (2605 => 2606)


--- branches/biocat/app/views/layouts/_footer.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/layouts/_footer.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,5 +1,5 @@
 <p class="copyright">
-	Copyright (c) 2007 - 2009 
+	Copyright &#169; 2007 - 2010 
 	<%= link_to "The University of Manchester", "http://www.manchester.ac.uk/", :target => '_blank' %>
 	and
 	<%= link_to "University of Southampton", "http://www.soton.ac.uk/", :target => '_blank' %>
@@ -12,6 +12,7 @@
 				<p><%= link_to "Front Page", "/" %></p>
 				<p><%= link_to "Home", "/home" %></p>
 				<p><%= link_to "Invite people to #{Conf.sitename}", invite_users_path %></p>
+        <p><%= link_to "Help pages", "http://wiki.myexperiment.org/index.php/Help" %></p>
 			</td>
 			<td class="middle">
 				<p><%= link_to "About Us", "http://wiki.myexperiment.org/", :target => '_blank' %></p>
@@ -33,7 +34,7 @@
 				<p><%= link_to "EPSRC", "http://www.epsrc.ac.uk/", :target => '_blank' %></p>
 				<p><%= link_to "JISC", "http://jisc.ac.uk/", :target => '_blank' %></p>
 				<p><%= link_to "Microsoft", "http://www.microsoft.com", :target => '_blank' %></p>
-<p style="height=0; border-bottom: 1px dotted #999999"/>
+        <p style="height=0; border-bottom: 1px dotted #999999"></p>
 				<p>Powered by:</p>
 				<%= link_to image_tag('rails.png'), "http://www.rubyonrails.com", :target => '_blank' %>
 				

Modified: branches/biocat/app/views/layouts/_myexperiment.rhtml (2605 => 2606)


--- branches/biocat/app/views/layouts/_myexperiment.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/layouts/_myexperiment.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -2,113 +2,120 @@
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-	<head>
-		<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
-		<%= t :site => "#{Conf.sitename} &#45; #{controller_visible_name(controller.controller_name.humanize)}", :separator => "&#45;" %>
-		<link rel="shortcut icon" href="" type="image/x-icon"/>
-		<%= stylesheet_link_tag 'reset-fonts-grids' %>
-		<%= stylesheet_link_tag 'base-min' %>
-		<%= stylesheet_link_tag 'acts_as_taggable_stylesheet' %>
-		<%= stylesheet_link_tag 'star_rating' %>
-		<%= stylesheet_link_tag Conf.stylesheet %>
-		<%= stylesheet_link_tag 'gadgets' %>
-		
-		<% if ["workflows", "users"].include?(controller.controller_name.downcase) and ["comments_timeline", "timeline"].include?(controller.action_name.downcase) -%>
-			<script src="" type="text/_javascript_"></script>
-		<% end -%>
-		
-		<%= _javascript__include_tag :defaults %>
-		<%= _javascript__include_tag "boxover.js" %>
-		
-		<%= render :partial => "layouts/feed_link_tags" %>
-	</head>
-	<body>
-		<div id="doc2" class="yui-t4">
-			<div id="hd">
-	  			<div id="myexp_header">
-	  				<div class="logo">
-						<%= link_to image_tag(Conf.site_logo), "/" %>
-					</div>
-					<div class="links">
-						<div style="text-align: left; float: left; width: 34%; padding-top: 6px;">
-							<span><b><%= link_to "About", "http://wiki.myexperiment.org/", :target => '_blank' %></b></span>
-							<span>&nbsp;|&nbsp;</span>
-							<span><b><%= link_to "Mailing List", "http://lists.nongnu.org/mailman/listinfo/myexperiment-discuss", :target => '_blank' %></b></span>
-							<span>&nbsp;|&nbsp;</span>
-							<span><b><%= link_to "Publications", "http://wiki.myexperiment.org/index.php/Papers", :target => '_blank' %></b></span>
-						</div>
-						<div style="text-align: right; float: right; width: 64%;">
-							<% if logged_in? -%>
-								<span><b><%= signout_link -%></b></span>
-							<% else -%>
-								<span><b><%= icon "login", new_session_url, nil, nil, "Log in" -%></b></span>
-								<span>&nbsp;|&nbsp;</span>
-								<span><b><%= icon "user", new_user_url, nil, nil, "Register" -%></b></span>
-							<% end %>
-							<span>&nbsp;|&nbsp;</span>
-							<span><b><%= icon "feedback", "/feedback", nil, nil, "Give us Feedback" -%></b></span>
-							<span>&nbsp;|&nbsp;</span>
-							<span><b><%= icon "user-invite", invite_users_path, nil, nil, "Invite" -%></b></span>
-						</div>
-						<div class="clearer">&nbsp;</div>
-					</div>
-					<div class="clearer">&nbsp;</div>
-				</div>
-				<div>
-					<ul class="tabnav">
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+    <%= t :site => "#{Conf.sitename} &#45; #{controller_visible_name(controller.controller_name.humanize)}", :separator => "&#45;" %>
+<% if @lod_rdf %>    <link rel="alternate" href="" @lod_rdf -%>" type="application/rdf+xml" title="RDF+XML" /><% end %>
+<% if @lod_xml %>    <link rel="alternate" href="" @lod_xml -%>" type="application/xml" title="REST XML" /><% end %>
+    <link rel="shortcut icon" href="" type="image/x-icon"/>
+    <%= stylesheet_link_tag 'reset-fonts-grids' %>
+    <%= stylesheet_link_tag 'base-min' %>
+    <%= stylesheet_link_tag 'acts_as_taggable_stylesheet' %>
+    <%= stylesheet_link_tag 'star_rating' %>
+    <%= stylesheet_link_tag Conf.stylesheet %>
+    <%= stylesheet_link_tag 'gadgets' %>
+    
+    <% if controller.action_name.downcase == "timeline" %>
+      <script src="" type="text/_javascript_"></script>
+    <% end -%>
+    
+    <%= _javascript__include_tag :defaults %>
+    <%= _javascript__include_tag "boxover.js" %>
+    
+    <%= render :partial => "layouts/feed_link_tags" %>
+
+    <% if @extra_head_content %>
+      <%= @extra_head_content -%>
+    <% end %>
+
+  </head>
+  <body>
+    <div id="doc2" class="yui-t4">
+      <div id="hd">
+          <div id="myexp_header">
+            <div class="logo">
+            <%= link_to image_tag(Conf.site_logo), "/" %>
+          </div>
+          <div class="links">
+            <div style="text-align: left; float: left; width: 34%; padding-top: 6px;">
+              <span><b><%= link_to "About", "http://wiki.myexperiment.org/", :target => '_blank' %></b></span>
+              <span>&nbsp;|&nbsp;</span>
+              <span><b><%= link_to "Mailing List", "http://lists.nongnu.org/mailman/listinfo/myexperiment-discuss", :target => '_blank' %></b></span>
+              <span>&nbsp;|&nbsp;</span>
+              <span><b><%= link_to "Publications", "http://wiki.myexperiment.org/index.php/Papers", :target => '_blank' %></b></span>
+            </div>
+            <div style="text-align: right; float: right; width: 64%;">
+              <% if logged_in? -%>
+                <span><b><%= signout_link -%></b></span>
+              <% else -%>
+                <span><b><%= icon "login", new_session_url, nil, nil, "Log in" -%></b></span>
+                <span>&nbsp;|&nbsp;</span>
+                <span><b><%= icon "user", new_user_url, nil, nil, "Register" -%></b></span>
+              <% end %>
+              <span>&nbsp;|&nbsp;</span>
+              <span><b><%= icon "feedback", "/feedback", nil, nil, "Give us Feedback" -%></b></span>
+              <span>&nbsp;|&nbsp;</span>
+              <span><b><%= icon "user-invite", invite_users_path, nil, nil, "Invite" -%></b></span>
+            </div>
+            <div class="clearer">&nbsp;</div>
+          </div>
+          <div class="clearer">&nbsp;</div>
+        </div>
+        <div>
+          <ul class="tabnav">
                                           <% Conf.main_tabs.each do |tab| %>
                                             <li <%= (tab['controller'] && controller.controller_name.match(tab['controller'])) ? 'id="selected_tabnav"' : '' %>>
                                               <%= link_to(tab['label'], tab['link']) -%>
                                             </li>
                                           <% end %>
-					</ul>
-				</div>
-	  			<div id="myexp_searchbar"><%= render :partial => "layouts/search" %></div>
-			</div>
-			<div id="bd">
-				<div id="yui-main"> 
-	  				<div class="yui-b">
-					    <div id="myexp_breadcrumbs_bar">
-								<%= render :partial => "layouts/breadcrumbs_bar" %>
-							</div>
-	    				<div id="myexp_content">
-								<% if flash[:error] %>
-									<div id="error_flash" class="box_standout" style="color: red; font-weight: bold; margin-bottom: 1.5em; line-height: 1.4;">
-										<%= link_to_function image_tag("close.gif", :style => "float: right;"), 
-																				 visual_effect(:toggle_slide, "error_flash", :duration => 0.3) %>
-										<%= white_list flash[:error] %>
-									</div>
-								<% end %>
-		    				<% if flash[:notice] %>
-									<div id="notice_flash" class="box_standout" style="color: green; font-weight: bold; margin-bottom: 1.5em; line-height: 1.4;">
-										<%= link_to_function image_tag("close.gif", :style => "float: right;"), 
-																				 visual_effect(:toggle_slide, "notice_flash", :duration => 0.3) %>
-										<%= white_list flash[:notice] %>
-									</div>
-								<% end %>
-								
-		    				<%= render :partial => "subnav" %>
-		    				
-								<%= yield :layout %>
-						</div>
-					</div>
-				</div>
-				<div id="myexp_sidebar" class="yui-b">
-					<%= render :partial => "layouts/sidebar" %>
-				</div>
-			</div>
-			<% cache(:controller => 'global_cache', :action ="" 'footer') do -%>
-				<div id="ft">
-					<%= render :partial => "layouts/footer" %>
-				</div>
-			<% end -%>
-			
-			<%= render :partial => "layouts/debug" if Conf.show_debug %> 
-		</div>
-		
-		<%= _javascript__include_tag "tabs.js" %>
-		<%= _javascript__include_tag "folds.js" %>
-		
-		<%= render :partial => "layouts/web_analytics" if Conf.google_web_analytics["enable"] %>
-	</body>
+          </ul>
+        </div>
+          <div id="myexp_searchbar"><%= render :partial => "layouts/search" %></div>
+      </div>
+      <div id="bd">
+        <div id="yui-main"> 
+            <div class="yui-b">
+              <div id="myexp_breadcrumbs_bar">
+                <%= render :partial => "layouts/breadcrumbs_bar" %>
+              </div>
+              <div id="myexp_content">
+                <% if flash[:error] %>
+                  <div id="error_flash" class="box_standout" style="color: red; font-weight: bold; margin-bottom: 1.5em; line-height: 1.4;">
+                    <%= link_to_function image_tag("close.gif", :style => "float: right;"), 
+                                         visual_effect(:toggle_slide, "error_flash", :duration => 0.3) %>
+                    <%= white_list flash[:error] %>
+                  </div>
+                <% end %>
+                <% if flash[:notice] %>
+                  <div id="notice_flash" class="box_standout" style="color: green; font-weight: bold; margin-bottom: 1.5em; line-height: 1.4;">
+                    <%= link_to_function image_tag("close.gif", :style => "float: right;"), 
+                                         visual_effect(:toggle_slide, "notice_flash", :duration => 0.3) %>
+                    <%= white_list flash[:notice] %>
+                  </div>
+                <% end %>
+                
+                <%= render :partial => "subnav" %>
+                
+                <%= yield :layout %>
+            </div>
+          </div>
+        </div>
+        <div id="myexp_sidebar" class="yui-b">
+          <%= render :partial => "layouts/sidebar" %>
+        </div>
+      </div>
+      <% cache(:controller => 'global_cache', :action ="" 'footer') do -%>
+        <div id="ft">
+          <%= render :partial => "layouts/footer" %>
+        </div>
+      <% end -%>
+      
+      <%= render :partial => "layouts/debug" if Conf.show_debug %> 
+    </div>
+    
+    <%= _javascript__include_tag "tabs.js" %>
+    <%= _javascript__include_tag "folds.js" %>
+    
+    <%= render :partial => "layouts/web_analytics" if Conf.google_web_analytics["enable"] %>
+  </body>
 </html>

Modified: branches/biocat/app/views/layouts/_paginate.rhtml (2605 => 2606)


--- branches/biocat/app/views/layouts/_paginate.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/layouts/_paginate.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,9 +1,32 @@
+<% if local_assigns[:sort_by] %>
+  <div style="float: right; margin: 1em;">
+    Sort by:
+    <select  = this.options[this.selectedIndex].value;">
+      <% sort_by.each do |args| %>
+        <option value="<%= url_for(request.query_parameters.merge("order" => args[:option])) -%>" <% if params[:order] == args[:option] -%> selected="selected"<% end -%>><%= args[:label] -%></option>
+      <% end %>
+    </select>
+
+    <% if local_assigns[:num_options] %>
+      <br />
+      <br />
+      Results per page:
+      <select  = this.options[this.selectedIndex].value;">
+        <% num_options.each do |num_option| %>
+          <option value="<%= url_for(request.query_parameters.merge("num" => num_option)) -%>" <% if params[:num] == num_option -%> selected="selected"<% end -%>><%= num_option -%></option>
+        <% end %>
+      </select>
+    <% end %>
+  </div>
+<% end %>
+
 <% if collection.page_count != collection.first_page -%>
 <div class="pagination">
+
   <ul>
   <% if collection.previous_page? -%>
          <li class="nextpage">
-	    	    <%= link_to '&#171; previous', { :query => @query, :type => @query_type, :page => collection.previous_page } %>
+	    	    <%= link_to('&#171; previous', request.query_parameters.merge("page" => collection.previous_page)) %>
 	    	 </li>
 	  <% else -%>
          <li class="disabledpage">&#171; previous</li>
@@ -15,7 +38,7 @@
             <li class="currentpage"><%= n %></li>
        <% else -%>
 	          <li><%= "..." if last_page+1 < n %>
-	           <%= link_to n, :query => @query, :type => @query_type, :id => params[:id], :page => n %>
+	           <%= link_to(n, request.query_parameters.merge("page" => n)) %>
 	          </li>
 	       <% end -%>
 	      <% last_page = n -%>
@@ -23,7 +46,7 @@
  
     <% if collection.next_page? -%>
      <li class="nextpage">
-        <%=  link_to 'next &#187;', { :query => @query, :type => @query_type, :page => collection.next_page } %>
+        <%= link_to('next &#187;', request.query_parameters.merge("page" => collection.next_page)) %>
      </li>
     <% else -%>
       <li class="disabledpage">next &#187;</li>

Modified: branches/biocat/app/views/licenses/_view.rhtml (2605 => 2606)


--- branches/biocat/app/views/licenses/_view.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/licenses/_view.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,3 +1,4 @@
+<% if license %>
                 <p style="text-align: center;">
                   <b><big><%= license_icon_link(license) %></big></b>
                   <br/>
@@ -12,4 +13,9 @@
 				</p>
 				<% end %>
 		        <br/>
-				<small><%= license.description %></small>
\ No newline at end of file
+				<small><%= license.description %></small>
+<% else %>
+  <p style="text-align: center;">
+    No license details.
+  </p>
+<% end %>

Modified: branches/biocat/app/views/licenses/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/licenses/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/licenses/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -36,3 +36,6 @@
 		<%= @license.description_html %>
 	</div>
 </div>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Modified: branches/biocat/app/views/mailer/account_confirmation.rhtml (2605 => 2606)


--- branches/biocat/app/views/mailer/account_confirmation.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/mailer/account_confirmation.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -7,7 +7,6 @@
 To confirm your email address and activate your account, please follow the link below (you might need to copy/paste it into your browser):
  
 <%= url_for : false,
-						:host => @base_url,
 						:controller => 'users',
 						:action ="" 'confirm_email',
 						:hash => @hash %>

Modified: branches/biocat/app/views/mailer/forgot_password.rhtml (2605 => 2606)


--- branches/biocat/app/views/mailer/forgot_password.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/mailer/forgot_password.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -7,7 +7,6 @@
 Follow the link below to reset your password (you might need to copy/paste it into your browser):
  
 <%= url_for : false,
-						:host => @base_url,
 						:controller => 'users',
 						:action ="" 'reset_password',
 						:reset_code => @reset_code %>

Modified: branches/biocat/app/views/mailer/friendship_invite_new_user.rhtml (2605 => 2606)


--- branches/biocat/app/views/mailer/friendship_invite_new_user.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/mailer/friendship_invite_new_user.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -13,17 +13,17 @@
 
 <% end -%>
 
-You can find out more about me (<%= @name -%>) from my profile page: <%= url_for : false, :host => @base_url, :controller => 'users', :id => @user_id, :action ="" 'show' %>
+You can find out more about me (<%= @name -%>) from my profile page: <%= url_for : false, :controller => 'users', :id => @user_id, :action ="" 'show' %>
 
 
 To become my friend you will have to follow these simple steps:
 
-1. Register for <%= indefinite_article(Conf.sitename) %> <%= Conf.sitename %> account at: <%= url_for : false, :host => @base_url, :controller => 'users', :action ="" 'new', :token => @token %>
+1. Register for <%= indefinite_article(Conf.sitename) %> <%= Conf.sitename %> account at: <%= url_for : false, :controller => 'users', :action ="" 'new', :token => @token %>
 2. Confirm your email address as instructed during the registration process.
 3. Accept my friendship request that you will receive directly to your account once the registration is complete.
 
 
-For more information browse: <%= "http://" + @base_url %>
+For more information browse: <%= Conf.base_uri %>
 
 
 Best regards,

Modified: branches/biocat/app/views/mailer/group_invite_new_user.rhtml (2605 => 2606)


--- branches/biocat/app/views/mailer/group_invite_new_user.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/mailer/group_invite_new_user.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -14,18 +14,18 @@
 <% end -%>
 
 You can find out more:
---> about this group : <%= "http://" + @base_url + group_path(@group_id) %> 
---> and about me (<%= @name -%>) from my profile page : <%= url_for : false, :host => @base_url, :controller => 'users', :id => @user_id, :action ="" 'show' %>
+--> about this group : <%= Conf.base_uri + group_path(@group_id) %> 
+--> and about me (<%= @name -%>) from my profile page : <%= url_for : false, :controller => 'users', :id => @user_id, :action ="" 'show' %>
 
 
 To join the group you will have to follow these simple steps:
 
-1. Register for <%= indefinite_article(Conf.sitename) %> <%= Conf.sitename %> account at: <%= url_for : false, :host => @base_url, :controller => 'users', :action ="" 'new', :token => @token %>
+1. Register for <%= indefinite_article(Conf.sitename) %> <%= Conf.sitename %> account at: <%= url_for : false, :controller => 'users', :action ="" 'new', :token => @token %>
 2. Confirm your email address as instructed during the registration process.
 3. Accept the group membership request that you will receive directly to your account once the registration is complete.
 
 
-For more information browse: <%= "http://" + @base_url %>
+For more information browse: <%= Conf.base_uri %>
 
 
 Best regards,

Modified: branches/biocat/app/views/mailer/invite_new_user.rhtml (2605 => 2606)


--- branches/biocat/app/views/mailer/invite_new_user.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/mailer/invite_new_user.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -11,11 +11,11 @@
 ---------------------------------------------------------------------------
 <% end -%>
 
-Join us at: <%= url_for : false, :host => @base_url, :controller => 'users', :action ="" 'new' %>
+Join us at: <%= url_for : false, :controller => 'users', :action ="" 'new' %>
 
 You can find out more:
---> about myExperiement.org: <%= "http://" + @base_url %>
---> and about me: <%= url_for : false, :host => @base_url, :controller => 'users', :id => @user_id, :action ="" 'show' %>
+--> about <%= Conf.sitename -%>: <%= Conf.base_uri %>
+--> and about me: <%= url_for : false, :controller => 'users', :id => @user_id, :action ="" 'show' %>
 
 
 Best regards,

Modified: branches/biocat/app/views/mailer/update_email_address.rhtml (2605 => 2606)


--- branches/biocat/app/views/mailer/update_email_address.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/mailer/update_email_address.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -7,7 +7,6 @@
 Please confirm this by following the link below (you might need to copy/paste it into your browser). Your old email address will be active until you have done this.
  
 <%= url_for : false,
-						:host => @base_url,
 						:controller => 'users',
 						:action ="" 'confirm_email',
 						:hash => @hash %>

Modified: branches/biocat/app/views/memberships/_table.rhtml (2605 => 2606)


--- branches/biocat/app/views/memberships/_table.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/memberships/_table.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -20,12 +20,12 @@
 <% for membership in collection %>
   <% allow_approval = allow_membership_pending_approval(membership) %>
   <tr class="<%= (odd_row = !odd_row) ? "odd_row" : "even_row" %>">
-    <% if user %><td width="100"><%= "<b>" unless membership.accepted? %><%= contributor(membership.user_id, "User", true, 60) %><%= "</b>" unless membership.accepted? %></td><% end %>
+    <% if user %><td width="100"><%= "<b>" unless membership.accepted? %><%= contributor(membership.user_id, "User", true, 60) %><%= "</b>" unless membership.accepted? %><%= '<div style="text-align: left"><b>Group Admin</b></div>' if membership.network.administrator?(membership.user_id) %></td><% end %>
     <% if network %><td width="100"><%= "<b>" unless membership.accepted? %><%= contributor(membership.network_id, "Network", true, 60) %><%= "</b>" unless membership.accepted? %></td><% end %>
     <td><%= "<b>" unless membership.accepted? %><%=datetime membership.created_at, false %><%= "</b>" unless membership.accepted? %></td>
     <td><%= "<b>" unless membership.accepted? %><%=datetime(membership.accepted_at, false) || "Pending" %><%= "</b>" unless membership.accepted? %></td>
     <td class="actions">
-      <% if my_page? membership.user.id or my_page? membership.network.owner.id %>
+      <% if my_page? membership.user.id or (logged_in? and membership.network.administrator?(current_user.id)) %>
         <%= icon "show", membership_path(membership.user_id, membership), nil, nil, "View" %>
 				<% if membership.accepted? %>
 					<%= icon "destroy", membership_path(membership.user_id, membership), nil, { :confirm => "Are you sure?", :method => :delete }, "Remove" %>
@@ -37,9 +37,17 @@
          	<%= icon "reject", membership_path(membership.user_id, membership), nil, :confirm => "Are you sure?", :method => :delete %>
 				<% end %>
       <% end %>
+      <% if membership.accepted? %>
+        <% if membership.network.administrator?(membership.user_id) %>
+		<%= icon "remove_group_admin", membership_path(membership.user_id, membership)+'?membership[administrator]=0', nil, {:method => :put}, "Remove group admin status" %>
+	<% else %>
+		<%= icon "make_group_admin", membership_path(membership.user_id, membership)+'?membership[administrator]=1', nil, {:method => :put}, "Add
+group admin status" %>
+	<% end %>
+      <% end %>
     </td>
   </tr>
 <% end %>
 </table>
 
-<% end %>
\ No newline at end of file
+<% end %>

Modified: branches/biocat/app/views/memberships/index.rhtml (2605 => 2606)


--- branches/biocat/app/views/memberships/index.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/memberships/index.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,152 +1,149 @@
 <% if @user %>
 
-  <% if my_page? @user.id %>
-  
-    <h1>My Memberships</h1>
+	<% if my_page? @user.id %>
 
-	<% memberships = @user.networks_membership_requests_pending %>
-	<div class="fold">
-  		<div class="foldTitle">
-			<p>Pending requests sent by users who want to join your Groups <span style="color: red;">(<%= memberships.length %>)</span></p>
-			<hr/>
-			<small><%= memberships.length %> requires approval from you</small>
-        </div>
-        <div class="foldContent" style="display: none;">
-			<div class="box_infotext"> 
-				<p>These are the membership requests sent by users who want to join your groups.</p>
+		<h1>My Memberships</h1>
+
+		<% memberships = @user.networks_membership_requests_pending(true) %>
+		<div class="fold">
+			<div class="foldTitle">
+				<p>Pending requests sent by users who want to join your Groups <span style="color: red;">(<%= memberships.length %>)</span></p>
+				<hr/>
+				<small><%= memberships.length %> requires approval from you</small>
 			</div>
-		    <% unless memberships.empty? %>
-		        <%= render :partial => "memberships/table", :locals => { :collection => memberships } %>
-		    <% else %>
-				<p><i>None</i></p>
-			<% end %>
-		</div>
-	</div>
-	
-	<% memberships = @user.memberships_invited %>
-	<div class="fold">
-  		<div class="foldTitle">
-			<p>Pending invitations you have been sent from users to join their Groups <span style="color: red;">(<%= memberships.length %>)</span></p>
-			<hr/>
-			<small><%= memberships.length %> requires approval from you</small>
-        </div>
-        <div class="foldContent" style="display: none;">
-		    <% unless memberships.empty? %>
-		    	<div class="box_infotext">
-		    		<p>These are the membership invites you have been sent from users to join their groups.</p>
-					<p>If the <b>Group</b> is taking a while, why not send the administrator a <%= link_to "message", messages_path %>?</p>
+			<div class="foldContent" style="display: none;">
+				<div class="box_infotext">
+					<p>These are the membership requests sent by users who want to join your groups.</p>
 				</div>
-		        <%= render :partial => "memberships/table", :locals => { :collection => memberships, :user => false } %>
-		    <% else %>
-				<p><i>None</i></p>
-			<% end %>
+				<% unless memberships.empty? %>
+					<%= render :partial => "memberships/table", :locals => { :collection => memberships } %>
+				<% else %>
+					<p><i>None</i></p>
+				<% end %>
+			</div>
 		</div>
-	</div>
-	
-	<% memberships = @user.memberships_requested %>
-	<div class="fold">
-  		<div class="foldTitle">
-			<p>Pending requests you have sent to users to join their Groups</p>
-			<hr/>
-			<small><%= memberships.length %> waiting approval from others</small>
-        </div>
-        <div class="foldContent" style="display: none;">
-			<div class="box_infotext">
-	    		<p>These are the membership requests you have sent to users to join their groups.</p>
-				<p>If the <b>Group</b> is taking a while, why not send the administrator a <%= link_to "message", messages_path %>?</p>
+
+		<% memberships = @user.memberships_invited %>
+		<div class="fold">
+			<div class="foldTitle">
+				<p>Pending invitations you have been sent from users to join their Groups <span style="color: red;">(<%= memberships.length %>)</span></p>
+				<hr/>
+				<small><%= memberships.length %> requires approval from you</small>
 			</div>
-		    <% unless memberships.empty? %>
-		        <%= render :partial => "memberships/table", :locals => { :collection => memberships, :user => false } %>
-		    <% else %>
-				<p><i>None</i></p>
-			<% end %>
+			<div class="foldContent" style="display: none;">
+				<% unless memberships.empty? %>
+					<div class="box_infotext">
+						<p>These are the membership invites you have been sent from users to join their groups.</p>
+						<p>If the <b>Group</b> is taking a while, why not send the administrator a <%= link_to "message", messages_path %>?</p>
+					</div>
+					<%= render :partial => "memberships/table", :locals => { :collection => memberships, :user => false } %>
+				<% else %>
+					<p><i>None</i></p>
+				<% end %>
+			</div>
 		</div>
-	</div>
-	
-	<% memberships = @user.networks_membership_invites_pending %>
-	<div class="fold">
-  		<div class="foldTitle">
-			<p>Pending invitations you have sent to users to join your Groups</p>
-			<hr/>
-			<small><%= memberships.length %> waiting approval from others</small>
-        </div>
-        <div class="foldContent" style="display: none;">
-		    <div class="box_infotext">
-				<p>These are the membership invitations you have sent to users to join your groups.</p>
+
+		<% memberships = @user.memberships_requested %>
+		<div class="fold">
+			<div class="foldTitle">
+				<p>Pending requests you have sent to users to join their Groups</p>
+				<hr/>
+				<small><%= memberships.length %> waiting approval from others</small>
 			</div>
+		<div class="foldContent" style="display: none;">
+			<div class="box_infotext">
+				<p>These are the membership requests you have sent to users to join their groups.</p>
+				<p>If the <b>Group</b> is taking a while, why not send the administrator a <%= link_to "message", messages_path %>?</p>
+			</div>
 			<% unless memberships.empty? %>
-		        <%= render :partial => "memberships/table", :locals => { :collection => memberships } %>
-		    <% else %>
+				<%= render :partial => "memberships/table", :locals => { :collection => memberships, :user => false } %>
+			<% else %>
 				<p><i>None</i></p>
 			<% end %>
+			</div>
 		</div>
-	</div>
-	
-	<% groups = @user.networks_owned %>
-	<div class="fold">
-  		<div class="foldTitle">
-			<p>Groups you are the administrator of (i.e: your Groups)</p>
-			<hr/>
-			<small><%= groups.length %> groups</small>
-        </div>
-        <div class="foldContent" style="display: none;">
-		    <% unless groups.empty? %>
-		    	<div class="box_infotext">
-		    		<p>These are the groups that you are the administrator of.</p>
-					<p>You can share Workflows and Files with them.</p>
+
+		<% memberships = @user.networks_membership_invites_pending %>
+		<div class="fold">
+			<div class="foldTitle">
+				<p>Pending invitations you have sent to users to join your Groups</p>
+				<hr/>
+				<small><%= memberships.length %> waiting approval from others</small>
+			</div>
+			<div class="foldContent" style="display: none;">
+				<div class="box_infotext">
+					<p>These are the membership invitations you have sent to users to join your groups.</p>
 				</div>
-		        <%= render :partial => "networks/table", :locals => { :collection => groups } %>
-		    <% else %>
-				<p><i>None</i></p>
-			<% end %>
+				<% unless memberships.empty? %>
+					<%= render :partial => "memberships/table", :locals => { :collection => memberships } %>
+				<% else %>
+					<p><i>None</i></p>
+				<% end %>
+			</div>
 		</div>
-	</div>
-	
-	<% groups = @user.networks %>
-	<div class="fold">
-  		<div class="foldTitle">
-			<p>Other Groups you are a member of</p>
-			<hr/>
-			<small><%= groups.length %> groups</small>
-        </div>
-        <div class="foldContent" style="display: none;">
-		    <% unless groups.empty? %>
-		    	<div class="box_infotext">
-		    		<p>These are the other groups that you are a member of.</p>
-					<p>You can share Workflows and Files with them.</p>
-				</div>
-		        <%= render :partial => "networks/table", :locals => { :collection => groups } %>
-		    <% else %>
-				<p><i>None</i></p>
-			<% end %>
+
+		<% groups = @user.networks_owned %>
+		<div class="fold">
+			<div class="foldTitle">
+				<p>Groups you are the administrator of (i.e: your Groups)</p>
+				<hr/>
+				<small><%= groups.length %> groups</small>
+			</div>
+			<div class="foldContent" style="display: none;">
+				<% unless groups.empty? %>
+					<div class="box_infotext">
+						<p>These are the groups that you are the administrator of.</p>
+						<p>You can share Workflows and Files with them.</p>
+					</div>
+					<%= render :partial => "networks/table", :locals => { :collection => groups } %>
+				<% else %>
+					<p><i>None</i></p>
+				<% end %>
+			</div>
 		</div>
-	</div>
-	
-  <% else %>
-  
-    <h1>Memberships for <%=name @user.id %></h1>
-    <% unless @user.memberships.empty? %>
-      <%= render :partial => "memberships/table", :locals => { :collection => @user.memberships, :user => false } %>
+
+		<% groups = @user.networks %>
+		<div class="fold">
+			<div class="foldTitle">
+				<p>Other Groups you are a member of</p>
+				<hr/>
+				<small><%= groups.length %> groups</small>
+			</div>
+			<div class="foldContent" style="display: none;">
+				<% unless groups.empty? %>
+					<div class="box_infotext">
+						<p>These are the other groups that you are a member of.</p>
+						<p>You can share Workflows and Files with them.</p>
+					</div>
+					<%= render :partial => "networks/table", :locals => { :collection => groups } %>
+				<% else %>
+					<p><i>None</i></p>
+				<% end %>
+			</div>
+		</div>
+
 	<% else %>
-		<p><i>None</i></p>
-    <% end %>
-  
-  <% end %>
 
-<% else %>
+		<h1>Memberships for <%=name @user.id %></h1>
+		<% unless @user.memberships.empty? %>
+			<%= render :partial => "memberships/table", :locals => { :collection => @user.memberships, :user => false } %>
+		<% else %>
+			<p><i>None</i></p>
+		<% end %>
 
+	<% end %>
+
+<% else %>
 	<% if false %>
+		<h2>All Memberships</h2>
 
-  <h2>All Memberships</h2>
+		<%= render :partial => "layouts/paginate", :locals => { :collection => @memberships } %>
 
-  <%= render :partial => "layouts/paginate", :locals => { :collection => @memberships } %>
+		<%= render :partial => "memberships/table", :locals => { :collection => @memberships } %>
 
-  <%= render :partial => "memberships/table", :locals => { :collection => @memberships } %>
+		<%= render :partial => "layouts/paginate", :locals => { :collection => @memberships } %>
 
-  <%= render :partial => "layouts/paginate", :locals => { :collection => @memberships } %>
-	
 	<% end %>
-
 <% end %>
 
-<br />
\ No newline at end of file
+<br />

Modified: branches/biocat/app/views/memberships/new.rhtml (2605 => 2606)


--- branches/biocat/app/views/memberships/new.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/memberships/new.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -3,28 +3,28 @@
 <%= error_messages_for :membership %>
 
 <% form_for(:membership, :url ="" user_memberships_path(params[:user_id]), :html => {:id => 'memb_request_form'}) do |f| %>
-  <%= f.hidden_field :user_id, :value => current_user.id %>
-	
+	<%= f.hidden_field :user_id, :value => current_user.id %>
+
 	<center>
-    <div class="box_standout" style="line-height: 1.6; width: 400px;">
+		<div class="box_standout" style="line-height: 1.6; width: 400px;">
 			<% if params[:network_id] -%>
-        <%= f.hidden_field :network_id, :value => @network.id %>
-				
+				<%= f.hidden_field :network_id, :value => @network.id %>
+
 				<b>Send a request to join the group:</b>
 				<br/>
 				<%= link_to_function h(@network.title) + expand_image, 
-														 visual_effect(:toggle_blind, "group_box", :duration => 0.3) -%>
-			  <br/>
-      	<small>(Administrator: <%= name @network.user_id -%>)</small>
-		  <% else -%>
-        <b>Select group:</b><br/>
-	  		<%= select_tag "membership[network_id]", options_from_collection_for_select(Network.find(:all, :order => "title ASC"), :id.to_i, :title), :style => "width: 360px; margin-top: 0.3em; margin-bottom: 0.5em" %>
-  		<% end -%>
+				                     visual_effect(:toggle_blind, "group_box", :duration => 0.3) -%>
+				 <br/>
+				<small>(Administrator: <%= name @network.user_id -%>)</small>
+			<% else -%>
+				<b>Select group:</b><br/>
+				<%= select_tag "membership[network_id]", options_from_collection_for_select(Network.find(:all, :order => "title ASC"), :id.to_i, :title), :style => "width: 360px; margin-top: 0.3em; margin-bottom: 0.5em" %>
+			<% end -%>
 		</div>
 	</center>
-	
+
 	<br/>
-	
+
 	<center>
 		<div class="box_currentuser_specific" style="text-align: center; width: 400px; font-size: 93%; padding-bottom: 1em;">
 			<center>
@@ -32,7 +32,7 @@
 					<tr>
 						<td style="line-height: 2; text-align: left;">
 							<%= info_icon_with_tooltip("This message is optional: if you leave it blank, your request will be sent without it") %>
-			      	<b>Message to group administrator:</b>
+							<b>Message to group administrator:</b>
 							<br/>
 							<%= text_area_tag "membership[message]", "", :rows => 4, :style => "width: 370px; font-size: 93%;" -%>
 						</td>
@@ -41,19 +41,19 @@
 			</center>
 		</div>
 	</center>
-	
+
 	<br/>
-	
+
 	<center>
 		<%= submit_tag "Send membership request", :id => "send", :style => 'width: 180px;', : "$('cancel').disabled=true;this.disabled=true;this.value='Sending your request...';this.form.submit();"  %>
-			
+
 		<% cancel_links_to = params[:network_id] ? url_for(:controller => 'networks', :action ="" 'show', :id => @network.id) : url_for(:controller => 'networks', :action ="" 'index') %>
 		<input type="submit" id="cancel" value="Return to Group" style="width: 180px"  cancel_links_to -%>';return false;" />
 	</center>
-	
+
 	<% if params[:network_id] -%>
 		<div id="group_box" style="display: none; margin-top: 3em;">
-		  <%= render :partial => "networks/table", :locals => { :collection => [ @network ] } %>
+			<%= render :partial => "networks/table", :locals => { :collection => [ @network ] } %>
 		</div>
 	<% end %>
 

Modified: branches/biocat/app/views/memberships/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/memberships/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/memberships/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -13,123 +13,119 @@
 				<% # UNDER DEVELOPMENT - SOON GROUP AVATARS WILL BE ADDED AND THERE WOULD BE NO NEED FOR THIS IF_ELSE_END BIT ALLTOGETHER %>
 				<% # THE CODE INSIDE THE BLOCK HAS DUPLICATED CHECK, BUT THAT WOULD BECOME THE ONLY ONE, ONCE THE OUTER IF IS REMOVED %>
 				<% if (@membership.is_invite? && !allow_approval) || (address@hidden && allow_approval) %>
-				  <td style="width: 15%; vertical-align: top;">
-				  	<% if (@membership.is_invite? && !allow_approval) || (address@hidden && allow_approval) %>
-						  <%= render :partial => "users/avatar", :locals => { :user => @membership.user, :size => 80 } %>
+					<td style="width: 15%; vertical-align: top;">
+						<% if (@membership.is_invite? && !allow_approval) || (address@hidden && allow_approval) %>
+							 <%= render :partial => "users/avatar", :locals => { :user => @membership.user, :size => 80 } %>
 							<br/>
-							<% # = link_to_function h(@membership.user.name) + expand_image, 
-															 visual_effect(:toggle_blind, "user_box", :duration => 0.3) -%>
+							<% # = link_to_function h(@membership.user.name) + expand_image, visual_effect(:toggle_blind, "user_box", :duration => 0.3) -%>
 						<% elsif (@membership.is_invite? && allow_approval) || (address@hidden && !allow_approval) %>
-						  <% #= avatar @membership.network, 80 # will start to work once the group avatars are introduced %>
-				    <% end %>
+							<% #= avatar @membership.network, 80 # will start to work once the group avatars are introduced %>
+						<% end %>
+						<% if (@membership.network.administrator?(@membership.user_id)) -%>
+							<div style="text-align: left"><b>Group Admin</b></div>
+						<% end -%>
 					</td>
-				  <td style="line-height: 1.6; width: 85%;">
+					<td style="line-height: 1.6; width: 85%;">
 				<% else %>
-				  <td style="line-height: 1.6;">
+					<td style="line-height: 1.6;">
 				<% end %>
 				<% # END OF BLOCK UNDER DEVELOPMENT %>
 					<b>
 						<% if @membership.accepted? %>
-						  <% if current_user.id.to_i == @membership.user_id.to_i %>
-							  You are a member of:
+							<% if current_user.id.to_i == @membership.user_id.to_i %>
+								You are a member of:
 							<% else %>
-							  <%= link_to_function h(@membership.user.name) + expand_image, 
-															 visual_effect(:toggle_blind, "user_box", :duration => 0.3) -%>
+								<%= link_to_function h(@membership.user.name) + expand_image, visual_effect(:toggle_blind, "user_box", :duration => 0.3) -%>
 								is a member of:
 							<% end %>
 						<% else %>
-						  <%= membership_invite_request_action_message(allow_approval, @membership.is_invite?, link_to_function(h(@membership.user.name) + expand_image, visual_effect(:toggle_blind, "user_box", :duration => 0.3)) ) -%>
+							<%= membership_invite_request_action_message(allow_approval, @membership.is_invite?, link_to_function(h(@membership.user.name) + expand_image, visual_effect(:toggle_blind, "user_box", :duration => 0.3)) ) -%>
 						<% end %>
 					</b>
 					<br/>
-					<%= link_to_function h(@membership.network.title) + expand_image, 
-															 visual_effect(:toggle_blind, 'group_box', :duration => 0.3) -%>
+					<%= link_to_function h(@membership.network.title) + expand_image,
+					                     visual_effect(:toggle_blind, 'group_box', :duration => 0.3) -%>
 					<br/>
-					
-					<p style="margin-top: 1.5em; color: #666666; font-size: 93%; text-align: center;">        
-		        <b><%= @membership.is_invite? ? "Invited" : "Requested" %> at:</b><br/>
-		        <%=datetime @membership.created_at %>
-		      </p>
-					 
-		      <% if @membership.accepted? %>
-		        <p style="margin-top: 0.5em; color: #666666; font-size: 93%; text-align: center;">
-		          <b>Accepted at:</b>
-		          <br/>
-		          <%=datetime @membership.accepted_at %>
-		        </p>
-		      <% else %>
-		        <% if allow_approval && @membership.message %>
-						  <p style="margin-top: 1.5em; font-size: 93%; text-align: center;">
-							  <b>Message from <%= @membership.is_invite? ? "group admin" : "user" %>:</b><br/>
+
+					<p style="margin-top: 1.5em; color: #666666; font-size: 93%; text-align: center;">
+						<b><%= @membership.is_invite? ? "Invited" : "Requested" %> at:</b><br/>
+						<%=datetime @membership.created_at %>
+					</p>
+
+					<% if @membership.accepted? %>
+						<p style="margin-top: 0.5em; color: #666666; font-size: 93%; text-align: center;">
+							<b>Accepted at:</b>
+							<br/>
+							<%=datetime @membership.accepted_at %>
+						</p>
+					<% else %>
+						<% if allow_approval && @membership.message %>
+							<p style="margin-top: 1.5em; font-size: 93%; text-align: center;">
+								<b>Message from <%= @membership.is_invite? ? "group admin" : "user" %>:</b><br/>
 							</p>
-							
+
 							<div style="padding: 0.3em 1em; border: 1px dotted #999999; background-color: #FFFFFF;">
 								<p style="font-size: 93%; text-align: center;">
-								  <%= white_list(simple_format(@membership.message)) -%>
-			          </p>
+									<%= white_list(simple_format(@membership.message)) -%>
+								</p>
 							</div>
 						<% end %>
 					<% end %>
-					
 				</td>
 			</tr>
 		</table>
 	</div>
 </center>
 
+<% unless @membership.accepted? %>
+	<br/>
+	<center>
+		<div class="box_currentuser_specific" style="width: 500px;">
+			<% if allow_approval %>
+				<p style="text-align: center; color: #990000; font-size: 108%;">
+					<b><i><%= pending_approval_message(allow_approval, @membership.is_invite?) -%></i></b>
+				</p>
 
+				<center>
+					<table>
+						<tr>
+							<td style="text-align: left;">
+								<%= info_icon_with_tooltip("Optional note to be sent to " + (@membership.is_invite? ? "group admin" : "user") + " along with your decision.<br/>(Delivered via internal messaging system.)<br/>Leave the field blank if you don't want to include any personal note in the message.") -%>
+								Response to <%= @membership.is_invite? ? "group admin" : "user" -%>:
+								<br/>
+								<%= text_area_tag "msg", "", :rows => 4, :style => "width: 480px; margin-top: 0.4em" -%>
+							</td>
+						</tr>
+					</table>
+				</center>
 
-<% unless @membership.accepted? %>
-<br/>
-<center>
-  <div class="box_currentuser_specific" style="width: 500px;">
-	  <% if allow_approval %>
-			<p style="text-align: center; color: #990000; font-size: 108%;">
-				<b><i><%= pending_approval_message(allow_approval, @membership.is_invite?) -%></i></b>
-			</p>	  
-		  
-			<center>  
-      <table>
-				<tr>
-					<td style="text-align: left;">
-						<%= info_icon_with_tooltip("Optional note to be sent to " + (@membership.is_invite? ? "group admin" : "user") + " along with your decision.<br/>(Delivered via internal messaging system.)<br/>Leave the field blank if you don't want to include any personal note in the message.") -%>
-						Response to <%= @membership.is_invite? ? "group admin" : "user" -%>:
-						<br/>
-						<%= text_area_tag "msg", "", :rows => 4, :style => "width: 480px; margin-top: 0.4em" -%>
-		     </td>
-				</tr>      
-			</table>
-			</center>
-    
-		
-		  <!-- These forms serve as 'data senders' for Accept/Reject buttons -->
-		
-		  <% form_tag(accept_membership_path(current_user.id, @membership)) do %>				  
-				<%= hidden_field_tag "accept_msg", "" %>
-		  <% end %>
-			
-			<% form_tag(user_membership_path(@membership.user_id, @membership) + "?return_to=" + currentusers_things_url('groups'), :method => :delete) do %>				  
-				<%= hidden_field_tag "reject_msg", "" %>
-		  <% end %>
-			
-			<!-- /forms -->
-		
-			<ul class="sectionIcons" style="margin-top: 1.5em;">
-				<li><%= icon "confirm", "", "Accept", {: "$('accept_msg').value=$('msg').value; $('accept_msg').form.submit();return(false);"}, "Accept" %></li>
-        <li><%= icon "reject", "", "Reject", {: "if(confirm('Are you sure?')) { $('reject_msg').value=$('msg').value; $('reject_msg').form.submit(); } return(false);"}, "Reject" %></li>
-      </ul>
-		<% else %>
-			<p style="color: #990000; font-size: 108%; text-align: center;">
-        <b><i><%= pending_approval_message(allow_approval, @membership.is_invite?) -%></i></b>
-			</p>
-		<% end %>
-  </div>
-</center>
-<% end %>
+				<!-- These forms serve as 'data senders' for Accept/Reject buttons -->
 
+				<% form_tag(accept_membership_path(current_user.id, @membership)) do %>
+					<%= hidden_field_tag "accept_msg", "" %>
+				<% end %>
 
+				<% form_tag(user_membership_path(@membership.user_id, @membership) + "?return_to=" + currentusers_things_url('groups'), :method => :delete) do %>
+					<%= hidden_field_tag "reject_msg", "" %>
+				<% end %>
+
+				<!-- /forms -->
+
+				<ul class="sectionIcons" style="margin-top: 1.5em;">
+					<li><%= icon "confirm", "", "Accept", {: "$('accept_msg').value=$('msg').value; $('accept_msg').form.submit();return(false);"}, "Accept" %></li>
+					<li><%= icon "reject", "", "Reject", {: "if(confirm('Are you sure?')) { $('reject_msg').value=$('msg').value; $('reject_msg').form.submit(); } return(false);"}, "Reject" %></li>
+				</ul>
+			<% else %>
+				<p style="color: #990000; font-size: 108%; text-align: center;">
+					<b><i><%= pending_approval_message(allow_approval, @membership.is_invite?) -%></i></b>
+				</p>
+			<% end %>
+		</div>
+	</center>
+<% end %>
+
 <div id="group_box" style="display: none; margin-top: 3em;">
-  <br/>
+	<br/>
 	<%= render :partial => "networks/table", :locals => { :collection => [ @membership.network ] } %>
 	<a name="group_box"></a>
 </div>
@@ -138,3 +134,6 @@
 	<%= render :partial => "users/table", :locals => { :collection => [ @membership.user ] } %>
 	<a name="user_box"></a>
 </div>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Modified: branches/biocat/app/views/messages/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/messages/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/messages/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -60,3 +60,6 @@
 		<% end %>
 	</div>
 </div>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Modified: branches/biocat/app/views/networks/_announcements.rhtml (2605 => 2606)


--- branches/biocat/app/views/networks/_announcements.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/networks/_announcements.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -6,7 +6,7 @@
 	
 	<p class="heading" style="margin: 0;">
 		<span style="position: relative; z-index: 1000; float: left;">
-			<%= feed_icon_tag "#{group.title} Group Announcements", formatted_group_announcements_path(group, :rss) -%>
+			<%= feed_icon_tag "#{h(group.title)} Group Announcements", formatted_group_announcements_path(group, :rss) -%>
 		</span>
 		<a name="group_announcements"></a>
 		<%= link_to "Announcements", group_announcements_url(group) -%>

Modified: branches/biocat/app/views/networks/_owner_box.rhtml (2605 => 2606)


--- branches/biocat/app/views/networks/_owner_box.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/networks/_owner_box.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,7 +1,7 @@
 <div class="contribution_section_box">
 	<p class="heading">
-		<%= info_icon_with_tooltip("The administrator is the person who manages this group on #{Conf.sitename} (which means they allow/reject/invite other users).") %>
-		Administrator
+		<%= info_icon_with_tooltip("The owner is the person who manages this group on #{Conf.sitename}. The owner and the group's administrators are able to allow/reject/invite other users).") %>
+		Owner
 	</p>
 	<p>
 		<center>

Modified: branches/biocat/app/views/networks/_table.rhtml (2605 => 2606)


--- branches/biocat/app/views/networks/_table.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/networks/_table.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -25,13 +25,15 @@
 							<b>Created: </b><%=datetime network.created_at, true -%>
 						</p>
 						
-						<div class="desc" style="font-size: 85%;">
+						<div class="desc">
+							<div style="font-size: 85%;">
 							<% if network.description and network.description.length > 0 -%>
 								<% desc = truncate(strip_html(network.description), 400) -%>
 								<%= query ? highlight_all(desc, query) : desc -%>
 							<% else -%>
 								<span class="none_text">No description</span>
 							<% end -%>
+							</div>
 						</div>
 						
 						<p class="standout" style="margin-top: 0.4em;">

Modified: branches/biocat/app/views/networks/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/networks/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/networks/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -25,9 +25,11 @@
 			<% end %>
 		<% end %>
 
-    <% if mine? @network %>
+    <% if @network.administrator?(current_user.id) %>
       <li><%= icon('announcement', new_group_announcement_path(@network), 'Make a new Group Announcement', nil, 'Make a Group Announcement') -%></li>
 			<li><%= icon('network-invite', invite_group_path(@network), 'Invite People', nil, 'Invite People') -%></li>
+    <% end %>
+    <% if mine? @network %>
 			<li><%= icon('edit', edit_group_path(@network), 'Edit', nil, 'Edit Group') %></li>
       <li><%= icon('destroy', group_path(@network), 'Delete Group', { :confirm => 'Are you sure?', :method => :delete }, 'Delete Group') %></li>
     <% end %>
@@ -105,12 +107,21 @@
 	<br/>
 	
 	<div class="contribution_section_box">
+<% address@hidden(true) -%>
+<% address@hidden() - admins -%>
 		<p class="heading">
+			Administrators
+			<a name="group_members"></a>
+		</p>
+		<div>
+			<%= render :partial => "networks/members", :locals => { :collection => admins, :size => 60 } %>
+		</div>
+		<p class="heading">
 			Members
 			<a name="group_members"></a>
 		</p>
 		<div>
-			<%= render :partial => "networks/members", :locals => { :collection => @network.members(true), :size => 60 } %>
+			<%= render :partial => "networks/members", :locals => { :collection => others, :size => 60 } %>
 		</div>
 	</div>
 	
@@ -156,10 +167,23 @@
 <a name="shared_items"></a>
 <div class="tabContainer">
   <div class="tabTitle">Shared Items (<%= @shared_items.length -%>)</div>
-  <div class="tabContent">
+  <div class="tabContent" id="shared_items">
 		<% unless @shared_items.empty? %>
   		<%= view_privileges_notice %>
+
+      <div style="float: right; margin: 0.5em;">
+        Sort:
+        <select  = this.options[this.selectedIndex].value;">
+          <% @item_sort_options.each do |option, label| %>
+            <option value="?item_sort=<%= option -%>#shared_items" <% if params[:item_sort] == option -%> selected="selected"<% end -%>><%= label -%></option>
+          <% end %>
+        </select>
+      </div>
+
 			<%= render :partial => "contributions/table", :locals => { :collection => @shared_items } %>
+
+      <div style="clear: both"></div>
+
 		<% else %>
 			<p class="none_text">No items have been shared with this Group yet.</p>
 		<% end %>
@@ -185,7 +209,7 @@
   </div>
 </div>
 
-<% if mine? @network %>
+<% if @network.administrator?(current_user.id) %>
   
   <% memberships = @network.memberships_accepted %>
   <a name="manage_memberships"></a>
@@ -239,3 +263,6 @@
 <div id="commentsBox">
 	<%= render :partial => "comments/comments", :locals => { :commentable => @network } %>
 </div>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Modified: branches/biocat/app/views/packs/_table.rhtml (2605 => 2606)


--- branches/biocat/app/views/packs/_table.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/packs/_table.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -47,8 +47,8 @@
 						
 						<p style="font-size: 85%;">
 							<a href="" pack_path(pack) + '#comments' -%>"><b>Comments: </b><%= pack.comments_count %></a> |
-							<b>Viewed:</b> <%= pluralize Viewing.total_site_viewings_count_for_contribution(pack.contribution.id), "time" %> |
-							<b>Downloaded:</b> <%= pluralize Download.total_site_downloads_count_for_contribution(pack.contribution.id), "time" %>
+							<b>Viewed:</b> <%=pluralize pack.contribution.site_viewings_count, "time" %> |
+				      <b>Downloaded:</b> <%=pluralize pack.contribution.site_downloads_count, "time" %>
 						</p>
 						
 						<% unless (tags = pack.tags).empty? %>

Deleted: branches/biocat/app/views/packs/all.rhtml (2605 => 2606)


--- branches/biocat/app/views/packs/all.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/packs/all.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,9 +0,0 @@
-<% t "All" -%>
-
-<h2>All Packs</h2>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @packs } %>
-
-<%= render :partial => "packs/table", :locals => { :collection => @packs } %>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @packs } %>
\ No newline at end of file

Modified: branches/biocat/app/views/packs/index.rhtml (2605 => 2606)


--- branches/biocat/app/views/packs/index.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/packs/index.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,8 +1,3 @@
-<ul class="sectionIcons">
-	<li><%= icon "pack", new_pack_path, nil, nil, "Create New Pack" %></li>
-	<li><%= icon "view-all", all_packs_path, nil, nil, "View All Packs" %></li>
-</ul>
-
 <div class="box_standout" style="margin: 1.5em 3em; padding: 0.7em 1.5em;">
 	<p>
 		<%= image_tag 'manhattan_studio/folder-closed_24.png', :style => "vertical-align: middle;" -%>
@@ -10,7 +5,7 @@
 			<%= link_to_function "What are Packs?" + expand_image("0.3em"), visual_effect(:toggle_blind, "packs_info_more", :duration => 0.4) %>
 		</span>
 	</p>
-	<div id="packs_info_more" style="margin-left: 36px; font-size: 93%; color: #333333; display: block;">
+	<div id="packs_info_more" style="margin-left: 36px; font-size: 93%; color: #333333; display: none;">
 		<p>
 			Packs allow you to <font style="color: black; font-weight: bolder;">collect different items</font> together, 
 			like you might with a "wish list" or "shopping basket".
@@ -26,21 +21,4 @@
 	</div>
 </div>
 
-<center>
-	
-</center>
-
-<% cache(:controller => 'packs', :action ="" 'all_tags') do -%>
-	<%= render :partial => "packs/all_tags" %>
-<% end -%>
-
-<%= render :partial => "contributions/most_tabs", :locals => { :type => "Pack" } %>
-
-<% unless (largest = Pack.most_items).empty? %>
-  <div class="tabContainer">
-  	<div class="tabTitle">Most items</div>
-  	<div class="tabContent">
-	    <%= render :partial => "packs/table", :locals => { :collection => largest } %>
-  	</div>
-  </div>
-<% end %>
+<%= render :partial => "content/index" -%>

Modified: branches/biocat/app/views/packs/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/packs/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/packs/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -81,8 +81,24 @@
 			</h4>
 			
 			<%= render :partial => "items", :locals => { :pack => @pack, :authorised_to_edit => @authorised_to_edit } -%>
+
+			<br/><br/>
+			<h4>
+				<%= info_icon_with_tooltip("This section shows all the relationships between the items in this pack.") -%>
+				Relationships <span class="count_text">(<%= @pack.relationships.length -%>)</span>
+			</h4>
+
+			<%= render :partial => "relationships/relationships", :locals => { :context => @pack, :show_delete => true } -%>
 			
+      <% if @authorised_to_edit %>
+        <br />
+        <ul class="sectionIcons">
+          <li><%= icon('manage', edit_relationships_pack_relationships_path(@pack), nil, nil, 'Edit Relationships') -%></li>
+        </ul>
+      <% end %>
+
 			<br/><br/>
+
 			<h3>
 				<%= info_icon_with_tooltip("This section provides a link to download all items in this pack as a single .zip archive") %>
 				Download
@@ -179,3 +195,6 @@
 <div id="commentsBox">
 	<%= render :partial => "comments/comments", :locals => { :commentable => @pack } %>
 </div>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Modified: branches/biocat/app/views/reviews/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/reviews/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/reviews/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -53,3 +53,6 @@
 		<% end %>
 	</div>
 </div>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Modified: branches/biocat/app/views/runners/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/runners/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/runners/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -45,3 +45,6 @@
 <div id="status_box" style="margin-top: 1.5em; font-weight: bold; color: #990000;">
 	<% render :partial => "status", :locals => { :service_valid => @runner.service_valid? } -%>
 </div>
+
+<%= render :partial => "contributions/alternative_formats" %>
+

Modified: branches/biocat/app/views/search/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/search/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/search/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,41 +1,16 @@
 <% t "Results" -%>
 
-<%= render :partial => 'extra_search_help' %>
-
 <h1>Search results for "<%= h @query -%>"</h1>
 
 <% if @total_count == 0 %>
 
   <p class="none_text">No search results.</p>
   
+  <%= render :partial => 'extra_search_help' %>
+
 <% else %>
 
-  <%= view_privileges_notice %>
-  <br/>
+  <%= render :partial => "content/index" -%>
 
-  <div id="tabsContainer" class="tabsContainer"></div>
-
-  <% @infos.each do |info| %>
-
-    <% name  = visible_name(info[:model]).pluralize
-       count = info[:results].length
-       total = info[:total_count] -%>
-
-    <div class="tabContainer">
-      <div class="tabTitle"><%= name %> (<%= count -%><% if count < total %> of <%= total -%><% end %>)</div>
-      <div class="tabContent">
-
-        <% if count < total %>
-          <p class="box_standout" style="font-size: 108%; margin-bottom: 1em; margin-top: 0.6em;"><b>
-            There are more results than shown here.  <a href="" search_path + "?type=#{info[:search_type]}&query=" + params[:query] -%>">See all <%= name %> results</a> for this query.
-          </b></p>
-        <% end %>
-
-        <%= render :partial => "#{info[:model].to_s.underscore.pluralize}/table", :locals => { :collection => info[:results], :query => @query } %>
-      </div>
-    </div>
-  <% end %>
-
 <% end %>
 
-<br />

Modified: branches/biocat/app/views/tags/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/tags/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/tags/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -30,6 +30,7 @@
 	<% end %>
 <% end -%>
 
+<%= render :partial => "contributions/alternative_formats" %>
 
 <% if false # legacy... %>
 <% if @internal_type -%>

Copied: branches/biocat/app/views/users/_listing.rhtml (from rev 2605, trunk/app/views/users/_listing.rhtml) (0 => 2606)


--- branches/biocat/app/views/users/_listing.rhtml	                        (rev 0)
+++ branches/biocat/app/views/users/_listing.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,93 @@
+<% cache(:controller => 'users_cache', :action ="" 'listing', :id => user.id) do -%>
+  <td style="width: 100px"><div style="text-align: center; font-weight: bold">Member</div><br /><br /><center><%= contributor(user.id, 'User', true, 60) %></center></td>
+  <td class="mid" style="text-align: left;">
+
+    <p style="margin-top:0; padding-top:0; font-weight:bold; font-size: 108%;">
+      <%= icon "user", nil, nil, nil, '' %>
+      <%= name user %>
+      <%= friend_badge(user) %>
+      <%= admin_badge(user) %>
+    </p>
+          
+    <% unless user.created_at.blank? %>
+      <p style="font-size: 85%;">
+        <b>Joined:</b>
+        <%= datetime user.created_at %>
+      </p>
+    <% end %>
+          
+    <div class="desc" style="font-size: 85%;">
+      <% unless user.profile.body.blank? %>
+        <% desc = truncate(strip_html(user.profile.body), 180) %>
+        <%= query ? highlight_all(desc, query) : desc %>
+      <% else -%>
+        <span class="none_text">No description</span>  
+      <% end %>
+    </div>
+          
+    <% unless user.last_seen_at.blank? %>
+      <p style="font-size: 85%;">
+        <b>Last active:</b>
+        <%= datetime user.last_seen_at %>
+      </p>
+    <% end %>
+          
+    <% if user.profile %>
+          
+      <p style="font-size: 85%;">
+        <% unless user.profile.website.blank? %>
+          <b>Website:</b>
+          <%= link_to h(user.profile.website), h(user.profile.website), :popup => true %>
+          |
+        <% end %>
+              
+        <% unless user.profile.email.blank? %>
+          <b>Email (public):</b>
+          <%= mail_to user.profile.email, nil, {  :encode => "hex", :replace_at => " [at] " } %>
+        <% end %>
+      </p>
+              
+      <p style="font-size: 85%;">
+        <% unless user.profile.field_or_industry.blank? %>
+          <b>Field/Industry:</b>
+          <%= h user.profile.field_or_industry %>
+          |
+        <% end %>
+              
+        <% unless user.profile.occupation_or_roles.blank? %>
+          <b>Occupation/Role(s):</b>
+          <%= h user.profile.occupation_or_roles %>
+        <% end %>
+      </p>
+
+    <% end %>
+  </td>
+<% end %>
+
+<td class="actions"  style="width: 90px;">
+  <%= icon "show", user_path(user.id), nil, nil, "View" %>
+  <% if mine? user %>
+    <%= icon "edit", edit_user_path(user), nil, nil, "Edit" %>
+  <% else %>
+    <!-- check if the profile that we are viewing now is a friend of current user -> stored for better performance -->
+    <% this_user_is_friend_of_current_user = (current_user != 0) && current_user.friend?(user.id) %>
+    <% unless !logged_in? || this_user_is_friend_of_current_user || current_user.friendship_pending?(user.id) %>
+      <%= icon "friendship", new_user_friendship_url(:user_id => user.id), nil, nil, "Request Friendship" %>
+    <% end %>
+
+    <%= icon "message", new_message_path(:user_id => user.id), nil, nil, "Message" %>
+
+    <% if logged_in? && this_user_is_friend_of_current_user %>
+      <% master_id, friendship_obj = current_user.friendship_from_self_id_and_friends_id(user.id) %>
+      <%= icon "friend_delete", user_friendship_path(master_id, friendship_obj) + "?return_to=" + currentusers_things_url('friends'), nil, {:confirm => "Are you sure you want to remove this user from your friend list?", :method => :delete}, "Cancel Friendship" %>
+    <% end %>
+  <% end %>
+
+  <% if Authorization.check(:action ="" 'destroy', :object => user, :user => current_user) %>
+    <%= icon "destroy", user_path(user) + "?return_to=" + CGI::escape(request.request_uri), nil, {
+      :confirm => "Are you sure you want to remove this user?", :method => :delete},
+      "Delete User" %>
+  <% end %>
+
+</td>
+

Modified: branches/biocat/app/views/users/_table.rhtml (2605 => 2606)


--- branches/biocat/app/views/users/_table.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/users/_table.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,96 +1,15 @@
 <% query ||= false -%>
 
 <% unless collection.empty? %>
-
-	<table class="alt_table">
-		<% odd_row = false -%> 
-		<% for user in collection %>
-			<% if user.activated? %>
-			  	<tr class="<%= (odd_row = !odd_row) ? "odd_row" : "even_row" %>">
-				<% cache(:controller => 'users_cache', :action ="" 'listing', :id => user.id) do -%>
-				    <td width="100px"><%= avatar user, 60 %></td>
-				    <td class="mid" style="text-align: left;">
-				    	<p style="margin-top:0; padding-top:0; font-weight:bold; font-size: 108%;">
-								<%= if user.profile then flag_icon(user.profile.location_country, user.profile.location) end %>
-								<%= name user.id %>
-								<%= friend_badge(user) %>
-								<%= admin_badge(user) %>
-							</p>
-						
-						<% unless user.created_at.blank? %>
-							<p style="font-size: 85%;">
-								<b>Joined:</b>
-								<%= datetime user.created_at %>
-							</p>
-						<% end %>
-						
-						<div class="desc" style="font-size: 85%;">
-							<% unless user.profile.body.blank? %>
-					  		<% desc = truncate(strip_html(user.profile.body), 180) %>
-								<%= query ? highlight_all(desc, query) : desc %>
-							<% else -%>
-								<span class="none_text">No description</span>	
-							<% end %>
-						</div>
-						
-						<% unless user.last_seen_at.blank? %>
-							<p style="font-size: 85%;">
-								<b>Last active:</b>
-								<%= datetime user.last_seen_at %>
-							</p>
-						<% end %>
-						
-						<% if user.profile %>
-						
-							<p style="font-size: 85%;">
-								<% unless user.profile.website.blank? %>
-									<b>Website:</b>
-									<%= link_to h(user.profile.website), h(user.profile.website), :popup => true %>
-									|
-								<% end %>
-								
-								<% unless user.profile.email.blank? %>
-									<b>Email (public):</b>
-						    	<%= mail_to user.profile.email, nil, {  :encode => "hex", :replace_at => " [at] " } %>
-								<% end %>
-							</p>
-								
-							<p style="font-size: 85%;">
-								<% unless user.profile.field_or_industry.blank? %>
-							    <b>Field/Industry:</b>
-									<%= h user.profile.field_or_industry %>
-									|
-							  <% end %>
-								
-								<% unless user.profile.occupation_or_roles.blank? %>
-							    <b>Occupation/Role(s):</b>
-									<%= h user.profile.occupation_or_roles %>
-							  <% end %>
-							</p>
-						
-						<% end %>
-					</td>
-				<% end %>
-				    <td class="actions"  style="width: 130px;">
-			      	<%= icon "show", user_path(user.id), nil, nil, "View Profile" %>
-			      	<% if mine? user %>
-				       	<%= icon "edit", edit_user_path(user), nil, nil, "Edit" %>
-						  <% else %>
-							  <!-- check if the profile that we are viewing now is a friend of current user -> stored for better performance -->
-			          <% this_user_is_friend_of_current_user = (current_user != 0) && user.friend?(current_user.id) %>
-								<% unless !logged_in? || this_user_is_friend_of_current_user || current_user.friendship_pending?(user.id) %>
-							  	<%= icon "friendship", new_user_friendship_url(:user_id => user.id), nil, nil, "Request Friendship" %>
-							  <% end %>
-							  <%= icon "message", new_message_path(:user_id => user.id), nil, nil, "Send Message" %>
-								<% if logged_in? && this_user_is_friend_of_current_user %>
-								  <% master_id, friendship_obj = current_user.friendship_from_self_id_and_friends_id(user.id) %>
-									<%= icon "friend_delete", user_friendship_path(master_id, friendship_obj) + "?return_to=" + currentusers_things_url('friends'), nil, {:confirm => "Are you sure you want to remove this user from your friend list?", :method => :delete}, "Cancel Friendship" %>
-								<% end %>
-			      	<% end %>
-			    	</td>
-			  	</tr>
-			<% end %>
-		<% end %>
-	</table>
-
+  <table class="alt_table">
+    <% odd_row = false -%> 
+    <% for user in collection %>
+      <% if user.activated? %>
+        <tr class="<%= (odd_row = !odd_row) ? "odd_row" : "even_row" %>">
+          <%= render :partial => "users/listing", :locals => { :user => user, :query => query } %>
+        </tr>
+      <% end %>
+    <% end %>
+  </table>
 <% end %>
+

Modified: branches/biocat/app/views/users/index.rhtml (2605 => 2606)


--- branches/biocat/app/views/users/index.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/users/index.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -6,6 +6,7 @@
 
 <div id="tabsContainer" class="tabsContainer" style="margin-top: 2.5em;"></div>
 
+<% if false %>
 <% unless (recent = User.most_recent(6)).empty? %>
   <div class="tabContainer">
   	<div class="tabTitle">Most Recent</div>
@@ -14,6 +15,7 @@
   	</div>
   </div>
 <% end %>
+<% end %>
 
 <% if false -%>
 <% unless (updated = User.last_updated(10)).empty? %>
@@ -60,4 +62,4 @@
 	    <%= render :partial => "users/table", :locals => { :collection => top_rated } %>
   	</div>
   </div>
-<% end %>
\ No newline at end of file
+<% end %>

Modified: branches/biocat/app/views/users/new.rhtml (2605 => 2606)


--- branches/biocat/app/views/users/new.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/users/new.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -28,6 +28,9 @@
 
   <center>
     <h2>Anti-spam verification</h2>
+  <% if true -%>
+    <%= recaptcha_tags -%> 
+  <% else -%>
     <table style="margin-top: 2em;">
       <tr>
         <td>
@@ -41,6 +44,7 @@
         </td>
       </tr>
     </table>
+  <% end -%>
   </center>
       
   <br />

Modified: branches/biocat/app/views/users/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/users/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/users/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -58,6 +58,12 @@
 				<% end %>
 			  <li><%= icon('history', userhistory_path(@user), nil, nil, 'View My History')%></li>
 	    <% end %>
+
+      <% if Authorization.check(:action ="" 'destroy', :object => @user, :user => current_user) %>
+					<li><%= icon "destroy", user_path(@user), nil, {
+            :confirm => "Are you sure you want to remove this user?", :method => :delete},
+            "Delete User" %></li>
+      <% end %>
 	</ul>
 <% end %>
 
@@ -464,6 +470,8 @@
   <%= render :partial => "networks/invite", :locals => { :current => current_user, :user => @user } %>
 <% end %>
 
+<%= render :partial => "contributions/alternative_formats" %>
+
 <% unless @tab.blank? -%>
 	<script>
 		$('user_tabnav').scrollTo();

Modified: branches/biocat/app/views/workflows/_breadcrumbs.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/_breadcrumbs.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/_breadcrumbs.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,6 +1,6 @@
 <li><%= link_to 'Workflows', workflows_path %></li>
 
-<% if ["show", "new", "edit", "search", "all", "new_version", "edit_version", "comments_timeline"].include? controller.action_name.to_s %>
+<% if ["show", "new", "edit", "search", "all", "new_version", "edit_version", "comments_timeline", "galaxy_tool"].include? controller.action_name.to_s %>
   <li><b>&#187;</b></li>
   
   <% case controller.action_name.to_s; when "show" %>
@@ -27,6 +27,10 @@
 		<li><%= link_to "#{h(@workflow.title)}", workflow_path(@workflow) %></li>
 		<li><b>&#187;</b></li>
     <li>Comments Timeline</li>
+	<% when "galaxy_tool" %>
+		<li><%= link_to "#{h(@workflow.title)}", workflow_path(@workflow) %></li>
+		<li><b>&#187;</b></li>
+    <li>Galaxy tool download</li>
   <% else %>
     <!-- no breadcrumb -->
   <% end %>

Deleted: branches/biocat/app/views/workflows/_license_form.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/_license_form.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/_license_form.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,46 +0,0 @@
-<% if params[:workflow] && !params[:workflow][:license_id].blank? %>
-	<% @license = License.find(params[:workflow][:license_id]) %>
-<% elsif edit %>
-	<% @license = License.find(@workflow.license_id) %>
-<% else %>
-    <% @license = License.find(2) %>
-<% end %>
-
-<div class="fold">
-    <div class="foldTitle">
-      <%= info_icon_with_tooltip("This section allows you to specify the <strong>rights</strong> that people have when they download and use this File, by setting the license. <br/><br/>By default, the license specifies that people are allowed to build on this File as long as they give the original author credit and share their resulting work under the same conditions.") %>
-      License/Rights
-			<% if edit %>
-				<hr />
-				<small>Current: <%= @license.title %> (<%= link_to h(@license.url), @license.url %>) </small>
-			<% else %>
-				<hr />
-				<small>Default: <%= @license.title %> (<%= link_to h(@license.url), @license.url %>)</small>
-			<% end %>
-    </div>
-    <div class="foldContent" style="display: none;">
-        <p class="box_infotext">
-            This section allows you to specify the <strong>rights</strong> that people have when they download and use this File, by setting the license.
-        </p>
-        <br />
-        <p>
-            <strong>What license do you want people to adhere to if they download and use this File?</strong>
-        </p>
-        <div style="padding-left: 1em;">
-          <%= select(:workflow, :license_id, License.find(:all).collect {|l| [l.title, l.id] },
-            { :selected => @license.id },
-            {  : remote_function(:update => 'license_info',
-                 :url ="" {:controller => 'licenses', :action="" 'update_license_info' },
-                 :with => "'license_id=' + escape(value)")}) %>
-
-        </div>
-        <hr/>
-        <div id="license_info" style="padding: 0 20px;">
-          <%= render :partial => "licenses/view", :locals => { :license => @license } %>
-		</div>
-    </div>
-</div>
-                
-
-                
-                

Modified: branches/biocat/app/views/workflows/_table.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/_table.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/_table.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -16,12 +16,12 @@
 			<% cache(:controller => 'workflows_cache', :action ="" 'listing', :id => workflow.id) do -%>
 			    <td style="width: 100px;">
 						<div class="workflow_type_box" style="width: auto; margin-bottom: 2em;">
-							<%= workflow.type_display_name -%>
+              <%= link_to(h(workflow.content_type.title), content_type_path(workflow.content_type)) %>
 						</div>
 			    	<p style="margin-top:0; padding-top:0; text-align: center;"><b><%= owner_text workflow -%></b></p>
 						<center><%= contributor(workflow.contribution.contributor_id, workflow.contribution.contributor_type, true, 60) %></center>
 					</td>
-			    <td style="text-align: left;">
+			    <td style="text-align: left; width: 587px">
 			      <a name="<%= workflow.title.gsub(/ /, "_") %>"></a>
 			      <p class="title">
 					  	<%= icon "workflow", nil, nil, nil, '' %>
@@ -68,30 +68,35 @@
 					  	</p>
 					  <% end %>
 						
-						<p style="font-size:85%;"><b>License: </b><% @license = License.find(workflow.license_id) %><%= link_to h(@license.title), license_path(@license) %></p>
+            <% if workflow.license_id.nil? %>
+              <p style="font-size:85%;"><b>License: </b>No license</p>
+            <% else %>
+              <p style="font-size:85%;"><b>License: </b><% @license = License.find(workflow.license_id) %><%= link_to h(@license.title), license_path(@license) %></p>
+            <% end %>
 					  
-						<table style="width: 99%;">
-							<tbody>
-								<tr>
-									<% unless workflow.image.nil? -%>
-										<td style="margin: 0; padding: 0.2em 0; border: 0; padding-right: 0.8em; width: 101px;">
-							      	<%= link_to image_tag(url_for_file_column(workflow, "image", "thumb"), :class => 'framed_nospace'), workflow_path(workflow) %>
-										</td>
-									<% end -%>
-									<td style="margin: 0; padding: 0.2em 0; border: 0;">
-										<div class="desc" style="font-size: 85%;">
-											<% if workflow.body and workflow.body.length > 0 -%>
-											  <% desc = truncate(strip_html(workflow.body), 500) -%>
-									      <%= query ? highlight_all(desc, query) : desc %>
-											<% else -%>
-												<span class="none_text">No description</span>								      
-									  	<% end -%>
-										</div>
-									</td>
-								</tr>
-							</tbody>
-						</table>
+            <% desc_style = "font-size: 85%;" %>
+
+            <% unless workflow.image.nil? -%>
+              <p style="margin: 0; border: 0; width: 101px; float: left">
+                <%= link_to image_tag(url_for_file_column(workflow, "image", "thumb"), :class => 'framed_nospace'), workflow_path(workflow) %>
+              </p>
+
+              <% desc_style << " margin-left: 110px; width: 250px;" %>
+            <% end -%>
+
+            <p style="margin: 0; padding: 0; border: 0;">
+              <div class="desc" style="<%= desc_style -%>">
+                <% if workflow.body and workflow.body.length > 0 -%>
+                  <% desc = truncate(strip_html(workflow.body), 500) -%>
+                  <%= query ? highlight_all(desc, query) : desc %>
+                <% else -%>
+                  <span class="none_text">No description</span>                      
+                <% end -%>
+              </div>
+            </p>
 					  
+            <div style="clear: both"></div>
+
 					  <p style="font-size: 85%;">
 							<a href="" workflow_path(workflow) + '#ratings' -%>"><b>Rating: </b><%= number_with_precision(workflow.rating, 1) %> / 5 (<%= pluralize workflow.ratings_count, 'rating' %>)</a> |
 							<a href="" workflow_path(workflow) + '#versions' -%>"><b>Versions: </b><%= workflow.versions_count %></a> |
@@ -101,8 +106,8 @@
 					  </p>
 						
 						<p style="font-size: 85%;">
-							<b>Viewed:</b> <%=pluralize Viewing.total_site_viewings_count_for_contribution(workflow.contribution.id), "time" %> |
-				      <b>Downloaded:</b> <%=pluralize Download.total_site_downloads_count_for_contribution(workflow.contribution.id), "time" %>
+							<b>Viewed:</b> <%=pluralize workflow.contribution.site_viewings_count, "time" %> |
+				      <b>Downloaded:</b> <%=pluralize workflow.contribution.site_downloads_count, "time" %>
 						</p>
 					  
 					  <% unless (tags = workflow.tags).empty? %>

Deleted: branches/biocat/app/views/workflows/all.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/all.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/all.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,9 +0,0 @@
-<% t "All" -%>
-
-<h2><%= feed_icon_tag "Latest Workflows", formatted_workflows_path(:rss), "margin-right: 0.3em;" -%> All Workflows</h2>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @workflows } %>
-
-<%= render :partial => "workflows/table", :locals => { :collection => @workflows } %>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @workflows } %>
\ No newline at end of file

Deleted: branches/biocat/app/views/workflows/comments_timeline.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/comments_timeline.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/comments_timeline.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,3 +0,0 @@
-<h1>Comments Timeline for Workflow: <%= contributable @workflow.id, 'Workflow' %></h1>
-
-<%= render :partial => "comments/timeline", :locals => { :comments_url_for_timeline => comments_workflow_url(@workflow) } %>

Modified: branches/biocat/app/views/workflows/edit.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/edit.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/edit.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -21,7 +21,7 @@
 
   <% if @workflow.owner?(current_user) %>
   	<%= render :partial => "contributions/sharing_form",  :locals => { :edit => true, :contributable => @workflow, :update_perms => true } %>
-    <%= render :partial => "workflows/license_form", :locals => { :edit => true } %>
+    <%= render :partial => "contributions/license_form", :locals => { :object => :workflow, :contributable => @workflow, :edit => true } %>
   <% end %>
 
   <p>

Copied: branches/biocat/app/views/workflows/galaxy_tool.rhtml (from rev 2605, trunk/app/views/workflows/galaxy_tool.rhtml) (0 => 2606)


--- branches/biocat/app/views/workflows/galaxy_tool.rhtml	                        (rev 0)
+++ branches/biocat/app/views/workflows/galaxy_tool.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,79 @@
+<% t "#{h @workflow.title} - Download as Galaxy tool" -%>
+
+<center>
+  <%= error_messages_for :workflow %>
+</center>
+
+<h1>Galaxy tool download</h1>
+
+<p><a href="" is a web-based framework to
+aggregate bioinformatics tools in a uniform way and connect them in pipelines.
+This download option provides you with a Galaxy tool that will execute this <a
+href="" 2</a> workflow from a Galaxy server.
+The zip archive contains the two files needed for a Galaxy tool and they have
+to be installed in the Galaxy server. (Administration privileges are required
+for installation.)</p>
+
+<p>To avoid potential issues, we recommend that you test workflows first in
+Taverna 2 Workbench before you create a tool to run them through Galaxy.</p>
+
+<h2>Galaxy tool generator</h2>
+
+<div style="margin: 2em; margin-left: 4em; margin-right: 4em; padding: 1em; background: #e0e0e0">
+
+  <% form_tag( { :action ="" :galaxy_tool_download } ) do %>
+
+    <p>
+      <b>Taverna server URL:</b><br />
+      <%= text_field_tag("server", "", :style => "width: 100%") %><br />
+      <small>E.g. http://www.example.com:8980/taverna-server</small>
+    </p>
+
+    <p style="text-align: center">
+      <%= submit_tag("Download Galaxy tool") %>
+    </p>
+
+  <% end %>
+</div>
+
+<h2>Requirements</h2>
+
+<p>The tool is implemented in Ruby, so a Ruby environment needs to be
+available.  For Debian based systems, use these commands:</p>
+
+<pre>
+$ sudo apt-get install ruby rubygems libopenssl-ruby ruby1.8-dev libxml2-dev
+$ sudo gem install t2-server --version 0.5.3
+$ sudo gem install rubyzip
+</pre>
+
+<h2>Taverna 2 server for testing purposes</h2>
+
+<p>Since the tool submits the workflow to a Taverna 2 server, you will need to
+have access to one. It is straightforward to install a Taverna 2 server but for
+your convenience the <a href="" Group</a>
+have provided a test server to use. Please note that this server should only be
+used for testing the workflow!  <i>When using our Taverna 2 test server, the
+only requirement is administration access to a Galaxy server to install the
+tool you are about to download.</i></p>
+
+<p>The URL to the test Taverna 2 server is:
+
+<div style="padding: 1em; font-family: monospace">http://taverna-server.biosemantics.org/</div>
+</p>
+
+<h2>Further information</h2>
+
+<ul>
+<li><a href="" to Galaxy tool</a></li>
+<li><a href="" a tool in Galaxy</a></li>
+<li><a href="" a Taverna 2 server</a></li>
+<li><a href="" Taverna Workbench</a></li>
+</ul>
+
+<p style="text-align: center; margin: 1em">
+  <% form_tag( { :action ="" "show" }, :method => :get) do %>
+    <%= submit_tag("Return to workflow") -%>
+  <% end %>
+</p>
+

Deleted: branches/biocat/app/views/workflows/index.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/index.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/index.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,17 +0,0 @@
-<div class="box_standout" style="text-align: center; margin-bottom: 1em; font-weight: bold; line-height: 1.5em;">
-	<% Workflow.count(:all, :group => 'content_type_id').sort{|x,y| y[1] <=> x[1]}.each do |arr| %>
-		  <span class="nowrap"><%= link_to((h(ContentType.find_by_id(arr[0]).title)),
-        search_workflows_path + "?query=kind:(#{ContentType.find_by_id(arr[0]).title})") %> (<%= arr[1] %>)</span>
-	<% end %>
-</div>
-
-<ul class="sectionIcons">
-	<li><%= icon "workflow", new_workflow_path, nil, nil, "Upload New Workflow" %></li>
-	<li><%= icon "view-all", all_workflows_path, nil, nil, "View All Workflows" %></li>
-</ul>
-
-<% cache(:controller => 'workflows', :action ="" 'all_tags') do -%>
-	<%= render :partial => "workflows/all_tags" %>
-<% end -%>
-
-<%= render :partial => "contributions/most_tabs", :locals => { :type => "Workflow" } %>

Copied: branches/biocat/app/views/workflows/index.rhtml (from rev 2605, trunk/app/views/workflows/index.rhtml) (0 => 2606)


--- branches/biocat/app/views/workflows/index.rhtml	                        (rev 0)
+++ branches/biocat/app/views/workflows/index.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,4 @@
+<h1>Workflows</h1>
+
+<%= render :partial => "content/index" -%>
+

Modified: branches/biocat/app/views/workflows/new.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/new.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/new.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -43,7 +43,7 @@
   <%= render :partial => "contributions/sharing_form", :locals => { :edit => false, :contributable => @workflow, :update_perms => true } -%>
                 
   <!-- License/Rights -->
-  <%= render :partial => "workflows/license_form", :locals => { :edit => false } -%>
+  <%= render :partial => "contributions/license_form", :locals => { :object => :workflow, :contributable => @workflow, :edit => false } -%>
   
 	
 	<!-- Terms and conditions -->
@@ -54,12 +54,12 @@
 	
 	<br/>
 
-	<!-- Upload and save -->
+	<!-- Upload and Continue -->
 	
-	<p class="step_text">5. Upload and save</p>
+	<p class="step_text">5. Upload and Continue</p>
 
   <p style="text-align: center;">
-    <%= submit_tag "Upload and Save", :disable_with => "Uploading and saving..." %>
+    <%= submit_tag "Upload and Continue", :disable_with => "Uploading..." %>
   </p>
   
 <% end %>

Modified: branches/biocat/app/views/workflows/rapid_miner/_internals.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/rapid_miner/_internals.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/rapid_miner/_internals.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -30,7 +30,7 @@
 		</div>
 	
 		<!-- Operator -->
-		<% sources = package.process.operators[0].processes[0].operators -%>
+		<% sources = package.all_operators -%>
 		<div class="fold">
 			<div class="foldTitle">
 		    Operators (<%= sources.length -%>)

Modified: branches/biocat/app/views/workflows/show.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/show.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/show.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -148,7 +148,7 @@
 			
 			<p>
 				<b>Type:</b>
-				<%= h(@workflow.type_display_name) %>
+				<%= link_to(h(@workflow.content_type.title), content_type_path(@workflow.content_type)) %>
 			</p>
 			
 			<br/>
@@ -210,24 +210,40 @@
 			
 			<br/>
 			
+      <% if @workflow.show_download_section? %>
+
+        <h3>
+          <%= info_icon_with_tooltip("This section provides links to the different downloads for this version of the Workflow") %>
+          Download
+        </h3>
+        
+        <% if @authorised_to_download %>
+
+          <br />
+
+          <ul class="sectionIcons">
+            <li style="margin-left: 0;"><%= icon('workflow', @named_download_url, "Download Workflow file/package (for version address@hidden)", nil, "Download Workflow File/Package (address@hidden)") -%></li>
+          </ul>
+
+          <% if @workflow.content_type.title == "Taverna 2" %>
+            <br />
+
+            <ul class="sectionIcons">
+              <li style="margin-left: 0;"><%= icon('workflow', galaxy_tool_path(:id => @workflow.id, :version => @viewing_version_number.to_s), "Download Workflow file/package (for version address@hidden)", nil, "Download Workflow as a Galaxy tool") -%></li>
+            </ul>
+          <% end %>
+
+        <% else %>
+          <p class="none_text">
+            You do not have permission to download this workflow
+          </p>
+        <% end %>
+        
+        <br/>
+        
+      <% end %>
+
 			<h3>
-				<%= info_icon_with_tooltip("This section provides links to the different downloads for this version of the Workflow") %>
-				Download
-			</h3>
-			
-			<% if @authorised_to_download %>
-				<ul class="sectionIcons">
-					<li style="margin-left: 0;"><%= icon('workflow', @named_download_url, "Download Workflow file/package (for version address@hidden)", nil, "Download Workflow File/Package (address@hidden)") -%></li>
-				</ul>
-			<% else %>
-				<p class="none_text">
-					You do not have permission to download this workflow
-				</p>
-			<% end %>
-			
-			<br/>
-			
-			<h3>
 				<%= info_icon_with_tooltip("This section provides options for running this version of the Workflow") %>
 				Run
 			</h3>
@@ -273,8 +289,9 @@
 </div>
 
 <div class="contribution_right_box">
-	<div class="contribution_section_box" style= "font-size: 100%; color: #CC0000; padding: 0.7em 0.9em; font-weight: bold;">
-		<%= @workflow.type_display_name -%> workflow
+	<div class="contribution_section_box" style= "font-size: 100%; padding: 0.7em 0.9em; font-weight: bold;">
+		<p><%= info_icon_with_tooltip("The type of workflow system that this Workflow is designed for.") %> Workflow Type</p>
+    <p><%= link_to(h(@workflow.content_type.title), content_type_path(@workflow.content_type)) %></p>
 	</div>
 	
 	<%= render :partial => "contributions/uploader_box", :locals => { :contributable => @workflow } %>
@@ -382,9 +399,11 @@
 <br/>
 
 <div id="commentsBox">
-	<%= render :partial => "comments/comments", :locals => { :commentable => @workflow, :url_to_timeline => comments_timeline_workflow_path(@workflow) } %>
+	<%= render :partial => "comments/comments", :locals => { :commentable => @workflow } %>
 </div>
 
+<%= render :partial => "contributions/alternative_formats" %>
+
 <!-- OpenURL context object -->
 
 <span class="Z3988" title="<%= workflow_context_object(@workflow) -%>"></span>

Modified: branches/biocat/app/views/workflows/tag_suggestions.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/tag_suggestions.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/tag_suggestions.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,28 +1,52 @@
+<%= _javascript__include_tag :fckeditor %>
 <%= _javascript__include_tag "tag_suggestions.js" %>
 
-<h1>Tags</h1>
+<h1>Extra workflow metadata</h1>
 
-<h2>Automatic tag suggestions</h2>
+<form action="" "/workflows/address@hidden/process_tag_suggestions" %>" method="post">
 
-<p>The following tag suggestions are based on the content of your workflow.
-Click on each tag that you wish to use.<p>
+  <% if @workflow.body.nil? || @workflow.body == "" %>
 
-<div class="box_simple" style="font-size: 93%; text-align: left; margin: 2em;">
-  <div id="suggestions"></div>
-</div>
+    <h2>Workflow description</h2>
 
-<p><em>Note: Tag suggestions are given by matching words in your workflow with other
-tags already used in <%= Conf.sitename %>.</em></p>
+    <p><em>The workflow you have uploaded has no description!  By entering a
+    description here, you will increase the chances of others finding your
+    workflow and help understand its purpose.</em></p>
 
-<h2>Summary</h2>
+      <!-- Description -->
+      <p><b>Description</b></p>
 
-<div id="summary-text"></div>
+    <%= fckeditor_textarea(:workflow, :body, :toolbarSet => 'Simple', :width => '500px', :height => '300px') %>
 
-<form action="" "/workflows/address@hidden/process_tag_suggestions" %>" method="post">
+  <% end %>
+
+  <h2>Tags</h2>
+
+  <h3>Automatic tag suggestions</h3>
+
+  <p>The following tag suggestions are based on the content of your workflow.
+  Click on each tag that you wish to use.<p>
+
+  <div class="tag_suggestion_box">
+    <div id="suggestions"></div>
+  </div>
+
+  <p><em>Note: Tag suggestions are given by matching words in your workflow
+  with other tags already used in <%= Conf.sitename %>.</em></p>
+
+  <p><b>Summary</b></p>
+
+  <div id="summary-text"></div>
+
+  <h2>Finish</h2>
+
+  <p>Complete the upload process.</p>
+
   <p style="text-align: center">
-    <input id="submit-button" type="submit" value="Continue" />
+    <input id="submit-button" type="submit" value="Finish" />
     <input type="hidden" name="tag_list" id="tag_list" />
   </p>
+
 </form>
 
 <script type="text/ecmascript">

Modified: branches/biocat/app/views/workflows/taverna2/_internals.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/taverna2/_internals.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/taverna2/_internals.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -226,14 +226,14 @@
         <% unless datalinks.blank? -%>
           <table class="simple">
             <tr>
+              <th>Source</th>
               <th>Sink</th>
-              <th>Source</th>
             </tr>
           
             <% datalinks.each do |dl| -%>
               <tr>
+                <td><%= h dl.source -%></td>
                 <td><%= h dl.sink -%></td>
-                <td><%= h dl.source -%></td>
               </tr>
             <% end %>
           </table>

Modified: branches/biocat/app/views/workflows/taverna_scufl/_internals.rhtml (2605 => 2606)


--- branches/biocat/app/views/workflows/taverna_scufl/_internals.rhtml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/app/views/workflows/taverna_scufl/_internals.rhtml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -147,14 +147,14 @@
 				<% unless links.blank? -%>
 					<table class="simple">
 					  <tr>
+					    <th>Source</th>
 					    <th>Sink</th>
-					    <th>Source</th>
 					  </tr>
 					
 						<% links.each do |l| -%>
 						  <tr>
+						    <td><%= h l.source -%></td>
 						    <td><%= h l.sink -%></td>
-						    <td><%= h l.source -%></td>
 						  </tr>
 						<% end %>
 					</table>

Deleted: branches/biocat/backup.sh (2605 => 2606)


--- branches/biocat/backup.sh	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/backup.sh	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,18 +0,0 @@
-#!/bin/bash
-
-RAILS_ROOT="/var/www/m2"
-DB="m2_production"
-DIRS="public/pictures public/workflow"
-USER="backup"
-BACKUP_DIR="/home/backup"
-
-SQL_DUMP="${BACKUP_DIR}/myexp-$(date +%Y-%m-%d).sql"
-PUB_DUMP="${BACKUP_DIR}/myexp-$(date +%Y-%m-%d).tgz"
-SQL_LINK="${BACKUP_DIR}/myexp.sql"
-PUB_LINK="${BACKUP_DIR}/myexp.tgz"
-
-/usr/bin/mysqldump ${DB} > ${SQL_DUMP}
-/bin/tar Cczf ${RAILS_ROOT} ${PUB_DUMP} ${DIRS}
-/bin/ln -sf ${SQL_DUMP} ${SQL_LINK}
-/bin/ln -sf ${PUB_DUMP} ${PUB_LINK}
-/bin/chown -h ${USER}:${USER} ${SQL_DUMP} ${PUB_DUMP} ${SQL_LINK} ${PUB_LINK}

Copied: branches/biocat/config/base_schema.xml (from rev 2605, trunk/config/base_schema.xml) (0 => 2606)


--- branches/biocat/config/base_schema.xml	                        (rev 0)
+++ branches/biocat/config/base_schema.xml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+<schema>
+
+  <table name="contributions">
+
+    <column type="integer"  name="contributor_id"/>
+    <column type="string"   name="contributor_type"/>
+    <column type="integer"  name="contributable_id"/>
+    <column type="string"   name="contributable_type"/>
+    <column type="integer"  name="policy_id"/>
+    <column type="datetime" name="created_at"/>
+    <column type="datetime" name="updated_at"/>
+    <column type="integer"  name="downloads_count"      default="0"/>
+    <column type="integer"  name="viewings_count"       default="0"/>
+    <column type="integer"  name="site_downloads_count" default="0"/>
+    <column type="integer"  name="site_viewings_count"  default="0"/>
+    <column type="float"    name="rating"/>
+    <column type="float"    name="rank"/>
+    <column type="string"   name="label"/>
+    <column type="integer"  name="content_type_id"/>
+    <column type="integer"  name="license_id"/>
+
+    <index>
+      <column name="contributable_id"/>
+      <column name="contributable_type"/>
+    </index>
+
+    <index>
+      <column name="contributor_id"/>
+      <column name="contributor_type"/>
+    </index>
+
+  </table>
+
+  <table name="downloads">
+
+    <column type="integer"  name="contribution_id"/>
+    <column type="integer"  name="user_id"/>
+    <column type="datetime" name="created_at"/>
+    <column type="string"   name="user_agent"/>
+    <column type="boolean"  name="accessed_from_site" default="0"/>
+    <column type="string"   name="kind"/>
+
+    <index>
+      <column name="contribution_id"/>
+    </index>
+
+  </table>
+
+  <table name="content_types">
+
+    <column type="integer"  name="user_id"/>
+    <column type="string"   name="title"/>
+    <column type="text"     name="description"/>
+    <column type="text"     name="description_html"/>
+    <column type="string"   name="category"/>
+    <column type="string"   name="mime_type"/>
+    <column type="datetime" name="created_at"/>
+    <column type="datetime" name="updated_at"/>
+
+  </table>
+  
+</schema>
+

Modified: branches/biocat/config/default_settings.yml (2605 => 2606)


--- branches/biocat/config/default_settings.yml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/config/default_settings.yml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -33,6 +33,16 @@
 
 site_logo: logo.png
 
+# contributor_models - These are the models for things that can contribute to
+#                      myExperiment.
+
+contributor_models: [User, Network]
+
+# contributable_models - These are the models for the things that myExperiment
+#                        contributors can contribute.
+
+contributable_models: [Workflow, Blob, Pack, Blog]
+
 # page_template - This is the page template for all the pages except for
 #                 the front page of the web site.
 #
@@ -234,7 +244,7 @@
 # max_search_size - The maximum number of items shown in each search result
 #                   page.
 
-max_search_size: 100
+max_search_size: 5000
 
 # email - These are the email addresses used for sending notifications and the
 #         email address to send feedback entered from the web site.
@@ -425,3 +435,8 @@
 
 label_icons:
 
+# rdfgen_enable
+
+rdfgen_enable: false
+
+rdfgen_tool:

Modified: branches/biocat/config/environment.rb (2605 => 2606)


--- branches/biocat/config/environment.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/config/environment.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -9,6 +9,8 @@
 
 # Bootstrap the Rails environment, frameworks, and default configuration
 require File.join(File.dirname(__FILE__), 'boot')
+require 'lib/conf'
+require 'uri'
 
 Rails::Initializer.run do |config|
   # Settings in config/environments/* take precedence over those specified here
@@ -19,7 +21,7 @@
   # Only load the plugins named here, by default all plugins in vendor/plugins are loaded
   # config.plugins = %W( exception_notification ssl_requirement )
 
-  config.plugins = ["engines", "encrypted_strings", "widgets", "white_list", "*"]
+  config.plugins = ["engines", "encrypted_strings", "widgets", "white_list", "white_list_formatted_content", "*"]
 
   # Add additional load paths for your own custom dirs
   # config.load_paths += %W( #{RAILS_ROOT}/extras )
@@ -45,6 +47,13 @@
   # config.active_record.default_timezone = :utc
   
   # See Rails::Configuration for more options
+
+  base_uri = URI.parse(Conf.base_uri)
+
+  config.action_mailer.default_url_options = {
+    :host => base_uri.host + (base_uri.port == 80 ? "" : ":#{base_uri.port.to_s}")
+  }
+
 end
 
 # Add new inflection rules using the following format 

Modified: branches/biocat/config/routes.rb (2605 => 2606)


--- branches/biocat/config/routes.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/config/routes.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -5,6 +5,45 @@
   # rest routes
   rest_routes(map)
 
+  # LoD routes
+  if Conf.rdfgen_enable
+
+    map.connect '/:contributable_type/:contributable_id/attributions/:attribution_id.:format',
+      :controller => 'linked_data', :action ="" 'attributions'
+
+    map.connect '/:contributable_type/:contributable_id/citations/:citation_id.:format',
+      :controller => 'linked_data', :action ="" 'citations'
+
+    map.connect '/:contributable_type/:contributable_id/comments/:comment_id.:format',
+      :controller => 'linked_data', :action ="" 'comments'
+
+    map.connect '/:contributable_type/:contributable_id/credits/:credit_id.:format',
+      :controller => 'linked_data', :action ="" 'credits'
+
+    map.connect '/users/:user_id/favourites/:favourite_id.:format',
+      :controller => 'linked_data', :action ="" 'favourites'
+
+    map.connect '/packs/:contributable_id/local_pack_entries/:local_pack_entry_id.:format',
+      :controller => 'linked_data', :action ="" 'local_pack_entries',
+      :contributable_type => 'packs'
+
+    map.connect '/packs/:contributable_id/remote_pack_entries/:remote_pack_entry_id.:format',
+      :controller => 'linked_data', :action ="" 'remote_pack_entries',
+      :contributable_type => 'packs'
+
+    map.connect '/:contributable_type/:contributable_id/policies/:policy_id.:format',
+      :controller => 'linked_data', :action ="" 'policies'
+
+    map.connect '/:contributable_type/:contributable_id/ratings/:rating_id.:format',
+      :controller => 'linked_data', :action ="" 'ratings'
+
+    map.connect '/tags/:tag_id/taggings/:tagging_id.:format',
+      :controller => 'linked_data', :action ="" 'taggings'
+  end
+
+  map.content '/content', :controller => 'content', :action ="" 'index', :conditions => { :method => :get }
+  map.formatted_content '/content.:format', :controller => 'content', :action ="" 'index', :conditions => { :method => :get }
+
   # Runners
   map.resources :runners, :member => { :verify => :get }
   
@@ -40,10 +79,8 @@
   
   # packs
   map.resources :packs, 
-    :collection => { :all => :get, :search => :get }, 
-    :member => { :comment => :post, 
-                 :comment_delete => :delete,
-                 :statistics => :get,
+    :collection => { :search => :get }, 
+    :member => { :statistics => :get,
                  :favourite => :post,
                  :favourite_delete => :delete,
                  :tag => :post,
@@ -56,36 +93,40 @@
                  :quick_add => :post,
                  :resolve_link => :post,
                  :items => :get } do |pack|
-    # No nested resources yet
+    pack.resources :comments, :collection => { :timeline => :get }
+    pack.resources :relationships, :collection => { :edit_relationships => :get }
   end
     
-
   # workflows (downloadable)
   map.resources :workflows, 
-    :collection => { :all => :get, :search => :get }, 
+    :collection => { :search => :get }, 
     :member => { :new_version => :get, 
                  :download => :get, 
                  :launch => :get,
                  :statistics => :get,
                  :favourite => :post, 
                  :favourite_delete => :delete, 
-                 :comment => :post, 
-                 :comment_delete => :delete, 
                  :rate => :post, 
                  :tag => :post, 
                  :create_version => :post, 
                  :destroy_version => :delete, 
                  :edit_version => :get, 
                  :update_version => :put, 
-                 :comments_timeline => :get, 
-                 :comments => :get,
                  :process_tag_suggestions => :post,
                  :tag_suggestions => :get } do |workflow|
     # workflows have nested citations
     workflow.resources :citations
     workflow.resources :reviews
+    workflow.resources :comments, :collection => { :timeline => :get }
   end
 
+  # 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'
+
+  map.galaxy_tool 'workflows/:id/versions/:version/galaxy_tool', :controller => 'workflows', :action ="" 'galaxy_tool'
+  map.galaxy_tool_download 'workflows/:id/versions/:version/galaxy_tool_download', :controller => 'workflows', :action ="" 'galaxy_tool_download'
+
   # curation
   ['workflows', 'files', 'packs'].each do |contributable_type|
     map.curation "#{contributable_type}/:contributable_id/curation",
@@ -98,19 +139,18 @@
   # files (downloadable)
   map.resources :files, 
     :controller => :blobs, 
-    :collection => { :all => :get, :search => :get }, 
+    :collection => { :search => :get }, 
     :member => { :download => :get,
                  :statistics => :get,
                  :favourite => :post,
                  :favourite_delete => :delete,
-                 :comment => :post, 
-                 :comment_delete => :delete, 
                  :rate => :post, 
                  :tag => :post } do |file|
     # Due to restrictions in the version of Rails used (v1.2.3), 
     # we cannot have reviews as nested resources in more than one top level resource.
     # ie: we cannot have polymorphic nested resources.
     #file.resources :reviews
+    file.resources :comments, :collection => { :timeline => :get }
   end
 
   # blogs
@@ -122,6 +162,9 @@
   # services
   map.resources :services, :collection => { :all => :get, :search => :get }
   
+  # content_types
+  map.resources :content_types
+
   # all downloads and viewings
   map.resources :downloads, :viewings
 
@@ -182,11 +225,10 @@
                  :membership_invite => :post,
                  :membership_invite_external => :post,
                  :membership_request => :get, 
-                 :comment => :post, 
-                 :comment_delete => :delete, 
                  :rate => :post, 
                  :tag => :post } do |group|
     group.resources :announcements, :controller => :group_announcements
+    group.resources :comments, :collection => { :timeline => :get }
   end
   
   # The priority is based upon order of creation: first created -> highest priority.
@@ -212,7 +254,7 @@
   map.connect ':controller/:id/download/:name', :action ="" 'named_download', :requirements => { :name => /.*/ }
   
   map.connect 'files/:id/download/:name', :controller => 'blobs', :action ="" 'named_download', :requirements => { :name => /.*/ }
-  
+
   # (general) announcements
   # NB! this is moved to the bottom of the file for it to be discovered
   # before 'announcements' resource within 'groups'

Copied: branches/biocat/config/schema.d/owl.xml (from rev 2605, trunk/config/schema.d/owl.xml) (0 => 2606)


--- branches/biocat/config/schema.d/owl.xml	                        (rev 0)
+++ branches/biocat/config/schema.d/owl.xml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<schema>
+
+  <table name="ontologies">
+
+    <column type="integer"  name="user_id"/>
+    <column type="string"   name="uri"/>
+    <column type="string"   name="title"/>
+    <column type="string"   name="prefix"/>
+    <column type="text"     name="description"/>
+    <column type="text"     name="description_html"/>
+    <column type="datetime" name="created_at"/>
+    <column type="datetime" name="updated_at"/>
+
+    <has-many target="predicates" foreign_key="ontology_id"/>
+
+  </table>
+
+  <table name="predicates">
+
+    <column type="integer"  name="ontology_id"/>
+    <column type="string"   name="title"/>
+    <column type="string"   name="phrase"/>
+    <column type="text"     name="description"/>
+    <column type="text"     name="description_html"/>
+    <column type="text"     name="equivalent_to"/>
+    <column type="datetime" name="created_at"/>
+    <column type="datetime" name="updated_at"/>
+
+    <belongs-to target="ontology"/>
+
+  </table>
+
+</schema>
+

Copied: branches/biocat/config/schema.d/packs.xml (from rev 2605, trunk/config/schema.d/packs.xml) (0 => 2606)


--- branches/biocat/config/schema.d/packs.xml	                        (rev 0)
+++ branches/biocat/config/schema.d/packs.xml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<schema>
+
+  <table name="packs">
+
+    <column type="integer"  name="contributor_id"/>
+    <column type="string"   name="contributor_type"/>
+    <column type="string"   name="title"/>
+    <column type="text"     name="description"/>
+    <column type="text"     name="description_html"/>
+    <column type="datetime" name="created_at"/>
+    <column type="datetime" name="updated_at"/>
+
+    <has-many target="relationships" as="context" dependent="destroy"/>
+
+  </table>
+
+  <table name="pack_contributable_entries">
+
+    <column type="integer"  name="pack_id"/>
+    <column type="integer"  name="contributable_id"/>
+    <column type="integer"  name="contributable_version"/>
+    <column type="string"   name="contributable_type"/>
+    <column type="text"     name="comment"/>
+    <column type="integer"  name="user_id"/>
+    <column type="datetime" name="created_at"/>
+    <column type="datetime" name="updated_at"/>
+
+  </table>
+
+  <table name="pack_remote_entries">
+
+    <column type="integer"  name="pack_id"/>
+    <column type="string"   name="title"/>
+    <column type="string"   name="uri"/>
+    <column type="string"   name="alternate_uri"/>
+    <column type="text"     name="comment"/>
+    <column type="integer"  name="user_id"/>
+    <column type="datetime" name="created_at"/>
+    <column type="datetime" name="updated_at"/>
+
+  </table>
+
+  <table name="relationships">
+
+    <column type="integer"  name="context_id"/>
+    <column type="string"   name="context_type"/>
+    <column type="integer"  name="user_id"/>
+    <column type="integer"  name="predicate_id"/>
+    <column type="string"   name="subject_type"/>
+    <column type="integer"  name="subject_id"/>
+    <column type="string"   name="objekt_type"/>
+    <column type="integer"  name="objekt_id"/>
+    <column type="datetime" name="created_at"/>
+
+    <belongs-to target="users"/>
+    <belongs-to target="contexts" polymorphic="true"/>
+
+    <belongs-to target="subjects" polymorphic="true"/>
+    <belongs-to target="predicates"/>
+    <belongs-to target="objekts"  polymorphic="true"/>
+
+  </table>
+ 
+</schema>
+

Copied: branches/biocat/config/schema.d/skos.xml (from rev 2605, trunk/config/schema.d/skos.xml) (0 => 2606)


--- branches/biocat/config/schema.d/skos.xml	                        (rev 0)
+++ branches/biocat/config/schema.d/skos.xml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,120 @@
+<?xml version="1.0"?>
+<schema>
+
+  <table name="vocabularies">
+
+    <column type="integer"  name="user_id"/>
+    <column type="string"   name="uri"/>
+    <column type="string"   name="title"/>
+    <column type="string"   name="prefix"/>
+    <column type="text"     name="description"/>
+    <column type="text"     name="description_html"/>
+    <column type="datetime" name="created_at"/>
+    <column type="datetime" name="updated_at"/>
+
+    <has-many target="concepts" dependent="destroy"/>
+
+  </table>
+  
+  <table name="concepts">
+
+    <column type="integer"  name="vocabulary_id"/>
+    <column type="string"   name="phrase"/>
+    <column type="text"     name="description"/>
+    <column type="text"     name="description_html"/>
+    <column type="datetime" name="created_at"/>
+    <column type="datetime" name="updated_at"/>
+
+    <belongs-to target="vocabularies"/>
+
+    <!-- Broader relations -->
+
+    <has-many target      = "broader_relations"
+              foreign_key = "object_concept_id"
+              class_name  = "ConceptRelation"
+              conditions  = "relation_type = 'broader'"/>
+
+    <has-many target      = "broader_concepts"
+              class_name  = "Concept"
+              through     = "broader_relations"
+              source      = "object_concept"/>
+
+    <!-- Narrower relations -->
+
+    <has-many target      = "narrower_relations"
+              foreign_key = "subject_concept_id"
+              class_name  = "ConceptRelation"
+              conditions  = "relation_type = 'broader'"/>
+
+    <has-many target      = "narrower_concepts"
+              class_name  = "Concept"
+              through     = "narrower_relations"
+              source      = "subject_concept"/>
+
+    <!-- Related relations -->
+
+    <has-many target      = "related_relations"
+              foreign_key = "subject_concept_id"
+              class_name  = "ConceptRelation"
+              conditions  = "relation_type = 'related'"/>
+
+    <has-many target      = "related_concepts"
+              class_name  = "Concept"
+              through     = "related_relations"
+              source      = "object_concept"/>
+
+    <!-- Labels -->
+
+    <has-many target="labels" dependent="destroy"/>
+
+    <has-many target      = "preferred_labels"
+              dependent   = "destroy"
+              class_name  = "Label"
+              foreign_key = "concept_id"
+              conditions  = "label_type = 'preferred'"/> 
+
+    <has-many target      = "alternate_labels"
+              dependent   = "destroy"
+              class_name  = "Label"
+              foreign_key = "concept_id"
+              conditions  = "label_type = 'alternate'"/> 
+
+    <has-many target      = "hidden_labels"
+              dependent   = "destroy"
+              class_name  = "Label"
+              foreign_key = "concept_id"
+              conditions  = "label_type = 'hidden'"/> 
+
+  </table>
+
+  <table name="concept_relations">
+
+    <column type="integer"  name="subject_concept_id"/>
+    <column type="integer"  name="object_concept_id"/>
+    <column type="string"   name="relation_type"/>
+
+    <belongs-to target      = "subject_concepts"
+                class_name  = "Concept"
+                foreign_key = "subject_concept_id"/>
+
+    <belongs-to target      = "object_concepts"
+                class_name  = "Concept"
+                foreign_key = "object_concept_id"/>
+
+  </table>
+
+  <table name="labels">
+
+    <column type="string"   name="text"/>
+    <column type="string"   name="label_type"/>
+    <column type="string"   name="language"/>
+    <column type="integer"  name="concept_id"/>
+    <column type="integer"  name="vocabulary_id"/>
+
+    <belongs-to target="vocabularies"/>
+    <belongs-to target="concepts"/>
+
+  </table>
+
+</schema>
+

Modified: branches/biocat/config/tables.xml


(Binary files differ)

Copied: branches/biocat/db/migrate/084_assign_categories_to_content_types.rb (from rev 2605, trunk/db/migrate/084_assign_categories_to_content_types.rb) (0 => 2606)


--- branches/biocat/db/migrate/084_assign_categories_to_content_types.rb	                        (rev 0)
+++ branches/biocat/db/migrate/084_assign_categories_to_content_types.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,23 @@
+# myExperiment: db/migrate/084_assign_categories_to_content_types.rb
+#
+# Copyright (c) 2010 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class AssignCategoriesToContentTypes < ActiveRecord::Migration
+  def self.up
+    workflow_content_types = Workflow.find(:all).group_by do |w| w.content_type_id end.keys
+
+    ContentType.find(:all).each do |content_type|
+      if workflow_content_types.include?(content_type.id)
+        category = "Workflow"
+      else
+        category = "Blob"
+      end
+
+      content_type.update_attribute("category", category)
+    end
+  end
+
+  def self.down
+  end
+end

Copied: branches/biocat/db/migrate/085_add_index_to_profiles_table.rb (from rev 2605, trunk/db/migrate/085_add_index_to_profiles_table.rb) (0 => 2606)


--- branches/biocat/db/migrate/085_add_index_to_profiles_table.rb	                        (rev 0)
+++ branches/biocat/db/migrate/085_add_index_to_profiles_table.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,13 @@
+class AddIndexToProfilesTable < ActiveRecord::Migration
+  def self.up
+
+    add_index :profiles, ["user_id"]
+
+  end
+
+  def self.down
+
+    remove_index :profiles, ["user_id"]
+
+  end
+end

Copied: branches/biocat/db/migrate/086_rename_indexes_to_automatic_names.rb (from rev 2605, trunk/db/migrate/086_rename_indexes_to_automatic_names.rb) (0 => 2606)


--- branches/biocat/db/migrate/086_rename_indexes_to_automatic_names.rb	                        (rev 0)
+++ branches/biocat/db/migrate/086_rename_indexes_to_automatic_names.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,40 @@
+# myExperiment: db/migrate/086_rename_indexes_to_automatic_names.rb
+#
+# Copyright (c) 2010 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+class RenameIndexesToAutomaticNames < ActiveRecord::Migration
+  def self.up
+    remove_index "bookmarks",         :name => "fk_bookmarks_user"
+    remove_index "comments",          :name => "fk_comments_user"
+    remove_index "contributions",     :name => "contributions_contributable_index"
+    remove_index "contributions",     :name => "contributions_contributor_index"
+    remove_index "friendships",       :name => "friendships_friend_id_index"
+    remove_index "friendships",       :name => "friendships_user_id_index"
+    remove_index "memberships",       :name => "memberships_network_id_index"
+    remove_index "memberships",       :name => "memberships_user_id_index"
+    remove_index "networks",          :name => "networks_used_id_index"
+    remove_index "permissions",       :name => "permissions_policy_id_index"
+    remove_index "ratings",           :name => "fk_ratings_user"
+    remove_index "reviews",           :name => "fk_reviews_user"
+    remove_index "workflow_versions", :name => "workflow_versions_workflow_id_index"
+
+    add_index "bookmarks",         ["user_id"]
+    add_index "comments",          ["user_id"]
+    add_index "contributions",     ["contributable_id", "contributable_type"]
+    add_index "contributions",     ["contributor_id", "contributor_type"]
+    add_index "friendships",       ["friend_id"]
+    add_index "friendships",       ["user_id"]
+    add_index "memberships",       ["network_id"]
+    add_index "memberships",       ["user_id"]
+    add_index "networks",          ["user_id"]
+    add_index "permissions",       ["policy_id"]
+    add_index "ratings",           ["user_id"]
+    add_index "reviews",           ["user_id"]
+    add_index "workflow_versions", ["workflow_id"]
+  end
+
+  def self.down
+
+  end
+end

Copied: branches/biocat/db/migrate/087_add_indexes_to_viewings_downloads.rb (from rev 2605, trunk/db/migrate/087_add_indexes_to_viewings_downloads.rb) (0 => 2606)


--- branches/biocat/db/migrate/087_add_indexes_to_viewings_downloads.rb	                        (rev 0)
+++ branches/biocat/db/migrate/087_add_indexes_to_viewings_downloads.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,15 @@
+class AddIndexesToViewingsDownloads < ActiveRecord::Migration
+  def self.up
+
+    add_index :viewings, ["contribution_id"]
+    add_index :downloads, ["contribution_id"]
+
+  end
+
+  def self.down
+
+    remove_index :viewings, ["contribution_id"]
+    remove_index :downloads, ["contribution_id"]
+
+  end
+end

Copied: branches/biocat/db/migrate/088_add_group_admins.rb (from rev 2605, trunk/db/migrate/088_add_group_admins.rb) (0 => 2606)


--- branches/biocat/db/migrate/088_add_group_admins.rb	                        (rev 0)
+++ branches/biocat/db/migrate/088_add_group_admins.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,10 @@
+
+class AddGroupAdmins < ActiveRecord::Migration
+  def self.up
+    add_column :memberships, :administrator, :boolean, :default => false
+  end
+
+  def self.down
+    remove_column :memberships, :administrator
+  end
+end

Copied: branches/biocat/db/migrate/089_remove_network_relationships.rb (from rev 2605, trunk/db/migrate/089_remove_network_relationships.rb) (0 => 2606)


--- branches/biocat/db/migrate/089_remove_network_relationships.rb	                        (rev 0)
+++ branches/biocat/db/migrate/089_remove_network_relationships.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,16 @@
+class RemoveNetworkRelationships < ActiveRecord::Migration
+  def self.up
+    drop_table :relationships
+  end
+
+  def self.down
+    create_table "relationships", :force => true do |t|
+      t.column "network_id",  :integer
+      t.column "relation_id", :integer
+      t.column "created_at",  :datetime
+      t.column "accepted_at", :datetime
+    end
+  end
+end
+
+

Modified: branches/biocat/lib/account_management.rb (2605 => 2606)


--- branches/biocat/lib/account_management.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/lib/account_management.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -48,7 +48,6 @@
     :policies                   => { :owner => :contributor },
     :profiles                   => { :owner => :user_id, :skip_on_merge => true },
     :ratings                    => { :owner => :user_id },
-    :relationships              => { :ignore => :true },
     :remote_workflows           => { :owner => :unknown },
     :reviews                    => { :owner => :user_id },
     :schema_info                => { :ignore => :true },

Modified: branches/biocat/lib/acts_as_contributable.rb (2605 => 2606)


--- branches/biocat/lib/acts_as_contributable.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/lib/acts_as_contributable.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -19,6 +19,9 @@
                   :dependent => :destroy
                   
           after_save :save_contributable_record
+          after_save :update_contribution_rank
+          after_save :update_contribution_rating
+          after_save :update_contribution_cache
 
           class_eval do
             extend Mib::Acts::Contributable::SingletonMethods
@@ -76,6 +79,46 @@
             contribution.save
           end
         end
+
+        def update_contribution_rank
+          if contribution
+
+            if respond_to?(:rank)
+              value = rank
+            else
+              value = 0.0
+            end
+
+            ActiveRecord::Base.record_timestamps = false
+            contribution.update_attribute(:rank, value)
+            ActiveRecord::Base.record_timestamps = true
+          end
+        end
+
+        def update_contribution_rating
+          if contribution
+
+            if respond_to?(:rating)
+              value = rating
+            else
+              value = 0.0
+            end
+
+            ActiveRecord::Base.record_timestamps = false
+            contribution.update_attribute(:rating, value)
+            ActiveRecord::Base.record_timestamps = true
+          end
+        end
+
+        def update_contribution_cache
+          if contribution
+            ActiveRecord::Base.record_timestamps = false
+            contribution.update_attribute(:label,           respond_to?(:label)           ? label           : nil)
+            contribution.update_attribute(:content_type_id, respond_to?(:content_type_id) ? content_type_id : nil)
+            contribution.update_attribute(:license_id,      respond_to?(:license_id)      ? license_id      : nil)
+            ActiveRecord::Base.record_timestamps = true
+          end
+        end
       end
     end
   end

Copied: branches/biocat/lib/api_statistics.rb (from rev 2605, trunk/lib/api_statistics.rb) (0 => 2606)


--- branches/biocat/lib/api_statistics.rb	                        (rev 0)
+++ branches/biocat/lib/api_statistics.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,49 @@
+# myExperiment: lib/api_statistics.rb
+#
+# Copyright (c) 2011 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+module APIStatistics
+
+  def self.statistics(ob)
+
+    total_viewings  = Viewing.count(:conditions => ['contribution_id = ?', ob.contribution.id])
+    site_viewings   = Viewing.count(:conditions => ['contribution_id = ? AND accessed_from_site = 1', ob.contribution.id])
+    other_viewings  = Viewing.count(:conditions => ['contribution_id = ? AND accessed_from_site = 0', ob.contribution.id])
+
+    total_downloads = Download.count(:conditions => ['contribution_id = ?', ob.contribution.id])
+    site_downloads  = Download.count(:conditions => ['contribution_id = ? AND accessed_from_site = 1', ob.contribution.id])
+    other_downloads = Download.count(:conditions => ['contribution_id = ? AND accessed_from_site = 0', ob.contribution.id])
+
+    result = XML::Node.new('statistics')
+
+    viewings_element = XML::Node.new('viewings')
+    
+    viewings_element << (XML::Node.new('total') << total_viewings)
+
+    viewings_breakdown_element = XML::Node.new('breakdown')
+
+    viewings_breakdown_element << (XML::Node.new('site')  << site_viewings)
+    viewings_breakdown_element << (XML::Node.new('other') << other_viewings)
+
+    viewings_element << viewings_breakdown_element
+
+    downloads_element = XML::Node.new('downloads')
+
+    downloads_element << (XML::Node.new('total') << total_downloads)
+
+    downloads_breakdown_element = XML::Node.new('breakdown')
+
+    downloads_breakdown_element << (XML::Node.new('site')  << site_downloads)
+    downloads_breakdown_element << (XML::Node.new('other') << other_downloads)
+
+    downloads_element << downloads_breakdown_element
+
+    result << viewings_element
+    result << downloads_element
+
+    result
+  end
+
+end
+

Modified: branches/biocat/lib/authorization.rb (2605 => 2606)


--- branches/biocat/lib/authorization.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/lib/authorization.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -102,6 +102,8 @@
 
     raise "Missing action in authorisation check" if opts[:action].nil?
 
+    opts[:user] = nil if opts[:user] == 0
+
     if opts[:model].nil? && opts[:object].nil? && (opts[:object_type].nil? || opts[:object_id])
       raise "Missing object / model in authorisation check"
     end
@@ -178,6 +180,32 @@
       return true
     end
     
+    # Rating permissions
+
+    if (object_type == 'Rating') && (action == 'create')
+
+      # Ratings can only be created by authenticated users
+      return false if user.nil?
+
+      # Ratings can only be set on things that a user can view
+      return Authorization.is_authorized?('view', nil, context, user) if context
+
+      return true
+    end
+    
+    # Tagging permissions
+
+    if (object_type == 'Tagging') && (action == 'create')
+
+      # Taggings can only be created by authenticated users
+      return false if user.nil?
+
+      # Taggings can only be set on things that a user can view
+      return Authorization.is_authorized?('view', nil, context, user) if context
+
+      return true
+    end
+    
     # Bookmark permissions
 
     if (object_type == 'Bookmark') && (action == 'create')
@@ -442,6 +470,40 @@
         # "action_name" used to work with original action name, rather than classification made inside the module
         is_authorized = Authorization.job_authorized?(thing_instance, action_name, user)
       
+      when "ContentType"
+
+        case action
+
+          when "view"
+            # anyone can view content types
+            is_authorized = true
+     
+          when "edit"
+            # the owner of the content type can edit
+            is_authorized = !user.nil? && thing_instance.user_id == user_id
+
+          when "destroy"
+            # noone can destroy them yet - they just fade away from view
+            is_authorized = false
+        end
+
+      when "User"
+
+        case action
+
+          when "view"
+            # everyone can view users
+            is_authorized = true
+
+          when "edit"
+            # the owner of a user record can edit
+            is_authorized = !user.nil? && user_id == thing_id
+
+          when "destroy"
+            # only adminstrators can delete accounts at present
+            is_authorized = user_is_administrator?(user)
+        end
+
       else
         # don't recognise the kind of "thing" that is being authorized, so
         # we don't specifically know that it needs to be blocked;
@@ -458,11 +520,11 @@
 
   def Authorization.categorize_action(action_name)
     case action_name
-      when 'show', 'index', 'view', 'search', 'favourite', 'favourite_delete', 'comment', 'comment_delete', 'comments', 'comments_timeline', 'rate', 'tag',  'items', 'statistics', 'curation', 'tag_suggestions', 'read', 'verify'
+      when 'show', 'index', 'view', 'search', 'favourite', 'favourite_delete', 'comment', 'comment_delete', 'comments', 'comments_timeline', 'rate', 'tag',  'items', 'statistics', 'curation', 'tag_suggestions', 'extra_metadata', 'read', 'verify'
         action = ''
-      when 'edit', 'new', 'create', 'update', 'new_version', 'create_version', 'destroy_version', 'edit_version', 'update_version', 'new_item', 'create_item', 'edit_item', 'update_item', 'quick_add', 'resolve_link', 'process_tag_suggestions'
+      when 'edit', 'new', 'create', 'update', 'new_version', 'create_version', 'destroy_version', 'edit_version', 'update_version', 'new_item', 'create_item', 'edit_item', 'update_item', 'quick_add', 'resolve_link', 'process_tag_suggestions', 'process_extra_metadata', 'edit_relationships'
         action = ''
-      when 'download', 'named_download', 'launch', 'submit_job', 'save_inputs', 'refresh_status', 'rerun', 'refresh_outputs', 'render_output', 'outputs_xml', 'outputs_package'
+      when 'download', 'named_download', 'launch', 'submit_job', 'save_inputs', 'refresh_status', 'rerun', 'refresh_outputs', 'render_output', 'outputs_xml', 'outputs_package', 'galaxy_tool', 'galaxy_tool_download'
         action = ''
       when 'destroy', 'delete', 'destroy_item'
         action = ''
@@ -507,6 +569,8 @@
         when "Runner"
           # the line below doesn't have a typo - "runners" should really be searched in "TavernaEnactor" model
           found_instance = TavernaEnactor.find(thing_id)
+        when "ContentType"
+          found_instance = ContentType.find(thing_id)
       end
     rescue ActiveRecord::RecordNotFound
       # do nothing; makes sure that app won't crash when the required object is not found;
@@ -732,173 +796,143 @@
     end
   end
 
-  def self.authorised_index(args = {})
+  def self.authorised_index(model, *args)
 
-    def self.get_friend_ids(user)
-      user.friendships_accepted.map  do |fs| fs.user_id   end +
-      user.friendships_completed.map do |fs| fs.friend_id end
-    end
+    def self.view_conditions(user_id = nil, friends = nil, networks = nil)
 
-    def self.get_network_ids(user)
-      (user.networks_owned + user.networks).map do |n| n.id end
+      return "((contributions.id IS NULL) OR (share_mode = 0 OR share_mode = 1 OR share_mode = 2))" if user_id.nil?
+
+      policy_part =
+        "((contributions.contributor_type = 'User' AND contributions.contributor_id = #{user_id}) OR
+          (share_mode = 0 OR share_mode = 1 OR share_mode = 2) OR
+          ((share_mode = 1 OR share_mode = 3 OR share_mode = 4 OR update_mode = 1 OR (update_mode = 0 AND (share_mode = 1 OR share_mode = 3))) AND
+           (contributions.contributor_type = 'User' AND contributions.contributor_id IN #{friends})))"
+
+      "((contributions.id IS NULL) OR (#{policy_part} OR #{permission_part(['view', 'download', 'edit'], user_id, networks)}))"
     end
 
-    def self.view_part(user_id = nil, friends = nil)
+    def self.download_conditions(user_id = nil, friends = nil, networks = nil)
 
-      return "share_mode = 0 OR share_mode = 1 OR share_mode = 2" if user_id.nil?
+      return "((contributions.id IS NULL) OR (share_mode = 0))" if user_id.nil?
 
-      "((contributions.contributor_type = 'User' AND contributions.contributor_id = #{user_id}) OR
-        (share_mode = 0 OR share_mode = 1 OR share_mode = 2) OR
-        ((share_mode = 3 OR share_mode = 4 OR update_mode = 1) AND
-         (contributions.contributor_type = 'User' AND contributions.contributor_id IN #{friends})))"
+      policy_part = 
+        "((contributions.contributor_type = 'User' AND contributions.contributor_id = #{user_id}) OR
+          (share_mode = 0) OR
+          ((share_mode = 1 OR share_mode = 3 OR update_mode = 1 OR (update_mode = 0 AND (share_mode = 1 OR share_mode = 3))) AND
+           (contributions.contributor_type = 'User' AND contributions.contributor_id IN #{friends})))"
+
+      "((contributions.id IS NULL) OR (#{policy_part} OR #{permission_part(['download', 'edit'], user_id, networks)}))"
     end
 
-    def self.download_part(user_id = nil, friends = nil)
+    def self.edit_conditions(user_id = nil, friends = nil, networks = nil)
 
-      return "share_mode = 0" if user_id.nil?
+      return "((contributions.id IS NULL) OR (share_mode = 0 AND update_mode = 0))" if user_id.nil?
 
-      "((contributions.contributor_type = 'User' AND contributions.contributor_id = #{user_id}) OR
-        (share_mode = 0) OR
-        ((share_mode = 1 OR share_mode = 3 OR update_mode = 1) AND
-         (contributions.contributor_type = 'User' AND contributions.contributor_id IN #{friends})))"
+      policy_part =
+        "((contributions.contributor_type = 'User' AND contributions.contributor_id = #{user_id}) OR
+          (share_mode = 0 AND update_mode = 0) OR
+          ((update_mode = 1 OR (update_mode = 0 AND (share_mode = 1 OR share_mode = 3))) AND
+           (contributions.contributor_type = 'User' AND contributions.contributor_id IN #{friends})))"
+
+      "((contributions.id IS NULL) OR (#{policy_part} OR #{permission_part(['edit'], user_id, networks)}))"
     end
 
-    def self.edit_part(user_id = nil, friends = nil)
+    def self.permission_part(permissions, user_id, networks)
 
-      return "share_mode = 0 AND update_mode = 0" if user_id.nil?
+      permission_test = permissions.map do |p| "permissions.#{p} = true" end.join(" OR ")
 
-      "((contributions.contributor_type = 'User' AND contributions.contributor_id = #{user_id}) OR
-        (share_mode = 0 AND update_mode = 0) OR
-        ((update_mode = 1 OR (update_mode = 0 AND (share_mode = 1 OR share_mode = 3))) AND
-         (contributions.contributor_type = 'User' AND contributions.contributor_id IN #{friends})))"
-    end
-
-    def self.permission_part(permission, user_id, networks)
-      "(permissions.id IS NOT NULL AND permissions.#{permission} = true AND
+      "(permissions.id IS NOT NULL AND (#{permission_test}) AND
         ((permissions.contributor_type = 'User'    AND permissions.contributor_id = #{user_id}) OR
          (permissions.contributor_type = 'Network' AND permissions.contributor_id IN #{networks})))"
     end
 
-    user = args[:user]
+    # extract the opts hash
 
-    # pagination
+    opts = args.last.class == Hash ? args.pop.clone : {}
 
-    if args[:limit]
-      if args[:offset]
-        limit_part = "LIMIT #{args[:offset]}, #{args[:limit]}"
-      else
-        limit_part = "LIMIT #{args[:limit]}"
-      end
-    else
-      limit_part = ""
-    end
+    user = opts.delete(:authorised_user)
 
-    # ordering
+    joins      = []
+    conditions = []
 
-    if args[:order]
-      order_part = "ORDER BY #{args[:order]}"
-    else
-      order_part = ''
+    if (user != 0) && (user != nil)
+
+      user_id = user.id
+
+      friend_ids = user.friendships_accepted.map  do |fs| fs.user_id   end +
+                   user.friendships_completed.map do |fs| fs.friend_id end
+
+      network_ids = (user.networks_owned + user.networks).map do |n| n.id end
+
+      friends  = friend_ids.empty?  ? "(-1)" : "(#{friend_ids.join(",")})"
+      networks = network_ids.empty? ? "(-1)" : "(#{network_ids.join(",")})"
     end
 
     # filtering
 
-    if args[:type]
-      where_part = "WHERE contributions.contributable_type = '#{args[:type].name}'"
-    else
-      where_part = ""
-    end
+    auth_id   = opts.delete(:auth_id)   || "#{model.table_name}.id"
+    auth_type = opts.delete(:auth_type) || "'#{model.name}'"
 
-    # result type
+    conditions.push(view_conditions(user_id, friends, networks))
+    conditions.push("contributions.contributable_type = #{auth_type}") if !opts.delete(:arbitrary_models) && model != Contribution
 
-    if !args[:type] || args[:contribution_records] 
-      contributable_type = nil
-      inner_select_part  = "contributions.*"
-      result_model       = Contribution
-      from_part          = "FROM contributions"
-    else
-      contributable_type = args[:type].name.underscore.pluralize
-      inner_select_part  = "#{contributable_type}.*"
-      result_model       = args[:type]
-      from_part          = "FROM #{contributable_type} INNER JOIN contributions ON contributions.contributable_id = #{contributable_type}.id"
+    # result model
+
+    if opts.delete(:contribution_records)
+      model = Contribution
     end
 
-    # selection
-
-    if args[:select]
-      select_part = args[:select]
-    else
-      select_part = "*"
+    if model != Contribution
+      joins.push("LEFT OUTER JOIN contributions ON contributions.contributable_id = #{auth_id} AND contributions.contributable_type = #{auth_type}")
     end
 
-    if (user != 0) && (user != nil)
+    # selection
 
-      # This is the version used for a member
+    opts[:select] = "#{model.table_name}.*" unless opts[:select]
 
-      user_id = user.id
+    # add in the extra joins needed for the authorisation checks
 
-      friend_ids  = get_friend_ids(user)
-      network_ids = get_network_ids(user)
+    joins.push("LEFT OUTER JOIN policies ON contributions.policy_id = policies.id")
+    joins.push("LEFT OUTER JOIN permissions ON policies.id = permissions.policy_id") if user_id || opts[:include_permissions]
 
-      friends  = friend_ids.empty?  ? "(-1)" : "(#{friend_ids.join(",")})"
-      networks = network_ids.empty? ? "(-1)" : "(#{network_ids.join(",")})"
+    # include the effective permissions in the result?
 
-      query = "
+    if opts.delete(:include_permissions)
 
-        SELECT #{select_part},
-          (view OR download OR edit) AS cascaded_view,
-          (download OR edit)         AS cascaded_download,
-          edit                       AS cascaded_edit FROM
-          
-          (SELECT #{inner_select_part},
+      opts[:select] << ", BIT_OR(#{view_conditions(user_id, friends, networks)})     AS view_permission"
+      opts[:select] << ", BIT_OR(#{download_conditions(user_id, friends, networks)}) AS download_permission"
+      opts[:select] << ", BIT_OR(#{edit_conditions(user_id, friends, networks)})     AS edit_permission"
+    end
 
-            BIT_OR(#{view_part(user_id, friends)}     OR #{permission_part('view',     user_id, networks)}) AS view,
-            BIT_OR(#{download_part(user_id, friends)} OR #{permission_part('download', user_id, networks)}) AS download,
-            BIT_OR(#{edit_part(user_id, friends)}     OR #{permission_part('edit',     user_id, networks)}) AS edit
+    # merge the joins
 
-            #{from_part}
-            #{args[:joins]}
-            INNER JOIN policies ON contributions.policy_id = policies.id
-            LEFT OUTER JOIN permissions ON policies.id = permissions.policy_id
-            #{where_part}
-            GROUP BY contributable_type, contributable_id) AS foo
-          
-          WHERE (view OR download OR edit) = true
-          #{order_part}
-          #{limit_part}"
+    if joins.length > 0
+      opts[:joins] = [] unless opts[:joins]
+      opts[:joins] = [opts[:joins]] unless opts[:joins].class == Array
+      opts[:joins] = joins + opts[:joins]
+      opts[:joins] = opts[:joins].join(" ") # Rails 1 does not support arrays here
+    end
 
-      result_model.find_by_sql(query)
+    # merge the conditions
 
-    else 
+    if conditions.length > 0
 
-      # This is the version used for non-members
+      conditions = conditions.map do |c| "(#{c})" end
 
-      query = "
+      case opts[:conditions].class.name
+        when "Array";  opts[:conditions][0] = "(#{([opts[:conditions][0]] + conditions).join(') AND (')})"
+        when "String"; opts[:conditions]    = "(#{([opts[:conditions]] + conditions).join(') AND (')})"
+        else;          opts[:conditions]    = "(#{conditions.join(') AND (')})"
+      end
+    end
 
-        SELECT #{select_part},
-          (view OR download OR edit) AS cascaded_view,
-          (download OR edit)         AS cascaded_download,
-          edit                       AS cascaded_edit FROM
-          
-          (SELECT #{inner_select_part},
+    # default to grouping by contributable type and id
 
-            BIT_OR(#{view_part})     AS view,
-            BIT_OR(#{download_part}) AS download,
-            BIT_OR(#{edit_part})     AS edit
+    opts[:group] ||= 'contributions.contributable_type, contributions.contributable_id'
 
-            #{from_part}
-            #{args[:joins]}
-            INNER JOIN policies ON contributions.policy_id = policies.id
-            #{where_part}
-            GROUP BY contributable_type, contributable_id) AS foo
-          
-          WHERE (view OR download OR edit) = true
-          #{order_part}
-          #{limit_part}"
+    # do it
 
-      result_model.find_by_sql(query)
-
-    end
+    model.find(*args + [opts])
   end
 end
 

Modified: branches/biocat/lib/bio_catalogue_import.rb (2605 => 2606)


--- branches/biocat/lib/bio_catalogue_import.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/lib/bio_catalogue_import.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -32,7 +32,6 @@
     @documents[uri] = rest_uri.read.to_s
     @documents[:retrieved_at][uri] = Time.now
 
-    puts "Got it."
     Kernel.sleep(@@biocat_wait_in_seconds)
 
     @documents[uri]
@@ -64,74 +63,62 @@
     (URI.parse(@@biocat_base_uri) + response[0].value).to_s unless response.length.zero?
   end
 
-  def self.import_service(service_uri)
+  def self.import_service(service_element, index_uri)
 
-    service_element = LibXML::XML::Parser.string(fetch_uri(service_uri)).parse.root
+    summary_element = service_element.find('bc:summary', @@biocat_ns)[0]
 
-    summary_uri     = get_attr(service_element, '/bc:service/bc:related/bc:summary/@xlink:href')
-    deployment_uri  = get_attr(service_element, '/bc:service/bc:related/bc:deployments/@xlink:href')
-    variants_uri    = get_attr(service_element, '/bc:service/bc:related/bc:variants/@xlink:href')
-    annotations_uri = get_attr(service_element, '/bc:service/bc:related/bc:annotations/@xlink:href')
-    monitoring_uri  = get_attr(service_element, '/bc:service/bc:related/bc:monitoring/@xlink:href')
-
-    summary_element     = LibXML::XML::Parser.string(fetch_uri(summary_uri)).parse.root
-    deployments_element = LibXML::XML::Parser.string(fetch_uri(deployment_uri)).parse.root
-    variants_element    = LibXML::XML::Parser.string(fetch_uri(variants_uri)).parse.root
-    annotations_element = LibXML::XML::Parser.string(fetch_uri(annotations_uri)).parse.root
-    monitoring_element  = LibXML::XML::Parser.string(fetch_uri(monitoring_uri)).parse.root
-
     service = Service.create(
-        :retrieved_at       => uri_retrieved_at(service_uri),
+        :retrieved_at       => uri_retrieved_at(index_uri),
 
         :contributor        => @federation_source,
 
-        :uri                => get_link(service_element, '/bc:service/@xlink:href'),
-        :name               => get_text(service_element, '/bc:service/bc:name/text()'),
-        :submitter_label    => get_attr(service_element, '/bc:service/bc:originalSubmitter/@resourceName'),
-        :created            => get_text(service_element, '/bc:service/dcterms:created/text()'),
-        :submitter_uri      => get_attr(service_element, '/bc:service/bc:originalSubmitter/@xlink:href'),
+        :uri                => get_link(service_element, '@xlink:href'),
+        :name               => get_text(service_element, 'bc:name/text()'),
+        :submitter_label    => get_attr(service_element, 'bc:originalSubmitter/@resourceName'),
+        :created            => get_text(service_element, 'dcterms:created/text()'),
+        :submitter_uri      => get_attr(service_element, 'bc:originalSubmitter/@xlink:href'),
 
-        :provider_uri         => get_link(summary_element, '/bc:service/bc:summary/bc:provider/@xlink:href'),
-        :provider_label       => get_text(summary_element, '/bc:service/bc:summary/bc:provider/bc:name/text()'),
-        :endpoint             => get_text(summary_element, '/bc:service/bc:summary/bc:endpoint/text()'),
-        :wsdl                 => get_text(summary_element, '/bc:service/bc:summary/bc:wsdl/text()'),
-        :city                 => get_text(summary_element, '/bc:service/bc:summary/bc:location/bc:city/text()'),
-        :country              => get_text(summary_element, '/bc:service/bc:summary/bc:location/bc:country/text()'),
-        :iso3166_country_code => get_text(summary_element, '/bc:service/bc:summary/bc:location/bc:iso3166CountryCode/text()'),
-        :flag_url             => get_link(summary_element, '/bc:service/bc:summary/bc:location/bc:flag/@xlink:href'),
-        :documentation_uri    => get_text(summary_element, '/bc:service/bc:summary/bc:documentationUrl/text()'),
-        :description          => get_text(summary_element, '/bc:service/bc:summary/dc:description/text()'),
+        :provider_uri         => get_link(summary_element, 'bc:provider/@xlink:href'),
+        :provider_label       => get_text(summary_element, 'bc:provider/bc:name/text()'),
+        :endpoint             => get_text(summary_element, 'bc:endpoint/text()'),
+        :wsdl                 => get_text(summary_element, 'bc:wsdl/text()'),
+        :city                 => get_text(summary_element, 'bc:location/bc:city/text()'),
+        :country              => get_text(summary_element, 'bc:location/bc:country/text()'),
+        :iso3166_country_code => get_text(summary_element, 'bc:location/bc:iso3166CountryCode/text()'),
+        :flag_url             => get_link(summary_element, 'bc:location/bc:flag/@xlink:href'),
+        :documentation_uri    => get_text(summary_element, 'bc:documentationUrl/text()'),
+        :description          => get_text(summary_element, 'dc:description/text()'),
         
-        :monitor_label            => get_text(summary_element, '/bc:service/bc:latestMonitoringStatus/bc:label/text()'),
-        :monitor_message          => get_text(summary_element, '/bc:service/bc:latestMonitoringStatus/bc:message/text()'),
-        :monitor_symbol_url       => get_link(summary_element, '/bc:service/bc:latestMonitoringStatus/bc:symbol/@xlink:href'),
-        :monitor_small_symbol_url => get_link(summary_element, '/bc:service/bc:latestMonitoringStatus/bc:smallSymbol/@xlink:href'),
-        :monitor_last_checked     => get_text(summary_element, '/bc:service/bc:latestMonitoringStatus/bc:lastChecked/text()'))
+        :monitor_label            => get_text(service_element, 'bc:latestMonitoringStatus/bc:label/text()'),
+        :monitor_message          => get_text(service_element, 'bc:latestMonitoringStatus/bc:message/text()'),
+        :monitor_symbol_url       => get_link(service_element, 'bc:latestMonitoringStatus/bc:symbol/@xlink:href'),
+        :monitor_small_symbol_url => get_link(service_element, 'bc:latestMonitoringStatus/bc:smallSymbol/@xlink:href'),
+        :monitor_last_checked     => get_text(service_element, 'bc:latestMonitoringStatus/bc:lastChecked/text()'))
 
     service.contribution.policy = create_default_policy(@federation_source)
     service.contribution.policy.share_mode = 0 # Make public
     service.contribution.policy.save
     service.contribution.save
 
-    summary_element.find('/bc:service/bc:summary/bc:category', @@biocat_ns).each do |category_element|
+    summary_element.find('bc:category', @@biocat_ns).each do |category_element|
       ServiceCategory.create(
           :service       => service,
-          :retrieved_at  => uri_retrieved_at(summary_uri),
+          :retrieved_at  => uri_retrieved_at(index_uri),
           :uri           => get_link(category_element, '@xlink:href'),
           :label         => get_text(category_element, 'text()'))
     end
 
-    summary_element.find('/bc:service/bc:summary/bc:serviceType', @@biocat_ns).each do |category_element|
+    summary_element.find('bc:serviceType', @@biocat_ns).each do |category_element|
       ServiceType.create(
           :service      => service,
-          :retrieved_at => uri_retrieved_at(summary_uri),
+          :retrieved_at => uri_retrieved_at(index_uri),
           :label        => get_text(category_element, 'text()'))
     end
 
-    summary_element.find('/bc:service/bc:summary/bc:tag', @@biocat_ns).each do |tag_element|
+    summary_element.find('bc:tag', @@biocat_ns).each do |tag_element|
       ServiceTag.create(
           :service      => service,
-          :retrieved_at => uri_retrieved_at(summary_uri),
+          :retrieved_at => uri_retrieved_at(index_uri),
           :uri          => get_link(tag_element, '@xlink:href'),
           :label        => get_text(tag_element, 'text()'))
     end
@@ -148,7 +135,7 @@
       if ServiceProvider.find_by_uri(provider_uri).nil?
         ServiceProvider.create(
             :uri          => provider_uri,
-            :retrieved_at => uri_retrieved_at(service_uri),
+            :retrieved_at => uri_retrieved_at(index_uri),
             :name         => get_text(deployment_element, 'bc:serviceProvider/bc:name/text()'),
             :description  => get_text(deployment_element, 'bc:serviceProvider/dc:description/text()'),
             :created      => get_text(deployment_element, 'bc:serviceProvider/dcterms:created/text()'))
@@ -159,7 +146,7 @@
       deployment = ServiceDeployment.create(
           :service              => service,
           :service_provider     => provider,
-          :retrieved_at         => uri_retrieved_at(service_uri),
+          :retrieved_at         => uri_retrieved_at(index_uri),
           :uri                  => get_link(deployment_element, '@xlink:href'),
           :endpoint             => get_text(deployment_element, 'bc:endpoint/text()'),
           :city                 => get_text(deployment_element, 'bc:location/bc:city/text()'),
@@ -176,11 +163,10 @@
   def self.import_biocatalogue_services(uri)
 
     while true
-
       doc = LibXML::XML::Parser.string(fetch_uri(uri)).parse.root
 
       doc.find("/bc:services/bc:results/bc:service", @@biocat_ns).each do |service_element|
-        import_service(get_link(service_element, '@xlink:href'))
+        import_service(service_element, uri)
       end
 
       next_doc = doc.find("/bc:services/bc:related/bc:next/@xlink:href", @@biocat_ns)
@@ -202,7 +188,7 @@
 
     @federation_source = FederationSource.find_by_name("BioCatalogue")
 
-    import_biocatalogue_services("http://www.biocatalogue.org/services")
+    import_biocatalogue_services("http://www.biocatalogue.org/services?include=summary")
   end
 end
 

Modified: branches/biocat/lib/conf.rb (2605 => 2606)


--- branches/biocat/lib/conf.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/lib/conf.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -6,7 +6,6 @@
 # Configuration module
 
 require 'yaml'
-require 'action_controller/test_process'
 
 class Conf
 
@@ -26,6 +25,14 @@
     self.fetch_entry('site_logo')
   end
 
+  def self.contributor_models
+    self.fetch_entry('contributor_models')
+  end
+
+  def self.contributable_models
+    self.fetch_entry('contributable_models')
+  end
+
   def self.notifications_email_address
     self.fetch_entry('notifications_email_address')
   end
@@ -35,7 +42,11 @@
   end
 
   def self.base_uri
-    self.fetch_entry('base_uri')
+    if RAILS_ENV == 'test'
+      "http://test.host"
+    else
+      self.fetch_entry('base_uri')
+    end
   end
 
   def self.admins
@@ -134,6 +145,14 @@
     self.fetch_entry('validate_email_veracity')
   end
 
+  def self.rdfgen_enable
+    self.fetch_entry('rdfgen_enable')
+  end
+
+  def self.rdfgen_tool
+    self.fetch_entry('rdfgen_tool')
+  end
+
   # This method is required to create an administrator in the test fixtures
 
   def self.admins=(value)
@@ -144,7 +163,7 @@
 
     @config = nil
 
-    if request.class != ActionController::TestRequest
+    if request.class.name != "ActionController::TestRequest"
       if @settings['virtual_hosts']
         @settings['virtual_hosts'].each do |name, settings|
 
@@ -157,15 +176,48 @@
           if settings['host'] && request.host == settings['host']
             @config = name
           end
+
+          if settings['prefix'] && request.path.starts_with?(settings['prefix'])
+            remainder = request.path[settings['prefix'].length..-1]
+
+            if remainder.empty? || remainder.match(/^[.\/]/)
+              @config = name
+            end
+          end
         end
       end
     end
 
+    if request.query_parameters['portal_url']
+      session['portal_url'] = request.query_parameters['portal_url']
+    end
+
     @config = session["portal"] if session["portal"]
   end
 
+  # helper function to convert model aliases
+
+  def self.to_model(str)
+    self.model_alias_convert(self.model_aliases, str)
+  end
+
+  def self.to_visible(str)
+    self.model_alias_convert(self.model_aliases.invert, str)
+  end
+
 private
 
+  def self.model_alias_convert(map, str)
+    map.each do |k, v|
+      return v                      if str == k
+      return v.underscore           if str == k.underscore
+      return v.pluralize            if str == k.pluralize
+      return v.underscore.pluralize if str == k.underscore.pluralize
+    end
+
+    str
+  end
+
   def self.fetch_entry(key, default = nil)
 
     if @config != nil

Copied: branches/biocat/lib/load_vocabulary.rb (from rev 2605, trunk/lib/load_vocabulary.rb) (0 => 2606)


--- branches/biocat/lib/load_vocabulary.rb	                        (rev 0)
+++ branches/biocat/lib/load_vocabulary.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,82 @@
+# myExperiment: lib/load_vocabulary.rb
+#
+# Copyright (c) 2010 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+module LoadVocabulary
+
+  def self.load_skos(data)
+
+    exising_vocabulary = Vocabulary.find_by_uri(data["uri"])
+    exising_vocabulary.destroy if exising_vocabulary
+
+    vocabulary = Vocabulary.create(
+        :uri         => data["uri"],
+        :title       => data["title"],
+        :prefix      => data["prefix"],
+        :description => data["description"])
+
+    data["concepts"].each do |concept|
+      
+      c = Concept.create(
+          :phrase      => concept["phrase"],
+          :description => concept["description"])
+
+      c.labels << Label.create(
+          :vocabulary => vocabulary,
+          :text       => concept["label"],
+          :label_type => 'preferred',
+          :language   => 'en')
+
+      if concept["alternate"]
+        concept["alternate"].each do |alternate|
+          
+          c.labels << Label.create(
+              :vocabulary => vocabulary,
+              :text       => alternate,
+              :label_type => 'alternate',
+              :language   => 'en')
+        end
+      end
+
+      if concept["hidden"]
+        concept["hidden"].each do |hidden|
+
+          c.labels << Label.create(
+              :vocabulary => vocabulary,
+              :text       => hidden,
+              :label_type => 'hidden',
+              :language   => 'en')
+        end
+      end
+
+      vocabulary.concepts << c
+
+    end
+  end
+
+  def self.load_ontology(data)
+
+    existing_ontology = Ontology.find_by_uri(data["uri"])
+    existing_ontology.destroy if existing_ontology
+
+    
+        :uri         => data["uri"],
+        :title       => data["title"],
+        :prefix      => data["prefix"],
+        :description => data["description"])
+
+    data["predicates"].each do |predicate|
+      
+      p = Predicate.create(
+          :title         => predicate["title"],
+          :phrase        => predicate["phrase"],
+          :description   => predicate["description"],
+          :equivalent_to => predicate["equivalent_to"])
+
+      ontology.predicates << p
+
+    end
+  end
+end
+

Modified: branches/biocat/lib/maintenance/GeneratePublicData.php (2605 => 2606)


--- branches/biocat/lib/maintenance/GeneratePublicData.php	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/lib/maintenance/GeneratePublicData.php	2011-06-15 15:27:14 UTC (rev 2606)
@@ -337,14 +337,6 @@
   PRIMARY KEY  (id),
   KEY fk_ratings_user (user_id)
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1;";
-	$createtable['relationships']="CREATE TABLE relationships (
-  id int(11) NOT NULL auto_increment,
-  network_id int(11) default NULL,
-  relation_id int(11) default NULL,
-  created_at datetime default NULL,
-  accepted_at datetime default NULL,
-  PRIMARY KEY  (id)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;";
 	$createtable['remote_workflows']="CREATE TABLE remote_workflows (
   id int(11) NOT NULL auto_increment,
   workflow_id int(11) default NULL,
@@ -496,7 +488,6 @@
 	$publicsql['policies']="select * from policies where view_public=1";
 	$publicsql['profiles']="select * from profiles";
         $publicsql['ratings']="select ratings.* from ratings inner join contributions on ratings.rateable_id=contributions.contributable_id and ratings.rateable_type=contributions.contributable_type inner join policies on contributions.policy_id=policies.id where policies.view_public=1";
-        $publicsql['relationships']="select * from relationships";
 	$publicsql['remote_workflows']="select remote_workflows.* from remote_workflows inner join contributions on remote_workflows.workflow_id=contributions.contributable_id and contributions.contributable_type='Workflow' inner join policies on contributions.policy_id=policies.id where policies.view_public=1 and 1=0";
 
         $publicsql['reviews']="select reviews.* from reviews inner join contributions on reviews.reviewable_id=contributions.contributable_id and reviews.reviewable_type=contributions.contributable_type inner join policies on contributions.policy_id=policies.id where policies.view_public=1";

Copied: branches/biocat/lib/maintenance/backup.rb (from rev 2605, trunk/lib/maintenance/backup.rb) (0 => 2606)


--- branches/biocat/lib/maintenance/backup.rb	                        (rev 0)
+++ branches/biocat/lib/maintenance/backup.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,275 @@
+# myExperiment: lib/maintenance/backup.rb
+#
+# Copyright (c) 2010 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+require 'rubygems'
+require 'mysql'
+
+module Maintenance::Backup
+
+  VERSION        = 1
+  WORKING_DIR    = "tmp/backup"
+  TMP_SQL_FILE   = "backup.sql"
+  TMP_TABLE_NAME = "temp_table_for_filter"
+  TMP_TABLE_FILE = "temp_table.sql"
+
+  DEFAULT_BACKUP_FILENAME = "backup.tgz"
+
+  def self.create(opts = {})
+
+    @backup_filename = ENV['FILE'] || DEFAULT_BACKUP_FILENAME
+
+    def self.backup_database
+
+      def self.table(opts = {})
+
+        args = []
+
+        if opts[:model]
+          models = opts[:model].find(:all)
+          source_table_name = opts[:model].table_name
+        else
+          source_table_name = opts[:name]
+        end
+
+        if opts[:filter]
+
+          if opts[:ids]
+
+            ids = opts[:ids]
+
+          else
+
+            models = models.select do |model|
+
+              if opts[:condition]
+                model.send(opts[:condition])
+              else
+                if opts[:auth_object]
+                  auth_object = model.send(opts[:auth_object])
+                else
+                  auth_object = model
+                end
+
+                if auth_object.class == User || auth_object.class == Network
+                  true
+                elsif auth_object.class == Contribution
+                  [0, 1, 2].include?(auth_object.policy.share_mode)
+                elsif auth_object.class == Policy
+                  [0, 1, 2].include?(auth_object.share_mode)
+                else
+                  auth_object && [0, 1, 2].include?(auth_object.contribution.policy.share_mode)
+                end
+              end
+            end
+
+            ids = models.map do |model| model.id.to_s end
+
+          end
+        end
+
+        if opts[:columns]
+          out_file   = "#{WORKING_DIR}/#{TMP_TABLE_FILE}"
+          table_name = TMP_TABLE_NAME
+
+          source_table_name = source_table_name
+          column_list       = opts[:columns].join(", ")
+
+          mysql_execute("address@hidden -e 'CREATE TABLE #{TMP_TABLE_NAME} LIKE #{source_table_name}'")
+          mysql_execute("address@hidden -e 'INSERT INTO #{TMP_TABLE_NAME} (#{column_list}) SELECT #{column_list} FROM #{source_table_name}'")
+
+          system("rm -f #{WORKING_DIR}/#{TMP_TABLE_FILE}")
+
+        else
+          out_file   = "#{WORKING_DIR}/#{TMP_SQL_FILE}"
+          table_name = source_table_name
+        end
+
+        args << "-u address@hidden"
+        args << "address@hidden"
+        args << "--no-data" if opts[:no_data]
+        args << "-h address@hidden"
+        args << "address@hidden"
+        args << "#{table_name} "
+        args << "--where=\"id in (#{ids.join(",")})\"" if opts[:filter]
+
+        # puts "cmd arguments = #{args.inspect}"
+
+        system("mysqldump >> #{out_file} #{args.join(' ')}")
+
+        if opts[:columns]
+          mysql_execute("address@hidden -e 'DROP TABLE #{TMP_TABLE_NAME}'")
+
+          system("sed -i -e 's/#{TMP_TABLE_NAME}/#{source_table_name}/' #{WORKING_DIR}/#{TMP_TABLE_FILE}")
+          system("cat >> #{WORKING_DIR}/#{TMP_SQL_FILE} #{WORKING_DIR}/#{TMP_TABLE_FILE}")
+        end
+      end
+
+      content_blob_ids = 
+
+        (Workflow.find(:all) + Workflow::Version.find(:all) + Blob.find(:all)).select do |x|
+          Authorization.is_authorized?('view', nil, x, nil)
+        end.map do |x|
+          x.content_blob_id
+        end
+
+      table(:model => ActivityLimit,          :no_data => true)
+      table(:model => Announcement)
+      table(:model => Attribution,            :filter  => true, :auth_object => "attributable")
+      table(:name  => "auto_tables")
+      table(:model => Blob,                   :filter  => true)
+      table(:model => Blog,                   :no_data => true)
+      table(:model => BlogPost,               :no_data => true)
+      table(:model => Bookmark)
+      table(:model => Citation,               :filter  => true, :auth_object => "workflow")
+      table(:model => ClientApplication,      :no_data => true)
+      table(:model => Comment,                :filter  => true, :auth_object => "commentable")
+      table(:model => ContentBlob,            :filter  => true, :ids => content_blob_ids)
+      table(:model => ContentType)
+      table(:model => Contribution,           :filter  => true)
+      table(:model => Creditation,            :filter  => true, :auth_object => "creditable")
+      table(:model => CurationEvent,          :no_data => true)
+      table(:model => Download,               :no_data => true)
+      table(:model => Experiment,             :no_data => true)
+      table(:model => Friendship)
+      table(:model => GroupAnnouncement,      :filter  => true, :condition   => "public")
+      table(:model => Job,                    :no_data => true)
+      table(:model => KeyPermission,          :no_data => true)
+      table(:model => License)
+      table(:model => LicenseAttribute)
+      table(:model => LicenseOption)
+      table(:model => Membership)
+      table(:model => Message,                :no_data => true)
+      table(:model => Network)
+      table(:model => OauthNonce,             :no_data => true)
+      table(:model => OauthToken,             :no_data => true)
+      table(:model => Pack,                   :filter  => true)
+      table(:model => PackContributableEntry, :filter  => true, :auth_object => "pack")
+      table(:model => PackRemoteEntry,        :filter  => true, :auth_object => "pack")
+      table(:model => PendingInvitation,      :no_data => true)
+      table(:model => Permission,             :no_data => true)
+      table(:model => Picture,                :filter  => true, :condition   => "selected?")
+      table(:model => PictureSelection)
+      table(:name  => "plugin_schema_info")
+      table(:model => Policy,                 :filter  => true)
+      table(:model => Profile)
+      table(:model => Rating,                 :filter  => true, :auth_object => "rateable")
+      table(:model => RemoteWorkflow,         :no_data => true)
+      table(:model => Review,                 :filter  => true, :auth_object => "reviewable")
+      table(:name  => "schema_info")
+      table(:name  => "sessions",             :no_data => true)
+      table(:model => Tag,                    :filter  => true, :condition   => "public?")
+      table(:model => Tagging,                :filter  => true, :auth_object => "taggable")
+      table(:model => TavernaEnactor,         :no_data => true)
+      table(:model => User,                   :columns => [:id, :name, :created_at, :activated_at])
+      table(:model => Viewing,                :no_data => true)
+      table(:model => Vocabulary)
+      table(:model => Workflow,               :filter  => true)
+      table(:model => Workflow::Version,      :filter  => true, :auth_object => "workflow")
+    end
+
+    def self.backup_files
+
+      def self.add_path(path, cmd)
+        cmd << " #{path}" if File.exist?(path)
+      end
+
+      cmd = "tar czf address@hidden"
+
+      Workflow.find(:all).select do |w|
+        if Authorization.is_authorized?('view', nil, w, nil)
+          add_path("public/workflow/image/#{w.id}", cmd)
+          add_path("public/workflow/svg/#{w.id}",   cmd)
+        end
+      end
+
+      Workflow::Version.find(:all).select do |wv|
+        if Authorization.is_authorized?('view', nil, wv.workflow, nil)
+          add_path("public/workflow/version/image/#{wv.id}", cmd)
+          add_path("public/workflow/version/svg/#{wv.id}",   cmd)
+        end
+      end
+
+      system("echo > #{WORKING_DIR}/version #{VERSION}")
+
+      add_path("#{WORKING_DIR}/#{TMP_SQL_FILE}", cmd)
+      add_path("#{WORKING_DIR}/version", cmd)
+
+      # puts "cmd = #{cmd.inspect}"
+
+      system(cmd)
+    end
+
+    db_config = YAML::load_file("config/database.yml")[ENV['RAILS_ENV'] || "development"]
+
+    @mysql_host     = db_config["host"]
+    @mysql_database = db_config["database"]
+    @mysql_user     = db_config["username"]
+    @mysql_password = db_config["password"]
+
+    FileUtils.rm_rf(WORKING_DIR)
+    FileUtils.mkdir_p(WORKING_DIR)
+
+    backup_database
+    backup_files
+
+    FileUtils.rm_rf(WORKING_DIR)
+  end
+
+  def self.restore(opts = {})
+
+    db_config = YAML::load_file("config/database.yml")[ENV['RAILS_ENV'] || "development"]
+
+    @mysql_host     = db_config["host"]
+    @mysql_database = db_config["database"]
+    @mysql_user     = db_config["username"]
+    @mysql_password = db_config["password"]
+
+    @backup_filename = ENV['FILE'] || DEFAULT_BACKUP_FILENAME
+
+    # Clear the file cache
+
+    Rake::Task['tmp:cache:clear'].execute
+
+    # Remove the pictures and workflow directories
+
+    FileUtils.rm_rf("public/pictures")
+    FileUtils.rm_rf("public/workflow")
+
+    # Recreate the database
+
+    mysql_execute("-e 'DROP DATABASE IF EXISTS address@hidden'")
+    mysql_execute("-e 'CREATE DATABASE address@hidden'")
+
+    # Extract the backup file
+
+    FileUtils.rm_rf(WORKING_DIR)
+    FileUtils.mkdir_p(WORKING_DIR)
+
+    system("tar xzf address@hidden")
+
+    # Check the version number
+
+    backup_version = File.read("#{WORKING_DIR}/version").to_i
+
+    if backup_version > VERSION
+      raise "Cannot restore backup as backup file version (#{backup_version}) is " +
+        "too high for the backup script to handle (#{VERSION})"
+    end
+
+    # Load database from the SQL dump
+
+    mysql_execute("< #{WORKING_DIR}/#{TMP_SQL_FILE} address@hidden")
+
+    FileUtils.rm_rf(WORKING_DIR)
+  end
+
+private
+
+  def self.mysql_execute(statement)
+    system("mysql -u address@hidden address@hidden -h address@hidden #{statement}")
+  end
+
+end
+

Modified: branches/biocat/lib/rest.rb (2605 => 2606)


--- branches/biocat/lib/rest.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/lib/rest.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -33,7 +33,14 @@
 def rest_routes(map)
   TABLES['REST'][:data].keys.each do |uri|
     TABLES['REST'][:data][uri].keys.each do |method|
-      map.connect "#{uri}.xml", :controller => 'api', :action ="" 'process_request', :uri => uri
+      formats = []
+
+      formats << "xml" if TABLES['REST'][:data][uri][method]["XML"]
+      formats << "rdf" if TABLES['REST'][:data][uri][method]["RDF"]
+
+      formats.each do |format|
+        map.connect "#{uri}.#{format}", :controller => 'api', :action ="" 'process_request', :uri => uri, :format => format
+      end
     end
   end
 end
@@ -198,7 +205,7 @@
       when 'xml'
 
         if query['version'] and model_data['Versioned'][i] == 'yes'
-          text = eval("ob.versions[#{(query['version'].to_i - 1).to_s}].#{accessor}")
+          text = eval("ob.find_version(#{query['version']}).#{accessor}")
         else
           text = eval("ob.#{accessor}")
         end
@@ -240,11 +247,20 @@
 
         else
 
+          foreign_ob = nil
+          text       = ""
+
+          if model_data['Foreign Accessor'][i]
+            foreign_ob = eval("ob.#{model_data['Foreign Accessor'][i]}")
+          end
+
           if accessor
-            if query['version'] and model_data['Versioned'][i] == 'yes'
-              text = eval("ob.versions[#{(query['version'].to_i - 1).to_s}].#{accessor}").to_s
-            else
-              text = eval("ob.#{accessor}").to_s
+            if model_data['Foreign Accessor'][i].nil? || foreign_ob
+              if query['version'] and model_data['Versioned'][i] == 'yes'
+                text = eval("ob.find_version(#{query['version']}).#{accessor}").to_s
+              else
+                text = eval("ob.#{accessor}").to_s
+              end
             end
           end
 
@@ -280,9 +296,8 @@
 def rest_get_request(ob, req_uri, user, uri, entity_name, query)
 
   if query['version']
-    return rest_response(400) unless ob.respond_to?('versions')
-    return rest_response(404) if query['version'].to_i < 1
-    return rest_response(404) if ob.versions[query['version'].to_i - 1].nil?
+    return rest_response(400, :reason => "Object does not support versioning") unless ob.respond_to?('versions')
+    return rest_response(404, :reason => "Specified version does not exist") if query['version'].to_i < 1
   end
 
   elements = query['elements'] ? query['elements'].split(',') : nil
@@ -315,14 +330,14 @@
   render(:xml => doc.to_s)
 end
 
-def rest_crud_request(req_uri, rules, user, query)
+def rest_crud_request(req_uri, format, rules, user, query)
 
   rest_name  = rules['REST Entity']
   model_name = rules['Model Entity']
 
   ob = eval(model_name.camelize).find_by_id(params[:id].to_i)
 
-  return rest_response(404) if ob.nil?
+  return rest_response(404, :reason => "Object not found") if ob.nil?
 
   perm_ob = ob
 
@@ -330,8 +345,8 @@
 
   case rules['Permission']
     when 'public'; # do nothing
-    when 'view';  return rest_response(401) if not Authorization.is_authorized?("show", nil, perm_ob, user)
-    when 'owner'; return rest_response(401) if logged_in?.nil? or object_owner(perm_ob) != user
+    when 'view';  return rest_response(401, :reason => "Not authorised") if not Authorization.is_authorized?("show", nil, perm_ob, user)
+    when 'owner'; return rest_response(401, :reason => "Not authorised") if logged_in?.nil? or object_owner(perm_ob) != user
   end
 
   rest_get_request(ob, params[:uri], user, eval("rest_resource_uri(ob)"), rest_name, query)
@@ -400,7 +415,7 @@
   range
 end
 
-def rest_index_request(req_uri, rules, user, query)
+def rest_index_request(req_uri, format, rules, user, query)
 
   rest_name  = rules['REST Entity']
   model_name = rules['Model Entity']
@@ -561,6 +576,7 @@
     when 'PackRemoteEntry';        return ob.uri
     when 'ContentType';            return nil
     when 'License';                return license_url(ob)
+    when 'CurationEvent';          return nil
 
     when 'Creditation';     return nil
     when 'Attribution';     return nil
@@ -602,6 +618,7 @@
     when 'Tagging';                return "#{base}/tagging.xml?id=#{ob.id}"
     when 'ContentType';            return "#{base}/type.xml?id=#{ob.id}"
     when 'License';                return "#{base}/license.xml?id=#{ob.id}"
+    when 'CurationEvent';          return "#{base}/curation-event.xml?id=#{ob.id}"
 
     when 'Creditation';     return "#{base}/credit.xml?id=#{ob.id}"
     when 'Attribution';     return nil
@@ -635,6 +652,7 @@
     when 'Bookmark';               return 'favourite'
     when 'ContentType';            return 'type'
     when 'License';                return 'license'
+    when 'CurationEvent';          return 'curation-event'
   end
 
   return 'object'
@@ -661,6 +679,7 @@
     when 'Workflow::Version';      return ob.title
     when 'ContentType';            return ob.title
     when 'License';                return ob.title
+    when 'CurationEvent';          return ob.category
   end
 
   return ''
@@ -753,7 +772,7 @@
   resource = eval(type).find_by_id(id)
 
   if resource.nil?
-    rest_response(404)
+    rest_response(404, :reason => "Couldn't find a #{type} with id #{id}")
     return nil
   end
 
@@ -764,13 +783,13 @@
   end
 
   if resource.nil?
-    rest_response(404)
+    rest_response(404, :reason => "#{type} #{id} does not have a version #{version}")
     return nil
   end
 
   if permission
     if !Authorization.is_authorized?(permission, nil, resource, user)
-      rest_response(401)
+      rest_response(401, :reason => "Not authorised for #{type} #{id}")
       return nil
     end
   end
@@ -778,19 +797,19 @@
   resource
 end
 
-def rest_access_redirect(req_uri, rules, user, query)
+def rest_access_redirect(opts = {})
 
-  return rest_response(400) if query['resource'].nil?
+  return rest_response(400, :reason => "Resource was not specified") if opts[:query]['resource'].nil?
 
-  bits = parse_resource_uri(query['resource'])
+  bits = parse_resource_uri(opts[:query]['resource'])
 
-  return rest_response(404) if bits.nil?
+  return rest_response(404, :reason => "Didn't understand the format of the specified resource") if bits.nil?
 
   ob = bits[0].find_by_id(bits[1])
 
-  return rest_response(404) if ob.nil?
+  return rest_response(404, :reason => "The specified resource does not exist") if ob.nil?
 
-  return rest_response(401) if !Authorization.is_authorized?('view', nil, ob, user)
+  return rest_response(401, :reason => "Not authorised for the specified resource") if !Authorization.is_authorized?('view', nil, ob, opts[:user])
 
   rest_response(307, :location => rest_access_uri(ob))
 end
@@ -840,20 +859,20 @@
       :update_mode => update_mode)
 end
 
-def workflow_aux(action, req_uri, rules, user, query)
+def workflow_aux(action, opts = {})
 
   # Obtain object
 
   case action
     when 'create':
-      return rest_response(401) unless Authorization.is_authorized_for_type?('create', 'Workflow', user, nil)
-      if query['id']
-        ob = obtain_rest_resource('Workflow', query['id'], query['version'], user, action)
+      return rest_response(401, :reason => "Not authorised to create a workflow") unless Authorization.is_authorized_for_type?('create', 'Workflow', opts[:user], nil)
+      if opts[:query]['id']
+        ob = obtain_rest_resource('Workflow', opts[:query]['id'], opts[:query]['version'], opts[:user], action)
       else
-        ob = Workflow.new(:contributor => user)
+        ob = Workflow.new(:contributor => opts[:user])
       end
     when 'read', 'update', 'destroy':
-      ob = obtain_rest_resource('Workflow', query['id'], query['version'], user, action)
+      ob = obtain_rest_resource('Workflow', opts[:query]['id'], opts[:query]['version'], opts[:user], action)
     else
       raise "Invalid action '#{action}'"
   end
@@ -862,7 +881,7 @@
 
   if action == "destroy"
 
-    return rest_response(400, :reason => "Cannot delete individual versions") if query['version']
+    return rest_response(400, :reason => "Cannot delete individual versions") if opts[:query]['version']
       
     ob.destroy
 
@@ -884,10 +903,22 @@
 
     # build the contributable
 
-    ob.title        = title        if title
-    ob.body         = description  if description
-    ob.license      = License.find_by_unique_name(license_type) if license_type
+    ob.title   = title        if title
+    ob.body    = description  if description
 
+    if license_type 
+      if license_type == ""
+        ob.license = nil
+      else
+        ob.license = License.find_by_unique_name(license_type)
+
+        if ob.license.nil?
+          ob.errors.add("License type")
+          return rest_response(400, :object => ob)
+        end
+      end
+    end
+
     # handle workflow type
 
     if type
@@ -959,7 +990,7 @@
       svg_file.close
     end
 
-    success = if (action == 'create' and query['id'])
+    success = if (action == 'create' and opts[:query]['id'])
       ob.save_as_new_version(revision_comment)
     else
       ob.save
@@ -969,10 +1000,10 @@
 
     # Elements to update if we're not dealing with a workflow version
 
-    if query['version'].nil?
+    if opts[:query]['version'].nil?
 
       if ob.contribution.policy.nil?
-        ob.contribution.policy = create_default_policy(user)
+        ob.contribution.policy = create_default_policy(opts[:user])
         ob.contribution.save
       end
 
@@ -982,34 +1013,34 @@
 
   ob = ob.versioned_resource if ob.respond_to?("versioned_resource")
 
-  rest_get_request(ob, "workflow", user,
+  rest_get_request(ob, "workflow", opts[:user],
       rest_resource_uri(ob), "workflow", { "id" => ob.id.to_s })
 end
 
-def post_workflow(req_uri, rules, user, query)
-  workflow_aux('create', req_uri, rules, user, query)
+def post_workflow(opts)
+  workflow_aux('create', opts)
 end
 
-def put_workflow(req_uri, rules, user, query)
-  workflow_aux('update', req_uri, rules, user, query)
+def put_workflow(opts)
+  workflow_aux('update', opts)
 end
 
-def delete_workflow(req_uri, rules, user, query)
-  workflow_aux('destroy', req_uri, rules, user, query)
+def delete_workflow(opts)
+  workflow_aux('destroy', opts)
 end
 
 # file handling
 
-def file_aux(action, req_uri, rules, user, query)
+def file_aux(action, opts = {})
 
   # Obtain object
 
   case action
     when 'create':
-      return rest_response(401) unless Authorization.is_authorized_for_type?('create', 'Blob', user, nil)
-      ob = Blob.new(:contributor => user)
+      return rest_response(401, :reason => "Not authorised to create a file") unless Authorization.is_authorized_for_type?('create', 'Blob', opts[:user], nil)
+      ob = Blob.new(:contributor => opts[:user])
     when 'read', 'update', 'destroy':
-      ob = obtain_rest_resource('Blob', query['id'], query['version'], user, action)
+      ob = obtain_rest_resource('Blob', opts[:query]['id'], opts[:query]['version'], opts[:user], action)
     else
       raise "Invalid action '#{action}'"
   end
@@ -1038,11 +1069,16 @@
     ob.title        = title        if title
     ob.body         = description  if description
 
-    if license_type
-      ob.license = License.find_by_unique_name(license_type)
-      if ob.license.nil?
-        ob.errors.add("License type")
-        return rest_response(400, :object => ob)
+    if license_type 
+      if license_type == ""
+        ob.license = nil
+      else
+        ob.license = License.find_by_unique_name(license_type)
+
+        if ob.license.nil?
+          ob.errors.add("License type")
+          return rest_response(400, :object => ob)
+        end
       end
     end
    
@@ -1081,41 +1117,41 @@
     end
 
     if ob.contribution.policy.nil?
-      ob.contribution.policy = create_default_policy(user)
+      ob.contribution.policy = create_default_policy(opts[:user])
       ob.contribution.save
     end
 
     update_permissions(ob, permissions)
   end
 
-  rest_get_request(ob, "file", user,
+  rest_get_request(ob, "file", opts[:user],
       rest_resource_uri(ob), "file", { "id" => ob.id.to_s })
 end
 
-def post_file(req_uri, rules, user, query)
-  file_aux('create', req_uri, rules, user, query)
+def post_file(opts)
+  file_aux('create', opts)
 end
 
-def put_file(req_uri, rules, user, query)
-  file_aux('update', req_uri, rules, user, query)
+def put_file(opts)
+  file_aux('update', opts)
 end
 
-def delete_file(req_uri, rules, user, query)
-  file_aux('destroy', req_uri, rules, user, query)
+def delete_file(opts)
+  file_aux('destroy', opts)
 end
 
 # pack handling
 
-def pack_aux(action, req_uri, rules, user, query)
+def pack_aux(action, opts = {})
 
   # Obtain object
 
   case action
     when 'create':
-      return rest_response(401) unless Authorization.is_authorized_for_type?('create', 'Pack', user, nil)
-      ob = Pack.new(:contributor => user)
+      return rest_response(401, :reason => "Not authorised to create a pack") unless Authorization.is_authorized_for_type?('create', 'Pack', opts[:user], nil)
+      ob = Pack.new(:contributor => opts[:user])
     when 'read', 'update', 'destroy':
-      ob = obtain_rest_resource('Pack', query['id'], query['version'], user, action)
+      ob = obtain_rest_resource('Pack', opts[:query]['id'], opts[:query]['version'], opts[:user], action)
     else
       raise "Invalid action '#{action}'"
   end
@@ -1145,30 +1181,30 @@
     end
 
     if ob.contribution.policy.nil?
-      ob.contribution.policy = create_default_policy(user)
+      ob.contribution.policy = create_default_policy(opts[:user])
       ob.contribution.save
     end
 
     update_permissions(ob, permissions)
   end
 
-  rest_get_request(ob, "pack", user,
+  rest_get_request(ob, "pack", opts[:user],
       rest_resource_uri(ob), "pack", { "id" => ob.id.to_s })
 end
 
-def post_pack(req_uri, rules, user, query)
-  pack_aux('create', req_uri, rules, user, query)
+def post_pack(opts)
+  pack_aux('create', opts)
 end
 
-def put_pack(req_uri, rules, user, query)
-  pack_aux('update', req_uri, rules, user, query)
+def put_pack(opts)
+  pack_aux('update', opts)
 end
 
-def delete_pack(req_uri, rules, user, query)
-  pack_aux('destroy', req_uri, rules, user, query)
+def delete_pack(opts)
+  pack_aux('destroy', opts)
 end
 
-def external_pack_item_aux(action, req_uri, rules, user, query)
+def external_pack_item_aux(action, opts = {})
 
   unless action == 'destroy'
 
@@ -1186,11 +1222,11 @@
   case action
     when 'create':
 
-      return rest_response(401) unless Authorization.is_authorized_for_type?('create', 'PackRemoteEntry', user, pack)
+      return rest_response(401, :reason => "Not authorised to create an external pack item") unless Authorization.is_authorized_for_type?('create', 'PackRemoteEntry', opts[:user], pack)
       return rest_response(400, :reason => "Pack not found") if pack.nil?
-      return rest_response(401) unless Authorization.is_authorized?('edit', nil, pack, user)
+      return rest_response(401, :reason => "Not authorised to change the specified pack") unless Authorization.is_authorized?('edit', nil, pack, opts[:user])
 
-      ob = PackRemoteEntry.new(:user => user,
+      ob = PackRemoteEntry.new(:user => opts[:user],
           :pack          => pack,
           :title         => title,
           :uri           => uri,
@@ -1199,10 +1235,10 @@
 
     when 'read', 'update', 'destroy':
 
-      ob = obtain_rest_resource('PackRemoteEntry', query['id'], query['version'], user, action)
+      ob = obtain_rest_resource('PackRemoteEntry', opts[:query]['id'], opts[:query]['version'], opts[:user], action)
 
       if ob
-        return rest_response(401) unless Authorization.is_authorized?('edit', nil, ob.pack, user)
+        return rest_response(401, :reason => "Not authorised to change the specified pack") unless Authorization.is_authorized?('edit', nil, ob.pack, opts[:user])
       end
 
     else
@@ -1227,23 +1263,23 @@
     end
   end
 
-  rest_get_request(ob, "external-pack-item", user,
+  rest_get_request(ob, "external-pack-item", opts[:user],
       rest_resource_uri(ob), "external-pack-item", { "id" => ob.id.to_s })
 end
 
-def post_external_pack_item(req_uri, rules, user, query)
-  external_pack_item_aux('create', req_uri, rules, user, query)
+def post_external_pack_item(opts)
+  external_pack_item_aux('create', opts)
 end
 
-def put_external_pack_item(req_uri, rules, user, query)
-  external_pack_item_aux('update', req_uri, rules, user, query)
+def put_external_pack_item(opts)
+  external_pack_item_aux('update', opts)
 end
 
-def delete_external_pack_item(req_uri, rules, user, query)
-  external_pack_item_aux('destroy', req_uri, rules, user, query)
+def delete_external_pack_item(opts)
+  external_pack_item_aux('destroy', opts)
 end
 
-def internal_pack_item_aux(action, req_uri, rules, user, query)
+def internal_pack_item_aux(action, opts = {})
 
   unless action == 'destroy'
 
@@ -1259,21 +1295,21 @@
   case action
     when 'create':
 
-      return rest_response(401) unless Authorization.is_authorized_for_type?('create', 'PackContributableEntry', user, pack)
+      return rest_response(401, :reason => "Not authorised to create an internal pack item") unless Authorization.is_authorized_for_type?('create', 'PackContributableEntry', opts[:user], pack)
       return rest_response(400, :reason => "Pack not found") if pack.nil?
-      return rest_response(401) unless Authorization.is_authorized?('edit', nil, pack, user)
+      return rest_response(401, :reason => "Not authorised to change the specified pack") unless Authorization.is_authorized?('edit', nil, pack, opts[:user])
 
-      ob = PackContributableEntry.new(:user => user,
+      ob = PackContributableEntry.new(:user => opts[:user],
           :pack          => pack,
           :contributable => item,
           :comment       => comment)
 
     when 'read', 'update', 'destroy':
 
-      ob = obtain_rest_resource('PackContributableEntry', query['id'], query['version'], user, action)
+      ob = obtain_rest_resource('PackContributableEntry', opts[:query]['id'], opts[:query]['version'], opts[:user], action)
 
       if ob
-        return rest_response(401) unless Authorization.is_authorized?('edit', nil, ob.pack, user)
+        return rest_response(401, :reason => "Not authorised to change the specified pack") unless Authorization.is_authorized?('edit', nil, ob.pack, opts[:user])
       end
 
     else
@@ -1295,23 +1331,23 @@
     end
   end
 
-  rest_get_request(ob, "internal-pack-item", user,
+  rest_get_request(ob, "internal-pack-item", opts[:user],
       rest_resource_uri(ob), "internal-pack-item", { "id" => ob.id.to_s })
 end
 
-def post_internal_pack_item(req_uri, rules, user, query)
-  internal_pack_item_aux('create', req_uri, rules, user, query)
+def post_internal_pack_item(opts)
+  internal_pack_item_aux('create', opts)
 end
 
-def put_internal_pack_item(req_uri, rules, user, query)
-  internal_pack_item_aux('update', req_uri, rules, user, query)
+def put_internal_pack_item(opts)
+  internal_pack_item_aux('update', opts)
 end
 
-def delete_internal_pack_item(req_uri, rules, user, query)
-  internal_pack_item_aux('destroy', req_uri, rules, user, query)
+def delete_internal_pack_item(opts)
+  internal_pack_item_aux('destroy', opts)
 end
 
-# def post_job(req_uri, rules, user, query)
+# def post_job(opts)
 #
 #   title       = params["job"]["title"]
 #   description = params["job"]["description"]
@@ -1331,14 +1367,14 @@
 #   runner     = TavernaEnactor.find_by_id(runner_bits[1].to_i)
 #   runnable   = Workflow.find_by_id(runnable_bits[1].to_i)
 #
-#   return rest_response(400) if experiment.nil? or not Authorization.is_authorized?('edit', nil, experiment, user)
-#   return rest_response(400) if runner.nil?     or not Authorization.is_authorized?('download', nil, runner, user)
-#   return rest_response(400) if runnable.nil?   or not Authorization.is_authorized?('view', nil, runnable, user)
+#   return rest_response(400) if experiment.nil? or not Authorization.is_authorized?('edit', nil, experiment, opts[:user])
+#   return rest_response(400) if runner.nil?     or not Authorization.is_authorized?('download', nil, runner, opts[:user])
+#   return rest_response(400) if runnable.nil?   or not Authorization.is_authorized?('view', nil, runnable, opts[:user])
 #
 #   puts "#{params[:job]}"
 #
 #   job = Job.new(:title => title, :description => description, :runnable => runnable, 
-#       :experiment => experiment, :runner => runner, :user => user,
+#       :experiment => experiment, :runner => runner, :user => opts[:user],
 #       :runnable_version => runnable.current_version)
 #
 #   inputs = { "Tags" => "aa,bb,aa,cc,aa" }
@@ -1367,14 +1403,14 @@
   }
 end
 
-def search(req_uri, rules, user, query)
+def search(opts)
 
-  search_query = query['query']
+  search_query = opts[:query]['query']
 
   # Start of curation hack
 
-  if search_query[0..1].downcase == 'c:' && user &&
-      Conf.curators.include?(user.username)
+  if search_query[0..1].downcase == 'c:' && opts[:user] &&
+      Conf.curators.include?(opts[:user].username)
 
     bits = search_query.match("^c:([a-z]*) ([0-9]+)-([0-9]+)$")
 
@@ -1389,9 +1425,9 @@
 
       obs = model.find(:all, :conditions => ['id >= ? AND id <= ?', bits[2], bits[3]])
 
-      obs = (obs.select do |c| c.respond_to?('contribution') == false or Authorization.is_authorized?("view", nil, c, user) end)
+      obs = (obs.select do |c| c.respond_to?('contribution') == false or Authorization.is_authorized?("view", nil, c, opts[:user]) end)
 
-      return produce_rest_list(req_uri, rules, query, obs, 'search', {}, user)
+      return produce_rest_list(opts[:req_uri], opts[:rules], opts[:query], obs, 'search', {}, opts[:user])
     end
   end
 
@@ -1401,11 +1437,11 @@
 
   # parse type option
 
-  if query['type']
+  if opts[:query]['type']
 
     models = []
 
-    query['type'].split(',').each do |type|
+    opts[:query]['type'].split(',').each do |type|
       case type
         when 'user';     models.push(User)
         when 'workflow'; models.push(Workflow)
@@ -1420,27 +1456,27 @@
 
   num = 25
 
-  if query['num']
-    num = query['num'].to_i
+  if opts[:query]['num']
+    num = opts[:query]['num'].to_i
   end
 
   num = 25  if num < 0
   num = 100 if num > 100
 
-  page  = query['page'] ? query['page'].to_i : 1
+  page  = opts[:query]['page'] ? opts[:query]['page'].to_i : 1
 
   page = 1 if page < 1
 
   attributes = {}
   attributes['query'] = search_query
-  attributes['type'] = query['type'] if models.length == 1
+  attributes['type'] = opts[:query]['type'] if models.length == 1
 
-  obs = paginated_search_index(search_query, models, num, page, user)
+  obs = paginated_search_index(search_query, models, num, page, opts[:user])
 
-  produce_rest_list(req_uri, rules, query, obs, 'search', attributes, user)
+  produce_rest_list(opts[:req_uri], opts[:rules], opts[:query], obs, 'search', attributes, opts[:user])
 end
 
-def user_count(req_uri, rules, user, query)
+def user_count(opts)
   
   users = User.find(:all).select do |user| user.activated? end
 
@@ -1453,7 +1489,7 @@
   render(:xml => doc.to_s)
 end
 
-def group_count(req_uri, rules, user, query)
+def group_count(opts)
   
   root = LibXML::XML::Node.new('group-count')
   root << Network.count.to_s
@@ -1464,10 +1500,10 @@
   render(:xml => doc.to_s)
 end
 
-def workflow_count(req_uri, rules, user, query)
+def workflow_count(opts)
   
   workflows = Workflow.find(:all).select do |w|
-    Authorization.is_authorized?('view', nil, w, user)
+    Authorization.is_authorized?('view', nil, w, opts[:user])
   end
 
   root = LibXML::XML::Node.new('workflow-count')
@@ -1479,10 +1515,10 @@
   render(:xml => doc.to_s)
 end
 
-def pack_count(req_uri, rules, user, query)
+def pack_count(opts)
   
   packs = Pack.find(:all).select do |p|
-    Authorization.is_authorized?('view', nil, p, user)
+    Authorization.is_authorized?('view', nil, p, opts[:user])
   end
 
   root = LibXML::XML::Node.new('pack-count')
@@ -1494,7 +1530,7 @@
   render(:xml => doc.to_s)
 end
 
-def content_type_count(req_uri, rules, user, query)
+def content_type_count(opts)
 
   root = LibXML::XML::Node.new('type-count')
   root << ContentType.count.to_s
@@ -1505,35 +1541,35 @@
   render(:xml => doc.to_s)
 end
 
-def get_tagged(req_uri, rules, user, query)
+def get_tagged(opts)
 
-  return rest_response(400) if query['tag'].nil?
+  return rest_response(400, :reason => "Did not specify a tag") if opts[:query]['tag'].nil?
 
-  tag = Tag.find_by_name(query['tag'])
+  tag = Tag.find_by_name(opts[:query]['tag'])
 
   obs = tag ? tag.tagged : []
 
   # filter out ones they are not allowed to get
-  obs = (obs.select do |c| c.respond_to?('contribution') == false or Authorization.is_authorized?("index", nil, c, user) end)
+  obs = (obs.select do |c| c.respond_to?('contribution') == false or Authorization.is_authorized?("index", nil, c, opts[:user]) end)
 
-  produce_rest_list("tagged", rules, query, obs, 'tagged', [], user)
+  produce_rest_list("tagged", opts[:rules], opts[:query], obs, 'tagged', [], opts[:user])
 end
 
-def tag_cloud(req_uri, rules, user, query)
+def tag_cloud(opts)
 
   num  = 25
   type = nil
 
-  if query['num']
-    if query['num'] == 'all'
+  if opts[:query]['num']
+    if opts[:query]['num'] == 'all'
       num = nil
     else
-      num = query['num'].to_i
+      num = opts[:query]['num'].to_i
     end
   end
 
-  if query['type'] and query['type'] != 'all'
-    type = query['type'].camelize
+  if opts[:query]['type'] and opts[:query]['type'] != 'all'
+    type = opts[:query]['type'].camelize
 
     type = 'Network' if type == 'Group'
     type = 'Blob'    if type == 'File'
@@ -1546,10 +1582,10 @@
   root = LibXML::XML::Node.new('tag-cloud')
   doc.root = root
 
-  root['type'] = query['type'] ? query['type'] : 'all'
+  root['type'] = opts[:query]['type'] ? opts[:query]['type'] : 'all'
 
   tags.each do |tag|
-    tag_node = rest_reference(tag, query)
+    tag_node = rest_reference(tag, opts[:query])
     tag_node['count'] = tag.taggings_count.to_s
     root << tag_node
   end
@@ -1557,11 +1593,14 @@
   render(:xml => doc.to_s)
 end
 
-def whoami_redirect(req_uri, rules, user, query)
-  if user.class == User
-    rest_response(307, :location => rest_access_uri(user))
+def whoami_redirect(opts)
+  if opts[:user].class == User
+    case opts[:format]
+      when "xml": rest_response(307, :location => rest_access_uri(opts[:user]))
+      when "rdf": rest_response(307, :location => formatted_user_url(:id => opts[:user].id, :format => 'rdf'))
+    end
   else
-    rest_response(401)
+    rest_response(401, :reason => "Not logged in")
   end
 end
 
@@ -1570,20 +1609,22 @@
     when :text
       if doc.find_first(query)
 
-        enc = doc.find_first(query)['encoding']
-        el  = doc.find_first("#{query}/text()")
+        enc  = doc.find_first(query)['encoding']
+        el   = doc.find_first("#{query}")
+        text = doc.find_first("#{query}/text()")
 
         if el
           if enc == 'base64'
-            return Base64::decode64(el.to_s)
+            return Base64::decode64(text.to_s)
           else
-            return el.to_s
+            return text.to_s
           end
         end
       end
     when :binary
-      el = doc.find_first("#{query}/text()")
-      return Base64::decode64(el.to_s) if el
+      el   = doc.find_first("#{query}")
+      text = doc.find_first("#{query}/text()")
+      return Base64::decode64(text.to_s) if el
     when :resource
       return resolve_resource_node(doc.find_first(query))
   end
@@ -1626,17 +1667,17 @@
 
 # Comments
 
-def comment_aux(action, req_uri, rules, user, query)
+def comment_aux(action, opts)
 
   # Obtain object
 
   case action
     when 'create':
-      return rest_response(401) unless Authorization.is_authorized_for_type?('create', 'Comment', user, nil)
+      return rest_response(401, :reason => "Not authorised to create a comment") unless Authorization.is_authorized_for_type?('create', 'Comment', opts[:user], nil)
 
-      ob = Comment.new(:user => user)
+      ob = Comment.new(:user => opts[:user])
     when 'read', 'update', 'destroy':
-      ob = obtain_rest_resource('Comment', query['id'], query['version'], user, action)
+      ob = obtain_rest_resource('Comment', opts[:query]['id'], opts[:query]['version'], opts[:user], action)
     else
       raise "Invalid action '#{action}'"
   end
@@ -1657,8 +1698,8 @@
     ob.comment = comment if comment
 
     if subject
-      return rest_response(400) unless [Blob, Network, Pack, Workflow].include?(subject.class)
-      return rest_response(401) unless Authorization.is_authorized_for_type?(action, 'Comment', user, subject)
+      return rest_response(400, :reason => "Specified resource does not support comments") unless [Blob, Network, Pack, Workflow].include?(subject.class)
+      return rest_response(401, :reason => "Not authorised to add a comment to the specified resource") unless Authorization.is_authorized_for_type?(action, 'Comment', opts[:user], subject)
       ob.commentable = subject
     end
 
@@ -1677,8 +1718,8 @@
       return matches[0] if matches.length == 1
     end
 
-    if comment[0..1].downcase == 'c:' && user && subject &&
-        Conf.curators.include?(user.username)
+    if comment[0..1].downcase == 'c:' && opts[:user] && subject &&
+        Conf.curators.include?(opts[:user].username)
 
       comment = comment[2..-1].strip
 
@@ -1708,7 +1749,7 @@
 
             if curation_type
               events.push(CurationEvent.new(:category => curation_type,
-                    :object => subject, :user => user, :details => details))
+                    :object => subject, :user => opts[:user], :details => details))
             else
               failed = true
             end
@@ -1726,7 +1767,7 @@
 
       subject.solr_save
 
-      return rest_get_request(ob, "comment", user, rest_resource_uri(ob),
+      return rest_get_request(ob, "comment", opts[:user], rest_resource_uri(ob),
         "comment", { "id" => ob.id.to_s })
     end
 
@@ -1735,34 +1776,34 @@
     return rest_response(400, :object => ob) unless ob.save
   end
 
-  rest_get_request(ob, "comment", user, rest_resource_uri(ob), "comment", { "id" => ob.id.to_s })
+  rest_get_request(ob, "comment", opts[:user], rest_resource_uri(ob), "comment", { "id" => ob.id.to_s })
 end
 
-def post_comment(req_uri, rules, user, query)
-  comment_aux('create', req_uri, rules, user, query)
+def post_comment(opts)
+  comment_aux('create', opts)
 end
 
-def put_comment(req_uri, rules, user, query)
-  comment_aux('update', req_uri, rules, user, query)
+def put_comment(opts)
+  comment_aux('update', opts)
 end
 
-def delete_comment(req_uri, rules, user, query)
-  comment_aux('destroy', req_uri, rules, user, query)
+def delete_comment(opts)
+  comment_aux('destroy', opts)
 end
 
 # Favourites
 
-def favourite_aux(action, req_uri, rules, user, query)
+def favourite_aux(action, opts)
 
   # Obtain object
 
   case action
     when 'create':
-      return rest_response(401) unless Authorization.is_authorized_for_type?('create', 'Bookmark', user, nil)
+      return rest_response(401, :reason => "Not authorised to create a favourite") unless Authorization.is_authorized_for_type?('create', 'Bookmark', opts[:user], nil)
 
-      ob = Bookmark.new(:user => user)
+      ob = Bookmark.new(:user => opts[:user])
     when 'read', 'update', 'destroy':
-      ob = obtain_rest_resource('Bookmark', query['id'], query['version'], user, action)
+      ob = obtain_rest_resource('Bookmark', opts[:query]['id'], opts[:query]['version'], opts[:user], action)
     else
       raise "Invalid action '#{action}'"
   end
@@ -1780,32 +1821,141 @@
     target = parse_element(data, :resource, '/favourite/object')
 
     if target
-      return rest_response(400) unless [Blob, Pack, Workflow].include?(target.class)
-      return rest_response(401) unless Authorization.is_authorized_for_type?(action, 'Bookmark', user, target)
+      return rest_response(400, :reason => "Specified resource is not a valid favourite target") unless [Blob, Pack, Workflow].include?(target.class)
+      return rest_response(401, :reason => "Not authorised to create the favourite") unless Authorization.is_authorized_for_type?(action, 'Bookmark', opts[:user], target)
       ob.bookmarkable = target
     end
 
     return rest_response(400, :object => ob) unless ob.save
   end
 
-  rest_get_request(ob, "favourite", user, rest_resource_uri(ob), "favourite", { "id" => ob.id.to_s })
+  rest_get_request(ob, "favourite", opts[:user], rest_resource_uri(ob), "favourite", { "id" => ob.id.to_s })
 end
 
-def post_favourite(req_uri, rules, user, query)
-  favourite_aux('create', req_uri, rules, user, query)
+def post_favourite(opts)
+  favourite_aux('create', opts)
 end
 
-def put_favourite(req_uri, rules, user, query)
-  favourite_aux('update', req_uri, rules, user, query)
+def put_favourite(opts)
+  favourite_aux('update', opts)
 end
 
-def delete_favourite(req_uri, rules, user, query)
-  favourite_aux('destroy', req_uri, rules, user, query)
+def delete_favourite(opts)
+  favourite_aux('destroy', opts)
 end
 
+# Ratings
+
+def rating_aux(action, opts)
+
+  # Obtain object
+
+  case action
+    when 'create':
+      return rest_response(401, :reason => "Not authorised to create a rating") unless Authorization.is_authorized_for_type?('create', 'Rating', opts[:user], nil)
+
+      ob = Rating.new(:user => opts[:user])
+    when 'read', 'update', 'destroy':
+      ob = obtain_rest_resource('Rating', opts[:query]['id'], opts[:query]['version'], opts[:user], action)
+    else
+      raise "Invalid action '#{action}'"
+  end
+
+  return if ob.nil? # appropriate rest response already given
+
+  if action == "destroy"
+
+    ob.destroy
+
+  else
+
+    data = ""
+
+    rating  = parse_element(data, :text,     '/rating/rating')
+    subject = parse_element(data, :resource, '/rating/subject')
+
+    ob.rating = rating if rating
+
+    if subject
+      return rest_response(400, :reason => "Specified resource does not support ratings") unless [Blob, Network, Pack, Workflow].include?(subject.class)
+      return rest_response(401, :reason => "Not authorised for the specified resource") unless Authorization.is_authorized_for_type?(action, 'Rating', opts[:user], subject)
+      ob.rateable = subject
+    end
+
+    return rest_response(400, :object => ob) unless ob.save
+  end
+
+  rest_get_request(ob, "rating", opts[:user], rest_resource_uri(ob), "rating", { "id" => ob.id.to_s })
+end
+
+def post_rating(opts)
+  rating_aux('create', opts)
+end
+
+def put_rating(opts)
+  rating_aux('update', opts)
+end
+
+def delete_rating(opts)
+  rating_aux('destroy', opts)
+end
+
+# Taggings
+
+def tagging_aux(action, opts)
+
+  # Obtain object
+
+  case action
+    when 'create':
+      return rest_response(401, :reason => "Not authorised to create a tagging") unless Authorization.is_authorized_for_type?('create', 'Tagging', opts[:user], nil)
+
+      ob = Tagging.new(:user => opts[:user])
+    when 'read', 'update', 'destroy':
+      ob = obtain_rest_resource('Tagging', opts[:query]['id'], opts[:query]['version'], opts[:user], action)
+    else
+      raise "Invalid action '#{action}'"
+  end
+
+  return if ob.nil? # appropriate rest response already given
+
+  if action == "destroy"
+
+    ob.destroy
+
+  else
+
+    data = ""
+
+    subject = parse_element(data, :resource, '/tagging/subject')
+    label   = parse_element(data, :text,     '/tagging/label')
+    tag     = parse_element(data, :resource, '/tagging/tag')
+
+    ob.label    = label   if label
+    ob.tag      = tag     if tag
+
+    if subject
+      return rest_response(401, :reason => "Not authorised for the specified resource") unless Authorization.is_authorized_for_type?(action, 'Rating', opts[:user], subject)
+      ob.taggable = subject
+    end
+
+    return rest_response(400, :object => ob) unless ob.save
+  end
+
+  rest_get_request(ob, "tagging", opts[:user], rest_resource_uri(ob), "tagging", { "id" => ob.id.to_s })
+end
+
+def post_tagging(opts)
+  tagging_aux('create', opts)
+end
+
+def delete_tagging(opts)
+  tagging_aux('destroy', opts)
+end
+
 # Call dispatcher
 
-def rest_call_request(req_uri, rules, user, query)
-  eval("#{rules['Function']}(req_uri, rules, user, query)")
+def rest_call_request(req_uri, format, rules, user, query)
+  eval("#{rules['Function']}(:req_uri => req_uri, :format => format, :rules => rules, :user => user, :query => query)")
 end
 

Modified: branches/biocat/lib/sanity_test.rb (2605 => 2606)


--- branches/biocat/lib/sanity_test.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/lib/sanity_test.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -92,9 +92,6 @@
   should_be_empty("All workflows must have a content type",
       workflows.select do |w| w.content_type.nil? end)
 
-  should_be_empty("All workflows must have a license",
-      workflows.select do |w| w.license.nil? end)
-
   # versioning
 
   should_be_empty("All workflows versions should be contiguous",

Copied: branches/biocat/lib/taverna_to_galaxy.rb (from rev 2605, trunk/lib/taverna_to_galaxy.rb) (0 => 2606)


--- branches/biocat/lib/taverna_to_galaxy.rb	                        (rev 0)
+++ branches/biocat/lib/taverna_to_galaxy.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,134 @@
+# myExperiment: lib/taverna_to_galaxy.rb
+# 
+# Copyright (c) 2011 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+require 'zip/zip'
+require 'workflow-to-galaxy'
+
+include Generator
+
+module TavernaToGalaxy
+
+  def self.generate(workflow, version, t2_server, zip_file_name)
+
+    wv = workflow.find_version(version)
+
+    doc = XML::Document.new
+    doc.root = XML::Node.new("workflow")
+    doc.root << wv.components
+
+    wkf_title   = wv.title
+    wkf_descr   = wv.body
+    wkf_inputs  = get_IOData(doc, "source")
+    wkf_outputs = get_IOData(doc, "sink")
+
+    w2g_workflow = W2GWorkflow.new(nil, wkf_title, wkf_descr, wkf_inputs, wkf_outputs)
+
+    w2g_rest_object = W2GRestObject.new("#{workflow.named_download_url}?version=#{version}", w2g_workflow)
+
+    # Set output files
+    xml_file    = wv.unique_name + ".xml"
+    script_file = wv.unique_name + ".rb"
+
+    # Set taverna server if not specified
+    t2_server = "http://localhost:8980/taverna-server"  if t2_server == ""
+
+    # Generate Galaxy tool's files
+    zip_file = Zip::ZipFile.open(zip_file_name, Zip::ZipFile::CREATE)
+
+    zip_file.get_output_stream(xml_file) do |stream|
+      generate_xml(w2g_rest_object, xml_file, stream)
+    end
+
+    zip_file.get_output_stream(script_file) do |stream|
+      generate_script(w2g_rest_object, t2_server, stream)
+    end
+
+    zip_file.close
+  end
+
+  #
+  # Populate _IOData_ objects for specified type: value +source+'+ is for inputs
+  # and +sink+ for outputs
+  #
+  def self.get_IOData(doc, type)
+    io_data = []
+
+    # Get all sources or sinks and create appropriate objects
+    doc.find("//workflow/components/dataflows/address@hidden'top']/#{type}s/#{type}").each do |node|
+      name = ''
+      descriptions = []
+      examples = []
+
+      node.each_element do |n|
+        if n.name.eql? "name"
+          name = n.children[0].to_s
+        elsif n.name.eql? "descriptions"
+          n.each_element do |d|
+            descriptions << d.children[0].to_s
+          end if n.children?
+        elsif n.name.eql? "examples"
+          n.each_element do |e|
+            examples << e.children[0].to_s
+          end if n.children?
+        end
+      end
+
+      io_data << W2GIOData.new(name, descriptions, examples)
+    end
+
+    io_data
+  end
+
+  class W2GRestObject
+
+    attr_reader(:uri, :workflow)
+
+    def initialize(uri, workflow)
+      @uri      = uri
+      @workflow = workflow
+    end
+
+  end
+
+  #
+  # Contains all available information about a workflow: _xml_uri_, _title_, _description_,
+  # _inputs_ and _outputs_. The _xml_uri_ specifies the XML description on myExperiment and
+  # not the XML of the workflow itself.
+  #
+  class W2GWorkflow
+
+    attr_reader(:xml_uri, :title, :description, :inputs, :outputs)
+
+    def initialize(xml_uri, title, description, inputs, outputs)
+      @xml_uri     = xml_uri
+      @title       = title
+      @description = description
+      @inputs      = inputs
+      @outputs     = outputs
+    end
+
+  end
+
+  #
+  # Contains all available information about an input or output: name, descriptions
+  # and examples. The last two are lists.
+  #--
+  # Currently both inputs and outputs contain the same information. If that
+  # changes we can subclass this one.
+  #
+  class W2GIOData
+
+    attr_reader(:name, :descriptions, :examples)
+
+    def initialize(name, descriptions, examples)
+      @name         = name
+      @descriptions = descriptions
+      @examples     = examples
+    end
+
+  end
+
+end
+

Modified: branches/biocat/lib/workflow_processors/blog2_template.rb (2605 => 2606)


--- branches/biocat/lib/workflow_processors/blog2_template.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/lib/workflow_processors/blog2_template.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -17,7 +17,7 @@
     
     # MUST be unique across all processors
     def self.display_name
-      "Blog2 Template"
+      "LabTrove Template"
     end
     
     def self.display_data_format

Copied: branches/biocat/lib/workflow_processors/galaxy.rb (from rev 2605, trunk/lib/workflow_processors/galaxy.rb) (0 => 2606)


--- branches/biocat/lib/workflow_processors/galaxy.rb	                        (rev 0)
+++ branches/biocat/lib/workflow_processors/galaxy.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,295 @@
+# myExperiment: lib/workflow_processors/galaxy.rb
+#
+# Copyright (c) 2010 University of Manchester and the University of Southampton.
+# See license.txt for details.
+
+module WorkflowProcessors
+
+  require 'libxml'
+  
+  class Galaxy < WorkflowProcessors::Interface
+
+    Mime::Type.register "application/vnd.galaxy.workflow+xml", :galaxy_workflow
+
+    # Begin Class Methods
+    
+    # These: 
+    # - provide information about the Workflow Type supported by this processor,
+    # - provide information about the processor's capabilites, and
+    # - provide any general functionality.
+    
+    # MUST be unique across all processors
+    def self.display_name 
+      "Galaxy"
+    end
+    
+    def self.display_data_format
+      "XML"
+    end
+    
+    def self.mime_type
+      "application/vnd.galaxy.workflow+xml"
+    end
+
+    # All the file extensions supported by this workflow processor.
+    # Must be all in lowercase.
+    def self.file_extensions_supported
+      []
+    end
+    
+    def self.can_determine_type_from_file?
+      false
+    end
+    
+    def self.recognised?(file)
+      false
+    end
+    
+    def self.can_infer_metadata?
+      false
+    end
+    
+    def self.can_generate_preview_image?
+      false
+    end
+    
+    def self.can_generate_preview_svg?
+      false
+    end
+    
+    def self.show_download_section?
+      false
+    end
+
+    def initialize(workflow_definition)
+      super(workflow_definition)
+      @model = WorkflowProcessors::GalaxyLib::Workflow.parse(workflow_definition)
+    end
+    
+    def get_workflow_model_object
+      @model
+    end
+
+    def get_components
+      @model.get_components
+    end
+
+    def get_search_terms
+
+      return "" if @model.nil?
+
+      words = StringIO.new
+
+      @model.steps.each do |step|
+        words << " #{step.name} #{step.tool}"
+      end
+
+      @model.inputs.each do |input|
+        words << " #{input.name} #{input.description}"
+      end
+
+      @model.outputs.each do |output|
+        words << " #{output.name}"
+      end
+
+      words.rewind
+      words.read
+    end
+  end
+
+  module GalaxyLib
+
+    class Workflow 
+
+      # Inputs to the workflow.
+      attr_accessor :inputs
+
+      # Outputs of the workflow.
+      attr_accessor :outputs
+
+      # The steps of the workflow.
+      attr_accessor :steps
+
+      # The connections of the workflow.
+      attr_accessor :connections
+
+      def self.parse(stream)
+
+        begin
+
+          doc = LibXML::XML::Parser.string("<?xml version='1.0' encoding='UTF-8'?><content>#{stream}</content>").parse
+
+          workflow = GalaxyLib::Workflow.new
+
+          workflow.inputs      = []
+          workflow.outputs     = []
+          workflow.steps       = []
+          workflow.connections = []
+
+          # Parse the context of the workflow.
+
+          doc.find("/content/steps/step/inputs/input").each do |input_element|
+
+            input = GalaxyLib::Input.new
+
+            input.step_id     = input_element.find("../../id/text()")[0].to_s
+            input.name        = input_element.find("name/text()")[0].to_s
+            input.description = CGI.unescapeHTML(input_element.find("description/text()")[0].to_s)
+
+            workflow.inputs << input
+          end
+
+          doc.find("/content/steps/step/outputs/output").each do |output_element|
+
+            output = GalaxyLib::Output.new
+
+            output.step_id = output_element.find("../../id/text()")[0].to_s
+            output.name    = output_element.find("name/text()")[0].to_s
+            output.type    = output_element.find("type/text()")[0].to_s
+
+            workflow.outputs << output
+          end
+
+          doc.find("/content/steps/step").each do |step_element|
+
+            step = GalaxyLib::Step.new
+
+            step.id          = step_element.find("id/text()")[0].to_s
+            step.name        = step_element.find("name/text()")[0].to_s
+            step.tool        = step_element.find("tool/text()")[0].to_s
+            step.description = CGI.unescapeHTML(step_element.find("description/text()")[0].to_s)
+
+            workflow.steps << step
+          end
+
+          doc.find("/content/connections/connection").each do |conn_element|
+
+            conn = GalaxyLib::Connection.new
+
+            conn.source_id     = conn_element.find("source_id/text()")[0].to_s
+            conn.source_output = conn_element.find("source_output/text()")[0].to_s
+            conn.sink_id       = conn_element.find("sink_id/text()")[0].to_s
+            conn.sink_input    = conn_element.find("sink_input/text()")[0].to_s
+
+            workflow.connections << conn
+          end
+
+          workflow
+        rescue
+          puts $!
+          nil
+        end
+      end
+
+      def get_components
+
+        components = XML::Node.new("components")
+
+        input_els      = XML::Node.new("inputs")
+        output_els     = XML::Node.new("outputs")
+        step_els       = XML::Node.new("steps")
+        connection_els = XML::Node.new("connections")
+
+        inputs.each do |input|
+          input_els << input.get_components
+        end
+
+        outputs.each do |output|
+          output_els << output.get_components
+        end
+
+        steps.each do |step|
+          step_els << step.get_components
+        end
+          
+        connections.each do |connection|
+          connection_els << connection.get_components
+        end
+          
+        components << input_els
+        components << output_els
+        components << step_els
+        components << connection_els
+      end
+    end
+
+    class Input
+
+      attr_accessor :step_id
+      attr_accessor :name
+      attr_accessor :description
+
+      def get_components
+
+        components = XML::Node.new("input")
+
+        components << (XML::Node.new("step-id") << step_id)
+        components << (XML::Node.new("name") << name)
+        components << (XML::Node.new("description") << description)
+
+        components
+      end
+
+    end
+
+    class Output
+
+      attr_accessor :step_id
+      attr_accessor :name
+      attr_accessor :type
+
+      def get_components
+
+        components = XML::Node.new("output")
+
+        components << (XML::Node.new("step-id") << step_id)
+        components << (XML::Node.new("name") << name)
+        components << (XML::Node.new("type") << type)
+
+        components
+      end
+
+    end
+
+    class Step
+   
+      attr_accessor :id   
+      attr_accessor :name
+      attr_accessor :tool
+      attr_accessor :description
+
+      def get_components
+
+        components = XML::Node.new("step")
+
+        components << (XML::Node.new("id") << id)
+        components << (XML::Node.new("name") << name)
+        components << (XML::Node.new("tool") << tool)
+        components << (XML::Node.new("description") << description)
+
+        components
+      end
+    end
+
+    class Connection
+
+      attr_accessor :source_id
+      attr_accessor :source_output
+      attr_accessor :sink_id
+      attr_accessor :sink_input
+
+      def get_components
+
+        components = XML::Node.new("connection")
+
+        components << (XML::Node.new("source-id")     << source_id)
+        components << (XML::Node.new("source-output") << source_output)
+        components << (XML::Node.new("sink-id")       << sink_id)
+        components << (XML::Node.new("sink-input")    << sink_input)
+
+        components
+      end
+    end
+  end
+end
+

Modified: branches/biocat/lib/workflow_processors/interface.rb (2605 => 2606)


--- branches/biocat/lib/workflow_processors/interface.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/lib/workflow_processors/interface.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -53,6 +53,10 @@
       false
     end
     
+    def self.show_download_section?
+      true
+    end
+
     # End Class Methods
 
 

Modified: branches/biocat/lib/workflow_processors/rapid_miner.rb (2605 => 2606)


--- branches/biocat/lib/workflow_processors/rapid_miner.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/lib/workflow_processors/rapid_miner.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -314,6 +314,24 @@
         components << input_els
         components << output_els
       end
+
+      def all_operators
+        
+        def aux(process, result)
+          process.operators.each do |operator|
+
+            result << operator
+
+            operator.processes.each do |sub_process|
+              aux(sub_process, result)
+            end
+          end
+
+          result
+        end
+
+        aux(process, [])
+      end
     end
   end
 end

Copied: branches/biocat/public/images/html_icon.png (from rev 2605, trunk/public/images/html_icon.png)


(Binary files differ)

Copied: branches/biocat/public/images/rdf_icon.png (from rev 2605, trunk/public/images/rdf_icon.png)


(Binary files differ)

Modified: branches/biocat/public/images/searchfade.png


(Binary files differ)

Copied: branches/biocat/public/images/xml_icon.png (from rev 2605, trunk/public/images/xml_icon.png)


(Binary files differ)

Modified: branches/biocat/public/index.html (2605 => 2606)


--- branches/biocat/public/index.html	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/public/index.html	2011-06-15 15:27:14 UTC (rev 2606)
@@ -43,7 +43,7 @@
 					<!-- Quick Start -->
 					<div class="box" style="margin-right: 20px; width: 320px;">
 						<b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
-						<div class="content" style="height: 34em;">
+						<div class="content" style="height: 29em;">
 							<div class="links">
 								<p class="heading" style="margin-left: 1em; margin-bottom: 1em; text-align: left;">
 									First time visitor? Try these videos:
@@ -67,10 +67,6 @@
 										<b>Find <a href=""
 									</li>
 									<li>
-										<img src="" alt="Find Files"/>
-										<b>Find <a href=""
-									</li>
-									<li>
 										<img src="" alt="Share Your Workflows and Files"/>
 										<b>Share Your <a href="" and <a href=""
 									</li>
@@ -79,20 +75,16 @@
 										<b><a href="" and Find <a href="" of Items</b>
 									</li>
 									<li>
-										<img src="" alt="Create and Join Groups"/>
-										<b><a href="" and Join <a href=""
-									</li>
-									<li>
 										<img src="" alt="Find People and Make Friends"/>
 										<b>Find <a href="" and Make Friends</b>
 									</li>
 									<li>
-										<img src="" alt="Send Messages"/>
-										<b>Send <a href=""
+										<img src="" alt="Create and Join Groups"/>
+										<b><a href="" and Join <a href=""
 									</li>
 									<li>
-										<img src="" alt="Get Feedback"/>
-										<b>Get Feedback</b>
+										<img src="" alt="Build your Profile and Reputation"/>
+										<b>Build your Profile and Reputation</b>
 									</li>
 									<li>
 										<img src="" alt="Tag and Rate things"/>
@@ -102,10 +94,6 @@
 										<img src="" alt="Write Reviews and Comments"/>
 										<b>Write Reviews and Comments</b>
 									</li>
-									<li>
-										<img src="" alt="Build your Profile and Reputation"/>
-										<b>Build your Profile and Reputation</b>
-									</li>
 								</ul>
 							</div>
 						</div>
@@ -115,7 +103,7 @@
 					<!-- Quick Links -->
 					<div class="box" style="margin-right: 20px; width: 280px;">
 						<b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
-						<div class="content" style="height: 34em; text-align: center;">
+						<div class="content" style="height: 29em; text-align: center;">
 							
 							<center>
 								<a href="" "Explore button" title="Explore myExperiment" class="letmein_button">
@@ -127,14 +115,12 @@
 							</center>
 							
 							<div class="links" style="font-size: 93%; border-top: 1px dotted #999999; margin-top: 1em;">
-								<p style="margin-top: 1em; font-size:150%"><a href="" target="_blank" style="font-weight: bolder;">About myExperiment</a></p>
+								<p style="margin-top: 1em; font-size:120%"><a href="" target="_blank" style="font-weight: bolder;">About myExperiment</a></p>
 								<p><a href="" target="_blank">Join the Mailing List</a></p>
-								<p><a href="" us Feedback</a></p>
+								<p><a href="" target="_blank">myExperiment Publications</a></p>
 								<p><a href="" target="_blank">For Developers</a></p>
-								<p><a href="" target="_blank">The myGrid Project</a></p>
-								<p><a href="" target="_blank">Taverna Workflow Workbench</a></p>
+								<p><a href="" us Feedback</a></p>
 								<p><a href="" target="_blank">The BioCatalogue Project</a></p>
-								<p><a href="" target="_blank">myExperiment Publications</a></p>
 							</div>
 						</div>
 						<b class="xbottom"><b class="xb5"></b><b class="xb6"></b><b class="xb7"></b><b class="xb1"></b></b>
@@ -143,7 +129,7 @@
 					<!-- Register / Login -->
 					<div class="box" style="width: 230px;">
 						<b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
-						<div class="content" style="height: 34em;">
+						<div class="content" style="height: 29em;">
 								<center>
 									<a href="" alt="Register button" title="Register for an account on myExperiment" class="register_button">
 									</a>
@@ -206,13 +192,13 @@
 					<br class="clearer"/>&nbsp;<br/>
 					
 					<div class="stats">
-						myExperiment has over 3000 users, 200 groups, 1000 workflows, 300 files and 90 packs
+						myExperiment has over 3000 members, 200 groups, 1000 workflows, 300 files and 100 packs
 					</div>
 					
 					<!-- About -->
 					<div class="box" style="margin-right: 20px; width: 320px;">
 						<b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
-						<div class="content" style="height: 32em; padding: 0.3em 1.5em;">
+						<div class="content" style="height: 22em; padding: 0.3em 1.5em;">
 							<p class="heading">
 								About
 							</p>
@@ -227,13 +213,6 @@
                 the ability to upload and share workflows, you will need to <a href="" up</b></a>.
               </p>
               <p>
-                The software that powers myexperiment.org is downloadable so that you can run your
-                own myExperiment instance. For more information visit our 
-                <a href="" target="_blank">developer pages</a>. 
-                The <a href="" target="_blank">source code</a>  is maintained on 
-                RubyForge and is available under the <b>BSD licence</b>.
-              </p>
-              <p>
                 See the <a href="" Wiki</b></a>
                 for
                 further information about myExperiment and how to join the user and developer communities.
@@ -245,28 +224,25 @@
 					<!-- Who -->						
 					<div class="box" style="margin-right: 20px; width: 320px;">
 						<b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
-						<div class="content" style="height: 32em; padding: 0.3em 1.5em;">
+						<div class="content" style="height: 22em; padding: 0.3em 1.5em;">
 							<p class="heading">
 								Who?
 							</p>
-							<p>
-                myExperiment is brought to you by a joint team from the universities of 
-								<a href="" target="_blank">Southampton</a> and 
-								<a href="" target="_blank">Manchester</a> in the UK, 
-								led by <a href="" target="_blank">David De Roure</a> 
-								and <a href="" target="_blank">Carole Goble</a>, and is funded by 
-								<a href="" target="_blank">JISC</a> under the 
-								Virtual Research Environments <a href="" target="_blank">programme</a> 
-								and by Microsoft's <a href="" target="_blank">Technical Computing Initiative</a>.
+
+              <p>
+                myExperiment is brought to you by a joint team from the
+                universities of
+                <a href="" target="_blank">Southampton</a>,
+                <a href="" target="_blank">Manchester</a> and
+                <a href="" target="_blank">Oxford</a> in the UK, led by
+                <a href="" target="_blank">David De Roure</a> and
+                <a href="" target="_blank">Carole Goble</a>. Funded by
+                <a href="" target="_blank">JISC</a> and the Microsoft
+                <a href="" target="_blank">Technical Computing Initiative</a>, the project is part of the
+                <a href="" target="_blank">myGrid</a> consortium (which develops the
+                <a href="" target="_blank">Taverna Workflow Workbench</a> for creating and executing scientific workflows) and
+                <a href="" target="_blank"><span style="white-space: nowrap">e-Research South</span></a>.
               </p>
-							<p>
-								myExperiment is part of the <a href="" target="_blank">myGrid</a> consortium, 
-								which develops the <a href="" target="_blank">Taverna Workflow Workbench</a> 
-								for creating and executing scientific workflows, 
-								and also builds on <a href="" target="_blank">CombeChem</a> - two of the original UK e-Science Pilot Projects. 
-								The related WHIP (<a href="" target="_blank">Triana</a> enactment) 
-								activity in Cardiff is supported by the <a href="" target="_blank">OMII-UK</a> Commissioned Software Programme.
-							</p>
 						</div>
 						<b class="xbottom"><b class="xb5"></b><b class="xb6"></b><b class="xb7"></b><b class="xb1"></b></b>
 					</div>
@@ -274,7 +250,7 @@
 					<!-- Latest Announcements -->
 					<div class="box" style="width: 190px;">
 						<b class="xtop"><b class="xb1"></b><b class="xb2"></b><b class="xb3"></b><b class="xb4"></b></b>
-						<div class="content" style="height: 32em; padding: 0.4em 0.8em;">
+						<div class="content" style="height: 22em; padding: 0.3em 1.5em; overflow: hidden">
 							<p class="heading">
 								<a href="" style="margin-right: 0.3em;">
 									<img src="" alt="Subscribe to site announcements icon" title="Subscribe to site announcements RSS feed" />
@@ -300,7 +276,7 @@
       </div>
 			<div id="ft">
         <p class="copyright">
-					Copyright (c) 2007 - 2008
+					Copyright &#169; 2007 - 2010
 					<a href="" target="_blank">The University of Manchester</a>
 					and
 					<a href="" target="_blank">University of Southampton</a>
@@ -320,7 +296,7 @@
 
         xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('MSXML2.XMLHTTP.3.0');
 
-        xhr.open('GET', '/announcements.xml?num=6&order=reverse', true);
+        xhr.open('GET', '/announcements.xml?num=4&order=reverse', true);
         xhr.setRequestHeader('Accept', 'application/xml');
 
         xhr. {

Copied: branches/biocat/public/_javascript_s/ellipsis.js (from rev 2605, trunk/public/_javascript_s/ellipsis.js) (0 => 2606)


--- branches/biocat/public/_javascript_s/ellipsis.js	                        (rev 0)
+++ branches/biocat/public/_javascript_s/ellipsis.js	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,35 @@
+// ellipsis.js
+
+function parentEl(el) {
+  return el.parentElement ? el.parentElement : el.parentNode;
+}
+
+function truncate_span(span) {
+
+  var targetWidth = parentEl(span).offsetWidth;
+
+  if (span.offsetWidth <= targetWidth)
+    return;
+
+  var text = span.innerHTML;
+  var pos  = text.length;
+
+  while ((span.offsetWidth > targetWidth) && (pos > 0)) {
+    pos--;
+    span.innerHTML = text.substring(0, pos) + "&hellip; "
+  }
+}
+
+function truncate_spans() {
+
+  var spans = document.getElementsByTagName('SPAN');
+
+  for (var i = 0; i < spans.length; i++) {
+    var span = spans[i];
+
+    if (span.className == 'truncate') {
+      truncate_span(span);
+    }
+  }
+}
+

Modified: branches/biocat/public/_javascript_s/tag_suggestions.js (2605 => 2606)


--- branches/biocat/public/_javascript_s/tag_suggestions.js	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/public/_javascript_s/tag_suggestions.js	2011-06-15 15:27:14 UTC (rev 2606)
@@ -43,7 +43,6 @@
 
   separator  = ' <span style="color: #999999;">|</span> ';
   markup     = "";
-  submitText = "";
   summary    = "";
 
   if (suggestions.length == 0) {
@@ -72,16 +71,13 @@
   }
 
   if (tagsToAdd.length == 0) {
-    submitText = 'Skip this step';
     summary = "<p>You have no tags to add to this workflow.</p>";
   } else {
-    submitText = 'Tag';
     summary = "<p>You are about tag this workflow with: " + commaList(tagsToAdd.sort()) + ".";
   }
 
   document.getElementById("suggestions").innerHTML  = markup;
   document.getElementById("tag_list").value         = tagsToAdd.join(", ");
-  document.getElementById("submit-button").value    = submitText;
   document.getElementById("summary-text").innerHTML = summary;
 }
 

Modified: branches/biocat/public/stylesheets/front.css (2605 => 2606)


--- branches/biocat/public/stylesheets/front.css	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/public/stylesheets/front.css	2011-06-15 15:27:14 UTC (rev 2606)
@@ -21,7 +21,7 @@
 
 .frontpage_header .text {
 	float: right;
-	width: 28em;
+	width: 30em;
 	text-align: center;
 	font-size: 131%;
 	color: #333333;
@@ -330,4 +330,4 @@
 
 a:hover.register_button {
 	background-position: -200px 0;
-}
\ No newline at end of file
+}

Modified: branches/biocat/public/stylesheets/gadgets.css (2605 => 2606)


--- branches/biocat/public/stylesheets/gadgets.css	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/public/stylesheets/gadgets.css	2011-06-15 15:27:14 UTC (rev 2606)
@@ -28,8 +28,12 @@
 
 .myexperiment_gadget .header {
 	border: #CCCCCC 0px solid;
-	border-width: 0 1px 1px 1px;
+	border-width: 1px 1px 1px 1px;
 	border-bottom-color: #CCCCCC;
+  border-radius: 8px 8px 0px 0px;
+  -moz-border-radius: 8px 8px 0px 0px;
+  -webkit-border-top-left-radius: 8px;
+  -webkit-border-top-right-radius: 8px;
 	background-image: url('/images/gadget-hd-bg.png');
 	background-position: top;
 	background-repeat: repeat-x;
@@ -53,8 +57,12 @@
 .myexperiment_gadget .contents {
 	border-top: none;
 	border-right: #CCCCCC 1px solid;
-	border-bottom: none;
+	border-bottom: #CCCCCC 1px solid;
 	border-left: #CCCCCC 1px solid;
+  border-radius: 0px 0px 8px 8px;
+  -moz-border-radius: 0px 0px 8px 8px;
+  -webkit-border-bottom-left-radius: 8px;
+  -webkit-border-bottom-right-radius: 8px;
 	color: #000000;
 	padding: 0.4em;
 	margin-top: 0;
@@ -228,4 +236,4 @@
 	font-weight: normal;
 }
 
-/* end css Asset Manager Gadget styles */
\ No newline at end of file
+/* end css Asset Manager Gadget styles */

Modified: branches/biocat/public/stylesheets/styles.css (2605 => 2606)


--- branches/biocat/public/stylesheets/styles.css	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/public/stylesheets/styles.css	2011-06-15 15:27:14 UTC (rev 2606)
@@ -152,6 +152,10 @@
 	background-repeat: no-repeat;
 	background-position: top center;
 	padding: 10px 0;
+  -moz-border-radius: 22px 22px 0px 0px;
+  -webkit-border-top-left-radius: 6px;
+  -webkit-border-top-right-radius: 6px;
+  border-radius: 22px 22px 0px 0px;
 }
 
 #myexp_searchbar * {
@@ -825,7 +829,6 @@
 }
 
 table.alt_table {
-	width: 100%;
 	border-collapse: collapse;
 }
 
@@ -852,7 +855,8 @@
 }
 
 table.alt_table .mid {
-	text-align: left :
+	text-align: left;
+  width: 390px;
 }
 
 table.alt_table tr td.actions { /*width: 135px;*/
@@ -883,18 +887,21 @@
 	text-align: left;
 	line-height: 1.3;
 	overflow: hidden;
+  word-wrap: break-word;
+  width: 360px;
 }
 
 table.alt_table .standout {
 	border: 1px dotted #999999;
 	margin: 0.2em 0;
-	padding: 0.5em 0.6em;
+	padding: 0.4em 0.6em;
 	background-color: #FFFFCC;
 	text-align: left;
 	line-height: 1;
 	font-size: 93%;
 	font-weight: bold;
 	color: #333333;
+	width: 360px;
 }
 
 table.alt_table tr td.contributable {
@@ -1946,6 +1953,15 @@
 	vertical-align: middle;
 }
 
+.tag_suggestion_box {
+	padding: 0.5em 0.8em;
+	border: 1px dotted #999999;
+  font-size: 93%;
+  text-align: left;
+  margin: 2em;
+  line-height: 20px;
+}
+
 .unselected_tag_suggestion {
   border: 1px SOLID white;
   padding-left: 4px;
@@ -2019,3 +2035,201 @@
   padding-top: 1em;
 }
 
+/* pivot */
+
+.pivot {
+  width: 737px;
+  margin: 0;
+}
+
+.pivot .left {
+  width: 150px;
+  float: left;
+  padding-right: 10px;
+}
+
+.pivot .left > DIV {
+  margin-bottom: 0.5em;
+}
+
+.pivot .main {
+  margin-left: 160px;
+}
+
+.pivot .main > DIV {
+  margin-bottom: 0.5em;
+}
+
+.pivot .summary {
+  clear: right;
+  background: #f0f0f0;
+  border: 1px solid #d8d8d8;
+  padding: 6px;
+}
+
+.pivot .summary DIV+DIV {
+  padding-top: 0.4em;
+}
+
+.pivot .sort {
+  float: right;
+}
+
+.pivot .filter {
+  margin-bottom: 1em;
+  padding: 2px;
+  background: #f0f0f0;
+  border-radius: 6px;
+  -moz-border-radius: 6px;
+}
+
+.pivot .category {
+  padding: 0.2em;
+  font-size: 110%;
+  margin-bottom: 0.2em;
+}
+
+.pivot .toggle_filter_query {
+  float: right;
+  vertical-align: middle;
+  position: relative;
+  top: 2px;
+}
+
+.pivot .options > DIV {
+  border: 1px solid transparent;
+  padding: 0.2em;
+  font-size: 90%;
+  padding-left: 0.2em;
+}
+
+.pivot .options > DIV:hover {
+  background: #d0d0f0;
+}
+
+.pivot .options > DIV.selected {
+  background: #ffe0c0;
+}
+
+.pivot .options > DIV.selected:hover {
+  background: #dfc0a0;
+}
+
+.pivot .options > DIV:first-child {
+  border-top-left-radius: 6px;
+  border-top-right-radius: 6px;
+  -moz-border-radius-topleft: 6px;
+  -moz-border-radius-topright: 6px;
+}
+
+.pivot .options > DIV:last-child {
+  border-bottom-left-radius: 6px;
+  border-bottom-right-radius: 6px;
+  -moz-border-radius-bottomleft: 6px;
+  -moz-border-radius-bottomright: 6px;
+}
+
+.pivot .checkbox {
+  display: inline;
+  padding-top: 0;
+  padding-bottom: 0;
+}
+
+.pivot .label {
+  width: 92px;
+  overflow: hidden;
+  display: inline-block;
+}
+
+.pivot .count {
+  float: right;
+}
+
+.pivot .crumbs {
+  margin-top: 0.5em;
+}
+
+.pivot .filter-in-use {
+  background: #d8d8d8;
+  padding: 2px;
+	line-height: 200%;
+}
+
+.pivot .filter-in-use A {
+  padding-left: 0px;
+  padding-right: 4px;
+  text-decoration: none; /* no underline */
+}
+
+.pivot .filter-in-use A IMG {
+  vertical-align: middle;
+  position: relative;
+  top: -2px;
+}
+
+.pivot .filter-in-use:hover {
+/*  background: #f0d0d0; */
+}
+
+.pivot .pagination {
+	padding: 0px;
+	margin: 0px;
+	text-align: left;
+}
+
+.pivot .filter_search_box {
+  border: 1px solid gray;
+}
+
+.pivot .filter_search_box INPUT.query {
+  width: 123px;
+  padding: 2px;
+  border: none;
+  outline: none;
+}
+
+.pivot .filter_search_box INPUT.submit {
+  vertical-align: middle;
+  position: relative;
+  top: -1px;
+}
+
+.pivot .filter_search_box IMG {
+  vertical-align: middle;
+  position: relative;
+  top: -1px;
+}
+
+.truncate {
+  white-space: nowrap;
+}
+
+.no-filter-query-results {
+  padding-top: 1em;
+	font-style: italic;
+}
+
+.pivot .no-results {
+  padding-top: 2em;
+  padding-bottom: 2em;
+	font-style: italic;
+}
+
+.pivot .search_box .query {
+  padding: 2px;
+  width: 450px;
+}
+
+.edit_relationships {
+  margin: 20px;
+}
+
+.edit_relationships > * {
+  display: block;
+  margin-bottom: 10px;
+}
+
+.relationship_sentences * {
+  vertical-align: middle;
+}
+

Modified: branches/biocat/test/fixtures/licenses.yml (2605 => 2606)


--- branches/biocat/test/fixtures/licenses.yml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/test/fixtures/licenses.yml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -9,3 +9,10 @@
   id: 2
   title: License 2
   description: This is the second license.
+
+license_3:
+  id: 3
+  title: by-sa
+  description: This is the by-sa license.
+  unique_name: by-sa
+

Deleted: branches/biocat/test/fixtures/relationships.yml (2605 => 2606)


--- branches/biocat/test/fixtures/relationships.yml	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/test/fixtures/relationships.yml	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,13 +0,0 @@
-# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
-one:
-  id: 1
-  network_id: 1
-  relation_id: 1
-  created_at: 2007-07-30 14:09:26
-  accepted_at: 2007-07-30 14:09:26
-two:
-  id: 2
-  network_id: 1
-  relation_id: 1
-  created_at: 2007-07-30 14:09:26
-  accepted_at: 2007-07-30 14:09:26

Copied: branches/biocat/test/functional/api_controller_test.rb (from rev 2605, trunk/test/functional/api_controller_test.rb) (0 => 2606)


--- branches/biocat/test/functional/api_controller_test.rb	                        (rev 0)
+++ branches/biocat/test/functional/api_controller_test.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -0,0 +1,739 @@
+require File.dirname(__FILE__) + '/../test_helper'
+require 'api_controller'
+require 'xml/libxml'
+require 'lib/rest'
+
+# Re-raise errors caught by the controller.
+class ApiController; def rescue_action(e) raise e end; end
+
+class ApiControllerTest < Test::Unit::TestCase
+
+  fixtures :workflows, :users, :content_types, :licenses
+
+  def setup
+    @controller = ApiController.new
+    @request    = TestRequestWithQuery.new
+    @response   = ActionController::TestResponse.new
+  end
+
+  def test_workflows
+
+    existing_workflows = Workflow.find(:all)
+
+    login_as(:john)
+
+    title        = "Unique tags"
+    title2       = "Unique tags again"
+    license_type = "by-sa"
+    content_type = "application/vnd.taverna.scufl+xml"
+    description  = "A workflow description."
+
+    content = Base64.encode64(File.read('test/fixtures/files/workflow_dilbert.xml'))
+
+    # post a workflow
+
+    rest_request(:post, 'workflow', "<?xml version='1.0'?>
+      <workflow>
+        <title>#{title}</title>
+        <description>#{description}</description>
+        <license-type>#{license_type}</license-type>
+        <content-type>#{content_type}</content-type>
+        <content>#{content}</content>
+      </workflow>")
+
+    assert_response(:success)
+
+    extra_workflows = Workflow.find(:all) - existing_workflows
+
+    assert_equal(extra_workflows.length, 1)
+
+    @workflow_id = extra_workflows.first.id
+
+    # get the workflow
+
+    response = rest_request(:get, 'workflow', nil, "id" => @workflow_id,
+        "elements" => "title,description,license-type,content-type,content")
+
+    assert_response(:success)
+  
+    assert_equal(title,        response.find_first('/workflow/title').inner_xml)
+    assert_equal(description,  response.find_first('/workflow/description').inner_xml)
+    assert_equal(license_type, response.find_first('/workflow/license-type').inner_xml)
+    assert_equal(content_type, response.find_first('/workflow/content-type').inner_xml)
+    assert_equal(content,      response.find_first('/workflow/content').inner_xml)
+
+    # it's private default, so make sure that another user can't get the
+    # workflow
+
+    setup
+    login_as(:jane)
+
+    rest_request(:get, 'workflow', nil, "id" => @workflow_id)
+
+    assert_response(:unauthorized)
+     
+    # update the workflow
+
+    setup
+    login_as(:john)
+
+    rest_request(:put, 'workflow', "<?xml version='1.0'?>
+      <workflow>
+        <title>#{title2}</title>
+      </workflow>", "id" => @workflow_id)
+
+    assert_response(:success)
+
+    # get the updated workflow
+
+    response = rest_request(:get, 'workflow', nil, "id" => @workflow_id,
+        "elements" => "title,description")
+
+    assert_response(:success)
+  
+    assert_equal(title2,      response.find_first('/workflow/title').inner_xml)
+    assert_equal(description, response.find_first('/workflow/description').inner_xml)
+
+    # delete the workflow
+
+    rest_request(:delete, 'workflow', nil, "id" => @workflow_id)
+
+    assert_response(:success)
+
+    # try to get the deleted workflow
+
+    rest_request(:get, 'workflow', nil, "id" => @workflow_id)
+
+    assert_response(:not_found)
+  end
+
+  def test_files
+
+    existing_files = Blob.find(:all)
+
+    login_as(:john)
+
+    title        = "Test file title"
+    title2       = "Updated test file title"
+    license_type = "by-sa"
+    content_type = "text/plain"
+    description  = "A description of the test file."
+
+    content = Base64.encode64("This is the content of this test file.")
+
+    # post a file
+
+    rest_request(:post, 'file', "<?xml version='1.0'?>
+      <file>
+        <title>#{title}</title>
+        <description>#{description}</description>
+        <license-type>#{license_type}</license-type>
+        <content-type>#{content_type}</content-type>
+        <content>#{content}</content>
+      </file>")
+
+    assert_response(:success)
+
+    extra_files = Blob.find(:all) - existing_files
+
+    assert_equal(extra_files.length, 1)
+
+    file = extra_files.first
+
+    # get the file
+
+    response = rest_request(:get, 'file', nil, "id" => file.id,
+        "elements" => "title,description,license-type,content-type,content")
+
+    assert_response(:success)
+
+    assert_equal(title,        response.find_first('/file/title').inner_xml)
+    assert_equal(description,  response.find_first('/file/description').inner_xml)
+    assert_equal(license_type, response.find_first('/file/license-type').inner_xml)
+    assert_equal(content_type, response.find_first('/file/content-type').inner_xml)
+    assert_equal(content,      response.find_first('/file/content').inner_xml)
+
+    # it's private default, so make sure that another user can't get the
+    # file
+
+    setup
+    login_as(:jane)
+
+    rest_request(:get, 'file', nil, "id" => file.id)
+
+    assert_response(:unauthorized)
+     
+    # update the file
+
+    setup
+    login_as(:john)
+
+    rest_request(:put, 'file', "<?xml version='1.0'?>
+      <file>
+        <title>#{title2}</title>
+      </file>", "id" => file.id)
+
+    assert_response(:success)
+
+    # get the updated file
+
+    response = rest_request(:get, 'file', nil, "id" => file.id,
+        "elements" => "title,description")
+
+    assert_response(:success)
+  
+    assert_equal(title2,      response.find_first('/file/title').inner_xml)
+    assert_equal(description, response.find_first('/file/description').inner_xml)
+
+    # delete the file
+
+    rest_request(:delete, 'file', nil, "id" => file.id)
+
+    assert_response(:success)
+
+    # try to get the deleted file
+
+    rest_request(:get, 'file', nil, "id" => file.id)
+
+    assert_response(:not_found)
+  end
+
+  def test_packs
+
+    existing_packs = Pack.find(:all)
+
+    login_as(:john)
+
+    title        = "A pack"
+    title2       = "An updated pack"
+    description  = "A pack description."
+
+    # post a pack
+
+    rest_request(:post, 'pack', "<?xml version='1.0'?>
+      <pack>
+        <title>#{title}</title>
+        <description>#{description}</description>
+        <permissions>
+          <permission>
+            <category>public</category>
+            <privilege type='view'/>
+            <privilege type='download'/>
+          </permission>
+        </permissions>
+      </pack>")
+    
+    assert_response(:success)
+
+    extra_packs = Pack.find(:all) - existing_packs
+
+    assert_equal(extra_packs.length, 1)
+
+    @pack_id = extra_packs.first.id
+
+    # get the pack
+
+    response = rest_request(:get, 'pack', nil, "id" => @pack_id,
+        "elements" => "title,description")
+
+    assert_response(:success)
+  
+    assert_equal(title,        response.find_first('/pack/title').inner_xml)
+    assert_equal(description,  response.find_first('/pack/description').inner_xml)
+
+    # make sure that another user can get the pack, since it's supposed to be
+    # public
+
+    setup;
+    login_as(:jane)
+
+    rest_request(:get, 'pack', nil, "id" => @pack_id)
+
+    assert_response(:success)
+     
+    # update the pack
+
+    setup
+    login_as(:john)
+
+    rest_request(:put, 'pack', "<?xml version='1.0'?>
+      <pack>
+        <title>#{title2}</title>
+      </pack>", "id" => @pack_id)
+
+    assert_response(:success)
+
+    # get the updated pack
+
+    response = rest_request(:get, 'pack', nil, "id" => @pack_id,
+        "elements" => "title,description")
+
+    assert_response(:success)
+  
+    assert_equal(title2,      response.find_first('/pack/title').inner_xml)
+    assert_equal(description, response.find_first('/pack/description').inner_xml)
+
+    # add an internal pack item
+
+    existing_internal_pack_items = PackContributableEntry.find(:all)
+
+    pack_uri     = rest_resource_uri(Pack.find(@pack_id))
+    workflow_uri = rest_resource_uri(Workflow.find(1))
+    comment1     = "It's an internal pack item."
+    comment2     = "It's an updated internal pack item."
+
+    rest_request(:post, 'internal-pack-item', "<?xml version='1.0'?>
+      <internal-pack-item>
+        <pack resource='#{pack_uri}'/>
+        <item resource='#{workflow_uri}'/>
+        <comment>#{comment1}</comment>
+      </internal-pack-item>")
+
+    assert_response(:success)
+
+    extra_internal_pack_items = PackContributableEntry.find(:all) - existing_internal_pack_items
+
+    assert_equal(extra_internal_pack_items.length, 1)
+
+    @internal_pack_item_id = extra_internal_pack_items.first.id
+    
+    # get the internal pack item
+
+    response = rest_request(:get, 'internal-pack-item', nil, "id" => @internal_pack_item_id)
+
+    assert_response(:success)
+
+    assert_equal(pack_uri,     response.find_first('/internal-pack-item/pack/@resource').value)
+    assert_equal(workflow_uri, response.find_first('/internal-pack-item/item/*/@resource').value)
+    assert_equal(comment1,     response.find_first('/internal-pack-item/comment').inner_xml)
+
+    # update the internal pack item
+
+    rest_request(:put, 'internal-pack-item', "<?xml version='1.0'?>
+      <internal-pack-item>
+        <comment>#{comment2}</comment>
+      </internal-pack-item>", "id" => @internal_pack_item_id)
+
+    assert_response(:success)
+
+    # get the updated internal pack item
+
+    response = rest_request(:get, 'internal-pack-item', nil, "id" => @internal_pack_item_id)
+
+    assert_response(:success)
+
+    assert_equal(comment2, response.find_first('/internal-pack-item/comment').inner_xml)
+
+    # delete the internal pack item
+
+    rest_request(:delete, 'internal-pack-item', nil, "id" => @internal_pack_item_id)
+
+    assert_response(:success)
+
+    # try to get the deleted internal pack item
+
+    response = rest_request(:get, 'internal-pack-item', nil, "id" => @internal_pack_item_id)
+
+    assert_response(:not_found)
+
+    # add an external pack item
+
+    existing_external_pack_items = PackRemoteEntry.find(:all)
+
+    external_uri  = "http://example.com/"
+    alternate_uri = "http://example.com/alternate"
+    comment3      = "It's an external pack item."
+    comment4      = "It's an updated external pack item."
+    title         = "Title for the external pack item."
+
+    rest_request(:post, 'external-pack-item', "<?xml version='1.0'?>
+      <external-pack-item>
+        <pack resource='#{pack_uri}'/>
+        <title>#{title}</title>
+        <uri>#{external_uri}</uri>
+        <alternate-uri>#{alternate_uri}</alternate-uri>
+        <comment>#{comment3}</comment>
+      </external-pack-item>")
+
+    assert_response(:success)
+
+    extra_external_pack_items = PackRemoteEntry.find(:all) - existing_external_pack_items
+
+    assert_equal(extra_external_pack_items.length, 1)
+
+    @external_pack_item_id = extra_external_pack_items.first.id
+    
+    # get the external pack item
+
+    response = rest_request(:get, 'external-pack-item', nil, "id" => @external_pack_item_id,
+      "elements" => "pack,title,uri,alternate-uri,comment")
+
+    assert_response(:success)
+
+    assert_equal(pack_uri,      response.find_first('/external-pack-item/pack/@resource').value)
+    assert_equal(external_uri,  response.find_first('/external-pack-item/uri').inner_xml)
+    assert_equal(alternate_uri, response.find_first('/external-pack-item/alternate-uri').inner_xml)
+    assert_equal(comment3,      response.find_first('/external-pack-item/comment').inner_xml)
+
+    # update the external pack item
+
+    rest_request(:put, 'external-pack-item', "<?xml version='1.0'?>
+      <external-pack-item>
+        <comment>#{comment4}</comment>
+      </external-pack-item>", "id" => @external_pack_item_id)
+
+    assert_response(:success)
+
+    # get the updated external pack item
+
+    response = rest_request(:get, 'external-pack-item', nil, "id" => @external_pack_item_id)
+
+    assert_response(:success)
+
+    assert_equal(comment4, response.find_first('/external-pack-item/comment').inner_xml)
+
+    # delete the external pack item
+
+    rest_request(:delete, 'external-pack-item', nil, "id" => @external_pack_item_id)
+
+    assert_response(:success)
+
+    # try to get the deleted external pack item
+
+    response = rest_request(:get, 'external-pack-item', nil, "id" => @external_pack_item_id)
+
+    assert_response(:not_found)
+
+    # delete the pack
+
+    rest_request(:delete, 'pack', nil, "id" => @pack_id)
+
+    assert_response(:success)
+
+    # try to get the deleted pack
+
+    rest_request(:get, 'pack', nil, "id" => @pack_id)
+
+    assert_response(:not_found)
+  end
+
+  def test_comments
+
+    login_as(:john)
+
+    # post a workflow to test with
+
+    content = Base64.encode64(File.read('test/fixtures/files/workflow_dilbert.xml'))
+
+    existing_workflows = Workflow.find(:all)
+
+    rest_request(:post, 'workflow', "<?xml version='1.0'?>
+      <workflow>
+        <title>Unique tags</title>
+        <description>A workflow description.</description>
+        <license-type>by-sa</license-type>
+        <content-type>application/vnd.taverna.scufl+xml</content-type>
+        <content>#{content}</content>
+      </workflow>")
+
+    assert_response(:success)
+
+    extra_workflows = Workflow.find(:all) - existing_workflows
+
+    assert_equal(extra_workflows.length, 1)
+
+    workflow = extra_workflows.first
+    workflow_url = rest_resource_uri(workflow)
+
+    # post a comment
+
+    comment_text  = "a test comment"
+    comment_text2 = "an updated test comment"
+
+    existing_comments = Comment.find(:all)
+
+    rest_request(:post, 'comment', "<?xml version='1.0'?>
+      <comment>
+        <comment>#{comment_text}</comment>
+        <subject resource='#{workflow_url}'/>
+      </comment>")
+
+    assert_response(:success)
+
+    extra_comments = Comment.find(:all) - existing_comments 
+    
+    assert_equal(extra_comments.length, 1)
+
+    comment = extra_comments.first
+
+    # update the comment (which should fail)
+
+    rest_request(:put, 'comment', "<?xml version='1.0'?>
+      <comment>
+        <comment>#{comment_text2}</comment>
+      </comment>", "id" => comment.id)
+
+    assert_response(:unauthorized)
+    
+    # delete the comment
+
+    rest_request(:delete, 'comment', nil, "id" => comment.id)
+
+    assert_response(:success)
+
+    # try to get the deleted comment
+
+    rest_request(:get, 'comment', nil, "id" => comment.id)
+
+    assert_response(:not_found)
+  end
+
+  def test_ratings
+
+    login_as(:john)
+
+    # post a workflow to test with
+
+    content = Base64.encode64(File.read('test/fixtures/files/workflow_dilbert.xml'))
+
+    existing_workflows = Workflow.find(:all)
+
+    rest_request(:post, 'workflow', "<?xml version='1.0'?>
+      <workflow>
+        <title>Unique tags</title>
+        <description>A workflow description.</description>
+        <license-type>by-sa</license-type>
+        <content-type>application/vnd.taverna.scufl+xml</content-type>
+        <content>#{content}</content>
+      </workflow>")
+
+    assert_response(:success)
+
+    extra_workflows = Workflow.find(:all) - existing_workflows
+
+    assert_equal(extra_workflows.length, 1)
+
+    workflow = extra_workflows.first
+    workflow_url = rest_resource_uri(workflow)
+
+    # post a rating
+
+    existing_ratings = Rating.find(:all)
+
+    rest_request(:post, 'rating', "<?xml version='1.0'?>
+      <rating>
+        <rating>4</rating>
+        <subject resource='#{workflow_url}'/>
+      </rating>")
+
+    assert_response(:success)
+
+    extra_ratings = Rating.find(:all) - existing_ratings 
+    
+    assert_equal(extra_ratings.length, 1)
+
+    rating = extra_ratings.first
+
+    assert_equal(rating.user, users(:john));
+    assert_equal(rating.rateable, workflow);
+    assert_equal(rating.rating, 4);
+
+    # update the rating (which should fail)
+
+    rest_request(:put, 'rating', "<?xml version='1.0'?>
+      <rating>
+        <rating>3</rating>
+      </rating>", "id" => rating.id)
+
+    assert_response(:success)
+    
+    rating.reload
+
+    assert_equal(rating.rating, 3);
+
+    # delete the rating
+
+    rest_request(:delete, 'rating', nil, "id" => rating.id)
+
+    assert_response(:success)
+
+    # try to get the deleted rating
+
+    rest_request(:get, 'rating', nil, "id" => rating.id)
+
+    assert_response(:not_found)
+  end
+
+  def test_favourites
+
+    login_as(:john)
+
+    # post a workflow to test with
+
+    content = Base64.encode64(File.read('test/fixtures/files/workflow_dilbert.xml'))
+
+    existing_workflows = Workflow.find(:all)
+
+    rest_request(:post, 'workflow', "<?xml version='1.0'?>
+      <workflow>
+        <title>Unique tags</title>
+        <description>A workflow description.</description>
+        <license-type>by-sa</license-type>
+        <content-type>application/vnd.taverna.scufl+xml</content-type>
+        <content>#{content}</content>
+      </workflow>")
+
+    assert_response(:success)
+
+    extra_workflows = Workflow.find(:all) - existing_workflows
+
+    assert_equal(extra_workflows.length, 1)
+
+    workflow = extra_workflows.first
+    workflow_url = rest_resource_uri(workflow)
+
+    # post a favourite
+
+    existing_favourites = Bookmark.find(:all)
+
+    rest_request(:post, 'favourite', "<?xml version='1.0'?>
+      <favourite>
+        <object resource='#{workflow_url}'/>
+      </favourite>")
+
+    assert_response(:success)
+
+    extra_favourites = Bookmark.find(:all) - existing_favourites 
+    
+    assert_equal(extra_favourites.length, 1)
+
+    favourite = extra_favourites.first
+
+    # delete the favourite
+
+    rest_request(:delete, 'favourite', nil, "id" => favourite.id)
+
+    assert_response(:success)
+
+    # try to get the deleted favourite
+
+    rest_request(:get, 'favourite', nil, "id" => favourite.id)
+
+    assert_response(:not_found)
+  end
+
+  def test_taggings
+
+    login_as(:john)
+
+    # post a workflow to test with
+
+    content = Base64.encode64(File.read('test/fixtures/files/workflow_dilbert.xml'))
+
+    existing_workflows = Workflow.find(:all)
+
+    rest_request(:post, 'workflow', "<?xml version='1.0'?>
+      <workflow>
+        <title>Unique tags</title>
+        <description>A workflow description.</description>
+        <license-type>by-sa</license-type>
+        <content-type>application/vnd.taverna.scufl+xml</content-type>
+        <content>#{content}</content>
+      </workflow>")
+
+    assert_response(:success)
+
+    extra_workflows = Workflow.find(:all) - existing_workflows
+
+    assert_equal(extra_workflows.length, 1)
+
+    workflow = extra_workflows.first
+    workflow_url = rest_resource_uri(workflow)
+
+    # post a tagging
+
+    existing_taggings = Tagging.find(:all)
+
+    rest_request(:post, 'tagging', "<?xml version='1.0'?>
+      <tagging>
+        <subject resource='#{workflow_url}'/>
+        <label>my test tag</label>
+      </tagging>")
+
+    assert_response(:success)
+
+    extra_taggings = Tagging.find(:all) - existing_taggings 
+    
+    assert_equal(extra_taggings.length, 1)
+
+    tagging = extra_taggings.first
+
+    assert_equal(tagging.user, users(:john));
+    assert_equal(tagging.taggable, workflow);
+    assert_equal(tagging.label, 'my test tag');
+
+    # update the tagging (which should fail)
+
+    rest_request(:put, 'tagging', "<?xml version='1.0'?>
+      <tagging>
+        <label>fail</label>
+      </tagging>", "id" => tagging.id)
+
+    assert_response(400)
+    
+    # delete the tagging
+
+    rest_request(:delete, 'tagging', nil, "id" => tagging.id)
+
+    assert_response(:success)
+
+    # try to get the deleted tagging
+
+    rest_request(:get, 'tagging', nil, "id" => tagging.id)
+
+    assert_response(:not_found)
+  end
+
+  private
+
+  def rest_request(method, uri, data = "" query = {})
+
+    @request.query_parameters!(query) if query
+
+    @request.env['RAW_POST_DATA'] = data if data
+
+    # puts "Sending: #{data.inspect}"
+
+    case method
+      when :get;    get(:process_request,     { :uri => uri } )
+      when :post;   post(:process_request,    { :uri => uri } )
+      when :put;    put(:process_request,     { :uri => uri } )
+      when :delete; delete(:process_request,  { :uri => uri } )
+    end
+
+    # puts "Response: #{LibXML::XML::Parser.string(@response.body).parse.root.to_s}"
+
+    LibXML::XML::Parser.string(@response.body).parse
+  end
+end
+
+# Custom version of the TestRequest, so that I can set the query parameters of
+# a request.
+
+class TestRequestWithQuery < ActionController::TestRequest
+
+  def query_parameters!(hash)
+    @custom_query_parameters = hash
+  end
+
+
+  def recycle!
+    super
+
+    if @custom_query_parameters
+      self.query_parameters = @custom_query_parameters
+      @custom_query_parameters = nil
+    end
+  end
+end
+

Modified: branches/biocat/test/functional/users_controller_test.rb (2605 => 2606)


--- branches/biocat/test/functional/users_controller_test.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/test/functional/users_controller_test.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -65,6 +65,6 @@
     delete :destroy, :id => 1
 
     assert_redirected_to :action ="" :index
-    assert_equal "Please contact the administrator to have your account removed.", flash[:notice]
+    assert_equal "You do not have permission to delete this user.", flash[:notice]
   end
 end

Deleted: branches/biocat/test/unit/relationship_test.rb (2605 => 2606)


--- branches/biocat/test/unit/relationship_test.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/test/unit/relationship_test.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,10 +0,0 @@
-require File.dirname(__FILE__) + '/../test_helper'
-
-class RelationshipTest < Test::Unit::TestCase
-  fixtures :relationships
-
-  # Replace this with your real tests.
-  def test_truth
-    assert true
-  end
-end

Modified: branches/biocat/vendor/plugins/acts_as_taggable_redux/lib/acts_as_taggable_helper.rb (2605 => 2606)


--- branches/biocat/vendor/plugins/acts_as_taggable_redux/lib/acts_as_taggable_helper.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/vendor/plugins/acts_as_taggable_redux/lib/acts_as_taggable_helper.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,7 +1,7 @@
 module ActsAsTaggableHelper
   # Create a link to the tag using restful routes and the rel-tag microformat
   def link_to_tag(tag)
-    link_to(tag.name, tag_url(tag), :rel => 'tag')
+    link_to(h(tag.name), tag_url(tag), :rel => 'tag')
   end
   
   # Generate a tag cloud of the top 100 tags by usage, uses the proposed hTagcloud microformat.
@@ -52,15 +52,15 @@
       
       if original
         unless link_to_type.blank?
-          html << link_to(tag.name, tag_url(tag) + "?type=#{link_to_type}", :class => classes[(tag.taggings_count - min) / divisor])
+          html << link_to(h(tag.name), tag_url(tag) + "?type=#{link_to_type}", :class => classes[(tag.taggings_count - min) / divisor])
         else
-          html << link_to(tag.name, tag_url(tag), :class => classes[(tag.taggings_count - min) / divisor])
+          html << link_to(h(tag.name), tag_url(tag), :class => classes[(tag.taggings_count - min) / divisor])
         end
       else
         unless link_to_type.blank?
-          html << "<a href='' class='#{classes[(tag.taggings_count - min) / divisor]}'>#{tag.name}</a>"
+          html << "<a href='' class='#{classes[(tag.taggings_count - min) / divisor]}'>#{h(tag.name)}</a>"
         else
-          html << "<a href='' class='#{classes[(tag.taggings_count - min) / divisor]}'>#{tag.name}</a>"
+          html << "<a href='' class='#{classes[(tag.taggings_count - min) / divisor]}'>#{h(tag.name)}</a>"
         end
       end
       
@@ -75,4 +75,4 @@
     html <<   %(  </ul>\n)
     html <<   %(</div>\n)
   end
-end
\ No newline at end of file
+end

Modified: branches/biocat/vendor/plugins/acts_as_taggable_redux/lib/tag.rb (2605 => 2606)


--- branches/biocat/vendor/plugins/acts_as_taggable_redux/lib/tag.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/vendor/plugins/acts_as_taggable_redux/lib/tag.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -41,6 +41,16 @@
     @tagged ||= taggings.collect(&:taggable)
   end
   
+  def tagged_auth(user)
+    tagged.select do |taggable|
+      Authorization.is_authorized?('view', nil, taggable, user)
+    end
+  end
+
+  def public?
+    tagged_auth(nil).length > 0
+  end
+
   # Compare tags by name
   def ==(comparison_object)
     super || name == comparison_object.to_s

Modified: branches/biocat/vendor/plugins/acts_as_taggable_redux/lib/tagging.rb (2605 => 2606)


--- branches/biocat/vendor/plugins/acts_as_taggable_redux/lib/tagging.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/vendor/plugins/acts_as_taggable_redux/lib/tagging.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -1,8 +1,19 @@
 class Tagging < ActiveRecord::Base
+
   belongs_to :tag, :counter_cache => true
   belongs_to :taggable, :polymorphic => true
   belongs_to :user
-  
+
+  validates_presence_of :tag
+  validates_presence_of :user
+  validates_presence_of :taggable
+
+  def before_validation
+    if taggable
+      return false unless taggable.respond_to?('add_tag')
+    end
+  end
+
   # returns the 'last created' Taggings
   # the maximum number of results is set by #limit#
   def self.latest(limit=10)
@@ -14,4 +25,10 @@
   def label
     return tag.name if tag
   end
+
+  def label=(label)
+    self.tag = Tag.find_by_name(label)    if self.tag.nil?
+    self.tag = Tag.create(:name => label) if self.tag.nil?
+  end
 end
+

Modified: branches/biocat/vendor/plugins/structured_data/lib/auto_migrate.rb (2605 => 2606)


--- branches/biocat/vendor/plugins/structured_data/lib/auto_migrate.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/vendor/plugins/structured_data/lib/auto_migrate.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -10,8 +10,9 @@
   AUTO_TABLE_NAME     = "auto_tables"
   SCHEMA              = "config/base_schema.xml"
   SCHEMA_D            = "config/schema.d"
-  COLUMN_ATTRIBUTES   = ['name', 'type']
-  HAS_MANY_ATTRIBUTES = ['target', 'through', 'foreign_key']
+  COLUMN_ATTRIBUTES     = ['name', 'type', 'default', 'limit']
+  BELONGS_TO_ATTRIBUTES = ['polymorphic', 'class_name', 'foreign_key']
+  HAS_MANY_ATTRIBUTES   = ['target', 'through', 'foreign_key', 'source', 'dependent', 'conditions', 'class_name', 'as']
 
   def self.schema
 
@@ -68,7 +69,9 @@
     end 
 
     (new_tables.keys - old_tables).each do |name|
+      unless tables.include?(name)
       conn.create_table(name) do |table| end
+      end
       AutoTable.create(:name => name)
     end
 
@@ -80,9 +83,17 @@
 
       old_columns = conn.columns(table_name).map do |column| column.name end - ["id"]
 
+      # and get detailed information about the existing columns
+
+      old_column_info = {}
+      
+      conn.columns(table_name).each do |c|
+        old_column_info[c.name] = c
+      end
+
       # determine the required columns
 
-      new_columns = new_tables[table_name].map do |column, definition| column end
+      new_columns = new_tables[table_name][:columns].map do |column, definition| column end
 
       # remove columns
 
@@ -93,9 +104,29 @@
       # add columns
 
       (new_columns - old_columns).each do |column_name|
-        conn.add_column(table_name, column_name, new_tables[table_name][column_name]["type"].to_sym)
+        default = new_tables[table_name][:columns][column_name]['default']
+        default = default.to_s unless default.nil?
+        conn.add_column(table_name, column_name, new_tables[table_name][:columns][column_name]["type"].to_sym, :default => default, :limit => new_tables[table_name][:columns][column_name]['limit'])
       end
 
+      # modify existing columns
+
+      (old_columns & new_columns).each do |column_name|
+
+        old_default = old_column_info[column_name].default
+        new_default = new_tables[table_name][:columns][column_name]['default']
+
+        old_default = old_default.to_s unless old_default.nil?
+        new_default = new_default.to_s unless new_default.nil?
+
+        old_type    = old_column_info[column_name].type
+        new_type    = new_tables[table_name][:columns][column_name]['type'].to_sym
+
+        if (old_default != new_default) || (old_type != new_type)
+          conn.change_column(table_name.to_sym, column_name.to_sym, new_type, :default => new_default)
+        end
+      end
+
       # get the list of existing indexes
 
       old_indexes = conn.indexes(table_name).map do |index| [index.columns] end
@@ -119,14 +150,9 @@
       end
     end
 
-    # adjust the indexes in each table
-
-    new_tables.keys.each do |table_name|
-    end
-
     # now that the schema has changed, load the models
 
-    load_models(new_tables.keys)
+    load_models(new_tables)
   end
 
   def self.destroy_auto_tables
@@ -148,22 +174,31 @@
     root = LibXML::XML::Parser.string(schema).parse.root
 
     root.find('/schema/table').each do |table|
-      tables[table['name']] ||= {}
 
+      tables[table['name']] ||= { :columns => {} }
+
+      if table['class_name']
+        tables[table['name']][:class_name] = table['class_name']
+      end
+
       table.find('column').each do |column|
-        tables[table['name']][column['name']] ||= {}
+        tables[table['name']][:columns][column['name']] ||= {}
 
         COLUMN_ATTRIBUTES.each do |attribute|
           if column[attribute] and attribute != 'name'
-            tables[table['name']][column['name']][attribute] = column[attribute]
+            tables[table['name']][:columns][column['name']][attribute] = column[attribute]
           end
         end
       end
+
+      table.find('belongs-to').each do |belongs_to|
+        attributes = {:table => table['name'], :type => 'belongs_to', :target => belongs_to['target']}
+
+        BELONGS_TO_ATTRIBUTES.each do |attribute|
+          attributes[attribute.to_sym] = belongs_to[attribute] if belongs_to[attribute]
     end
 
-    root.find('/schema/table').each do |table|
-      table.find('belongs-to').each do |belongs_to|
-        assocs.push(:table => table['name'], :type => 'belongs_to', :target => belongs_to['target'])
+        assocs.push(attributes)
       end
 
       table.find('has-many').each do |has_many|
@@ -175,10 +210,7 @@
 
         assocs.push(attributes)
       end
-    end
 
-    root.find('/schema/table').each do |table|
-
       indexes[table['name']] ||= []
 
       table.find('index').each do |index|
@@ -189,21 +221,48 @@
     [tables, assocs, indexes]
   end
 
+  def self.get_model(name)
+
+    c = Object
+
+    name.split("::").each do |bit|
+      c = c.const_get(bit)
+    end
+
+    c
+  end
+
+  def self.set_model(name, c)
+
+    container = Object
+    bits = name.split("::")
+    
+    bits[0..-2].each do |bit|
+      container = container.const_get(bit)
+    end
+
+    container.const_set(bits[-1].to_sym, c)
+  end
+
   def self.load_models(tables)
-    tables.each do |table|
+    tables.each do |table, options|
 
       class_name = table.singularize.camelize
 
+      class_name = options[:class_name] if options[:class_name]
+
       begin
-        Object.const_get(class_name)
+        get_model(class_name)
       rescue NameError
 
+        # logger.info("Structured data: instantiating #{class_name}")
+
         # model object not defined.  create it
 
         c = Class.new(ActiveRecord::Base)
         c.class_eval("acts_as_structured_data(:class_name => '#{class_name}')")
 
-        Object.const_set(class_name.to_sym, c)
+        set_model(class_name, c)
       end
     end
   end

Modified: branches/biocat/vendor/plugins/structured_data/lib/structured_data.rb (2605 => 2606)


--- branches/biocat/vendor/plugins/structured_data/lib/structured_data.rb	2011-06-07 12:48:36 UTC (rev 2605)
+++ branches/biocat/vendor/plugins/structured_data/lib/structured_data.rb	2011-06-15 15:27:14 UTC (rev 2606)
@@ -24,13 +24,23 @@
 
           bits.push(":through => :#{association[:through]}") if association[:through]
           bits.push(":foreign_key => :#{association[:foreign_key]}") if association[:foreign_key]
+          bits.push(":source => :#{association[:source]}") if association[:source]
+          bits.push(":dependent => :#{association[:dependent]}") if association[:dependent]
+          bits.push(":conditions => \"#{association[:conditions]}\"") if association[:conditions]
+          bits.push(":class_name => \"#{association[:class_name]}\"") if association[:class_name]
+          bits.push(":as => :#{association[:as]}") if association[:as]
 
           line = "has_many #{bits.join(', ')}"
           self.class_eval(line)
 
         when 'belongs_to'
+          bits = [":#{association[:target].singularize}"]
 
-          line = "belongs_to :#{association[:target].singularize}"
+          bits.push(":polymorphic => #{association[:polymorphic]}") if association[:polymorphic]
+          bits.push(":class_name => \"#{association[:class_name]}\"") if association[:class_name]
+          bits.push(":foreign_key => :#{association[:foreign_key]}") if association[:foreign_key]
+
+          line = "belongs_to #{bits.join(', ')}"
           self.class_eval(line)
         end
       end

reply via email to

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