@@ -17,7 +17,7 | |||||
17 |
|
17 | |||
18 | class QueriesController < ApplicationController |
|
18 | class QueriesController < ApplicationController | |
19 | menu_item :issues |
|
19 | menu_item :issues | |
20 |
before_action :find_query, : |
|
20 | before_action :find_query, :only => [:edit, :update, :destroy] | |
21 | before_action :find_optional_project, :only => [:new, :create] |
|
21 | before_action :find_optional_project, :only => [:new, :create] | |
22 |
|
22 | |||
23 | accept_api_auth :index |
|
23 | accept_api_auth :index | |
@@ -85,6 +85,25 class QueriesController < ApplicationController | |||||
85 | redirect_to_items(:set_filter => 1) |
|
85 | redirect_to_items(:set_filter => 1) | |
86 | end |
|
86 | end | |
87 |
|
87 | |||
|
88 | # Returns the values for a query filter | |||
|
89 | def filter | |||
|
90 | q = query_class.new | |||
|
91 | if params[:project_id].present? | |||
|
92 | q.project = Project.find(params[:project_id]) | |||
|
93 | end | |||
|
94 | ||||
|
95 | unless User.current.allowed_to?(q.class.view_permission, q.project, :global => true) | |||
|
96 | raise Unauthorized | |||
|
97 | end | |||
|
98 | ||||
|
99 | filter = q.available_filters[params[:name].to_s] | |||
|
100 | values = filter ? filter.values : [] | |||
|
101 | ||||
|
102 | render :json => values | |||
|
103 | rescue ActiveRecord::RecordNotFound | |||
|
104 | render_404 | |||
|
105 | end | |||
|
106 | ||||
88 | private |
|
107 | private | |
89 |
|
108 | |||
90 | def find_query |
|
109 | def find_query |
@@ -78,80 +78,37 class IssueQuery < Query | |||||
78 | end |
|
78 | end | |
79 |
|
79 | |||
80 | def initialize_available_filters |
|
80 | def initialize_available_filters | |
81 | principals = [] |
|
|||
82 | subprojects = [] |
|
|||
83 | versions = [] |
|
|||
84 | categories = [] |
|
|||
85 | issue_custom_fields = [] |
|
|||
86 |
|
||||
87 | if project |
|
|||
88 | principals += project.principals.visible |
|
|||
89 | unless project.leaf? |
|
|||
90 | subprojects = project.descendants.visible.to_a |
|
|||
91 | principals += Principal.member_of(subprojects).visible |
|
|||
92 | end |
|
|||
93 | versions = project.shared_versions.to_a |
|
|||
94 | categories = project.issue_categories.to_a |
|
|||
95 | issue_custom_fields = project.all_issue_custom_fields |
|
|||
96 | else |
|
|||
97 | if all_projects.any? |
|
|||
98 | principals += Principal.member_of(all_projects).visible |
|
|||
99 | end |
|
|||
100 | versions = Version.visible.where(:sharing => 'system').to_a |
|
|||
101 | issue_custom_fields = IssueCustomField.where(:is_for_all => true) |
|
|||
102 | end |
|
|||
103 | principals.uniq! |
|
|||
104 | principals.sort! |
|
|||
105 | principals.reject! {|p| p.is_a?(GroupBuiltin)} |
|
|||
106 | users = principals.select {|p| p.is_a?(User)} |
|
|||
107 |
|
||||
108 | add_available_filter "status_id", |
|
81 | add_available_filter "status_id", | |
109 | :type => :list_status, :values => IssueStatus.sorted.collect{|s| [s.name, s.id.to_s] } |
|
82 | :type => :list_status, :values => lambda { IssueStatus.sorted.collect{|s| [s.name, s.id.to_s] } } | |
110 |
|
83 | |||
111 | if project.nil? |
|
84 | add_available_filter("project_id", | |
112 | project_values = [] |
|
85 | :type => :list, :values => lambda { project_values } | |
113 | if User.current.logged? && User.current.memberships.any? |
|
86 | ) if project.nil? | |
114 | project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"] |
|
|||
115 | end |
|
|||
116 | project_values += all_projects_values |
|
|||
117 | add_available_filter("project_id", |
|
|||
118 | :type => :list, :values => project_values |
|
|||
119 | ) unless project_values.empty? |
|
|||
120 | end |
|
|||
121 |
|
87 | |||
122 | add_available_filter "tracker_id", |
|
88 | add_available_filter "tracker_id", | |
123 | :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] } |
|
89 | :type => :list, :values => trackers.collect{|s| [s.name, s.id.to_s] } | |
|
90 | ||||
124 | add_available_filter "priority_id", |
|
91 | add_available_filter "priority_id", | |
125 | :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } |
|
92 | :type => :list, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] } | |
126 |
|
93 | |||
127 | author_values = [] |
|
|||
128 | author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? |
|
|||
129 | author_values += users.collect{|s| [s.name, s.id.to_s] } |
|
|||
130 | add_available_filter("author_id", |
|
94 | add_available_filter("author_id", | |
131 | :type => :list, :values => author_values |
|
95 | :type => :list, :values => lambda { author_values } | |
132 | ) unless author_values.empty? |
|
96 | ) | |
133 |
|
97 | |||
134 | assigned_to_values = [] |
|
|||
135 | assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? |
|
|||
136 | assigned_to_values += (Setting.issue_group_assignment? ? |
|
|||
137 | principals : users).collect{|s| [s.name, s.id.to_s] } |
|
|||
138 | add_available_filter("assigned_to_id", |
|
98 | add_available_filter("assigned_to_id", | |
139 | :type => :list_optional, :values => assigned_to_values |
|
99 | :type => :list_optional, :values => lambda { assigned_to_values } | |
140 | ) unless assigned_to_values.empty? |
|
100 | ) | |
141 |
|
101 | |||
142 | group_values = Group.givable.visible.collect {|g| [g.name, g.id.to_s] } |
|
|||
143 | add_available_filter("member_of_group", |
|
102 | add_available_filter("member_of_group", | |
144 |
:type => :list_optional, :values => |
|
103 | :type => :list_optional, :values => lambda { Group.givable.visible.collect {|g| [g.name, g.id.to_s] } } | |
145 | ) unless group_values.empty? |
|
104 | ) | |
146 |
|
105 | |||
147 | role_values = Role.givable.collect {|r| [r.name, r.id.to_s] } |
|
|||
148 | add_available_filter("assigned_to_role", |
|
106 | add_available_filter("assigned_to_role", | |
149 |
:type => :list_optional, :values => |
|
107 | :type => :list_optional, :values => lambda { Role.givable.collect {|r| [r.name, r.id.to_s] } } | |
150 | ) unless role_values.empty? |
|
108 | ) | |
151 |
|
109 | |||
152 | add_available_filter "fixed_version_id", |
|
110 | add_available_filter "fixed_version_id", | |
153 | :type => :list_optional, |
|
111 | :type => :list_optional, :values => lambda { fixed_version_values } | |
154 | :values => Version.sort_by_status(versions).collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s, l("version_status_#{s.status}")] } |
|
|||
155 |
|
112 | |||
156 | add_available_filter "fixed_version.due_date", |
|
113 | add_available_filter "fixed_version.due_date", | |
157 | :type => :date, |
|
114 | :type => :date, | |
@@ -164,7 +121,7 class IssueQuery < Query | |||||
164 |
|
121 | |||
165 | add_available_filter "category_id", |
|
122 | add_available_filter "category_id", | |
166 | :type => :list_optional, |
|
123 | :type => :list_optional, | |
167 | :values => categories.collect{|s| [s.name, s.id.to_s] } |
|
124 | :values => lambda { project.issue_categories.collect{|s| [s.name, s.id.to_s] } } if project | |
168 |
|
125 | |||
169 | add_available_filter "subject", :type => :text |
|
126 | add_available_filter "subject", :type => :text | |
170 | add_available_filter "description", :type => :text |
|
127 | add_available_filter "description", :type => :text | |
@@ -188,18 +145,20 class IssueQuery < Query | |||||
188 | :type => :list, :values => [["<< #{l(:label_me)} >>", "me"]] |
|
145 | :type => :list, :values => [["<< #{l(:label_me)} >>", "me"]] | |
189 | end |
|
146 | end | |
190 |
|
147 | |||
191 |
if |
|
148 | if project && !project.leaf? | |
192 | add_available_filter "subproject_id", |
|
149 | add_available_filter "subproject_id", | |
193 | :type => :list_subprojects, |
|
150 | :type => :list_subprojects, | |
194 | :values => subprojects.collect{|s| [s.name, s.id.to_s] } |
|
151 | :values => lambda { subproject_values } | |
195 | end |
|
152 | end | |
196 |
|
153 | |||
|
154 | ||||
|
155 | issue_custom_fields = project ? project.all_issue_custom_fields : IssueCustomField.where(:is_for_all => true) | |||
197 | add_custom_fields_filters(issue_custom_fields) |
|
156 | add_custom_fields_filters(issue_custom_fields) | |
198 |
|
157 | |||
199 | add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version |
|
158 | add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version | |
200 |
|
159 | |||
201 | IssueRelation::TYPES.each do |relation_type, options| |
|
160 | IssueRelation::TYPES.each do |relation_type, options| | |
202 | add_available_filter relation_type, :type => :relation, :label => options[:name] |
|
161 | add_available_filter relation_type, :type => :relation, :label => options[:name], :values => lambda {all_projects_values} | |
203 | end |
|
162 | end | |
204 | add_available_filter "parent_id", :type => :tree, :label => :field_parent_issue |
|
163 | add_available_filter "parent_id", :type => :tree, :label => :field_parent_issue | |
205 | add_available_filter "child_id", :type => :tree, :label => :label_subtask_plural |
|
164 | add_available_filter "child_id", :type => :tree, :label => :label_subtask_plural |
@@ -160,6 +160,40 class QueryAssociationCustomFieldColumn < QueryCustomFieldColumn | |||||
160 | end |
|
160 | end | |
161 | end |
|
161 | end | |
162 |
|
162 | |||
|
163 | class QueryFilter | |||
|
164 | include Redmine::I18n | |||
|
165 | ||||
|
166 | def initialize(field, options) | |||
|
167 | @field = field.to_s | |||
|
168 | @options = options | |||
|
169 | @options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, '')) | |||
|
170 | # Consider filters with a Proc for values as remote by default | |||
|
171 | @remote = options.key?(:remote) ? options[:remote] : options[:values].is_a?(Proc) | |||
|
172 | end | |||
|
173 | ||||
|
174 | def [](arg) | |||
|
175 | if arg == :values | |||
|
176 | values | |||
|
177 | else | |||
|
178 | @options[arg] | |||
|
179 | end | |||
|
180 | end | |||
|
181 | ||||
|
182 | def values | |||
|
183 | @values ||= begin | |||
|
184 | values = @options[:values] | |||
|
185 | if values.is_a?(Proc) | |||
|
186 | values = values.call | |||
|
187 | end | |||
|
188 | values | |||
|
189 | end | |||
|
190 | end | |||
|
191 | ||||
|
192 | def remote | |||
|
193 | @remote | |||
|
194 | end | |||
|
195 | end | |||
|
196 | ||||
163 | class Query < ActiveRecord::Base |
|
197 | class Query < ActiveRecord::Base | |
164 | class StatementInvalid < ::ActiveRecord::StatementInvalid |
|
198 | class StatementInvalid < ::ActiveRecord::StatementInvalid | |
165 | end |
|
199 | end | |
@@ -404,12 +438,17 class Query < ActiveRecord::Base | |||||
404 | # Returns a representation of the available filters for JSON serialization |
|
438 | # Returns a representation of the available filters for JSON serialization | |
405 | def available_filters_as_json |
|
439 | def available_filters_as_json | |
406 | json = {} |
|
440 | json = {} | |
407 |
available_filters.each do |field, |
|
441 | available_filters.each do |field, filter| | |
408 | options = options.slice(:type, :name, :values) |
|
442 | options = {:type => filter[:type], :name => filter[:name]} | |
409 | if options[:values] && values_for(field) |
|
443 | options[:remote] = true if filter.remote | |
410 | missing = Array(values_for(field)).select(&:present?) - options[:values].map(&:last) |
|
444 | ||
411 | if missing.any? && respond_to?(method = "find_#{field}_filter_values") |
|
445 | if has_filter?(field) || !filter.remote | |
412 |
|
|
446 | options[:values] = filter.values | |
|
447 | if options[:values] && values_for(field) | |||
|
448 | missing = Array(values_for(field)).select(&:present?) - options[:values].map(&:last) | |||
|
449 | if missing.any? && respond_to?(method = "find_#{field}_filter_values") | |||
|
450 | options[:values] += send(method, missing) | |||
|
451 | end | |||
413 | end |
|
452 | end | |
414 | end |
|
453 | end | |
415 | json[field] = options.stringify_keys |
|
454 | json[field] = options.stringify_keys | |
@@ -432,6 +471,65 class Query < ActiveRecord::Base | |||||
432 | @all_projects_values = values |
|
471 | @all_projects_values = values | |
433 | end |
|
472 | end | |
434 |
|
473 | |||
|
474 | def project_values | |||
|
475 | project_values = [] | |||
|
476 | if User.current.logged? && User.current.memberships.any? | |||
|
477 | project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"] | |||
|
478 | end | |||
|
479 | project_values += all_projects_values | |||
|
480 | project_values | |||
|
481 | end | |||
|
482 | ||||
|
483 | def subproject_values | |||
|
484 | project.descendants.visible.collect{|s| [s.name, s.id.to_s] } | |||
|
485 | end | |||
|
486 | ||||
|
487 | def principals | |||
|
488 | @principal ||= begin | |||
|
489 | principals = [] | |||
|
490 | if project | |||
|
491 | principals += project.principals.visible | |||
|
492 | unless project.leaf? | |||
|
493 | principals += Principal.member_of(project.descendants.visible).visible | |||
|
494 | end | |||
|
495 | else | |||
|
496 | principals += Principal.member_of(all_projects).visible | |||
|
497 | end | |||
|
498 | principals.uniq! | |||
|
499 | principals.sort! | |||
|
500 | principals.reject! {|p| p.is_a?(GroupBuiltin)} | |||
|
501 | principals | |||
|
502 | end | |||
|
503 | end | |||
|
504 | ||||
|
505 | def users | |||
|
506 | principals.select {|p| p.is_a?(User)} | |||
|
507 | end | |||
|
508 | ||||
|
509 | def author_values | |||
|
510 | author_values = [] | |||
|
511 | author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? | |||
|
512 | author_values += users.collect{|s| [s.name, s.id.to_s] } | |||
|
513 | author_values | |||
|
514 | end | |||
|
515 | ||||
|
516 | def assigned_to_values | |||
|
517 | assigned_to_values = [] | |||
|
518 | assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? | |||
|
519 | assigned_to_values += (Setting.issue_group_assignment? ? principals : users).collect{|s| [s.name, s.id.to_s] } | |||
|
520 | assigned_to_values | |||
|
521 | end | |||
|
522 | ||||
|
523 | def fixed_version_values | |||
|
524 | versions = [] | |||
|
525 | if project | |||
|
526 | versions = project.shared_versions.to_a | |||
|
527 | else | |||
|
528 | versions = Version.visible.where(:sharing => 'system').to_a | |||
|
529 | end | |||
|
530 | Version.sort_by_status(versions).collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s, l("version_status_#{s.status}")] } | |||
|
531 | end | |||
|
532 | ||||
435 | # Adds available filters |
|
533 | # Adds available filters | |
436 | def initialize_available_filters |
|
534 | def initialize_available_filters | |
437 | # implemented by sub-classes |
|
535 | # implemented by sub-classes | |
@@ -441,7 +539,7 class Query < ActiveRecord::Base | |||||
441 | # Adds an available filter |
|
539 | # Adds an available filter | |
442 | def add_available_filter(field, options) |
|
540 | def add_available_filter(field, options) | |
443 | @available_filters ||= ActiveSupport::OrderedHash.new |
|
541 | @available_filters ||= ActiveSupport::OrderedHash.new | |
444 | @available_filters[field] = options |
|
542 | @available_filters[field] = QueryFilter.new(field, options) | |
445 | @available_filters |
|
543 | @available_filters | |
446 | end |
|
544 | end | |
447 |
|
545 | |||
@@ -457,9 +555,6 class Query < ActiveRecord::Base | |||||
457 | unless @available_filters |
|
555 | unless @available_filters | |
458 | initialize_available_filters |
|
556 | initialize_available_filters | |
459 | @available_filters ||= {} |
|
557 | @available_filters ||= {} | |
460 | @available_filters.each do |field, options| |
|
|||
461 | options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, '')) |
|
|||
462 | end |
|
|||
463 | end |
|
558 | end | |
464 | @available_filters |
|
559 | @available_filters | |
465 | end |
|
560 | end |
@@ -42,65 +42,38 class TimeEntryQuery < Query | |||||
42 | def initialize_available_filters |
|
42 | def initialize_available_filters | |
43 | add_available_filter "spent_on", :type => :date_past |
|
43 | add_available_filter "spent_on", :type => :date_past | |
44 |
|
44 | |||
45 | principals = [] |
|
45 | add_available_filter("project_id", | |
46 | versions = [] |
|
46 | :type => :list, :values => lambda { project_values } | |
47 | if project |
|
47 | ) if project.nil? | |
48 | principals += project.principals.visible.sort |
|
48 | ||
49 |
|
|
49 | if project && !project.leaf? | |
50 | subprojects = project.descendants.visible.to_a |
|
50 | add_available_filter "subproject_id", | |
51 |
|
|
51 | :type => :list_subprojects, | |
52 | add_available_filter "subproject_id", |
|
52 | :values => lambda { subproject_values } | |
53 | :type => :list_subprojects, |
|
|||
54 | :values => subprojects.collect{|s| [s.name, s.id.to_s] } |
|
|||
55 | principals += Principal.member_of(subprojects).visible |
|
|||
56 | end |
|
|||
57 | end |
|
|||
58 | versions = project.shared_versions.to_a |
|
|||
59 | else |
|
|||
60 | if all_projects.any? |
|
|||
61 | # members of visible projects |
|
|||
62 | principals += Principal.member_of(all_projects).visible |
|
|||
63 | # project filter |
|
|||
64 | project_values = [] |
|
|||
65 | if User.current.logged? && User.current.memberships.any? |
|
|||
66 | project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"] |
|
|||
67 | end |
|
|||
68 | project_values += all_projects_values |
|
|||
69 | add_available_filter("project_id", |
|
|||
70 | :type => :list, :values => project_values |
|
|||
71 | ) unless project_values.empty? |
|
|||
72 | end |
|
|||
73 | end |
|
53 | end | |
74 |
|
54 | |||
75 | add_available_filter("issue_id", :type => :tree, :label => :label_issue) |
|
55 | add_available_filter("issue_id", :type => :tree, :label => :label_issue) | |
76 | add_available_filter("issue.tracker_id", |
|
56 | add_available_filter("issue.tracker_id", | |
77 | :type => :list, |
|
57 | :type => :list, | |
78 | :name => l("label_attribute_of_issue", :name => l(:field_tracker)), |
|
58 | :name => l("label_attribute_of_issue", :name => l(:field_tracker)), | |
79 | :values => Tracker.sorted.map {|t| [t.name, t.id.to_s]}) |
|
59 | :values => lambda { Tracker.sorted.map {|t| [t.name, t.id.to_s]} }) | |
80 | add_available_filter("issue.status_id", |
|
60 | add_available_filter("issue.status_id", | |
81 | :type => :list, |
|
61 | :type => :list, | |
82 | :name => l("label_attribute_of_issue", :name => l(:field_status)), |
|
62 | :name => l("label_attribute_of_issue", :name => l(:field_status)), | |
83 | :values => IssueStatus.sorted.map {|s| [s.name, s.id.to_s]}) |
|
63 | :values => lambda { IssueStatus.sorted.map {|s| [s.name, s.id.to_s]} }) | |
84 | add_available_filter("issue.fixed_version_id", |
|
64 | add_available_filter("issue.fixed_version_id", | |
85 | :type => :list, |
|
65 | :type => :list, | |
86 | :name => l("label_attribute_of_issue", :name => l(:field_fixed_version)), |
|
66 | :name => l("label_attribute_of_issue", :name => l(:field_fixed_version)), | |
87 | :values => Version.sort_by_status(versions).collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s, l("version_status_#{s.status}")] }) |
|
67 | :values => lambda { fixed_version_values }) if project | |
88 |
|
||||
89 | principals.uniq! |
|
|||
90 | principals.sort! |
|
|||
91 | users = principals.select {|p| p.is_a?(User)} |
|
|||
92 |
|
68 | |||
93 | users_values = [] |
|
|||
94 | users_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? |
|
|||
95 | users_values += users.collect{|s| [s.name, s.id.to_s] } |
|
|||
96 | add_available_filter("user_id", |
|
69 | add_available_filter("user_id", | |
97 |
:type => :list_optional, :values => |
|
70 | :type => :list_optional, :values => lambda { author_values } | |
98 | ) unless users_values.empty? |
|
71 | ) | |
99 |
|
72 | |||
100 | activities = (project ? project.activities : TimeEntryActivity.shared) |
|
73 | activities = (project ? project.activities : TimeEntryActivity.shared) | |
101 | add_available_filter("activity_id", |
|
74 | add_available_filter("activity_id", | |
102 | :type => :list, :values => activities.map {|a| [a.name, a.id.to_s]} |
|
75 | :type => :list, :values => activities.map {|a| [a.name, a.id.to_s]} | |
103 | ) unless activities.empty? |
|
76 | ) | |
104 |
|
77 | |||
105 | add_available_filter "comments", :type => :text |
|
78 | add_available_filter "comments", :type => :text | |
106 | add_available_filter "hours", :type => :float |
|
79 | add_available_filter "hours", :type => :float |
@@ -3,7 +3,9 var operatorLabels = <%= raw_json Query.operators_labels %>; | |||||
3 | var operatorByType = <%= raw_json Query.operators_by_filter_type %>; |
|
3 | var operatorByType = <%= raw_json Query.operators_by_filter_type %>; | |
4 | var availableFilters = <%= raw_json query.available_filters_as_json %>; |
|
4 | var availableFilters = <%= raw_json query.available_filters_as_json %>; | |
5 | var labelDayPlural = <%= raw_json l(:label_day_plural) %>; |
|
5 | var labelDayPlural = <%= raw_json l(:label_day_plural) %>; | |
6 | var allProjects = <%= raw_json query.all_projects_values %>; |
|
6 | ||
|
7 | var filtersUrl = <%= raw_json queries_filter_path(:project_id => @query.project.try(:id), :type => @query.type) %>; | |||
|
8 | ||||
7 | $(document).ready(function(){ |
|
9 | $(document).ready(function(){ | |
8 | initFilters(); |
|
10 | initFilters(); | |
9 | <% query.filters.each do |field, options| %> |
|
11 | <% query.filters.each do |field, options| %> |
@@ -199,6 +199,7 Rails.application.routes.draw do | |||||
199 | match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete |
|
199 | match '/issues', :controller => 'issues', :action => 'destroy', :via => :delete | |
200 |
|
200 | |||
201 | resources :queries, :except => [:show] |
|
201 | resources :queries, :except => [:show] | |
|
202 | get '/queries/filter', :to => 'queries#filter', :as => 'queries_filter' | |||
202 |
|
203 | |||
203 | resources :news, :only => [:index, :show, :edit, :update, :destroy] |
|
204 | resources :news, :only => [:index, :show, :edit, :update, :destroy] | |
204 | match '/news/:id/comments', :to => 'comments#create', :via => :post |
|
205 | match '/news/:id/comments', :to => 'comments#create', :via => :post |
@@ -120,6 +120,18 function initFilters() { | |||||
120 | function addFilter(field, operator, values) { |
|
120 | function addFilter(field, operator, values) { | |
121 | var fieldId = field.replace('.', '_'); |
|
121 | var fieldId = field.replace('.', '_'); | |
122 | var tr = $('#tr_'+fieldId); |
|
122 | var tr = $('#tr_'+fieldId); | |
|
123 | ||||
|
124 | var filterOptions = availableFilters[field]; | |||
|
125 | if (!filterOptions) return; | |||
|
126 | ||||
|
127 | if (filterOptions['remote'] && filterOptions['values'] == null) { | |||
|
128 | $.getJSON(filtersUrl, {'name': field}).done(function(data) { | |||
|
129 | filterOptions['values'] = data; | |||
|
130 | addFilter(field, operator, values) ; | |||
|
131 | }); | |||
|
132 | return; | |||
|
133 | } | |||
|
134 | ||||
123 | if (tr.length > 0) { |
|
135 | if (tr.length > 0) { | |
124 | tr.show(); |
|
136 | tr.show(); | |
125 | } else { |
|
137 | } else { | |
@@ -134,7 +146,7 function addFilter(field, operator, values) { | |||||
134 | }); |
|
146 | }); | |
135 | } |
|
147 | } | |
136 |
|
148 | |||
137 | function buildFilterRow(field, operator, values) { |
|
149 | function buildFilterRow(field, operator, values, loadedValues) { | |
138 | var fieldId = field.replace('.', '_'); |
|
150 | var fieldId = field.replace('.', '_'); | |
139 | var filterTable = $("#filters-table"); |
|
151 | var filterTable = $("#filters-table"); | |
140 | var filterOptions = availableFilters[field]; |
|
152 | var filterOptions = availableFilters[field]; | |
@@ -212,8 +224,8 function buildFilterRow(field, operator, values) { | |||||
212 | ); |
|
224 | ); | |
213 | $('#values_'+fieldId).val(values[0]); |
|
225 | $('#values_'+fieldId).val(values[0]); | |
214 | select = tr.find('td.values select'); |
|
226 | select = tr.find('td.values select'); | |
215 |
for (i = 0; i < |
|
227 | for (i = 0; i < filterValues.length; i++) { | |
216 |
var filterValue = |
|
228 | var filterValue = filterValues[i]; | |
217 | var option = $('<option>'); |
|
229 | var option = $('<option>'); | |
218 | option.val(filterValue[1]).text(filterValue[0]); |
|
230 | option.val(filterValue[1]).text(filterValue[0]); | |
219 | if (values[0] == filterValue[1]) { option.attr('selected', true); } |
|
231 | if (values[0] == filterValue[1]) { option.attr('selected', true); } |
@@ -18,7 +18,12 | |||||
18 | require File.expand_path('../../test_helper', __FILE__) |
|
18 | require File.expand_path('../../test_helper', __FILE__) | |
19 |
|
19 | |||
20 | class QueriesControllerTest < Redmine::ControllerTest |
|
20 | class QueriesControllerTest < Redmine::ControllerTest | |
21 | fixtures :projects, :users, :members, :member_roles, :roles, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :queries, :enabled_modules |
|
21 | fixtures :projects, :enabled_modules, | |
|
22 | :users, :email_addresses, | |||
|
23 | :members, :member_roles, :roles, | |||
|
24 | :trackers, :issue_statuses, :issue_categories, :enumerations, :versions, | |||
|
25 | :issues, :custom_fields, :custom_values, | |||
|
26 | :queries | |||
22 |
|
27 | |||
23 | def setup |
|
28 | def setup | |
24 | User.current = nil |
|
29 | User.current = nil | |
@@ -397,4 +402,24 class QueriesControllerTest < Redmine::ControllerTest | |||||
397 | assert_response :success |
|
402 | assert_response :success | |
398 | assert_include 'addFilter("subject", "=", ["foo\/bar"]);', response.body |
|
403 | assert_include 'addFilter("subject", "=", ["foo\/bar"]);', response.body | |
399 | end |
|
404 | end | |
|
405 | ||||
|
406 | def test_filter_with_project_id_should_return_filter_values | |||
|
407 | @request.session[:user_id] = 2 | |||
|
408 | get :filter, :project_id => 1, :name => 'fixed_version_id' | |||
|
409 | ||||
|
410 | assert_response :success | |||
|
411 | assert_equal 'application/json', response.content_type | |||
|
412 | json = ActiveSupport::JSON.decode(response.body) | |||
|
413 | assert_include ["eCookbook - 2.0", "3", "open"], json | |||
|
414 | end | |||
|
415 | ||||
|
416 | def test_filter_without_project_id_should_return_filter_values | |||
|
417 | @request.session[:user_id] = 2 | |||
|
418 | get :filter, :name => 'fixed_version_id' | |||
|
419 | ||||
|
420 | assert_response :success | |||
|
421 | assert_equal 'application/json', response.content_type | |||
|
422 | json = ActiveSupport::JSON.decode(response.body) | |||
|
423 | assert_include ["OnlineStore - Systemwide visible version", "7", "open"], json | |||
|
424 | end | |||
400 | end |
|
425 | end |
@@ -21,6 +21,7 class RoutingQueriesTest < Redmine::RoutingTest | |||||
21 | def test_queries |
|
21 | def test_queries | |
22 | should_route 'GET /queries/new' => 'queries#new' |
|
22 | should_route 'GET /queries/new' => 'queries#new' | |
23 | should_route 'POST /queries' => 'queries#create' |
|
23 | should_route 'POST /queries' => 'queries#create' | |
|
24 | should_route 'GET /queries/filter' => 'queries#filter' | |||
24 |
|
25 | |||
25 | should_route 'GET /queries/1/edit' => 'queries#edit', :id => '1' |
|
26 | should_route 'GET /queries/1/edit' => 'queries#edit', :id => '1' | |
26 | should_route 'PUT /queries/1' => 'queries#update', :id => '1' |
|
27 | should_route 'PUT /queries/1' => 'queries#update', :id => '1' |
@@ -118,6 +118,28 class QueryTest < ActiveSupport::TestCase | |||||
118 | assert_not_include 'start_date', query.available_filters |
|
118 | assert_not_include 'start_date', query.available_filters | |
119 | end |
|
119 | end | |
120 |
|
120 | |||
|
121 | def test_filter_values_without_project_should_be_arrays | |||
|
122 | q = IssueQuery.new | |||
|
123 | assert_nil q.project | |||
|
124 | ||||
|
125 | q.available_filters.each do |name, filter| | |||
|
126 | values = filter.values | |||
|
127 | assert (values.nil? || values.is_a?(Array)), | |||
|
128 | "#values for #{name} filter returned a #{values.class.name}" | |||
|
129 | end | |||
|
130 | end | |||
|
131 | ||||
|
132 | def test_filter_values_with_project_should_be_arrays | |||
|
133 | q = IssueQuery.new(:project => Project.find(1)) | |||
|
134 | assert_not_nil q.project | |||
|
135 | ||||
|
136 | q.available_filters.each do |name, filter| | |||
|
137 | values = filter.values | |||
|
138 | assert (values.nil? || values.is_a?(Array)), | |||
|
139 | "#values for #{name} filter returned a #{values.class.name}" | |||
|
140 | end | |||
|
141 | end | |||
|
142 | ||||
121 | def find_issues_with_query(query) |
|
143 | def find_issues_with_query(query) | |
122 | Issue.joins(:status, :tracker, :project, :priority).where( |
|
144 | Issue.joins(:status, :tracker, :project, :priority).where( | |
123 | query.statement |
|
145 | query.statement |
@@ -27,6 +27,28 class TimeEntryQueryTest < ActiveSupport::TestCase | |||||
27 | :groups_users, |
|
27 | :groups_users, | |
28 | :enabled_modules |
|
28 | :enabled_modules | |
29 |
|
29 | |||
|
30 | def test_filter_values_without_project_should_be_arrays | |||
|
31 | q = TimeEntryQuery.new | |||
|
32 | assert_nil q.project | |||
|
33 | ||||
|
34 | q.available_filters.each do |name, filter| | |||
|
35 | values = filter.values | |||
|
36 | assert (values.nil? || values.is_a?(Array)), | |||
|
37 | "#values for #{name} filter returned a #{values.class.name}" | |||
|
38 | end | |||
|
39 | end | |||
|
40 | ||||
|
41 | def test_filter_values_with_project_should_be_arrays | |||
|
42 | q = TimeEntryQuery.new(:project => Project.find(1)) | |||
|
43 | assert_not_nil q.project | |||
|
44 | ||||
|
45 | q.available_filters.each do |name, filter| | |||
|
46 | values = filter.values | |||
|
47 | assert (values.nil? || values.is_a?(Array)), | |||
|
48 | "#values for #{name} filter returned a #{values.class.name}" | |||
|
49 | end | |||
|
50 | end | |||
|
51 | ||||
30 | def test_cross_project_activity_filter_should_propose_non_active_activities |
|
52 | def test_cross_project_activity_filter_should_propose_non_active_activities | |
31 | activity = TimeEntryActivity.create!(:name => 'Disabled', :active => false) |
|
53 | activity = TimeEntryActivity.create!(:name => 'Disabled', :active => false) | |
32 | assert !activity.active? |
|
54 | assert !activity.active? |
General Comments 0
You need to be logged in to leave comments.
Login now