Diff
Modified: trunk/app/controllers/application.rb (2544 => 2545)
--- trunk/app/controllers/application.rb 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/controllers/application.rb 2010-11-29 16:03:02 UTC (rev 2545)
@@ -369,4 +369,633 @@
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 => 'rank DESC'
+ },
+
+ {
+ :option => 'title',
+ :label => 'Title',
+ :order => 'label, rank DESC'
+ },
+
+ {
+ :option => 'latest',
+ :label => 'Latest',
+ :order => 'created_at DESC, rank DESC'
+ },
+
+ {
+ :option => 'last_updated',
+ :label => 'Last updated',
+ :order => 'updated_at DESC, rank DESC'
+ },
+
+ {
+ :option => 'rating',
+ :label => 'Community rating',
+ :order => 'rating DESC, rank DESC'
+ },
+
+ {
+ :option => 'viewings',
+ :label => 'Most viewed',
+ :order => 'site_viewings_count DESC, rank DESC'
+ },
+
+ {
+ :option => 'downloads',
+ :label => 'Most downloaded',
+ :order => 'site_downloads_count DESC, rank DESC'
+ },
+
+ {
+ :option => 'type',
+ :label => 'Type',
+ :joins => [ :content_types ],
+ :order => 'content_types.title, rank DESC'
+ },
+
+ {
+ :option => 'licence',
+ :label => 'Licence',
+ :joins => [ :licences ],
+ :order => 'licenses.title, rank DESC'
+ }
+ ],
+
+ :num_options => ['10', '20', '25', '50', '100'],
+
+ :filters =>
+ [
+ {
+ :title => 'category',
+ :query_option => 'CATEGORY',
+ :id_column => 'contributions.contributable_type',
+ :label_column => 'contributions.contributable_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 => '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 contributions.contributable_type = taggings.taggable_type AND contributions.contributable_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 = contributions.contributable_type AND creditations.creditable_id = contributions.contributable_id",
+ :curation_events => "INNER JOIN curation_events ON curation_events.object_type = contributions.contributable_type AND curation_events.object_id = contributions.contributable_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][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["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 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 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(other_filter[:id_column], filter_list[:expr][:terms]) unless other_filter == filter
+ end
+ joins += other_filter[:joins] if other_filter[:joins]
+ end
+ end
+
+ 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
+
+ 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 ")
+
+ objects = Authorization.authorised_index(Contribution,
+ :all,
+ :include_permissions => true,
+ :select => "#{filter[:id_column]} AS filter_id, #{filter[:label_column]} AS filter_label, COUNT(DISTINCT contributions.contributable_type, contributions.contributable_id) AS filter_count",
+ :joins => joins.length.zero? ? nil : joins.uniq.map do |j| pivot_options[:joins][j] end.join(" "),
+ :conditions => conditions,
+ :group => "#{filter[:id_column]} #{calculate_having_clause(filter, opts)}",
+ :limit => limit,
+ :order => "COUNT(DISTINCT contributions.contributable_type, contributions.contributable_id) DESC, #{filter[:label_column]}",
+ :authorised_user => user).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
+
+ # 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
+
+ # determine joins, conditions and order for the main results
+
+ joins = []
+ conditions = []
+
+ pivot_options[:filters].each do |filter|
+ if filter_list = find_filter(opts[:filters], filter[:query_option])
+ conditions << comparison(filter[:id_column], 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 => true,
+ :page => { :size => params["num"] ? params["num"].to_i : nil, :current => params["page"] },
+ :joins => joins.length.zero? ? nil : joins.uniq.map do |j| pivot_options[:joins][j] end.join(" "),
+ :conditions => conditions.length.zero? ? nil : conditions.join(" AND "),
+ :group => "contributions.contributable_type, contributions.contributable_id #{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
+
+ filters, cancel_filter_query_url = calculate_filters(params, opts, 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.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: trunk/app/controllers/blobs_controller.rb (2544 => 2545)
--- trunk/app/controllers/blobs_controller.rb 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/controllers/blobs_controller.rb 2010-11-29 16:03:02 UTC (rev 2545)
@@ -4,6 +4,9 @@
# See license.txt for details.
class BlobsController < ApplicationController
+
+ include ApplicationHelper
+
before_filter :login_required, :except => [:index, :show, :download, :named_download, :statistics, :search]
before_filter :find_blob_auth, :except => [:search, :index, :new, :create]
@@ -51,9 +54,24 @@
# GET /files
def index
- @contributions = Contribution.contributions_list(Blob, params, current_user)
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
Copied: trunk/app/controllers/content_controller.rb (from rev 2544, branches/discovery/app/controllers/content_controller.rb) (0 => 2545)
--- trunk/app/controllers/content_controller.rb (rev 0)
+++ trunk/app/controllers/content_controller.rb 2010-11-29 16:03:02 UTC (rev 2545)
@@ -0,0 +1,25 @@
+# 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
+ @pivot = contributions_list(Contribution, params, current_user)
+ # 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
+
Modified: trunk/app/controllers/packs_controller.rb (2544 => 2545)
--- trunk/app/controllers/packs_controller.rb 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/controllers/packs_controller.rb 2010-11-29 16:03:02 UTC (rev 2545)
@@ -27,9 +27,24 @@
# GET /packs
def index
- @contributions = Contribution.contributions_list(Pack, params, current_user)
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
Modified: trunk/app/controllers/workflows_controller.rb (2544 => 2545)
--- trunk/app/controllers/workflows_controller.rb 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/controllers/workflows_controller.rb 2010-11-29 16:03:02 UTC (rev 2545)
@@ -4,6 +4,9 @@
# See license.txt for details.
class WorkflowsController < ApplicationController
+
+ include ApplicationHelper
+
before_filter :login_required, :except => [:index, :show, :download, :named_download, :statistics, :launch, :search]
before_filter :find_workflows_rss, : [:index]
@@ -163,8 +166,20 @@
def index
respond_to do |format|
format.html do
- @contributions = Contribution.contributions_list(Workflow, params, current_user)
- # index.rhtml
+ @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)
Modified: trunk/app/models/contribution.rb (2544 => 2545)
--- trunk/app/models/contribution.rb 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/models/contribution.rb 2010-11-29 16:03:02 UTC (rev 2545)
@@ -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
@@ -16,81 +18,6 @@
:order => "created_at DESC",
:dependent => :destroy
- def self.order_options
- [
- {
- "order" => "rank DESC",
- "option" => "rank",
- "label" => "Rank"
- },
-
- {
- "order" => "label, rank DESC",
- "option" => "title",
- "label" => "Title"
- },
-
- {
- "order" => "created_at DESC, rank DESC",
- "option" => "latest",
- "label" => "Latest"
- },
-
- {
- "order" => "updated_at DESC, rank DESC",
- "option" => "last_updated",
- "label" => "Last updated"
- },
-
- {
- "order" => "rating DESC, rank DESC",
- "option" => "rating",
- "label" => "Community rating"
- },
-
- {
- "order" => "site_viewings_count DESC, rank DESC",
- "option" => "viewings",
- "label" => "Most viewed"
- },
-
- {
- "order" => "site_downloads_count DESC, rank DESC",
- "option" => "downloads",
- "label" => "Most downloaded"
- },
-
- {
- "joins" => "LEFT OUTER JOIN content_types ON contributions.content_type_id = content_types.id",
- "order" => "content_types.title, rank DESC",
- "option" => "type",
- "label" => "Type"
- },
-
- {
- "joins" => "LEFT OUTER JOIN licenses ON contributions.license_id = licenses.id",
- "order" => "licenses.title, rank DESC",
- "option" => "licence",
- "label" => "Licence"
- }
- ]
- end
-
- def self.contributions_list(klass = nil, params = nil, user = nil)
-
- sort_options = Contribution.order_options.find do |x| x["option"] == params["order"] end
-
- sort_options ||= Contribution.order_options.first
-
- results = Authorization.authorised_index(klass,
- :all,
- :authorised_user => user,
- :contribution_records => true,
- :page => { :size => 10, :current => params["page"] },
- :joins => sort_options["joins"],
- :order => sort_options["order"])
- end
-
# returns the 'most downloaded' Contributions
# (only takes into account downloads, that is internal usage)
# the maximum number of results is set by #limit#
Modified: trunk/app/views/blobs/_table.rhtml (2544 => 2545)
--- trunk/app/views/blobs/_table.rhtml 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/views/blobs/_table.rhtml 2010-11-29 16:03:02 UTC (rev 2545)
@@ -81,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: trunk/app/views/blobs/index.rhtml (2544 => 2545)
--- trunk/app/views/blobs/index.rhtml 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/views/blobs/index.rhtml 2010-11-29 16:03:02 UTC (rev 2545)
@@ -1,21 +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>
-</ul>
-
-<% cache(:controller => 'files', :action ="" 'all_tags') do -%>
- <%= render :partial => "blobs/all_tags" %>
-<% end -%>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @contributions, :sort_by => Contribution.order_options } %>
-
-<%= render :partial => "contributions/list", :locals => { :collection => @contributions, :table => true } %>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @contributions } %>
-
Copied: trunk/app/views/blobs/index.rhtml (from rev 2544, branches/discovery/app/views/blobs/index.rhtml) (0 => 2545)
--- trunk/app/views/blobs/index.rhtml (rev 0)
+++ trunk/app/views/blobs/index.rhtml 2010-11-29 16:03:02 UTC (rev 2545)
@@ -0,0 +1,4 @@
+<h1>Files</h1>
+
+<%= render :partial => "content/index" -%>
+
Modified: trunk/app/views/layouts/_paginate.rhtml (2544 => 2545)
--- trunk/app/views/layouts/_paginate.rhtml 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/views/layouts/_paginate.rhtml 2010-11-29 16:03:02 UTC (rev 2545)
@@ -3,9 +3,20 @@
Sort by:
<select = this.options[this.selectedIndex].value;">
<% sort_by.each do |args| %>
- <option value="?order=<%= args["option"] -%>" <% if params[:order] == args["option"] -%> selected="selected"<% end -%>><%= args["label"] -%></option>
+ <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 %>
Modified: trunk/app/views/packs/_table.rhtml (2544 => 2545)
--- trunk/app/views/packs/_table.rhtml 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/views/packs/_table.rhtml 2010-11-29 16:03:02 UTC (rev 2545)
@@ -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? %>
Modified: trunk/app/views/packs/index.rhtml (2544 => 2545)
--- trunk/app/views/packs/index.rhtml 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/views/packs/index.rhtml 2010-11-29 16:03:02 UTC (rev 2545)
@@ -1,7 +1,3 @@
-<ul class="sectionIcons">
- <li><%= icon "pack", new_pack_path, nil, nil, "Create New Pack" %></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;" -%>
@@ -9,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".
@@ -25,17 +21,4 @@
</div>
</div>
-<center>
-
-</center>
-
-<% cache(:controller => 'packs', :action ="" 'all_tags') do -%>
- <%= render :partial => "packs/all_tags" %>
-<% end -%>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @contributions, :sort_by => Contribution.order_options } %>
-
-<%= render :partial => "contributions/list", :locals => { :collection => @contributions, :table => true } %>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @contributions } %>
-
+<%= render :partial => "content/index" -%>
Modified: trunk/app/views/workflows/_table.rhtml (2544 => 2545)
--- trunk/app/views/workflows/_table.rhtml 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/views/workflows/_table.rhtml 2010-11-29 16:03:02 UTC (rev 2545)
@@ -21,7 +21,7 @@
<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, '' %>
@@ -74,28 +74,29 @@
<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> |
@@ -105,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: trunk/app/views/workflows/index.rhtml (2544 => 2545)
--- trunk/app/views/workflows/index.rhtml 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/app/views/workflows/index.rhtml 2010-11-29 16:03:02 UTC (rev 2545)
@@ -1,23 +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>
-
-<h1><%= feed_icon_tag "Latest Workflows", formatted_workflows_path(:rss), "margin-right: 0.3em;" -%> Workflows</h1>
-
-<ul class="sectionIcons">
- <li><%= icon "workflow", new_workflow_path, nil, nil, "Upload New Workflow" %></li>
-</ul>
-
-<% cache(:controller => 'workflows', :action ="" 'all_tags') do -%>
- <%= render :partial => "workflows/all_tags" %>
-<% end -%>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @contributions, :sort_by => Contribution.order_options } %>
-
-<%= render :partial => "contributions/list", :locals => { :collection => @contributions, :table => true } %>
-
-<%= render :partial => "layouts/paginate", :locals => { :collection => @contributions } %>
-
Copied: trunk/app/views/workflows/index.rhtml (from rev 2544, branches/discovery/app/views/workflows/index.rhtml) (0 => 2545)
--- trunk/app/views/workflows/index.rhtml (rev 0)
+++ trunk/app/views/workflows/index.rhtml 2010-11-29 16:03:02 UTC (rev 2545)
@@ -0,0 +1,4 @@
+<h1>Workflows</h1>
+
+<%= render :partial => "content/index" -%>
+
Modified: trunk/config/routes.rb (2544 => 2545)
--- trunk/config/routes.rb 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/config/routes.rb 2010-11-29 16:03:02 UTC (rev 2545)
@@ -41,6 +41,9 @@
: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 }
Copied: trunk/public/_javascript_s/ellipsis.js (from rev 2544, branches/discovery/public/_javascript_s/ellipsis.js) (0 => 2545)
--- trunk/public/_javascript_s/ellipsis.js (rev 0)
+++ trunk/public/_javascript_s/ellipsis.js 2010-11-29 16:03:02 UTC (rev 2545)
@@ -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) + "… "
+ }
+}
+
+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: trunk/public/stylesheets/styles.css (2544 => 2545)
--- trunk/public/stylesheets/styles.css 2010-11-29 15:15:08 UTC (rev 2544)
+++ trunk/public/stylesheets/styles.css 2010-11-29 16:03:02 UTC (rev 2545)
@@ -829,7 +829,6 @@
}
table.alt_table {
- width: 100%;
border-collapse: collapse;
}
@@ -887,6 +886,8 @@
text-align: left;
line-height: 1.3;
overflow: hidden;
+ word-wrap: break-word;
+ width: 360px;
}
table.alt_table .standout {
@@ -2032,3 +2033,186 @@
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: 8px;
+}
+
+.pivot .summary DIV {
+ padding-top: 0.2em;
+ padding-bottom: 0.2em;
+}
+
+.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: 127px;
+ 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;
+}
+
+.result-count {
+}
+
+.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;
+}
+