@@ -0,0 +1,9 | |||||
|
1 | class AddCustomFieldIsFilter < ActiveRecord::Migration | |||
|
2 | def self.up | |||
|
3 | add_column :custom_fields, :is_filter, :boolean, :null => false, :default => false | |||
|
4 | end | |||
|
5 | ||||
|
6 | def self.down | |||
|
7 | remove_column :custom_fields, :is_filter | |||
|
8 | end | |||
|
9 | end |
@@ -259,10 +259,10 class ProjectsController < ApplicationController | |||||
259 | end |
|
259 | end | |
260 |
|
260 | |||
261 | if @query.valid? |
|
261 | if @query.valid? | |
262 | @issue_count = Issue.count(:include => [:status, :project], :conditions => @query.statement) |
|
262 | @issue_count = Issue.count(:include => [:status, :project, :custom_values], :conditions => @query.statement) | |
263 | @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page'] |
|
263 | @issue_pages = Paginator.new self, @issue_count, @results_per_page, params['page'] | |
264 | @issues = Issue.find :all, :order => sort_clause, |
|
264 | @issues = Issue.find :all, :order => sort_clause, | |
265 | :include => [ :assigned_to, :status, :tracker, :project, :priority ], |
|
265 | :include => [ :assigned_to, :status, :tracker, :project, :priority, :custom_values ], | |
266 | :conditions => @query.statement, |
|
266 | :conditions => @query.statement, | |
267 | :limit => @issue_pages.items_per_page, |
|
267 | :limit => @issue_pages.items_per_page, | |
268 | :offset => @issue_pages.current.offset |
|
268 | :offset => @issue_pages.current.offset |
@@ -1,5 +1,5 | |||||
1 | # redMine - project management software |
|
1 | # redMine - project management software | |
2 | # Copyright (C) 2006 Jean-Philippe Lang |
|
2 | # Copyright (C) 2006-2007 Jean-Philippe Lang | |
3 | # |
|
3 | # | |
4 | # This program is free software; you can redistribute it and/or |
|
4 | # This program is free software; you can redistribute it and/or | |
5 | # modify it under the terms of the GNU General Public License |
|
5 | # modify it under the terms of the GNU General Public License | |
@@ -48,6 +48,7 class Query < ActiveRecord::Base | |||||
48 | :list_one_or_more => [ "*", "=" ], |
|
48 | :list_one_or_more => [ "*", "=" ], | |
49 | :date => [ "<t+", ">t+", "t+", "t", ">t-", "<t-", "t-" ], |
|
49 | :date => [ "<t+", ">t+", "t+", "t", ">t-", "<t-", "t-" ], | |
50 | :date_past => [ ">t-", "<t-", "t-", "t" ], |
|
50 | :date_past => [ ">t-", "<t-", "t-", "t" ], | |
|
51 | :string => [ "=", "~", "!", "!~" ], | |||
51 | :text => [ "~", "!~" ] } |
|
52 | :text => [ "~", "!~" ] } | |
52 |
|
53 | |||
53 | cattr_reader :operators_by_filter_type |
|
54 | cattr_reader :operators_by_filter_type | |
@@ -60,7 +61,7 class Query < ActiveRecord::Base | |||||
60 |
|
61 | |||
61 | def validate |
|
62 | def validate | |
62 | filters.each_key do |field| |
|
63 | filters.each_key do |field| | |
63 |
errors.add field |
|
64 | errors.add label_for(field), :activerecord_error_blank unless | |
64 | # filter requires one or more values |
|
65 | # filter requires one or more values | |
65 | (values_for(field) and !values_for(field).first.empty?) or |
|
66 | (values_for(field) and !values_for(field).first.empty?) or | |
66 | # filter doesn't require any value |
|
67 | # filter doesn't require any value | |
@@ -87,6 +88,21 class Query < ActiveRecord::Base | |||||
87 | unless @project.children.empty? |
|
88 | unless @project.children.empty? | |
88 | @available_filters["subproject_id"] = { :type => :list_one_or_more, :order => 13, :values => @project.children.collect{|s| [s.name, s.id.to_s] } } |
|
89 | @available_filters["subproject_id"] = { :type => :list_one_or_more, :order => 13, :values => @project.children.collect{|s| [s.name, s.id.to_s] } } | |
89 | end |
|
90 | end | |
|
91 | @project.all_custom_fields.select(&:is_filter?).each do |field| | |||
|
92 | case field.field_format | |||
|
93 | when "string", "int" | |||
|
94 | options = { :type => :string, :order => 20 } | |||
|
95 | when "text" | |||
|
96 | options = { :type => :text, :order => 20 } | |||
|
97 | when "list" | |||
|
98 | options = { :type => :list_optional, :values => field.possible_values, :order => 20} | |||
|
99 | when "date" | |||
|
100 | options = { :type => :date, :order => 20 } | |||
|
101 | when "bool" | |||
|
102 | options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 } | |||
|
103 | end | |||
|
104 | @available_filters["cf_#{field.id}"] = options.merge({ :name => field.name }) | |||
|
105 | end | |||
90 | # remove category filter if no category defined |
|
106 | # remove category filter if no category defined | |
91 | @available_filters.delete "category_id" if @available_filters["category_id"][:values].empty? |
|
107 | @available_filters.delete "category_id" if @available_filters["category_id"][:values].empty? | |
92 | end |
|
108 | end | |
@@ -126,6 +142,11 class Query < ActiveRecord::Base | |||||
126 | has_filter?(field) ? filters[field][:values] : nil |
|
142 | has_filter?(field) ? filters[field][:values] : nil | |
127 | end |
|
143 | end | |
128 |
|
144 | |||
|
145 | def label_for(field) | |||
|
146 | label = @available_filters[field][:name] if @available_filters.has_key?(field) | |||
|
147 | label ||= field.gsub(/\_id$/, "") | |||
|
148 | end | |||
|
149 | ||||
129 | def statement |
|
150 | def statement | |
130 | sql = "1=1" |
|
151 | sql = "1=1" | |
131 | if has_filter?("subproject_id") |
|
152 | if has_filter?("subproject_id") | |
@@ -142,40 +163,56 class Query < ActiveRecord::Base | |||||
142 | filters.each_key do |field| |
|
163 | filters.each_key do |field| | |
143 | next if field == "subproject_id" |
|
164 | next if field == "subproject_id" | |
144 | v = values_for field |
|
165 | v = values_for field | |
145 |
next unless v and !v.empty? |
|
166 | next unless v and !v.empty? | |
|
167 | ||||
146 | sql = sql + " AND " unless sql.empty? |
|
168 | sql = sql + " AND " unless sql.empty? | |
|
169 | sql << "(" | |||
|
170 | ||||
|
171 | if field =~ /^cf_(\d+)$/ | |||
|
172 | # custom field | |||
|
173 | db_table = CustomValue.table_name | |||
|
174 | db_field = "value" | |||
|
175 | sql << "#{db_table}.custom_field_id = #{$1} AND " | |||
|
176 | else | |||
|
177 | # regular field | |||
|
178 | db_table = Issue.table_name | |||
|
179 | db_field = field | |||
|
180 | end | |||
|
181 | ||||
147 | case operator_for field |
|
182 | case operator_for field | |
148 | when "=" |
|
183 | when "=" | |
149 |
sql = sql + "#{ |
|
184 | sql = sql + "#{db_table}.#{db_field} IN (" + v.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")" | |
150 | when "!" |
|
185 | when "!" | |
151 |
sql = sql + "#{ |
|
186 | sql = sql + "#{db_table}.#{db_field} NOT IN (" + v.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")" | |
152 | when "!*" |
|
187 | when "!*" | |
153 |
sql = sql + "#{ |
|
188 | sql = sql + "#{db_table}.#{db_field} IS NULL" | |
154 | when "*" |
|
189 | when "*" | |
155 |
sql = sql + "#{ |
|
190 | sql = sql + "#{db_table}.#{db_field} IS NOT NULL" | |
156 | when "o" |
|
191 | when "o" | |
157 | sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id" |
|
192 | sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_false}" if field == "status_id" | |
158 | when "c" |
|
193 | when "c" | |
159 | sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_true}" if field == "status_id" |
|
194 | sql = sql + "#{IssueStatus.table_name}.is_closed=#{connection.quoted_true}" if field == "status_id" | |
160 | when ">t-" |
|
195 | when ">t-" | |
161 |
sql = sql + "#{ |
|
196 | sql = sql + "#{db_table}.#{db_field} >= '%s'" % connection.quoted_date(Date.today - v.first.to_i) | |
162 | when "<t-" |
|
197 | when "<t-" | |
163 |
sql = sql + "#{ |
|
198 | sql = sql + "#{db_table}.#{db_field} BETWEEN '#{connection.quoted_date(Date.new(0))}' AND '" + (Date.today - v.first.to_i).strftime("%Y-%m-%d") + "'" | |
164 | when "t-" |
|
199 | when "t-" | |
165 |
sql = sql + "#{ |
|
200 | sql = sql + "#{db_table}.#{db_field} = '" + (Date.today - v.first.to_i).strftime("%Y-%m-%d") + "'" | |
166 | when ">t+" |
|
201 | when ">t+" | |
167 |
sql = sql + "#{ |
|
202 | sql = sql + "#{db_table}.#{db_field} >= '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'" | |
168 | when "<t+" |
|
203 | when "<t+" | |
169 |
sql = sql + "#{ |
|
204 | sql = sql + "#{db_table}.#{db_field} BETWEEN '#{connection.quoted_date(Date.new(0))}' AND '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'" | |
170 | when "t+" |
|
205 | when "t+" | |
171 |
sql = sql + "#{ |
|
206 | sql = sql + "#{db_table}.#{db_field} = '" + (Date.today + v.first.to_i).strftime("%Y-%m-%d") + "'" | |
172 | when "t" |
|
207 | when "t" | |
173 |
sql = sql + "#{ |
|
208 | sql = sql + "#{db_table}.#{db_field} = '%s'" % connection.quoted_date(Date.today) | |
174 | when "~" |
|
209 | when "~" | |
175 |
sql = sql + "#{ |
|
210 | sql = sql + "#{db_table}.#{db_field} LIKE '%#{connection.quote_string(v.first)}%'" | |
176 | when "!~" |
|
211 | when "!~" | |
177 |
sql = sql + "#{ |
|
212 | sql = sql + "#{db_table}.#{db_field} NOT LIKE '%#{connection.quote_string(v.first)}%'" | |
178 | end |
|
213 | end | |
|
214 | sql << ")" | |||
|
215 | ||||
179 | end if filters and valid? |
|
216 | end if filters and valid? | |
180 | sql |
|
217 | sql | |
181 | end |
|
218 | end |
@@ -84,6 +84,7 when "IssueCustomField" %> | |||||
84 | |
|
84 | | |
85 | <p><%= f.check_box :is_required %></p> |
|
85 | <p><%= f.check_box :is_required %></p> | |
86 | <p><%= f.check_box :is_for_all %></p> |
|
86 | <p><%= f.check_box :is_for_all %></p> | |
|
87 | <p><%= f.check_box :is_filter %></p> | |||
87 |
|
88 | |||
88 | <% when "UserCustomField" %> |
|
89 | <% when "UserCustomField" %> | |
89 | <p><%= f.check_box :is_required %></p> |
|
90 | <p><%= f.check_box :is_required %></p> |
@@ -66,7 +66,7 function toggle_multi_select(field) { | |||||
66 | <tr <%= 'style="display:none;"' unless query.has_filter?(field) %> id="tr_<%= field %>"> |
|
66 | <tr <%= 'style="display:none;"' unless query.has_filter?(field) %> id="tr_<%= field %>"> | |
67 | <td valign="top" style="width:200px;"> |
|
67 | <td valign="top" style="width:200px;"> | |
68 | <%= check_box_tag 'fields[]', field, query.has_filter?(field), :onclick => "toggle_filter('#{field}');", :id => "cb_#{field}" %> |
|
68 | <%= check_box_tag 'fields[]', field, query.has_filter?(field), :onclick => "toggle_filter('#{field}');", :id => "cb_#{field}" %> | |
69 | <label for="cb_<%= field %>"><%= l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) %></label> |
|
69 | <label for="cb_<%= field %>"><%= filter[1][:name] || l(("field_"+field.to_s.gsub(/\_id$/, "")).to_sym) %></label> | |
70 | </td> |
|
70 | </td> | |
71 | <td valign="top" style="width:150px;"> |
|
71 | <td valign="top" style="width:150px;"> | |
72 | <%= select_tag "operators[#{field}]", options_for_select(operators_for_select(options[:type]), query.operator_for(field)), :id => "operators_#{field}", :onchange => "toggle_operator('#{field}');", :class => "select-small", :style => "vertical-align: top;" %> |
|
72 | <%= select_tag "operators[#{field}]", options_for_select(operators_for_select(options[:type]), query.operator_for(field)), :id => "operators_#{field}", :onchange => "toggle_operator('#{field}');", :class => "select-small", :style => "vertical-align: top;" %> | |
@@ -81,7 +81,7 function toggle_multi_select(field) { | |||||
81 | <%= link_to_function image_tag('expand.png'), "toggle_multi_select('#{field}');" %> |
|
81 | <%= link_to_function image_tag('expand.png'), "toggle_multi_select('#{field}');" %> | |
82 | <% when :date, :date_past %> |
|
82 | <% when :date, :date_past %> | |
83 | <%= text_field_tag "values[#{field}][]", query.values_for(field), :id => "values_#{field}", :size => 3, :class => "select-small" %> <%= l(:label_day_plural) %> |
|
83 | <%= text_field_tag "values[#{field}][]", query.values_for(field), :id => "values_#{field}", :size => 3, :class => "select-small" %> <%= l(:label_day_plural) %> | |
84 | <% when :text %> |
|
84 | <% when :string, :text %> | |
85 | <%= text_field_tag "values[#{field}][]", query.values_for(field), :id => "values_#{field}", :size => 30, :class => "select-small" %> |
|
85 | <%= text_field_tag "values[#{field}][]", query.values_for(field), :id => "values_#{field}", :size => 30, :class => "select-small" %> | |
86 | <% end %> |
|
86 | <% end %> | |
87 | </div> |
|
87 | </div> | |
@@ -93,7 +93,7 function toggle_multi_select(field) { | |||||
93 | </td> |
|
93 | </td> | |
94 | <td align="right" valign="top"> |
|
94 | <td align="right" valign="top"> | |
95 | <%= l(:label_filter_add) %>: |
|
95 | <%= l(:label_filter_add) %>: | |
96 | <%= select_tag 'add_filter_select', options_for_select([["",""]] + query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.collect{|field| [l(("field_"+field[0].to_s.gsub(/\_id$/, "")).to_sym), field[0]] unless query.has_filter?(field[0])}.compact), :onchange => "add_filter();", :class => "select-small" %> |
|
96 | <%= select_tag 'add_filter_select', options_for_select([["",""]] + query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.collect{|field| [ field[1][:name] || l(("field_"+field[0].to_s.gsub(/\_id$/, "")).to_sym), field[0]] unless query.has_filter?(field[0])}.compact), :onchange => "add_filter();", :class => "select-small" %> | |
97 | </td> |
|
97 | </td> | |
98 | </tr> |
|
98 | </tr> | |
99 | </table> |
|
99 | </table> |
@@ -147,6 +147,7 field_hours: Stunden | |||||
147 | field_activity: Aktivität |
|
147 | field_activity: Aktivität | |
148 | field_spent_on: Datum |
|
148 | field_spent_on: Datum | |
149 | field_identifier: Identifier |
|
149 | field_identifier: Identifier | |
|
150 | field_is_filter: Used as a filter | |||
150 |
|
151 | |||
151 | setting_app_title: Applikation Titel |
|
152 | setting_app_title: Applikation Titel | |
152 | setting_app_subtitle: Applikation Untertitel |
|
153 | setting_app_subtitle: Applikation Untertitel |
@@ -147,6 +147,7 field_hours: Hours | |||||
147 | field_activity: Activity |
|
147 | field_activity: Activity | |
148 | field_spent_on: Date |
|
148 | field_spent_on: Date | |
149 | field_identifier: Identifier |
|
149 | field_identifier: Identifier | |
|
150 | field_is_filter: Used as a filter | |||
150 |
|
151 | |||
151 | setting_app_title: Application title |
|
152 | setting_app_title: Application title | |
152 | setting_app_subtitle: Application subtitle |
|
153 | setting_app_subtitle: Application subtitle |
@@ -147,6 +147,7 field_hours: Hours | |||||
147 | field_activity: Activity |
|
147 | field_activity: Activity | |
148 | field_spent_on: Fecha |
|
148 | field_spent_on: Fecha | |
149 | field_identifier: Identifier |
|
149 | field_identifier: Identifier | |
|
150 | field_is_filter: Used as a filter | |||
150 |
|
151 | |||
151 | setting_app_title: Título del aplicación |
|
152 | setting_app_title: Título del aplicación | |
152 | setting_app_subtitle: Subtítulo del aplicación |
|
153 | setting_app_subtitle: Subtítulo del aplicación |
@@ -147,6 +147,7 field_hours: Heures | |||||
147 | field_activity: Activité |
|
147 | field_activity: Activité | |
148 | field_spent_on: Date |
|
148 | field_spent_on: Date | |
149 | field_identifier: Identifiant |
|
149 | field_identifier: Identifiant | |
|
150 | field_is_filter: Utilisé comme filtre | |||
150 |
|
151 | |||
151 | setting_app_title: Titre de l'application |
|
152 | setting_app_title: Titre de l'application | |
152 | setting_app_subtitle: Sous-titre de l'application |
|
153 | setting_app_subtitle: Sous-titre de l'application |
@@ -147,6 +147,7 field_hours: Hours | |||||
147 | field_activity: Activity |
|
147 | field_activity: Activity | |
148 | field_spent_on: Data |
|
148 | field_spent_on: Data | |
149 | field_identifier: Identifier |
|
149 | field_identifier: Identifier | |
|
150 | field_is_filter: Used as a filter | |||
150 |
|
151 | |||
151 | setting_app_title: Titolo applicazione |
|
152 | setting_app_title: Titolo applicazione | |
152 | setting_app_subtitle: Sottotitolo applicazione |
|
153 | setting_app_subtitle: Sottotitolo applicazione |
@@ -148,6 +148,7 field_hours: 時間 | |||||
148 | field_activity: 活動 |
|
148 | field_activity: 活動 | |
149 | field_spent_on: 日付 |
|
149 | field_spent_on: 日付 | |
150 | field_identifier: 識別子 |
|
150 | field_identifier: 識別子 | |
|
151 | field_is_filter: Used as a filter | |||
151 |
|
152 | |||
152 | setting_app_title: アプリケーションのタイトル |
|
153 | setting_app_title: アプリケーションのタイトル | |
153 | setting_app_subtitle: アプリケーションのサブタイトル |
|
154 | setting_app_subtitle: アプリケーションのサブタイトル |
@@ -150,6 +150,7 field_hours: Hours | |||||
150 | field_activity: 活动 |
|
150 | field_activity: 活动 | |
151 | field_spent_on: 日期 |
|
151 | field_spent_on: 日期 | |
152 | field_identifier: Identifier |
|
152 | field_identifier: Identifier | |
|
153 | field_is_filter: Used as a filter | |||
153 |
|
154 | |||
154 | setting_app_title: 应用程序标题 |
|
155 | setting_app_title: 应用程序标题 | |
155 | setting_app_subtitle: 应用程序子标题 |
|
156 | setting_app_subtitle: 应用程序子标题 |
General Comments 0
You need to be logged in to leave comments.
Login now