diff --git a/queries/trunk/app/controllers/projects_controller.rb b/queries/trunk/app/controllers/projects_controller.rb index 6718b83..d912f35 100644 --- a/queries/trunk/app/controllers/projects_controller.rb +++ b/queries/trunk/app/controllers/projects_controller.rb @@ -208,8 +208,10 @@ class ProjectsController < ApplicationController sort_init 'issues.id', 'desc' sort_update - search_filter_init_list_issues - search_filter_update if params[:set_filter] + @query = Query.new + params[:fields].each do |field| + @query.add_filter(field, params[:operators][field], params[:values][field]) + end if params[:fields] @results_per_page_options = [ 15, 25, 50, 100 ] if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i @@ -219,11 +221,11 @@ class ProjectsController < ApplicationController @results_per_page = session[:results_per_page] || 25 end - @issue_count = Issue.count(:include => [:status, :project], :conditions => search_filter_clause) + @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement) @issue_pages = Paginator.new self, @issue_count, @results_per_page, @params['page'] @issues = Issue.find :all, :order => sort_clause, :include => [ :author, :status, :tracker, :project ], - :conditions => search_filter_clause, + :conditions => @query.statement, :limit => @issue_pages.items_per_page, :offset => @issue_pages.current.offset diff --git a/queries/trunk/app/controllers/queries_controller.rb b/queries/trunk/app/controllers/queries_controller.rb new file mode 100644 index 0000000..be7cb24 --- /dev/null +++ b/queries/trunk/app/controllers/queries_controller.rb @@ -0,0 +1,55 @@ +class QueriesController < ApplicationController + layout 'base' + + def index + list + render :action => 'list' + end + + # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) + verify :method => :post, :only => [ :destroy, :create, :update ], + :redirect_to => { :action => :list } + + def list + @query_pages, @queries = paginate :queries, :per_page => 10 + end + + def show + @query = Query.find(params[:id]) + end + + def new + @query = Query.new(params[:query]) + + params[:fields].each do |field| + @query.add_filter(field, params[:operators][field], params[:values][field]) + end if params[:fields] + + if request.post? and @query.save + flash[:notice] = 'Query was successfully created.' + redirect_to :action => 'list' + end + end + + def edit + @query = Query.find(params[:id]) + + if request.post? + @query.filters = {} + params[:fields].each do |field| + @query.add_filter(field, params[:operators][field], params[:values][field]) + end if params[:fields] + @query.attributes = params[:query] + + if @query.save + flash[:notice] = 'Query was successfully updated.' + redirect_to :action => 'show', :id => @query + end + end + end + + def destroy + Query.find(params[:id]).destroy + redirect_to :action => 'list' + end +end diff --git a/queries/trunk/app/helpers/queries_helper.rb b/queries/trunk/app/helpers/queries_helper.rb new file mode 100644 index 0000000..c40ef33 --- /dev/null +++ b/queries/trunk/app/helpers/queries_helper.rb @@ -0,0 +1,2 @@ +module QueriesHelper +end diff --git a/queries/trunk/app/models/query.rb b/queries/trunk/app/models/query.rb new file mode 100644 index 0000000..8201d08 --- /dev/null +++ b/queries/trunk/app/models/query.rb @@ -0,0 +1,123 @@ +class Query < ActiveRecord::Base + serialize :filters + + validates_presence_of :name + + @@operators = { "=" => "Egal", + "!" => "Different", + "o" => "Ouvert", + "c" => "Ferme", + "!*" => "Aucun", + "*" => "Tous", + " "Dans moins de", + ">t+" => "Dans plus de", + "t+" => "Dans exactement", + "t" => "Aujourd'hui", + ">t-" => "Il y a moins de", + " "Il y a plus de", + "t-" => "Il y a exactement" } + + @@operators_by_filter_type = { :list => [ "=", "!" ], + :list_status => [ "o", "=", "!", "c" ], + :list_optional => [ "=", "!", "!*", "*" ], + :date => [ "t+", "t+", "t", ">t-", " [ ">t-", " { :type => :list_status, :order => 1, + :values => IssueStatus.find(:all).collect{|s| [s.name, s.id.to_s] } }, + "tracker_id" => { :type => :list, :order => 2, + :values => Tracker.find(:all).collect{|s| [s.name, s.id.to_s] } }, + "assigned_to_id" => { :type => :list_optional, :order => 3, + :values => User.find(:all).collect{|s| [s.display_name, s.id.to_s] } }, + "priority_id" => { :type => :list, :order => 4, + :values => Enumeration.find(:all, :conditions => ['opt=?','IPRI']).collect{|s| [s.name, s.id.to_s] } }, + "created_on" => { :type => :date_past, :order => 5 }, + "updated_on" => { :type => :date_past, :order => 6 }, + "start_date" => { :type => :date, :order => 7 }, + "due_date" => { :type => :date, :order => 8 } } + + cattr_accessor :available_filters + + def initialize(attributes = nil) + super + self.filters ||= { 'status_id' => {:operator => "o"} } + end + + def validate + errors.add_to_base "Au moins un critere doit etre selectionne" unless filters && !filters.empty? + filters.each_key do |field| + errors.add field.gsub(/\_id$/, ""), "doit etre renseigne" unless + # filter requires one or more values + (values_for(field) and !values_for(field).first.empty?) or + # filter doesn't require any value + ["o", "c", "!*", "*", "t"].include? operator_for(field) + end if filters + end + + def add_filter(field, operator, values) + # values must be an array + return unless values and values.is_a? Array + # check if field is defined as an available filter + if @@available_filters.has_key? field + filter_options = @@available_filters[field] + # check if operator is allowed for that filter + if @@operators_by_filter_type[filter_options[:type]].include? operator + filters[field] = {:operator => operator, :values => values } + end + end + end + + def has_filter?(field) + filters and filters[field] + end + + def operator_for(field) + has_filter?(field) ? filters[field][:operator] : nil + end + + def values_for(field) + has_filter?(field) ? filters[field][:values] : nil + end + + def statement + sql = "1=1" + filters.each_key do |field| + sql = sql + " AND " unless sql.empty? + v = values_for field + case operator_for field + when "=" + sql = sql + "issues.#{field} IN (" + v.each(&:to_i).join(",") + ")" + when "!" + sql = sql + "issues.#{field} NOT IN (" + v.each(&:to_i).join(",") + ")" + when "!*" + sql = sql + "issues.#{field} IS NULL" + when "*" + sql = sql + "issues.#{field} IS NOT NULL" + when "o" + sql = sql + "issue_statuses.is_closed=#{connection.quoted_false}" if field == "status_id" + when "c" + sql = sql + "issue_statuses.is_closed=#{connection.quoted_true}" if field == "status_id" + when ">t-" + sql = sql + "issues.#{field} >= '%s'" % connection.quoted_date(Date.today - v.first.to_i) + when "t+" + sql = sql + "issues.#{field} >= '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'" + when " +
<%= start_form_tag :action => 'list_issues' %> - - - - - - - - - - - - - -
<%=l(:field_status)%>:
<%= search_filter_tag 'status_id', :class => 'select-small' %>
<%=l(:field_tracker)%>:
<%= search_filter_tag 'tracker_id', :class => 'select-small' %>
<%=l(:field_priority)%>:
<%= search_filter_tag 'priority_id', :class => 'select-small' %>
<%=l(:field_category)%>:
<%= search_filter_tag 'category_id', :class => 'select-small' %>
<%=l(:field_fixed_version)%>:
<%= search_filter_tag 'fixed_version_id', :class => 'select-small' %>
<%=l(:field_author)%>:
<%= search_filter_tag 'author_id', :class => 'select-small' %>
<%=l(:field_assigned_to)%>:
<%= search_filter_tag 'assigned_to_id', :class => 'select-small' %>
<%=l(:label_subproject_plural)%>:
<%= search_filter_tag 'subproject_id', :class => 'select-small' %>
- <%= hidden_field_tag 'set_filter', 1 %> - <%= submit_tag l(:button_apply), :class => 'button-small' %> - - <%= link_to l(:button_clear), :action => 'list_issues', :id => @project, :set_filter => 1 %> -
+<%= render :partial => 'queries/filters', :locals => {:query => @query} %> +<%= submit_tag l(:button_apply), :class => "button-small" %> <%= end_form_tag %> - +
  diff --git a/queries/trunk/app/views/queries/_filters.rhtml b/queries/trunk/app/views/queries/_filters.rhtml new file mode 100644 index 0000000..8d84b56 --- /dev/null +++ b/queries/trunk/app/views/queries/_filters.rhtml @@ -0,0 +1,72 @@ + + +
Filtres +
+<% Query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.each do |filter| %> + <% field = filter[0] + options = filter[1] %> + + + + + + +<% end %> +
+ <%= check_box_tag 'fields[]', field, query.has_filter?(field), :onclick => "toggle_filter('#{field}');", :id => "cb_#{field}" %> + + + <%= select_tag "operators[#{field}]", options_for_select(Query.operators_for_select(options[:type]), query.operator_for(field)), :onchange => "toggle_operator('#{field}');", :class => "select-small", :style => "vertical-align: top;" %> + +
+ <% case options[:type] + when :list, :list_optional, :list_status %> + + <%= link_to_function '+', "toggle_multi_select('#{field}');" %> + <% when :date, :date_past %> + <%= text_field_tag "values[#{field}][]", query.values_for(field), :size => 3, :class => "select-small" %> jours + <% end %> +
+
+ \ No newline at end of file diff --git a/queries/trunk/app/views/queries/_form.rhtml b/queries/trunk/app/views/queries/_form.rhtml new file mode 100644 index 0000000..7bbe608 --- /dev/null +++ b/queries/trunk/app/views/queries/_form.rhtml @@ -0,0 +1,18 @@ +<%= error_messages_for 'query' %> + + +
+ +
+

+<%= text_field 'query', 'name', :size => 80 %>

+ +

+<%= check_box 'query', 'is_public' %>

+
+ +<%= render :partial => 'filters', :locals => {:query => query}%> + +
+ + diff --git a/queries/trunk/app/views/queries/edit.rhtml b/queries/trunk/app/views/queries/edit.rhtml new file mode 100644 index 0000000..c57d741 --- /dev/null +++ b/queries/trunk/app/views/queries/edit.rhtml @@ -0,0 +1,8 @@ +

Editing query

+ +<%= start_form_tag :action => 'edit', :id => @query %> + <%= render :partial => 'form', :locals => {:query => @query} %> + <%= submit_tag 'Edit' %> +<%= end_form_tag %> + +<%= debug @query.statement %> \ No newline at end of file diff --git a/queries/trunk/app/views/queries/list.rhtml b/queries/trunk/app/views/queries/list.rhtml new file mode 100644 index 0000000..8620290 --- /dev/null +++ b/queries/trunk/app/views/queries/list.rhtml @@ -0,0 +1,27 @@ +

Listing queries

+ + + + <% for column in Query.content_columns %> + + <% end %> + + +<% for query in @queries %> + + <% for column in Query.content_columns %> + + <% end %> + + + + +<% end %> +
<%= column.human_name %>
<%=h query.send(column.name) %><%= link_to 'Show', :action => 'show', :id => query %><%= link_to 'Edit', :action => 'edit', :id => query %><%= link_to 'Destroy', { :action => 'destroy', :id => query }, :confirm => 'Are you sure?', :post => true %>
+ +<%= link_to 'Previous page', { :page => @query_pages.current.previous } if @query_pages.current.previous %> +<%= link_to 'Next page', { :page => @query_pages.current.next } if @query_pages.current.next %> + +
+ +<%= link_to 'New query', :action => 'new' %> diff --git a/queries/trunk/app/views/queries/new.rhtml b/queries/trunk/app/views/queries/new.rhtml new file mode 100644 index 0000000..0b1668a --- /dev/null +++ b/queries/trunk/app/views/queries/new.rhtml @@ -0,0 +1,9 @@ +

New query

+ +<%= start_form_tag :action => 'new' %> + <%= render :partial => 'form', :locals => {:query => @query} %> + <%= submit_tag "Create" %> +<%= end_form_tag %> + +<%= debug @query %> +<%= debug params %> diff --git a/queries/trunk/app/views/queries/show.rhtml b/queries/trunk/app/views/queries/show.rhtml new file mode 100644 index 0000000..6118f73 --- /dev/null +++ b/queries/trunk/app/views/queries/show.rhtml @@ -0,0 +1,8 @@ +<% for column in Query.content_columns %> +

+ <%= column.human_name %>: <%=h @query.send(column.name) %> +

+<% end %> + +<%= link_to 'Edit', :action => 'edit', :id => @query %> | +<%= link_to 'Back', :action => 'list' %> diff --git a/queries/trunk/public/stylesheets/application.css b/queries/trunk/public/stylesheets/application.css index 7af699a..9c05dfe 100644 --- a/queries/trunk/public/stylesheets/application.css +++ b/queries/trunk/public/stylesheets/application.css @@ -213,7 +213,7 @@ select { vertical-align: middle; } -select.select-small +.select-small { border: 1px solid #7F9DB9; padding: 1px;