myexperiment-hackers
[Top][All Lists]
Advanced

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

[myexperiment-hackers] [2545] trunk: svn merge -r 2460:2543 svn+ssh://ad


From: noreply
Subject: [myexperiment-hackers] [2545] trunk: svn merge -r 2460:2543 svn+ssh://address@hidden/var/svn/ myexperiment/branches/discovery
Date: Mon, 29 Nov 2010 11:03:02 -0500 (EST)

Revision
2545
Author
dgc
Date
2010-11-29 11:03:02 -0500 (Mon, 29 Nov 2010)

Log Message

svn merge -r 2460:2543 svn+ssh://address@hidden/var/svn/myexperiment/branches/discovery

Modified Paths

Added Paths

Removed Paths

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) + "&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: 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;
+}
+

reply via email to

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