@@ -0,0 +1,55 | |||||
|
1 | class QueriesController < ApplicationController | |||
|
2 | layout 'base' | |||
|
3 | ||||
|
4 | def index | |||
|
5 | list | |||
|
6 | render :action => 'list' | |||
|
7 | end | |||
|
8 | ||||
|
9 | # GETs should be safe (see http://www.w3.org/2001/tag/doc/whenToUseGet.html) | |||
|
10 | verify :method => :post, :only => [ :destroy, :create, :update ], | |||
|
11 | :redirect_to => { :action => :list } | |||
|
12 | ||||
|
13 | def list | |||
|
14 | @query_pages, @queries = paginate :queries, :per_page => 10 | |||
|
15 | end | |||
|
16 | ||||
|
17 | def show | |||
|
18 | @query = Query.find(params[:id]) | |||
|
19 | end | |||
|
20 | ||||
|
21 | def new | |||
|
22 | @query = Query.new(params[:query]) | |||
|
23 | ||||
|
24 | params[:fields].each do |field| | |||
|
25 | @query.add_filter(field, params[:operators][field], params[:values][field]) | |||
|
26 | end if params[:fields] | |||
|
27 | ||||
|
28 | if request.post? and @query.save | |||
|
29 | flash[:notice] = 'Query was successfully created.' | |||
|
30 | redirect_to :action => 'list' | |||
|
31 | end | |||
|
32 | end | |||
|
33 | ||||
|
34 | def edit | |||
|
35 | @query = Query.find(params[:id]) | |||
|
36 | ||||
|
37 | if request.post? | |||
|
38 | @query.filters = {} | |||
|
39 | params[:fields].each do |field| | |||
|
40 | @query.add_filter(field, params[:operators][field], params[:values][field]) | |||
|
41 | end if params[:fields] | |||
|
42 | @query.attributes = params[:query] | |||
|
43 | ||||
|
44 | if @query.save | |||
|
45 | flash[:notice] = 'Query was successfully updated.' | |||
|
46 | redirect_to :action => 'show', :id => @query | |||
|
47 | end | |||
|
48 | end | |||
|
49 | end | |||
|
50 | ||||
|
51 | def destroy | |||
|
52 | Query.find(params[:id]).destroy | |||
|
53 | redirect_to :action => 'list' | |||
|
54 | end | |||
|
55 | end |
@@ -0,0 +1,123 | |||||
|
1 | class Query < ActiveRecord::Base | |||
|
2 | serialize :filters | |||
|
3 | ||||
|
4 | validates_presence_of :name | |||
|
5 | ||||
|
6 | @@operators = { "=" => "Egal", | |||
|
7 | "!" => "Different", | |||
|
8 | "o" => "Ouvert", | |||
|
9 | "c" => "Ferme", | |||
|
10 | "!*" => "Aucun", | |||
|
11 | "*" => "Tous", | |||
|
12 | "<t+" => "Dans moins de", | |||
|
13 | ">t+" => "Dans plus de", | |||
|
14 | "t+" => "Dans exactement", | |||
|
15 | "t" => "Aujourd'hui", | |||
|
16 | ">t-" => "Il y a moins de", | |||
|
17 | "<t-" => "Il y a plus de", | |||
|
18 | "t-" => "Il y a exactement" } | |||
|
19 | ||||
|
20 | @@operators_by_filter_type = { :list => [ "=", "!" ], | |||
|
21 | :list_status => [ "o", "=", "!", "c" ], | |||
|
22 | :list_optional => [ "=", "!", "!*", "*" ], | |||
|
23 | :date => [ "<t+", ">t+", "t+", "t", ">t-", "<t-", "t-" ], | |||
|
24 | :date_past => [ ">t-", "<t-", "t-", "t" ] } | |||
|
25 | ||||
|
26 | @@available_filters = { "status_id" => { :type => :list_status, :order => 1, | |||
|
27 | :values => IssueStatus.find(:all).collect{|s| [s.name, s.id.to_s] } }, | |||
|
28 | "tracker_id" => { :type => :list, :order => 2, | |||
|
29 | :values => Tracker.find(:all).collect{|s| [s.name, s.id.to_s] } }, | |||
|
30 | "assigned_to_id" => { :type => :list_optional, :order => 3, | |||
|
31 | :values => User.find(:all).collect{|s| [s.display_name, s.id.to_s] } }, | |||
|
32 | "priority_id" => { :type => :list, :order => 4, | |||
|
33 | :values => Enumeration.find(:all, :conditions => ['opt=?','IPRI']).collect{|s| [s.name, s.id.to_s] } }, | |||
|
34 | "created_on" => { :type => :date_past, :order => 5 }, | |||
|
35 | "updated_on" => { :type => :date_past, :order => 6 }, | |||
|
36 | "start_date" => { :type => :date, :order => 7 }, | |||
|
37 | "due_date" => { :type => :date, :order => 8 } } | |||
|
38 | ||||
|
39 | cattr_accessor :available_filters | |||
|
40 | ||||
|
41 | def initialize(attributes = nil) | |||
|
42 | super | |||
|
43 | self.filters ||= { 'status_id' => {:operator => "o"} } | |||
|
44 | end | |||
|
45 | ||||
|
46 | def validate | |||
|
47 | errors.add_to_base "Au moins un critere doit etre selectionne" unless filters && !filters.empty? | |||
|
48 | filters.each_key do |field| | |||
|
49 | errors.add field.gsub(/\_id$/, ""), "doit etre renseigne" unless | |||
|
50 | # filter requires one or more values | |||
|
51 | (values_for(field) and !values_for(field).first.empty?) or | |||
|
52 | # filter doesn't require any value | |||
|
53 | ["o", "c", "!*", "*", "t"].include? operator_for(field) | |||
|
54 | end if filters | |||
|
55 | end | |||
|
56 | ||||
|
57 | def add_filter(field, operator, values) | |||
|
58 | # values must be an array | |||
|
59 | return unless values and values.is_a? Array | |||
|
60 | # check if field is defined as an available filter | |||
|
61 | if @@available_filters.has_key? field | |||
|
62 | filter_options = @@available_filters[field] | |||
|
63 | # check if operator is allowed for that filter | |||
|
64 | if @@operators_by_filter_type[filter_options[:type]].include? operator | |||
|
65 | filters[field] = {:operator => operator, :values => values } | |||
|
66 | end | |||
|
67 | end | |||
|
68 | end | |||
|
69 | ||||
|
70 | def has_filter?(field) | |||
|
71 | filters and filters[field] | |||
|
72 | end | |||
|
73 | ||||
|
74 | def operator_for(field) | |||
|
75 | has_filter?(field) ? filters[field][:operator] : nil | |||
|
76 | end | |||
|
77 | ||||
|
78 | def values_for(field) | |||
|
79 | has_filter?(field) ? filters[field][:values] : nil | |||
|
80 | end | |||
|
81 | ||||
|
82 | def statement | |||
|
83 | sql = "1=1" | |||
|
84 | filters.each_key do |field| | |||
|
85 | sql = sql + " AND " unless sql.empty? | |||
|
86 | v = values_for field | |||
|
87 | case operator_for field | |||
|
88 | when "=" | |||
|
89 | sql = sql + "issues.#{field} IN (" + v.each(&:to_i).join(",") + ")" | |||
|
90 | when "!" | |||
|
91 | sql = sql + "issues.#{field} NOT IN (" + v.each(&:to_i).join(",") + ")" | |||
|
92 | when "!*" | |||
|
93 | sql = sql + "issues.#{field} IS NULL" | |||
|
94 | when "*" | |||
|
95 | sql = sql + "issues.#{field} IS NOT NULL" | |||
|
96 | when "o" | |||
|
97 | sql = sql + "issue_statuses.is_closed=#{connection.quoted_false}" if field == "status_id" | |||
|
98 | when "c" | |||
|
99 | sql = sql + "issue_statuses.is_closed=#{connection.quoted_true}" if field == "status_id" | |||
|
100 | when ">t-" | |||
|
101 | sql = sql + "issues.#{field} >= '%s'" % connection.quoted_date(Date.today - v.first.to_i) | |||
|
102 | when "<t-" | |||
|
103 | sql = sql + "issues.#{field} <= '" + (Date.today - v.first.to_i).strftime("%Y-%m-%d") + "'" | |||
|
104 | when "t-" | |||
|
105 | sql = sql + "issues.#{field} = '" + (Date.today - v.first.to_i).strftime("%Y-%m-%d") + "'" | |||
|
106 | when ">t+" | |||
|
107 | sql = sql + "issues.#{field} >= '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'" | |||
|
108 | when "<t+" | |||
|
109 | sql = sql + "issues.#{field} <= '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'" | |||
|
110 | when "t+" | |||
|
111 | sql = sql + "issues.#{field} = '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'" | |||
|
112 | when "t" | |||
|
113 | sql = sql + "issues.#{field} = '%s'" % connection.quoted_date(Date.today) | |||
|
114 | end | |||
|
115 | end if filters | |||
|
116 | sql | |||
|
117 | end | |||
|
118 | ||||
|
119 | def self.operators_for_select(filter_type) | |||
|
120 | @@operators_by_filter_type[filter_type].collect {|o| [@@operators[o], o]} | |||
|
121 | end | |||
|
122 | ||||
|
123 | end |
@@ -0,0 +1,72 | |||||
|
1 | <script> | |||
|
2 | ||||
|
3 | function toggle_filter(field) { | |||
|
4 | check_box = $('cb_' + field); | |||
|
5 | ||||
|
6 | if (check_box.checked) { | |||
|
7 | Element.show("operators[" + field + "]"); | |||
|
8 | toggle_operator(field); | |||
|
9 | } else { | |||
|
10 | Element.hide("operators[" + field + "]"); | |||
|
11 | Element.hide("values_div[" + field + "]"); | |||
|
12 | } | |||
|
13 | } | |||
|
14 | ||||
|
15 | function toggle_operator(field) { | |||
|
16 | operator = $("operators[" + field + "]"); | |||
|
17 | switch (operator.value) { | |||
|
18 | case "!*": | |||
|
19 | case "*": | |||
|
20 | case "t": | |||
|
21 | case "o": | |||
|
22 | case "c": | |||
|
23 | Element.hide("values_div[" + field + "]"); | |||
|
24 | break; | |||
|
25 | default: | |||
|
26 | Element.show("values_div[" + field + "]"); | |||
|
27 | break; | |||
|
28 | } | |||
|
29 | } | |||
|
30 | ||||
|
31 | function toggle_multi_select(field) { | |||
|
32 | select = $('values[' + field + '][]'); | |||
|
33 | if (select.multiple == true) { | |||
|
34 | select.multiple = false; | |||
|
35 | } else { | |||
|
36 | select.multiple = true; | |||
|
37 | } | |||
|
38 | } | |||
|
39 | ||||
|
40 | </script> | |||
|
41 | ||||
|
42 | <fieldset><legend>Filtres</legend> | |||
|
43 | <table> | |||
|
44 | <% Query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.each do |filter| %> | |||
|
45 | <% field = filter[0] | |||
|
46 | options = filter[1] %> | |||
|
47 | <tr> | |||
|
48 | <td valign="top" width="200"> | |||
|
49 | <%= check_box_tag 'fields[]', field, query.has_filter?(field), :onclick => "toggle_filter('#{field}');", :id => "cb_#{field}" %> | |||
|
50 | <label for="cb_<%= field %>"><%= l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) %></label> | |||
|
51 | </td> | |||
|
52 | <td valign="top" width="150"> | |||
|
53 | <%= 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;" %> | |||
|
54 | </td> | |||
|
55 | <td valign="top"> | |||
|
56 | <div id="values_div[<%= field %>]"> | |||
|
57 | <% case options[:type] | |||
|
58 | when :list, :list_optional, :list_status %> | |||
|
59 | <select <%= "multiple=true" if query.values_for(field) and query.values_for(field).length > 1 %>" name="values[<%= field %>][]" id="values[<%= field %>][]" class="select-small" style="vertical-align: top;"> | |||
|
60 | <%= options_for_select options[:values], query.values_for(field) %> | |||
|
61 | </select> | |||
|
62 | <%= link_to_function '+', "toggle_multi_select('#{field}');" %> | |||
|
63 | <% when :date, :date_past %> | |||
|
64 | <%= text_field_tag "values[#{field}][]", query.values_for(field), :size => 3, :class => "select-small" %> jours | |||
|
65 | <% end %> | |||
|
66 | </div> | |||
|
67 | </td> | |||
|
68 | </tr> | |||
|
69 | <script>toggle_filter('<%= field %>');</script> | |||
|
70 | <% end %> | |||
|
71 | </table> | |||
|
72 | </fieldset> No newline at end of file |
@@ -0,0 +1,18 | |||||
|
1 | <%= error_messages_for 'query' %> | |||
|
2 | ||||
|
3 | <!--[form:query]--> | |||
|
4 | <div class="box"> | |||
|
5 | ||||
|
6 | <div class="tabular"> | |||
|
7 | <p><label for="query_name">Name</label> | |||
|
8 | <%= text_field 'query', 'name', :size => 80 %></p> | |||
|
9 | ||||
|
10 | <p><label for="query_is_public">Public</label> | |||
|
11 | <%= check_box 'query', 'is_public' %></p> | |||
|
12 | </div> | |||
|
13 | ||||
|
14 | <%= render :partial => 'filters', :locals => {:query => query}%> | |||
|
15 | ||||
|
16 | </div> | |||
|
17 | <!--[eoform:query]--> | |||
|
18 |
@@ -0,0 +1,8 | |||||
|
1 | <h1>Editing query</h1> | |||
|
2 | ||||
|
3 | <%= start_form_tag :action => 'edit', :id => @query %> | |||
|
4 | <%= render :partial => 'form', :locals => {:query => @query} %> | |||
|
5 | <%= submit_tag 'Edit' %> | |||
|
6 | <%= end_form_tag %> | |||
|
7 | ||||
|
8 | <%= debug @query.statement %> No newline at end of file |
@@ -0,0 +1,27 | |||||
|
1 | <h1>Listing queries</h1> | |||
|
2 | ||||
|
3 | <table> | |||
|
4 | <tr> | |||
|
5 | <% for column in Query.content_columns %> | |||
|
6 | <th><%= column.human_name %></th> | |||
|
7 | <% end %> | |||
|
8 | </tr> | |||
|
9 | ||||
|
10 | <% for query in @queries %> | |||
|
11 | <tr> | |||
|
12 | <% for column in Query.content_columns %> | |||
|
13 | <td><%=h query.send(column.name) %></td> | |||
|
14 | <% end %> | |||
|
15 | <td><%= link_to 'Show', :action => 'show', :id => query %></td> | |||
|
16 | <td><%= link_to 'Edit', :action => 'edit', :id => query %></td> | |||
|
17 | <td><%= link_to 'Destroy', { :action => 'destroy', :id => query }, :confirm => 'Are you sure?', :post => true %></td> | |||
|
18 | </tr> | |||
|
19 | <% end %> | |||
|
20 | </table> | |||
|
21 | ||||
|
22 | <%= link_to 'Previous page', { :page => @query_pages.current.previous } if @query_pages.current.previous %> | |||
|
23 | <%= link_to 'Next page', { :page => @query_pages.current.next } if @query_pages.current.next %> | |||
|
24 | ||||
|
25 | <br /> | |||
|
26 | ||||
|
27 | <%= link_to 'New query', :action => 'new' %> |
@@ -0,0 +1,9 | |||||
|
1 | <h1>New query</h1> | |||
|
2 | ||||
|
3 | <%= start_form_tag :action => 'new' %> | |||
|
4 | <%= render :partial => 'form', :locals => {:query => @query} %> | |||
|
5 | <%= submit_tag "Create" %> | |||
|
6 | <%= end_form_tag %> | |||
|
7 | ||||
|
8 | <%= debug @query %> | |||
|
9 | <%= debug params %> |
@@ -0,0 +1,8 | |||||
|
1 | <% for column in Query.content_columns %> | |||
|
2 | <p> | |||
|
3 | <b><%= column.human_name %>:</b> <%=h @query.send(column.name) %> | |||
|
4 | </p> | |||
|
5 | <% end %> | |||
|
6 | ||||
|
7 | <%= link_to 'Edit', :action => 'edit', :id => @query %> | | |||
|
8 | <%= link_to 'Back', :action => 'list' %> |
@@ -208,8 +208,10 class ProjectsController < ApplicationController | |||||
208 | sort_init 'issues.id', 'desc' |
|
208 | sort_init 'issues.id', 'desc' | |
209 | sort_update |
|
209 | sort_update | |
210 |
|
210 | |||
211 | search_filter_init_list_issues |
|
211 | @query = Query.new | |
212 | search_filter_update if params[:set_filter] |
|
212 | params[:fields].each do |field| | |
|
213 | @query.add_filter(field, params[:operators][field], params[:values][field]) | |||
|
214 | end if params[:fields] | |||
213 |
|
215 | |||
214 | @results_per_page_options = [ 15, 25, 50, 100 ] |
|
216 | @results_per_page_options = [ 15, 25, 50, 100 ] | |
215 | if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i |
|
217 | if params[:per_page] and @results_per_page_options.include? params[:per_page].to_i | |
@@ -219,11 +221,11 class ProjectsController < ApplicationController | |||||
219 | @results_per_page = session[:results_per_page] || 25 |
|
221 | @results_per_page = session[:results_per_page] || 25 | |
220 | end |
|
222 | end | |
221 |
|
223 | |||
222 |
@issue_count = Issue.count(:include => [:status, :project], :conditions => |
|
224 | @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement) | |
223 | @issue_pages = Paginator.new self, @issue_count, @results_per_page, @params['page'] |
|
225 | @issue_pages = Paginator.new self, @issue_count, @results_per_page, @params['page'] | |
224 | @issues = Issue.find :all, :order => sort_clause, |
|
226 | @issues = Issue.find :all, :order => sort_clause, | |
225 | :include => [ :author, :status, :tracker, :project ], |
|
227 | :include => [ :author, :status, :tracker, :project ], | |
226 |
:conditions => |
|
228 | :conditions => @query.statement, | |
227 | :limit => @issue_pages.items_per_page, |
|
229 | :limit => @issue_pages.items_per_page, | |
228 | :offset => @issue_pages.current.offset |
|
230 | :offset => @issue_pages.current.offset | |
229 |
|
231 |
@@ -7,28 +7,12 | |||||
7 | </small> |
|
7 | </small> | |
8 | </div> |
|
8 | </div> | |
9 |
|
9 | |||
|
10 | <div> | |||
10 | <%= start_form_tag :action => 'list_issues' %> |
|
11 | <%= start_form_tag :action => 'list_issues' %> | |
11 | <table cellpadding=2> |
|
12 | <%= render :partial => 'queries/filters', :locals => {:query => @query} %> | |
12 | <tr> |
|
13 | <%= submit_tag l(:button_apply), :class => "button-small" %> | |
13 | <td valign="bottom"><small><%=l(:field_status)%>:</small><br /><%= search_filter_tag 'status_id', :class => 'select-small' %></td> |
|
|||
14 | <td valign="bottom"><small><%=l(:field_tracker)%>:</small><br /><%= search_filter_tag 'tracker_id', :class => 'select-small' %></td> |
|
|||
15 | <td valign="bottom"><small><%=l(:field_priority)%>:</small><br /><%= search_filter_tag 'priority_id', :class => 'select-small' %></td> |
|
|||
16 | <td valign="bottom"><small><%=l(:field_category)%>:</small><br /><%= search_filter_tag 'category_id', :class => 'select-small' %></td> |
|
|||
17 | <td valign="bottom"><small><%=l(:field_fixed_version)%>:</small><br /><%= search_filter_tag 'fixed_version_id', :class => 'select-small' %></td> |
|
|||
18 | <td valign="bottom"><small><%=l(:field_author)%>:</small><br /><%= search_filter_tag 'author_id', :class => 'select-small' %></td> |
|
|||
19 | <td valign="bottom"><small><%=l(:field_assigned_to)%>:</small><br /><%= search_filter_tag 'assigned_to_id', :class => 'select-small' %></td> |
|
|||
20 | <td valign="bottom"><small><%=l(:label_subproject_plural)%>:</small><br /><%= search_filter_tag 'subproject_id', :class => 'select-small' %></td> |
|
|||
21 | <td valign="bottom"> |
|
|||
22 | <%= hidden_field_tag 'set_filter', 1 %> |
|
|||
23 | <%= submit_tag l(:button_apply), :class => 'button-small' %> |
|
|||
24 | </td> |
|
|||
25 | <td valign="bottom"> |
|
|||
26 | <%= link_to l(:button_clear), :action => 'list_issues', :id => @project, :set_filter => 1 %> |
|
|||
27 | </td> |
|
|||
28 | </tr> |
|
|||
29 | </table> |
|
|||
30 | <%= end_form_tag %> |
|
14 | <%= end_form_tag %> | |
31 |
|
15 | </div> | ||
32 | |
|
16 | | |
33 | <table class="listTableContent"> |
|
17 | <table class="listTableContent"> | |
34 | <tr> |
|
18 | <tr> |
General Comments 0
You need to be logged in to leave comments.
Login now