##// END OF EJS Templates
added back "fixed version" in issue/show and filters...
Jean-Philippe Lang -
r162:a8137d5d1780
parent child
Show More
@@ -1,166 +1,167
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Query < ActiveRecord::Base
19 19 belongs_to :project
20 20 belongs_to :user
21 21 serialize :filters
22 22
23 23 attr_protected :project, :user
24 24
25 25 validates_presence_of :name, :on => :save
26 26
27 27 @@operators = { "=" => :label_equals,
28 28 "!" => :label_not_equals,
29 29 "o" => :label_open_issues,
30 30 "c" => :label_closed_issues,
31 31 "!*" => :label_none,
32 32 "*" => :label_all,
33 33 "<t+" => :label_in_less_than,
34 34 ">t+" => :label_in_more_than,
35 35 "t+" => :label_in,
36 36 "t" => :label_today,
37 37 ">t-" => :label_less_than_ago,
38 38 "<t-" => :label_more_than_ago,
39 39 "t-" => :label_ago,
40 40 "~" => :label_contains,
41 41 "!~" => :label_not_contains }
42 42
43 43 cattr_reader :operators
44 44
45 45 @@operators_by_filter_type = { :list => [ "=", "!" ],
46 46 :list_status => [ "o", "=", "!", "c", "*" ],
47 47 :list_optional => [ "=", "!", "!*", "*" ],
48 48 :date => [ "<t+", ">t+", "t+", "t", ">t-", "<t-", "t-" ],
49 49 :date_past => [ ">t-", "<t-", "t-", "t" ],
50 50 :text => [ "~", "!~" ] }
51 51
52 52 cattr_reader :operators_by_filter_type
53 53
54 54 def initialize(attributes = nil)
55 55 super attributes
56 56 self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
57 57 self.is_public = true
58 58 end
59 59
60 60 def validate
61 61 filters.each_key do |field|
62 62 errors.add field.gsub(/\_id$/, ""), :activerecord_error_blank unless
63 63 # filter requires one or more values
64 64 (values_for(field) and !values_for(field).first.empty?) or
65 65 # filter doesn't require any value
66 66 ["o", "c", "!*", "*", "t"].include? operator_for(field)
67 67 end if filters
68 68 end
69 69
70 70 def available_filters
71 71 return @available_filters if @available_filters
72 72 @available_filters = { "status_id" => { :type => :list_status, :order => 1, :values => IssueStatus.find(:all).collect{|s| [s.name, s.id.to_s] } },
73 73 "tracker_id" => { :type => :list, :order => 2, :values => Tracker.find(:all).collect{|s| [s.name, s.id.to_s] } },
74 74 "priority_id" => { :type => :list, :order => 3, :values => Enumeration.find(:all, :conditions => ['opt=?','IPRI']).collect{|s| [s.name, s.id.to_s] } },
75 "subject" => { :type => :text, :order => 7 },
76 "created_on" => { :type => :date_past, :order => 8 },
77 "updated_on" => { :type => :date_past, :order => 9 },
78 "start_date" => { :type => :date, :order => 10 },
79 "due_date" => { :type => :date, :order => 11 } }
75 "subject" => { :type => :text, :order => 8 },
76 "created_on" => { :type => :date_past, :order => 9 },
77 "updated_on" => { :type => :date_past, :order => 10 },
78 "start_date" => { :type => :date, :order => 11 },
79 "due_date" => { :type => :date, :order => 12 } }
80 80 unless project.nil?
81 81 # project specific filters
82 82 @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => @project.users.collect{|s| [s.name, s.id.to_s] } }
83 83 @available_filters["author_id"] = { :type => :list, :order => 5, :values => @project.users.collect{|s| [s.name, s.id.to_s] } }
84 84 @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } }
85 @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.collect{|s| [s.name, s.id.to_s] } }
85 86 # remove category filter if no category defined
86 87 @available_filters.delete "category_id" if @available_filters["category_id"][:values].empty?
87 88 end
88 89 @available_filters
89 90 end
90 91
91 92 def add_filter(field, operator, values)
92 93 # values must be an array
93 94 return unless values and values.is_a? Array # and !values.first.empty?
94 95 # check if field is defined as an available filter
95 96 if available_filters.has_key? field
96 97 filter_options = available_filters[field]
97 98 # check if operator is allowed for that filter
98 99 #if @@operators_by_filter_type[filter_options[:type]].include? operator
99 100 # allowed_values = values & ([""] + (filter_options[:values] || []).collect {|val| val[1]})
100 101 # filters[field] = {:operator => operator, :values => allowed_values } if (allowed_values.first and !allowed_values.first.empty?) or ["o", "c", "!*", "*", "t"].include? operator
101 102 #end
102 103 filters[field] = {:operator => operator, :values => values }
103 104 end
104 105 end
105 106
106 107 def add_short_filter(field, expression)
107 108 return unless expression
108 109 parms = expression.scan(/^(o|c|\!|\*)?(.*)$/).first
109 110 add_filter field, (parms[0] || "="), [parms[1] || ""]
110 111 end
111 112
112 113 def has_filter?(field)
113 114 filters and filters[field]
114 115 end
115 116
116 117 def operator_for(field)
117 118 has_filter?(field) ? filters[field][:operator] : nil
118 119 end
119 120
120 121 def values_for(field)
121 122 has_filter?(field) ? filters[field][:values] : nil
122 123 end
123 124
124 125 def statement
125 126 sql = "1=1"
126 127 sql << " AND issues.project_id=%d" % project.id if project
127 128 filters.each_key do |field|
128 129 v = values_for field
129 130 next unless v and !v.empty?
130 131 sql = sql + " AND " unless sql.empty?
131 132 case operator_for field
132 133 when "="
133 134 sql = sql + "issues.#{field} IN (" + v.each(&:to_i).join(",") + ")"
134 135 when "!"
135 136 sql = sql + "issues.#{field} NOT IN (" + v.each(&:to_i).join(",") + ")"
136 137 when "!*"
137 138 sql = sql + "issues.#{field} IS NULL"
138 139 when "*"
139 140 sql = sql + "issues.#{field} IS NOT NULL"
140 141 when "o"
141 142 sql = sql + "issue_statuses.is_closed=#{connection.quoted_false}" if field == "status_id"
142 143 when "c"
143 144 sql = sql + "issue_statuses.is_closed=#{connection.quoted_true}" if field == "status_id"
144 145 when ">t-"
145 146 sql = sql + "issues.#{field} >= '%s'" % connection.quoted_date(Date.today - v.first.to_i)
146 147 when "<t-"
147 148 sql = sql + "issues.#{field} <= '" + (Date.today - v.first.to_i).strftime("%Y-%m-%d") + "'"
148 149 when "t-"
149 150 sql = sql + "issues.#{field} = '" + (Date.today - v.first.to_i).strftime("%Y-%m-%d") + "'"
150 151 when ">t+"
151 152 sql = sql + "issues.#{field} >= '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'"
152 153 when "<t+"
153 154 sql = sql + "issues.#{field} <= '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'"
154 155 when "t+"
155 156 sql = sql + "issues.#{field} = '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'"
156 157 when "t"
157 158 sql = sql + "issues.#{field} = '%s'" % connection.quoted_date(Date.today)
158 159 when "~"
159 160 sql = sql + "issues.#{field} LIKE '%#{connection.quote_string(v.first)}%'"
160 161 when "!~"
161 162 sql = sql + "issues.#{field} NOT LIKE '%#{connection.quote_string(v.first)}%'"
162 163 end
163 164 end if filters and valid?
164 165 sql
165 166 end
166 167 end
@@ -1,107 +1,111
1 1 <div class="contextual">
2 2 <%= l(:label_export_to) %><%= link_to 'PDF', {:action => 'export_pdf', :id => @issue}, :class => 'icon icon-pdf' %>
3 3 </div>
4 4
5 5 <h2><%= @issue.tracker.name %> #<%= @issue.id %> - <%=h @issue.subject %></h2>
6 6
7 7 <div class="box">
8 8 <table width="100%">
9 9 <tr>
10 10 <td style="width:15%"><b><%=l(:field_status)%> :</b></td><td style="width:35%"><%= @issue.status.name %></td>
11 11 <td style="width:15%"><b><%=l(:field_priority)%> :</b></td><td style="width:35%"><%= @issue.priority.name %></td>
12 12 </tr>
13 13 <tr>
14 14 <td><b><%=l(:field_assigned_to)%> :</b></td><td><%= @issue.assigned_to ? @issue.assigned_to.name : "-" %></td>
15 15 <td><b><%=l(:field_category)%> :</b></td><td><%=h @issue.category ? @issue.category.name : "-" %></td>
16 16 </tr>
17 17 <tr>
18 18 <td><b><%=l(:field_author)%> :</b></td><td><%= link_to_user @issue.author %></td>
19 19 <td><b><%=l(:field_start_date)%> :</b></td><td><%= format_date(@issue.start_date) %></td>
20 20 </tr>
21 21 <tr>
22 22 <td><b><%=l(:field_created_on)%> :</b></td><td><%= format_date(@issue.created_on) %></td>
23 23 <td><b><%=l(:field_due_date)%> :</b></td><td><%= format_date(@issue.due_date) %></td>
24 24 </tr>
25 25 <tr>
26 26 <td><b><%=l(:field_updated_on)%> :</b></td><td><%= format_date(@issue.updated_on) %></td>
27 27 <td><b><%=l(:field_done_ratio)%> :</b></td><td><%= @issue.done_ratio %> %</td>
28 28 </tr>
29 29 <tr>
30 <td><b><%=l(:field_fixed_version)%> :</b></td><td><%= @issue.fixed_version ? @issue.fixed_version.name : "-" %></td>
31 <td></td><td></td>
32 </tr>
33 <tr>
30 34 <% n = 0
31 35 for custom_value in @custom_values %>
32 36 <td><b><%= custom_value.custom_field.name %> :</b></td><td><%=h show_value custom_value %></td>
33 37 <% n = n + 1
34 38 if (n > 1)
35 39 n = 0 %>
36 40 </tr><tr>
37 41 <%end
38 42 end %>
39 43 </tr>
40 44 </table>
41 45 <hr />
42 46 <br />
43 47
44 48 <b><%=l(:field_description)%> :</b><br /><br />
45 49 <%= textilizable @issue.description %>
46 50 <br />
47 51
48 52 <div class="contextual">
49 53 <%= link_to_if_authorized l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue}, :class => 'icon icon-edit' %>
50 54 <%= link_to_if_authorized l(:button_move), {:controller => 'projects', :action => 'move_issues', :id => @project, "issue_ids[]" => @issue.id }, :class => 'icon icon-move' %>
51 55 <%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => l(:text_are_you_sure), :post => true, :class => 'icon icon-del' %>
52 56 </div>
53 57
54 58 <% if authorize_for('issues', 'change_status') and @status_options and !@status_options.empty? %>
55 59 <%= start_form_tag ({:controller => 'issues', :action => 'change_status', :id => @issue}) %>
56 60 <%=l(:label_change_status)%> :
57 61 <select name="new_status_id">
58 62 <%= options_from_collection_for_select @status_options, "id", "name" %>
59 63 </select>
60 64 <%= submit_tag l(:button_change) %>
61 65 <%= end_form_tag %>
62 66 <% end %>
63 67 &nbsp;
64 68 </div>
65 69
66 70 <div id="history" class="box">
67 71 <h3><%=l(:label_history)%>
68 72 <% if @journals_count > @journals.length %>(<%= l(:label_last_changes, @journals.length) %>)<% end %></h3>
69 73 <%= render :partial => 'history', :locals => { :journals => @journals } %>
70 74 <% if @journals_count > @journals.length %>
71 75 <p><center><small>[ <%= link_to l(:label_change_view_all), :action => 'history', :id => @issue %> ]</small></center></p>
72 76 <% end %>
73 77 </div>
74 78
75 79 <div class="box">
76 80 <h3><%=l(:label_attachment_plural)%></h3>
77 81 <table width="100%">
78 82 <% for attachment in @issue.attachments %>
79 83 <tr>
80 84 <td><%= link_to attachment.filename, { :action => 'download', :id => @issue, :attachment_id => attachment }, :class => 'icon icon-attachment' %> (<%= human_size(attachment.filesize) %>)</td>
81 85 <td><%= format_date(attachment.created_on) %></td>
82 86 <td><%= attachment.author.display_name %></td>
83 87 <td><div class="contextual"><%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy_attachment', :id => @issue, :attachment_id => attachment }, :confirm => l(:text_are_you_sure), :post => true, :class => 'icon icon-del' %></div></td>
84 88 </tr>
85 89 <% end %>
86 90 </table>
87 91 <br />
88 92 <% if authorize_for('issues', 'add_attachment') %>
89 93 <%= start_form_tag ({ :controller => 'issues', :action => 'add_attachment', :id => @issue }, :multipart => true, :class => "tabular") %>
90 94 <p id="attachments_p"><label><%=l(:label_attachment_new)%>
91 95 <%= image_to_function "add.png", "addFileField();return false" %></label>
92 96 <%= file_field_tag 'attachments[]', :size => 30 %> <em>(<%= l(:label_max_size) %>: <%= human_size(Attachment.max_size) %>)</em></p>
93 97 <%= submit_tag l(:button_add) %>
94 98 <%= end_form_tag %>
95 99 <% end %>
96 100 </div>
97 101
98 102 <% if authorize_for('issues', 'add_note') %>
99 103 <div class="box">
100 104 <h3><%= l(:label_add_note) %></h3>
101 105 <%= start_form_tag ({:controller => 'issues', :action => 'add_note', :id => @issue}, :class => "tabular" ) %>
102 106 <p><label for="notes"><%=l(:field_notes)%></label>
103 107 <%= text_area_tag 'notes', '', :cols => 60, :rows => 10 %></p>
104 108 <%= submit_tag l(:button_add) %>
105 109 <%= end_form_tag %>
106 110 </div>
107 111 <% end %>
General Comments 0
You need to be logged in to leave comments. Login now