##// END OF EJS Templates
Ability to sort issues by grouped column (#3511)....
Jean-Philippe Lang -
r10543:9f148e098b07
parent child
Show More
@@ -1,442 +1,443
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 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
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class IssuesController < ApplicationController
18 class IssuesController < ApplicationController
19 menu_item :new_issue, :only => [:new, :create]
19 menu_item :new_issue, :only => [:new, :create]
20 default_search_scope :issues
20 default_search_scope :issues
21
21
22 before_filter :find_issue, :only => [:show, :edit, :update]
22 before_filter :find_issue, :only => [:show, :edit, :update]
23 before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
23 before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
24 before_filter :find_project, :only => [:new, :create]
24 before_filter :find_project, :only => [:new, :create]
25 before_filter :authorize, :except => [:index]
25 before_filter :authorize, :except => [:index]
26 before_filter :find_optional_project, :only => [:index]
26 before_filter :find_optional_project, :only => [:index]
27 before_filter :check_for_default_issue_status, :only => [:new, :create]
27 before_filter :check_for_default_issue_status, :only => [:new, :create]
28 before_filter :build_new_issue_from_params, :only => [:new, :create]
28 before_filter :build_new_issue_from_params, :only => [:new, :create]
29 accept_rss_auth :index, :show
29 accept_rss_auth :index, :show
30 accept_api_auth :index, :show, :create, :update, :destroy
30 accept_api_auth :index, :show, :create, :update, :destroy
31
31
32 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
32 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
33
33
34 helper :journals
34 helper :journals
35 helper :projects
35 helper :projects
36 include ProjectsHelper
36 include ProjectsHelper
37 helper :custom_fields
37 helper :custom_fields
38 include CustomFieldsHelper
38 include CustomFieldsHelper
39 helper :issue_relations
39 helper :issue_relations
40 include IssueRelationsHelper
40 include IssueRelationsHelper
41 helper :watchers
41 helper :watchers
42 include WatchersHelper
42 include WatchersHelper
43 helper :attachments
43 helper :attachments
44 include AttachmentsHelper
44 include AttachmentsHelper
45 helper :queries
45 helper :queries
46 include QueriesHelper
46 include QueriesHelper
47 helper :repositories
47 helper :repositories
48 include RepositoriesHelper
48 include RepositoriesHelper
49 helper :sort
49 helper :sort
50 include SortHelper
50 include SortHelper
51 include IssuesHelper
51 include IssuesHelper
52 helper :timelog
52 helper :timelog
53 include Redmine::Export::PDF
53 include Redmine::Export::PDF
54
54
55 def index
55 def index
56 retrieve_query
56 retrieve_query
57 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
57 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
58 sort_update(@query.sortable_columns)
58 sort_update(@query.sortable_columns)
59 @query.sort_criteria = sort_criteria.to_a
59
60
60 if @query.valid?
61 if @query.valid?
61 case params[:format]
62 case params[:format]
62 when 'csv', 'pdf'
63 when 'csv', 'pdf'
63 @limit = Setting.issues_export_limit.to_i
64 @limit = Setting.issues_export_limit.to_i
64 when 'atom'
65 when 'atom'
65 @limit = Setting.feeds_limit.to_i
66 @limit = Setting.feeds_limit.to_i
66 when 'xml', 'json'
67 when 'xml', 'json'
67 @offset, @limit = api_offset_and_limit
68 @offset, @limit = api_offset_and_limit
68 else
69 else
69 @limit = per_page_option
70 @limit = per_page_option
70 end
71 end
71
72
72 @issue_count = @query.issue_count
73 @issue_count = @query.issue_count
73 @issue_pages = Paginator.new self, @issue_count, @limit, params['page']
74 @issue_pages = Paginator.new self, @issue_count, @limit, params['page']
74 @offset ||= @issue_pages.current.offset
75 @offset ||= @issue_pages.current.offset
75 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
76 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
76 :order => sort_clause,
77 :order => sort_clause,
77 :offset => @offset,
78 :offset => @offset,
78 :limit => @limit)
79 :limit => @limit)
79 @issue_count_by_group = @query.issue_count_by_group
80 @issue_count_by_group = @query.issue_count_by_group
80
81
81 respond_to do |format|
82 respond_to do |format|
82 format.html { render :template => 'issues/index', :layout => !request.xhr? }
83 format.html { render :template => 'issues/index', :layout => !request.xhr? }
83 format.api {
84 format.api {
84 Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
85 Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
85 }
86 }
86 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
87 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
87 format.csv { send_data(issues_to_csv(@issues, @project, @query, params), :type => 'text/csv; header=present', :filename => 'export.csv') }
88 format.csv { send_data(issues_to_csv(@issues, @project, @query, params), :type => 'text/csv; header=present', :filename => 'export.csv') }
88 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
89 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
89 end
90 end
90 else
91 else
91 respond_to do |format|
92 respond_to do |format|
92 format.html { render(:template => 'issues/index', :layout => !request.xhr?) }
93 format.html { render(:template => 'issues/index', :layout => !request.xhr?) }
93 format.any(:atom, :csv, :pdf) { render(:nothing => true) }
94 format.any(:atom, :csv, :pdf) { render(:nothing => true) }
94 format.api { render_validation_errors(@query) }
95 format.api { render_validation_errors(@query) }
95 end
96 end
96 end
97 end
97 rescue ActiveRecord::RecordNotFound
98 rescue ActiveRecord::RecordNotFound
98 render_404
99 render_404
99 end
100 end
100
101
101 def show
102 def show
102 @journals = @issue.journals.includes(:user, :details).reorder("#{Journal.table_name}.id ASC").all
103 @journals = @issue.journals.includes(:user, :details).reorder("#{Journal.table_name}.id ASC").all
103 @journals.each_with_index {|j,i| j.indice = i+1}
104 @journals.each_with_index {|j,i| j.indice = i+1}
104 @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
105 @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
105 @journals.reverse! if User.current.wants_comments_in_reverse_order?
106 @journals.reverse! if User.current.wants_comments_in_reverse_order?
106
107
107 @changesets = @issue.changesets.visible.all
108 @changesets = @issue.changesets.visible.all
108 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
109 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
109
110
110 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
111 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
111 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
112 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
112 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
113 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
113 @priorities = IssuePriority.active
114 @priorities = IssuePriority.active
114 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
115 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
115 respond_to do |format|
116 respond_to do |format|
116 format.html {
117 format.html {
117 retrieve_previous_and_next_issue_ids
118 retrieve_previous_and_next_issue_ids
118 render :template => 'issues/show'
119 render :template => 'issues/show'
119 }
120 }
120 format.api
121 format.api
121 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
122 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
122 format.pdf {
123 format.pdf {
123 pdf = issue_to_pdf(@issue, :journals => @journals)
124 pdf = issue_to_pdf(@issue, :journals => @journals)
124 send_data(pdf, :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf")
125 send_data(pdf, :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf")
125 }
126 }
126 end
127 end
127 end
128 end
128
129
129 # Add a new issue
130 # Add a new issue
130 # The new issue will be created from an existing one if copy_from parameter is given
131 # The new issue will be created from an existing one if copy_from parameter is given
131 def new
132 def new
132 respond_to do |format|
133 respond_to do |format|
133 format.html { render :action => 'new', :layout => !request.xhr? }
134 format.html { render :action => 'new', :layout => !request.xhr? }
134 format.js { render :partial => 'update_form' }
135 format.js { render :partial => 'update_form' }
135 end
136 end
136 end
137 end
137
138
138 def create
139 def create
139 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
140 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
140 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
141 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
141 if @issue.save
142 if @issue.save
142 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
143 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
143 respond_to do |format|
144 respond_to do |format|
144 format.html {
145 format.html {
145 render_attachment_warning_if_needed(@issue)
146 render_attachment_warning_if_needed(@issue)
146 flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
147 flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
147 redirect_to(params[:continue] ? { :action => 'new', :project_id => @issue.project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
148 redirect_to(params[:continue] ? { :action => 'new', :project_id => @issue.project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
148 { :action => 'show', :id => @issue })
149 { :action => 'show', :id => @issue })
149 }
150 }
150 format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
151 format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
151 end
152 end
152 return
153 return
153 else
154 else
154 respond_to do |format|
155 respond_to do |format|
155 format.html { render :action => 'new' }
156 format.html { render :action => 'new' }
156 format.api { render_validation_errors(@issue) }
157 format.api { render_validation_errors(@issue) }
157 end
158 end
158 end
159 end
159 end
160 end
160
161
161 def edit
162 def edit
162 return unless update_issue_from_params
163 return unless update_issue_from_params
163
164
164 respond_to do |format|
165 respond_to do |format|
165 format.html { }
166 format.html { }
166 format.xml { }
167 format.xml { }
167 end
168 end
168 end
169 end
169
170
170 def update
171 def update
171 return unless update_issue_from_params
172 return unless update_issue_from_params
172 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
173 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
173 saved = false
174 saved = false
174 begin
175 begin
175 saved = @issue.save_issue_with_child_records(params, @time_entry)
176 saved = @issue.save_issue_with_child_records(params, @time_entry)
176 rescue ActiveRecord::StaleObjectError
177 rescue ActiveRecord::StaleObjectError
177 @conflict = true
178 @conflict = true
178 if params[:last_journal_id]
179 if params[:last_journal_id]
179 @conflict_journals = @issue.journals_after(params[:last_journal_id]).all
180 @conflict_journals = @issue.journals_after(params[:last_journal_id]).all
180 @conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
181 @conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
181 end
182 end
182 end
183 end
183
184
184 if saved
185 if saved
185 render_attachment_warning_if_needed(@issue)
186 render_attachment_warning_if_needed(@issue)
186 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
187 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
187
188
188 respond_to do |format|
189 respond_to do |format|
189 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
190 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
190 format.api { render_api_ok }
191 format.api { render_api_ok }
191 end
192 end
192 else
193 else
193 respond_to do |format|
194 respond_to do |format|
194 format.html { render :action => 'edit' }
195 format.html { render :action => 'edit' }
195 format.api { render_validation_errors(@issue) }
196 format.api { render_validation_errors(@issue) }
196 end
197 end
197 end
198 end
198 end
199 end
199
200
200 # Bulk edit/copy a set of issues
201 # Bulk edit/copy a set of issues
201 def bulk_edit
202 def bulk_edit
202 @issues.sort!
203 @issues.sort!
203 @copy = params[:copy].present?
204 @copy = params[:copy].present?
204 @notes = params[:notes]
205 @notes = params[:notes]
205
206
206 if User.current.allowed_to?(:move_issues, @projects)
207 if User.current.allowed_to?(:move_issues, @projects)
207 @allowed_projects = Issue.allowed_target_projects_on_move
208 @allowed_projects = Issue.allowed_target_projects_on_move
208 if params[:issue]
209 if params[:issue]
209 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
210 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
210 if @target_project
211 if @target_project
211 target_projects = [@target_project]
212 target_projects = [@target_project]
212 end
213 end
213 end
214 end
214 end
215 end
215 target_projects ||= @projects
216 target_projects ||= @projects
216
217
217 if @copy
218 if @copy
218 @available_statuses = [IssueStatus.default]
219 @available_statuses = [IssueStatus.default]
219 else
220 else
220 @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
221 @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
221 end
222 end
222 @custom_fields = target_projects.map{|p|p.all_issue_custom_fields}.reduce(:&)
223 @custom_fields = target_projects.map{|p|p.all_issue_custom_fields}.reduce(:&)
223 @assignables = target_projects.map(&:assignable_users).reduce(:&)
224 @assignables = target_projects.map(&:assignable_users).reduce(:&)
224 @trackers = target_projects.map(&:trackers).reduce(:&)
225 @trackers = target_projects.map(&:trackers).reduce(:&)
225 @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
226 @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
226 @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
227 @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
227 if @copy
228 if @copy
228 @attachments_present = @issues.detect {|i| i.attachments.any?}.present?
229 @attachments_present = @issues.detect {|i| i.attachments.any?}.present?
229 @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
230 @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
230 end
231 end
231
232
232 @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
233 @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
233 render :layout => false if request.xhr?
234 render :layout => false if request.xhr?
234 end
235 end
235
236
236 def bulk_update
237 def bulk_update
237 @issues.sort!
238 @issues.sort!
238 @copy = params[:copy].present?
239 @copy = params[:copy].present?
239 attributes = parse_params_for_bulk_issue_attributes(params)
240 attributes = parse_params_for_bulk_issue_attributes(params)
240
241
241 unsaved_issue_ids = []
242 unsaved_issue_ids = []
242 moved_issues = []
243 moved_issues = []
243
244
244 if @copy && params[:copy_subtasks].present?
245 if @copy && params[:copy_subtasks].present?
245 # Descendant issues will be copied with the parent task
246 # Descendant issues will be copied with the parent task
246 # Don't copy them twice
247 # Don't copy them twice
247 @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
248 @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
248 end
249 end
249
250
250 @issues.each do |issue|
251 @issues.each do |issue|
251 issue.reload
252 issue.reload
252 if @copy
253 if @copy
253 issue = issue.copy({},
254 issue = issue.copy({},
254 :attachments => params[:copy_attachments].present?,
255 :attachments => params[:copy_attachments].present?,
255 :subtasks => params[:copy_subtasks].present?
256 :subtasks => params[:copy_subtasks].present?
256 )
257 )
257 end
258 end
258 journal = issue.init_journal(User.current, params[:notes])
259 journal = issue.init_journal(User.current, params[:notes])
259 issue.safe_attributes = attributes
260 issue.safe_attributes = attributes
260 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
261 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
261 if issue.save
262 if issue.save
262 moved_issues << issue
263 moved_issues << issue
263 else
264 else
264 # Keep unsaved issue ids to display them in flash error
265 # Keep unsaved issue ids to display them in flash error
265 unsaved_issue_ids << issue.id
266 unsaved_issue_ids << issue.id
266 end
267 end
267 end
268 end
268 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
269 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
269
270
270 if params[:follow]
271 if params[:follow]
271 if @issues.size == 1 && moved_issues.size == 1
272 if @issues.size == 1 && moved_issues.size == 1
272 redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
273 redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
273 elsif moved_issues.map(&:project).uniq.size == 1
274 elsif moved_issues.map(&:project).uniq.size == 1
274 redirect_to :controller => 'issues', :action => 'index', :project_id => moved_issues.map(&:project).first
275 redirect_to :controller => 'issues', :action => 'index', :project_id => moved_issues.map(&:project).first
275 end
276 end
276 else
277 else
277 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
278 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
278 end
279 end
279 end
280 end
280
281
281 def destroy
282 def destroy
282 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
283 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
283 if @hours > 0
284 if @hours > 0
284 case params[:todo]
285 case params[:todo]
285 when 'destroy'
286 when 'destroy'
286 # nothing to do
287 # nothing to do
287 when 'nullify'
288 when 'nullify'
288 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
289 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
289 when 'reassign'
290 when 'reassign'
290 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
291 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
291 if reassign_to.nil?
292 if reassign_to.nil?
292 flash.now[:error] = l(:error_issue_not_found_in_project)
293 flash.now[:error] = l(:error_issue_not_found_in_project)
293 return
294 return
294 else
295 else
295 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
296 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
296 end
297 end
297 else
298 else
298 # display the destroy form if it's a user request
299 # display the destroy form if it's a user request
299 return unless api_request?
300 return unless api_request?
300 end
301 end
301 end
302 end
302 @issues.each do |issue|
303 @issues.each do |issue|
303 begin
304 begin
304 issue.reload.destroy
305 issue.reload.destroy
305 rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
306 rescue ::ActiveRecord::RecordNotFound # raised by #reload if issue no longer exists
306 # nothing to do, issue was already deleted (eg. by a parent)
307 # nothing to do, issue was already deleted (eg. by a parent)
307 end
308 end
308 end
309 end
309 respond_to do |format|
310 respond_to do |format|
310 format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
311 format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
311 format.api { render_api_ok }
312 format.api { render_api_ok }
312 end
313 end
313 end
314 end
314
315
315 private
316 private
316 def find_issue
317 def find_issue
317 # Issue.visible.find(...) can not be used to redirect user to the login form
318 # Issue.visible.find(...) can not be used to redirect user to the login form
318 # if the issue actually exists but requires authentication
319 # if the issue actually exists but requires authentication
319 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
320 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
320 unless @issue.visible?
321 unless @issue.visible?
321 deny_access
322 deny_access
322 return
323 return
323 end
324 end
324 @project = @issue.project
325 @project = @issue.project
325 rescue ActiveRecord::RecordNotFound
326 rescue ActiveRecord::RecordNotFound
326 render_404
327 render_404
327 end
328 end
328
329
329 def find_project
330 def find_project
330 project_id = params[:project_id] || (params[:issue] && params[:issue][:project_id])
331 project_id = params[:project_id] || (params[:issue] && params[:issue][:project_id])
331 @project = Project.find(project_id)
332 @project = Project.find(project_id)
332 rescue ActiveRecord::RecordNotFound
333 rescue ActiveRecord::RecordNotFound
333 render_404
334 render_404
334 end
335 end
335
336
336 def retrieve_previous_and_next_issue_ids
337 def retrieve_previous_and_next_issue_ids
337 retrieve_query_from_session
338 retrieve_query_from_session
338 if @query
339 if @query
339 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
340 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
340 sort_update(@query.sortable_columns, 'issues_index_sort')
341 sort_update(@query.sortable_columns, 'issues_index_sort')
341 limit = 500
342 limit = 500
342 issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])
343 issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])
343 if (idx = issue_ids.index(@issue.id)) && idx < limit
344 if (idx = issue_ids.index(@issue.id)) && idx < limit
344 if issue_ids.size < 500
345 if issue_ids.size < 500
345 @issue_position = idx + 1
346 @issue_position = idx + 1
346 @issue_count = issue_ids.size
347 @issue_count = issue_ids.size
347 end
348 end
348 @prev_issue_id = issue_ids[idx - 1] if idx > 0
349 @prev_issue_id = issue_ids[idx - 1] if idx > 0
349 @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
350 @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
350 end
351 end
351 end
352 end
352 end
353 end
353
354
354 # Used by #edit and #update to set some common instance variables
355 # Used by #edit and #update to set some common instance variables
355 # from the params
356 # from the params
356 # TODO: Refactor, not everything in here is needed by #edit
357 # TODO: Refactor, not everything in here is needed by #edit
357 def update_issue_from_params
358 def update_issue_from_params
358 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
359 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
359 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
360 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
360 @time_entry.attributes = params[:time_entry]
361 @time_entry.attributes = params[:time_entry]
361
362
362 @issue.init_journal(User.current)
363 @issue.init_journal(User.current)
363
364
364 issue_attributes = params[:issue]
365 issue_attributes = params[:issue]
365 if issue_attributes && params[:conflict_resolution]
366 if issue_attributes && params[:conflict_resolution]
366 case params[:conflict_resolution]
367 case params[:conflict_resolution]
367 when 'overwrite'
368 when 'overwrite'
368 issue_attributes = issue_attributes.dup
369 issue_attributes = issue_attributes.dup
369 issue_attributes.delete(:lock_version)
370 issue_attributes.delete(:lock_version)
370 when 'add_notes'
371 when 'add_notes'
371 issue_attributes = issue_attributes.slice(:notes)
372 issue_attributes = issue_attributes.slice(:notes)
372 when 'cancel'
373 when 'cancel'
373 redirect_to issue_path(@issue)
374 redirect_to issue_path(@issue)
374 return false
375 return false
375 end
376 end
376 end
377 end
377 @issue.safe_attributes = issue_attributes
378 @issue.safe_attributes = issue_attributes
378 @priorities = IssuePriority.active
379 @priorities = IssuePriority.active
379 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
380 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
380 true
381 true
381 end
382 end
382
383
383 # TODO: Refactor, lots of extra code in here
384 # TODO: Refactor, lots of extra code in here
384 # TODO: Changing tracker on an existing issue should not trigger this
385 # TODO: Changing tracker on an existing issue should not trigger this
385 def build_new_issue_from_params
386 def build_new_issue_from_params
386 if params[:id].blank?
387 if params[:id].blank?
387 @issue = Issue.new
388 @issue = Issue.new
388 if params[:copy_from]
389 if params[:copy_from]
389 begin
390 begin
390 @copy_from = Issue.visible.find(params[:copy_from])
391 @copy_from = Issue.visible.find(params[:copy_from])
391 @copy_attachments = params[:copy_attachments].present? || request.get?
392 @copy_attachments = params[:copy_attachments].present? || request.get?
392 @copy_subtasks = params[:copy_subtasks].present? || request.get?
393 @copy_subtasks = params[:copy_subtasks].present? || request.get?
393 @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks)
394 @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks)
394 rescue ActiveRecord::RecordNotFound
395 rescue ActiveRecord::RecordNotFound
395 render_404
396 render_404
396 return
397 return
397 end
398 end
398 end
399 end
399 @issue.project = @project
400 @issue.project = @project
400 else
401 else
401 @issue = @project.issues.visible.find(params[:id])
402 @issue = @project.issues.visible.find(params[:id])
402 end
403 end
403
404
404 @issue.project = @project
405 @issue.project = @project
405 @issue.author ||= User.current
406 @issue.author ||= User.current
406 # Tracker must be set before custom field values
407 # Tracker must be set before custom field values
407 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
408 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
408 if @issue.tracker.nil?
409 if @issue.tracker.nil?
409 render_error l(:error_no_tracker_in_project)
410 render_error l(:error_no_tracker_in_project)
410 return false
411 return false
411 end
412 end
412 @issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?
413 @issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?
413 @issue.safe_attributes = params[:issue]
414 @issue.safe_attributes = params[:issue]
414
415
415 @priorities = IssuePriority.active
416 @priorities = IssuePriority.active
416 @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
417 @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
417 @available_watchers = (@issue.project.users.sort + @issue.watcher_users).uniq
418 @available_watchers = (@issue.project.users.sort + @issue.watcher_users).uniq
418 end
419 end
419
420
420 def check_for_default_issue_status
421 def check_for_default_issue_status
421 if IssueStatus.default.nil?
422 if IssueStatus.default.nil?
422 render_error l(:error_no_default_issue_status)
423 render_error l(:error_no_default_issue_status)
423 return false
424 return false
424 end
425 end
425 end
426 end
426
427
427 def parse_params_for_bulk_issue_attributes(params)
428 def parse_params_for_bulk_issue_attributes(params)
428 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
429 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
429 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
430 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
430 if custom = attributes[:custom_field_values]
431 if custom = attributes[:custom_field_values]
431 custom.reject! {|k,v| v.blank?}
432 custom.reject! {|k,v| v.blank?}
432 custom.keys.each do |k|
433 custom.keys.each do |k|
433 if custom[k].is_a?(Array)
434 if custom[k].is_a?(Array)
434 custom[k] << '' if custom[k].delete('__none__')
435 custom[k] << '' if custom[k].delete('__none__')
435 else
436 else
436 custom[k] = '' if custom[k] == '__none__'
437 custom[k] = '' if custom[k] == '__none__'
437 end
438 end
438 end
439 end
439 end
440 end
440 attributes
441 attributes
441 end
442 end
442 end
443 end
@@ -1,234 +1,242
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Helpers to sort tables using clickable column headers.
3 # Helpers to sort tables using clickable column headers.
4 #
4 #
5 # Author: Stuart Rackham <srackham@methods.co.nz>, March 2005.
5 # Author: Stuart Rackham <srackham@methods.co.nz>, March 2005.
6 # Jean-Philippe Lang, 2009
6 # Jean-Philippe Lang, 2009
7 # License: This source code is released under the MIT license.
7 # License: This source code is released under the MIT license.
8 #
8 #
9 # - Consecutive clicks toggle the column's sort order.
9 # - Consecutive clicks toggle the column's sort order.
10 # - Sort state is maintained by a session hash entry.
10 # - Sort state is maintained by a session hash entry.
11 # - CSS classes identify sort column and state.
11 # - CSS classes identify sort column and state.
12 # - Typically used in conjunction with the Pagination module.
12 # - Typically used in conjunction with the Pagination module.
13 #
13 #
14 # Example code snippets:
14 # Example code snippets:
15 #
15 #
16 # Controller:
16 # Controller:
17 #
17 #
18 # helper :sort
18 # helper :sort
19 # include SortHelper
19 # include SortHelper
20 #
20 #
21 # def list
21 # def list
22 # sort_init 'last_name'
22 # sort_init 'last_name'
23 # sort_update %w(first_name last_name)
23 # sort_update %w(first_name last_name)
24 # @items = Contact.find_all nil, sort_clause
24 # @items = Contact.find_all nil, sort_clause
25 # end
25 # end
26 #
26 #
27 # Controller (using Pagination module):
27 # Controller (using Pagination module):
28 #
28 #
29 # helper :sort
29 # helper :sort
30 # include SortHelper
30 # include SortHelper
31 #
31 #
32 # def list
32 # def list
33 # sort_init 'last_name'
33 # sort_init 'last_name'
34 # sort_update %w(first_name last_name)
34 # sort_update %w(first_name last_name)
35 # @contact_pages, @items = paginate :contacts,
35 # @contact_pages, @items = paginate :contacts,
36 # :order_by => sort_clause,
36 # :order_by => sort_clause,
37 # :per_page => 10
37 # :per_page => 10
38 # end
38 # end
39 #
39 #
40 # View (table header in list.rhtml):
40 # View (table header in list.rhtml):
41 #
41 #
42 # <thead>
42 # <thead>
43 # <tr>
43 # <tr>
44 # <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
44 # <%= sort_header_tag('id', :title => 'Sort by contact ID') %>
45 # <%= sort_header_tag('last_name', :caption => 'Name') %>
45 # <%= sort_header_tag('last_name', :caption => 'Name') %>
46 # <%= sort_header_tag('phone') %>
46 # <%= sort_header_tag('phone') %>
47 # <%= sort_header_tag('address', :width => 200) %>
47 # <%= sort_header_tag('address', :width => 200) %>
48 # </tr>
48 # </tr>
49 # </thead>
49 # </thead>
50 #
50 #
51 # - Introduces instance variables: @sort_default, @sort_criteria
51 # - Introduces instance variables: @sort_default, @sort_criteria
52 # - Introduces param :sort
52 # - Introduces param :sort
53 #
53 #
54
54
55 module SortHelper
55 module SortHelper
56 class SortCriteria
56 class SortCriteria
57
57
58 def initialize
58 def initialize
59 @criteria = []
59 @criteria = []
60 end
60 end
61
61
62 def available_criteria=(criteria)
62 def available_criteria=(criteria)
63 unless criteria.is_a?(Hash)
63 unless criteria.is_a?(Hash)
64 criteria = criteria.inject({}) {|h,k| h[k] = k; h}
64 criteria = criteria.inject({}) {|h,k| h[k] = k; h}
65 end
65 end
66 @available_criteria = criteria
66 @available_criteria = criteria
67 end
67 end
68
68
69 def from_param(param)
69 def from_param(param)
70 @criteria = param.to_s.split(',').collect {|s| s.split(':')[0..1]}
70 @criteria = param.to_s.split(',').collect {|s| s.split(':')[0..1]}
71 normalize!
71 normalize!
72 end
72 end
73
73
74 def criteria=(arg)
74 def criteria=(arg)
75 @criteria = arg
75 @criteria = arg
76 normalize!
76 normalize!
77 end
77 end
78
78
79 def to_param
79 def to_param
80 @criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
80 @criteria.collect {|k,o| k + (o ? '' : ':desc')}.join(',')
81 end
81 end
82
82
83 def to_sql
83 def to_sql
84 sql = @criteria.collect do |k,o|
84 sql = @criteria.collect do |k,o|
85 if s = @available_criteria[k]
85 if s = @available_criteria[k]
86 (o ? s.to_a : s.to_a.collect {|c| append_desc(c)}).join(', ')
86 (o ? s.to_a : s.to_a.collect {|c| append_desc(c)}).join(', ')
87 end
87 end
88 end.compact.join(', ')
88 end.compact.join(', ')
89 sql.blank? ? nil : sql
89 sql.blank? ? nil : sql
90 end
90 end
91
91
92 def to_a
93 @criteria.dup
94 end
95
92 def add!(key, asc)
96 def add!(key, asc)
93 @criteria.delete_if {|k,o| k == key}
97 @criteria.delete_if {|k,o| k == key}
94 @criteria = [[key, asc]] + @criteria
98 @criteria = [[key, asc]] + @criteria
95 normalize!
99 normalize!
96 end
100 end
97
101
98 def add(*args)
102 def add(*args)
99 r = self.class.new.from_param(to_param)
103 r = self.class.new.from_param(to_param)
100 r.add!(*args)
104 r.add!(*args)
101 r
105 r
102 end
106 end
103
107
104 def first_key
108 def first_key
105 @criteria.first && @criteria.first.first
109 @criteria.first && @criteria.first.first
106 end
110 end
107
111
108 def first_asc?
112 def first_asc?
109 @criteria.first && @criteria.first.last
113 @criteria.first && @criteria.first.last
110 end
114 end
111
115
112 def empty?
116 def empty?
113 @criteria.empty?
117 @criteria.empty?
114 end
118 end
115
119
116 private
120 private
117
121
118 def normalize!
122 def normalize!
119 @criteria ||= []
123 @criteria ||= []
120 @criteria = @criteria.collect {|s| s = s.to_a; [s.first, (s.last == false || s.last == 'desc') ? false : true]}
124 @criteria = @criteria.collect {|s| s = s.to_a; [s.first, (s.last == false || s.last == 'desc') ? false : true]}
121 @criteria = @criteria.select {|k,o| @available_criteria.has_key?(k)} if @available_criteria
125 @criteria = @criteria.select {|k,o| @available_criteria.has_key?(k)} if @available_criteria
122 @criteria.slice!(3)
126 @criteria.slice!(3)
123 self
127 self
124 end
128 end
125
129
126 # Appends DESC to the sort criterion unless it has a fixed order
130 # Appends DESC to the sort criterion unless it has a fixed order
127 def append_desc(criterion)
131 def append_desc(criterion)
128 if criterion =~ / (asc|desc)$/i
132 if criterion =~ / (asc|desc)$/i
129 criterion
133 criterion
130 else
134 else
131 "#{criterion} DESC"
135 "#{criterion} DESC"
132 end
136 end
133 end
137 end
134 end
138 end
135
139
136 def sort_name
140 def sort_name
137 controller_name + '_' + action_name + '_sort'
141 controller_name + '_' + action_name + '_sort'
138 end
142 end
139
143
140 # Initializes the default sort.
144 # Initializes the default sort.
141 # Examples:
145 # Examples:
142 #
146 #
143 # sort_init 'name'
147 # sort_init 'name'
144 # sort_init 'id', 'desc'
148 # sort_init 'id', 'desc'
145 # sort_init ['name', ['id', 'desc']]
149 # sort_init ['name', ['id', 'desc']]
146 # sort_init [['name', 'desc'], ['id', 'desc']]
150 # sort_init [['name', 'desc'], ['id', 'desc']]
147 #
151 #
148 def sort_init(*args)
152 def sort_init(*args)
149 case args.size
153 case args.size
150 when 1
154 when 1
151 @sort_default = args.first.is_a?(Array) ? args.first : [[args.first]]
155 @sort_default = args.first.is_a?(Array) ? args.first : [[args.first]]
152 when 2
156 when 2
153 @sort_default = [[args.first, args.last]]
157 @sort_default = [[args.first, args.last]]
154 else
158 else
155 raise ArgumentError
159 raise ArgumentError
156 end
160 end
157 end
161 end
158
162
159 # Updates the sort state. Call this in the controller prior to calling
163 # Updates the sort state. Call this in the controller prior to calling
160 # sort_clause.
164 # sort_clause.
161 # - criteria can be either an array or a hash of allowed keys
165 # - criteria can be either an array or a hash of allowed keys
162 #
166 #
163 def sort_update(criteria, sort_name=nil)
167 def sort_update(criteria, sort_name=nil)
164 sort_name ||= self.sort_name
168 sort_name ||= self.sort_name
165 @sort_criteria = SortCriteria.new
169 @sort_criteria = SortCriteria.new
166 @sort_criteria.available_criteria = criteria
170 @sort_criteria.available_criteria = criteria
167 @sort_criteria.from_param(params[:sort] || session[sort_name])
171 @sort_criteria.from_param(params[:sort] || session[sort_name])
168 @sort_criteria.criteria = @sort_default if @sort_criteria.empty?
172 @sort_criteria.criteria = @sort_default if @sort_criteria.empty?
169 session[sort_name] = @sort_criteria.to_param
173 session[sort_name] = @sort_criteria.to_param
170 end
174 end
171
175
172 # Clears the sort criteria session data
176 # Clears the sort criteria session data
173 #
177 #
174 def sort_clear
178 def sort_clear
175 session[sort_name] = nil
179 session[sort_name] = nil
176 end
180 end
177
181
178 # Returns an SQL sort clause corresponding to the current sort state.
182 # Returns an SQL sort clause corresponding to the current sort state.
179 # Use this to sort the controller's table items collection.
183 # Use this to sort the controller's table items collection.
180 #
184 #
181 def sort_clause()
185 def sort_clause()
182 @sort_criteria.to_sql
186 @sort_criteria.to_sql
183 end
187 end
184
188
189 def sort_criteria
190 @sort_criteria
191 end
192
185 # Returns a link which sorts by the named column.
193 # Returns a link which sorts by the named column.
186 #
194 #
187 # - column is the name of an attribute in the sorted record collection.
195 # - column is the name of an attribute in the sorted record collection.
188 # - the optional caption explicitly specifies the displayed link text.
196 # - the optional caption explicitly specifies the displayed link text.
189 # - 2 CSS classes reflect the state of the link: sort and asc or desc
197 # - 2 CSS classes reflect the state of the link: sort and asc or desc
190 #
198 #
191 def sort_link(column, caption, default_order)
199 def sort_link(column, caption, default_order)
192 css, order = nil, default_order
200 css, order = nil, default_order
193
201
194 if column.to_s == @sort_criteria.first_key
202 if column.to_s == @sort_criteria.first_key
195 if @sort_criteria.first_asc?
203 if @sort_criteria.first_asc?
196 css = 'sort asc'
204 css = 'sort asc'
197 order = 'desc'
205 order = 'desc'
198 else
206 else
199 css = 'sort desc'
207 css = 'sort desc'
200 order = 'asc'
208 order = 'asc'
201 end
209 end
202 end
210 end
203 caption = column.to_s.humanize unless caption
211 caption = column.to_s.humanize unless caption
204
212
205 sort_options = { :sort => @sort_criteria.add(column.to_s, order).to_param }
213 sort_options = { :sort => @sort_criteria.add(column.to_s, order).to_param }
206 url_options = params.merge(sort_options)
214 url_options = params.merge(sort_options)
207
215
208 # Add project_id to url_options
216 # Add project_id to url_options
209 url_options = url_options.merge(:project_id => params[:project_id]) if params.has_key?(:project_id)
217 url_options = url_options.merge(:project_id => params[:project_id]) if params.has_key?(:project_id)
210
218
211 link_to_content_update(h(caption), url_options, :class => css)
219 link_to_content_update(h(caption), url_options, :class => css)
212 end
220 end
213
221
214 # Returns a table header <th> tag with a sort link for the named column
222 # Returns a table header <th> tag with a sort link for the named column
215 # attribute.
223 # attribute.
216 #
224 #
217 # Options:
225 # Options:
218 # :caption The displayed link name (defaults to titleized column name).
226 # :caption The displayed link name (defaults to titleized column name).
219 # :title The tag's 'title' attribute (defaults to 'Sort by :caption').
227 # :title The tag's 'title' attribute (defaults to 'Sort by :caption').
220 #
228 #
221 # Other options hash entries generate additional table header tag attributes.
229 # Other options hash entries generate additional table header tag attributes.
222 #
230 #
223 # Example:
231 # Example:
224 #
232 #
225 # <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
233 # <%= sort_header_tag('id', :title => 'Sort by contact ID', :width => 40) %>
226 #
234 #
227 def sort_header_tag(column, options = {})
235 def sort_header_tag(column, options = {})
228 caption = options.delete(:caption) || column.to_s.humanize
236 caption = options.delete(:caption) || column.to_s.humanize
229 default_order = options.delete(:default_order) || 'asc'
237 default_order = options.delete(:default_order) || 'asc'
230 options[:title] = l(:label_sort_by, "\"#{caption}\"") unless options[:title]
238 options[:title] = l(:label_sort_by, "\"#{caption}\"") unless options[:title]
231 content_tag('th', sort_link(column, caption, default_order), options)
239 content_tag('th', sort_link(column, caption, default_order), options)
232 end
240 end
233 end
241 end
234
242
@@ -1,1063 +1,1068
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 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
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class QueryColumn
18 class QueryColumn
19 attr_accessor :name, :sortable, :groupable, :default_order
19 attr_accessor :name, :sortable, :groupable, :default_order
20 include Redmine::I18n
20 include Redmine::I18n
21
21
22 def initialize(name, options={})
22 def initialize(name, options={})
23 self.name = name
23 self.name = name
24 self.sortable = options[:sortable]
24 self.sortable = options[:sortable]
25 self.groupable = options[:groupable] || false
25 self.groupable = options[:groupable] || false
26 if groupable == true
26 if groupable == true
27 self.groupable = name.to_s
27 self.groupable = name.to_s
28 end
28 end
29 self.default_order = options[:default_order]
29 self.default_order = options[:default_order]
30 @caption_key = options[:caption] || "field_#{name}"
30 @caption_key = options[:caption] || "field_#{name}"
31 end
31 end
32
32
33 def caption
33 def caption
34 l(@caption_key)
34 l(@caption_key)
35 end
35 end
36
36
37 # Returns true if the column is sortable, otherwise false
37 # Returns true if the column is sortable, otherwise false
38 def sortable?
38 def sortable?
39 !@sortable.nil?
39 !@sortable.nil?
40 end
40 end
41
41
42 def sortable
42 def sortable
43 @sortable.is_a?(Proc) ? @sortable.call : @sortable
43 @sortable.is_a?(Proc) ? @sortable.call : @sortable
44 end
44 end
45
45
46 def value(issue)
46 def value(issue)
47 issue.send name
47 issue.send name
48 end
48 end
49
49
50 def css_classes
50 def css_classes
51 name
51 name
52 end
52 end
53 end
53 end
54
54
55 class QueryCustomFieldColumn < QueryColumn
55 class QueryCustomFieldColumn < QueryColumn
56
56
57 def initialize(custom_field)
57 def initialize(custom_field)
58 self.name = "cf_#{custom_field.id}".to_sym
58 self.name = "cf_#{custom_field.id}".to_sym
59 self.sortable = custom_field.order_statement || false
59 self.sortable = custom_field.order_statement || false
60 self.groupable = custom_field.group_statement || false
60 self.groupable = custom_field.group_statement || false
61 @cf = custom_field
61 @cf = custom_field
62 end
62 end
63
63
64 def caption
64 def caption
65 @cf.name
65 @cf.name
66 end
66 end
67
67
68 def custom_field
68 def custom_field
69 @cf
69 @cf
70 end
70 end
71
71
72 def value(issue)
72 def value(issue)
73 cv = issue.custom_values.select {|v| v.custom_field_id == @cf.id}.collect {|v| @cf.cast_value(v.value)}
73 cv = issue.custom_values.select {|v| v.custom_field_id == @cf.id}.collect {|v| @cf.cast_value(v.value)}
74 cv.size > 1 ? cv.sort {|a,b| a.to_s <=> b.to_s} : cv.first
74 cv.size > 1 ? cv.sort {|a,b| a.to_s <=> b.to_s} : cv.first
75 end
75 end
76
76
77 def css_classes
77 def css_classes
78 @css_classes ||= "#{name} #{@cf.field_format}"
78 @css_classes ||= "#{name} #{@cf.field_format}"
79 end
79 end
80 end
80 end
81
81
82 class Query < ActiveRecord::Base
82 class Query < ActiveRecord::Base
83 class StatementInvalid < ::ActiveRecord::StatementInvalid
83 class StatementInvalid < ::ActiveRecord::StatementInvalid
84 end
84 end
85
85
86 belongs_to :project
86 belongs_to :project
87 belongs_to :user
87 belongs_to :user
88 serialize :filters
88 serialize :filters
89 serialize :column_names
89 serialize :column_names
90 serialize :sort_criteria, Array
90 serialize :sort_criteria, Array
91
91
92 attr_protected :project_id, :user_id
92 attr_protected :project_id, :user_id
93
93
94 validates_presence_of :name
94 validates_presence_of :name
95 validates_length_of :name, :maximum => 255
95 validates_length_of :name, :maximum => 255
96 validate :validate_query_filters
96 validate :validate_query_filters
97
97
98 @@operators = { "=" => :label_equals,
98 @@operators = { "=" => :label_equals,
99 "!" => :label_not_equals,
99 "!" => :label_not_equals,
100 "o" => :label_open_issues,
100 "o" => :label_open_issues,
101 "c" => :label_closed_issues,
101 "c" => :label_closed_issues,
102 "!*" => :label_none,
102 "!*" => :label_none,
103 "*" => :label_any,
103 "*" => :label_any,
104 ">=" => :label_greater_or_equal,
104 ">=" => :label_greater_or_equal,
105 "<=" => :label_less_or_equal,
105 "<=" => :label_less_or_equal,
106 "><" => :label_between,
106 "><" => :label_between,
107 "<t+" => :label_in_less_than,
107 "<t+" => :label_in_less_than,
108 ">t+" => :label_in_more_than,
108 ">t+" => :label_in_more_than,
109 "t+" => :label_in,
109 "t+" => :label_in,
110 "t" => :label_today,
110 "t" => :label_today,
111 "w" => :label_this_week,
111 "w" => :label_this_week,
112 ">t-" => :label_less_than_ago,
112 ">t-" => :label_less_than_ago,
113 "<t-" => :label_more_than_ago,
113 "<t-" => :label_more_than_ago,
114 "t-" => :label_ago,
114 "t-" => :label_ago,
115 "~" => :label_contains,
115 "~" => :label_contains,
116 "!~" => :label_not_contains,
116 "!~" => :label_not_contains,
117 "=p" => :label_any_issues_in_project,
117 "=p" => :label_any_issues_in_project,
118 "=!p" => :label_any_issues_not_in_project,
118 "=!p" => :label_any_issues_not_in_project,
119 "!p" => :label_no_issues_in_project}
119 "!p" => :label_no_issues_in_project}
120
120
121 cattr_reader :operators
121 cattr_reader :operators
122
122
123 @@operators_by_filter_type = { :list => [ "=", "!" ],
123 @@operators_by_filter_type = { :list => [ "=", "!" ],
124 :list_status => [ "o", "=", "!", "c", "*" ],
124 :list_status => [ "o", "=", "!", "c", "*" ],
125 :list_optional => [ "=", "!", "!*", "*" ],
125 :list_optional => [ "=", "!", "!*", "*" ],
126 :list_subprojects => [ "*", "!*", "=" ],
126 :list_subprojects => [ "*", "!*", "=" ],
127 :date => [ "=", ">=", "<=", "><", "<t+", ">t+", "t+", "t", "w", ">t-", "<t-", "t-", "!*", "*" ],
127 :date => [ "=", ">=", "<=", "><", "<t+", ">t+", "t+", "t", "w", ">t-", "<t-", "t-", "!*", "*" ],
128 :date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "t-", "t", "w", "!*", "*" ],
128 :date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "t-", "t", "w", "!*", "*" ],
129 :string => [ "=", "~", "!", "!~", "!*", "*" ],
129 :string => [ "=", "~", "!", "!~", "!*", "*" ],
130 :text => [ "~", "!~", "!*", "*" ],
130 :text => [ "~", "!~", "!*", "*" ],
131 :integer => [ "=", ">=", "<=", "><", "!*", "*" ],
131 :integer => [ "=", ">=", "<=", "><", "!*", "*" ],
132 :float => [ "=", ">=", "<=", "><", "!*", "*" ],
132 :float => [ "=", ">=", "<=", "><", "!*", "*" ],
133 :relation => ["=", "=p", "=!p", "!p", "!*", "*"]}
133 :relation => ["=", "=p", "=!p", "!p", "!*", "*"]}
134
134
135 cattr_reader :operators_by_filter_type
135 cattr_reader :operators_by_filter_type
136
136
137 @@available_columns = [
137 @@available_columns = [
138 QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
138 QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
139 QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
139 QueryColumn.new(:tracker, :sortable => "#{Tracker.table_name}.position", :groupable => true),
140 QueryColumn.new(:parent, :sortable => ["#{Issue.table_name}.root_id", "#{Issue.table_name}.lft ASC"], :default_order => 'desc', :caption => :field_parent_issue),
140 QueryColumn.new(:parent, :sortable => ["#{Issue.table_name}.root_id", "#{Issue.table_name}.lft ASC"], :default_order => 'desc', :caption => :field_parent_issue),
141 QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true),
141 QueryColumn.new(:status, :sortable => "#{IssueStatus.table_name}.position", :groupable => true),
142 QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true),
142 QueryColumn.new(:priority, :sortable => "#{IssuePriority.table_name}.position", :default_order => 'desc', :groupable => true),
143 QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
143 QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
144 QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement("authors")}, :groupable => true),
144 QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement("authors")}, :groupable => true),
145 QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
145 QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
146 QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
146 QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
147 QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
147 QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
148 QueryColumn.new(:fixed_version, :sortable => lambda {Version.fields_for_order_statement}, :groupable => true),
148 QueryColumn.new(:fixed_version, :sortable => lambda {Version.fields_for_order_statement}, :groupable => true),
149 QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
149 QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"),
150 QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
150 QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"),
151 QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
151 QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"),
152 QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
152 QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
153 QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
153 QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
154 QueryColumn.new(:relations, :caption => :label_related_issues)
154 QueryColumn.new(:relations, :caption => :label_related_issues)
155 ]
155 ]
156 cattr_reader :available_columns
156 cattr_reader :available_columns
157
157
158 scope :visible, lambda {|*args|
158 scope :visible, lambda {|*args|
159 user = args.shift || User.current
159 user = args.shift || User.current
160 base = Project.allowed_to_condition(user, :view_issues, *args)
160 base = Project.allowed_to_condition(user, :view_issues, *args)
161 user_id = user.logged? ? user.id : 0
161 user_id = user.logged? ? user.id : 0
162 {
162 {
163 :conditions => ["(#{table_name}.project_id IS NULL OR (#{base})) AND (#{table_name}.is_public = ? OR #{table_name}.user_id = ?)", true, user_id],
163 :conditions => ["(#{table_name}.project_id IS NULL OR (#{base})) AND (#{table_name}.is_public = ? OR #{table_name}.user_id = ?)", true, user_id],
164 :include => :project
164 :include => :project
165 }
165 }
166 }
166 }
167
167
168 def initialize(attributes=nil, *args)
168 def initialize(attributes=nil, *args)
169 super attributes
169 super attributes
170 self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
170 self.filters ||= { 'status_id' => {:operator => "o", :values => [""]} }
171 @is_for_all = project.nil?
171 @is_for_all = project.nil?
172 end
172 end
173
173
174 def validate_query_filters
174 def validate_query_filters
175 filters.each_key do |field|
175 filters.each_key do |field|
176 if values_for(field)
176 if values_for(field)
177 case type_for(field)
177 case type_for(field)
178 when :integer
178 when :integer
179 add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^[+-]?\d+$/) }
179 add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^[+-]?\d+$/) }
180 when :float
180 when :float
181 add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^[+-]?\d+(\.\d*)?$/) }
181 add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^[+-]?\d+(\.\d*)?$/) }
182 when :date, :date_past
182 when :date, :date_past
183 case operator_for(field)
183 case operator_for(field)
184 when "=", ">=", "<=", "><"
184 when "=", ">=", "<=", "><"
185 add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && (!v.match(/^\d{4}-\d{2}-\d{2}$/) || (Date.parse(v) rescue nil).nil?) }
185 add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && (!v.match(/^\d{4}-\d{2}-\d{2}$/) || (Date.parse(v) rescue nil).nil?) }
186 when ">t-", "<t-", "t-"
186 when ">t-", "<t-", "t-"
187 add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^\d+$/) }
187 add_filter_error(field, :invalid) if values_for(field).detect {|v| v.present? && !v.match(/^\d+$/) }
188 end
188 end
189 end
189 end
190 end
190 end
191
191
192 add_filter_error(field, :blank) unless
192 add_filter_error(field, :blank) unless
193 # filter requires one or more values
193 # filter requires one or more values
194 (values_for(field) and !values_for(field).first.blank?) or
194 (values_for(field) and !values_for(field).first.blank?) or
195 # filter doesn't require any value
195 # filter doesn't require any value
196 ["o", "c", "!*", "*", "t", "w"].include? operator_for(field)
196 ["o", "c", "!*", "*", "t", "w"].include? operator_for(field)
197 end if filters
197 end if filters
198 end
198 end
199
199
200 def add_filter_error(field, message)
200 def add_filter_error(field, message)
201 m = label_for(field) + " " + l(message, :scope => 'activerecord.errors.messages')
201 m = label_for(field) + " " + l(message, :scope => 'activerecord.errors.messages')
202 errors.add(:base, m)
202 errors.add(:base, m)
203 end
203 end
204
204
205 # Returns true if the query is visible to +user+ or the current user.
205 # Returns true if the query is visible to +user+ or the current user.
206 def visible?(user=User.current)
206 def visible?(user=User.current)
207 (project.nil? || user.allowed_to?(:view_issues, project)) && (self.is_public? || self.user_id == user.id)
207 (project.nil? || user.allowed_to?(:view_issues, project)) && (self.is_public? || self.user_id == user.id)
208 end
208 end
209
209
210 def editable_by?(user)
210 def editable_by?(user)
211 return false unless user
211 return false unless user
212 # Admin can edit them all and regular users can edit their private queries
212 # Admin can edit them all and regular users can edit their private queries
213 return true if user.admin? || (!is_public && self.user_id == user.id)
213 return true if user.admin? || (!is_public && self.user_id == user.id)
214 # Members can not edit public queries that are for all project (only admin is allowed to)
214 # Members can not edit public queries that are for all project (only admin is allowed to)
215 is_public && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
215 is_public && !@is_for_all && user.allowed_to?(:manage_public_queries, project)
216 end
216 end
217
217
218 def trackers
218 def trackers
219 @trackers ||= project.nil? ? Tracker.find(:all, :order => 'position') : project.rolled_up_trackers
219 @trackers ||= project.nil? ? Tracker.find(:all, :order => 'position') : project.rolled_up_trackers
220 end
220 end
221
221
222 # Returns a hash of localized labels for all filter operators
222 # Returns a hash of localized labels for all filter operators
223 def self.operators_labels
223 def self.operators_labels
224 operators.inject({}) {|h, operator| h[operator.first] = l(operator.last); h}
224 operators.inject({}) {|h, operator| h[operator.first] = l(operator.last); h}
225 end
225 end
226
226
227 def available_filters
227 def available_filters
228 return @available_filters if @available_filters
228 return @available_filters if @available_filters
229 @available_filters = {
229 @available_filters = {
230 "status_id" => {
230 "status_id" => {
231 :type => :list_status, :order => 0,
231 :type => :list_status, :order => 0,
232 :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] }
232 :values => IssueStatus.find(:all, :order => 'position').collect{|s| [s.name, s.id.to_s] }
233 },
233 },
234 "tracker_id" => {
234 "tracker_id" => {
235 :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] }
235 :type => :list, :order => 2, :values => trackers.collect{|s| [s.name, s.id.to_s] }
236 },
236 },
237 "priority_id" => {
237 "priority_id" => {
238 :type => :list, :order => 3, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
238 :type => :list, :order => 3, :values => IssuePriority.all.collect{|s| [s.name, s.id.to_s] }
239 },
239 },
240 "subject" => { :type => :text, :order => 8 },
240 "subject" => { :type => :text, :order => 8 },
241 "created_on" => { :type => :date_past, :order => 9 },
241 "created_on" => { :type => :date_past, :order => 9 },
242 "updated_on" => { :type => :date_past, :order => 10 },
242 "updated_on" => { :type => :date_past, :order => 10 },
243 "start_date" => { :type => :date, :order => 11 },
243 "start_date" => { :type => :date, :order => 11 },
244 "due_date" => { :type => :date, :order => 12 },
244 "due_date" => { :type => :date, :order => 12 },
245 "estimated_hours" => { :type => :float, :order => 13 },
245 "estimated_hours" => { :type => :float, :order => 13 },
246 "done_ratio" => { :type => :integer, :order => 14 }
246 "done_ratio" => { :type => :integer, :order => 14 }
247 }
247 }
248 IssueRelation::TYPES.each do |relation_type, options|
248 IssueRelation::TYPES.each do |relation_type, options|
249 @available_filters[relation_type] = {
249 @available_filters[relation_type] = {
250 :type => :relation, :order => @available_filters.size + 100,
250 :type => :relation, :order => @available_filters.size + 100,
251 :label => options[:name]
251 :label => options[:name]
252 }
252 }
253 end
253 end
254 principals = []
254 principals = []
255 if project
255 if project
256 principals += project.principals.sort
256 principals += project.principals.sort
257 unless project.leaf?
257 unless project.leaf?
258 subprojects = project.descendants.visible.all
258 subprojects = project.descendants.visible.all
259 if subprojects.any?
259 if subprojects.any?
260 @available_filters["subproject_id"] = {
260 @available_filters["subproject_id"] = {
261 :type => :list_subprojects, :order => 13,
261 :type => :list_subprojects, :order => 13,
262 :values => subprojects.collect{|s| [s.name, s.id.to_s] }
262 :values => subprojects.collect{|s| [s.name, s.id.to_s] }
263 }
263 }
264 principals += Principal.member_of(subprojects)
264 principals += Principal.member_of(subprojects)
265 end
265 end
266 end
266 end
267 else
267 else
268 if all_projects.any?
268 if all_projects.any?
269 # members of visible projects
269 # members of visible projects
270 principals += Principal.member_of(all_projects)
270 principals += Principal.member_of(all_projects)
271 # project filter
271 # project filter
272 project_values = []
272 project_values = []
273 if User.current.logged? && User.current.memberships.any?
273 if User.current.logged? && User.current.memberships.any?
274 project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"]
274 project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"]
275 end
275 end
276 project_values += all_projects_values
276 project_values += all_projects_values
277 @available_filters["project_id"] = {
277 @available_filters["project_id"] = {
278 :type => :list, :order => 1, :values => project_values
278 :type => :list, :order => 1, :values => project_values
279 } unless project_values.empty?
279 } unless project_values.empty?
280 end
280 end
281 end
281 end
282 principals.uniq!
282 principals.uniq!
283 principals.sort!
283 principals.sort!
284 users = principals.select {|p| p.is_a?(User)}
284 users = principals.select {|p| p.is_a?(User)}
285
285
286 assigned_to_values = []
286 assigned_to_values = []
287 assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
287 assigned_to_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
288 assigned_to_values += (Setting.issue_group_assignment? ?
288 assigned_to_values += (Setting.issue_group_assignment? ?
289 principals : users).collect{|s| [s.name, s.id.to_s] }
289 principals : users).collect{|s| [s.name, s.id.to_s] }
290 @available_filters["assigned_to_id"] = {
290 @available_filters["assigned_to_id"] = {
291 :type => :list_optional, :order => 4, :values => assigned_to_values
291 :type => :list_optional, :order => 4, :values => assigned_to_values
292 } unless assigned_to_values.empty?
292 } unless assigned_to_values.empty?
293
293
294 author_values = []
294 author_values = []
295 author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
295 author_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
296 author_values += users.collect{|s| [s.name, s.id.to_s] }
296 author_values += users.collect{|s| [s.name, s.id.to_s] }
297 @available_filters["author_id"] = {
297 @available_filters["author_id"] = {
298 :type => :list, :order => 5, :values => author_values
298 :type => :list, :order => 5, :values => author_values
299 } unless author_values.empty?
299 } unless author_values.empty?
300
300
301 group_values = Group.all.collect {|g| [g.name, g.id.to_s] }
301 group_values = Group.all.collect {|g| [g.name, g.id.to_s] }
302 @available_filters["member_of_group"] = {
302 @available_filters["member_of_group"] = {
303 :type => :list_optional, :order => 6, :values => group_values
303 :type => :list_optional, :order => 6, :values => group_values
304 } unless group_values.empty?
304 } unless group_values.empty?
305
305
306 role_values = Role.givable.collect {|r| [r.name, r.id.to_s] }
306 role_values = Role.givable.collect {|r| [r.name, r.id.to_s] }
307 @available_filters["assigned_to_role"] = {
307 @available_filters["assigned_to_role"] = {
308 :type => :list_optional, :order => 7, :values => role_values
308 :type => :list_optional, :order => 7, :values => role_values
309 } unless role_values.empty?
309 } unless role_values.empty?
310
310
311 if User.current.logged?
311 if User.current.logged?
312 @available_filters["watcher_id"] = {
312 @available_filters["watcher_id"] = {
313 :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]]
313 :type => :list, :order => 15, :values => [["<< #{l(:label_me)} >>", "me"]]
314 }
314 }
315 end
315 end
316
316
317 if project
317 if project
318 # project specific filters
318 # project specific filters
319 categories = project.issue_categories.all
319 categories = project.issue_categories.all
320 unless categories.empty?
320 unless categories.empty?
321 @available_filters["category_id"] = {
321 @available_filters["category_id"] = {
322 :type => :list_optional, :order => 6,
322 :type => :list_optional, :order => 6,
323 :values => categories.collect{|s| [s.name, s.id.to_s] }
323 :values => categories.collect{|s| [s.name, s.id.to_s] }
324 }
324 }
325 end
325 end
326 versions = project.shared_versions.all
326 versions = project.shared_versions.all
327 unless versions.empty?
327 unless versions.empty?
328 @available_filters["fixed_version_id"] = {
328 @available_filters["fixed_version_id"] = {
329 :type => :list_optional, :order => 7,
329 :type => :list_optional, :order => 7,
330 :values => versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] }
330 :values => versions.sort.collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s] }
331 }
331 }
332 end
332 end
333 add_custom_fields_filters(project.all_issue_custom_fields)
333 add_custom_fields_filters(project.all_issue_custom_fields)
334 else
334 else
335 # global filters for cross project issue list
335 # global filters for cross project issue list
336 system_shared_versions = Version.visible.find_all_by_sharing('system')
336 system_shared_versions = Version.visible.find_all_by_sharing('system')
337 unless system_shared_versions.empty?
337 unless system_shared_versions.empty?
338 @available_filters["fixed_version_id"] = {
338 @available_filters["fixed_version_id"] = {
339 :type => :list_optional, :order => 7,
339 :type => :list_optional, :order => 7,
340 :values => system_shared_versions.sort.collect{|s|
340 :values => system_shared_versions.sort.collect{|s|
341 ["#{s.project.name} - #{s.name}", s.id.to_s]
341 ["#{s.project.name} - #{s.name}", s.id.to_s]
342 }
342 }
343 }
343 }
344 end
344 end
345 add_custom_fields_filters(
345 add_custom_fields_filters(
346 IssueCustomField.find(:all,
346 IssueCustomField.find(:all,
347 :conditions => {
347 :conditions => {
348 :is_filter => true,
348 :is_filter => true,
349 :is_for_all => true
349 :is_for_all => true
350 }))
350 }))
351 end
351 end
352 add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
352 add_associations_custom_fields_filters :project, :author, :assigned_to, :fixed_version
353 if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
353 if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
354 User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
354 User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
355 @available_filters["is_private"] = {
355 @available_filters["is_private"] = {
356 :type => :list, :order => 16,
356 :type => :list, :order => 16,
357 :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]]
357 :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]]
358 }
358 }
359 end
359 end
360 Tracker.disabled_core_fields(trackers).each {|field|
360 Tracker.disabled_core_fields(trackers).each {|field|
361 @available_filters.delete field
361 @available_filters.delete field
362 }
362 }
363 @available_filters.each do |field, options|
363 @available_filters.each do |field, options|
364 options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, ''))
364 options[:name] ||= l(options[:label] || "field_#{field}".gsub(/_id$/, ''))
365 end
365 end
366 @available_filters
366 @available_filters
367 end
367 end
368
368
369 # Returns a representation of the available filters for JSON serialization
369 # Returns a representation of the available filters for JSON serialization
370 def available_filters_as_json
370 def available_filters_as_json
371 json = {}
371 json = {}
372 available_filters.each do |field, options|
372 available_filters.each do |field, options|
373 json[field] = options.slice(:type, :name, :values).stringify_keys
373 json[field] = options.slice(:type, :name, :values).stringify_keys
374 end
374 end
375 json
375 json
376 end
376 end
377
377
378 def all_projects
378 def all_projects
379 @all_projects ||= Project.visible.all
379 @all_projects ||= Project.visible.all
380 end
380 end
381
381
382 def all_projects_values
382 def all_projects_values
383 return @all_projects_values if @all_projects_values
383 return @all_projects_values if @all_projects_values
384
384
385 values = []
385 values = []
386 Project.project_tree(all_projects) do |p, level|
386 Project.project_tree(all_projects) do |p, level|
387 prefix = (level > 0 ? ('--' * level + ' ') : '')
387 prefix = (level > 0 ? ('--' * level + ' ') : '')
388 values << ["#{prefix}#{p.name}", p.id.to_s]
388 values << ["#{prefix}#{p.name}", p.id.to_s]
389 end
389 end
390 @all_projects_values = values
390 @all_projects_values = values
391 end
391 end
392
392
393 def add_filter(field, operator, values)
393 def add_filter(field, operator, values)
394 # values must be an array
394 # values must be an array
395 return unless values.nil? || values.is_a?(Array)
395 return unless values.nil? || values.is_a?(Array)
396 # check if field is defined as an available filter
396 # check if field is defined as an available filter
397 if available_filters.has_key? field
397 if available_filters.has_key? field
398 filter_options = available_filters[field]
398 filter_options = available_filters[field]
399 # check if operator is allowed for that filter
399 # check if operator is allowed for that filter
400 #if @@operators_by_filter_type[filter_options[:type]].include? operator
400 #if @@operators_by_filter_type[filter_options[:type]].include? operator
401 # allowed_values = values & ([""] + (filter_options[:values] || []).collect {|val| val[1]})
401 # allowed_values = values & ([""] + (filter_options[:values] || []).collect {|val| val[1]})
402 # filters[field] = {:operator => operator, :values => allowed_values } if (allowed_values.first and !allowed_values.first.empty?) or ["o", "c", "!*", "*", "t"].include? operator
402 # filters[field] = {:operator => operator, :values => allowed_values } if (allowed_values.first and !allowed_values.first.empty?) or ["o", "c", "!*", "*", "t"].include? operator
403 #end
403 #end
404 filters[field] = {:operator => operator, :values => (values || [''])}
404 filters[field] = {:operator => operator, :values => (values || [''])}
405 end
405 end
406 end
406 end
407
407
408 def add_short_filter(field, expression)
408 def add_short_filter(field, expression)
409 return unless expression && available_filters.has_key?(field)
409 return unless expression && available_filters.has_key?(field)
410 field_type = available_filters[field][:type]
410 field_type = available_filters[field][:type]
411 @@operators_by_filter_type[field_type].sort.reverse.detect do |operator|
411 @@operators_by_filter_type[field_type].sort.reverse.detect do |operator|
412 next unless expression =~ /^#{Regexp.escape(operator)}(.*)$/
412 next unless expression =~ /^#{Regexp.escape(operator)}(.*)$/
413 add_filter field, operator, $1.present? ? $1.split('|') : ['']
413 add_filter field, operator, $1.present? ? $1.split('|') : ['']
414 end || add_filter(field, '=', expression.split('|'))
414 end || add_filter(field, '=', expression.split('|'))
415 end
415 end
416
416
417 # Add multiple filters using +add_filter+
417 # Add multiple filters using +add_filter+
418 def add_filters(fields, operators, values)
418 def add_filters(fields, operators, values)
419 if fields.is_a?(Array) && operators.is_a?(Hash) && (values.nil? || values.is_a?(Hash))
419 if fields.is_a?(Array) && operators.is_a?(Hash) && (values.nil? || values.is_a?(Hash))
420 fields.each do |field|
420 fields.each do |field|
421 add_filter(field, operators[field], values && values[field])
421 add_filter(field, operators[field], values && values[field])
422 end
422 end
423 end
423 end
424 end
424 end
425
425
426 def has_filter?(field)
426 def has_filter?(field)
427 filters and filters[field]
427 filters and filters[field]
428 end
428 end
429
429
430 def type_for(field)
430 def type_for(field)
431 available_filters[field][:type] if available_filters.has_key?(field)
431 available_filters[field][:type] if available_filters.has_key?(field)
432 end
432 end
433
433
434 def operator_for(field)
434 def operator_for(field)
435 has_filter?(field) ? filters[field][:operator] : nil
435 has_filter?(field) ? filters[field][:operator] : nil
436 end
436 end
437
437
438 def values_for(field)
438 def values_for(field)
439 has_filter?(field) ? filters[field][:values] : nil
439 has_filter?(field) ? filters[field][:values] : nil
440 end
440 end
441
441
442 def value_for(field, index=0)
442 def value_for(field, index=0)
443 (values_for(field) || [])[index]
443 (values_for(field) || [])[index]
444 end
444 end
445
445
446 def label_for(field)
446 def label_for(field)
447 label = available_filters[field][:name] if available_filters.has_key?(field)
447 label = available_filters[field][:name] if available_filters.has_key?(field)
448 label ||= l("field_#{field.to_s.gsub(/_id$/, '')}", :default => field)
448 label ||= l("field_#{field.to_s.gsub(/_id$/, '')}", :default => field)
449 end
449 end
450
450
451 def available_columns
451 def available_columns
452 return @available_columns if @available_columns
452 return @available_columns if @available_columns
453 @available_columns = ::Query.available_columns.dup
453 @available_columns = ::Query.available_columns.dup
454 @available_columns += (project ?
454 @available_columns += (project ?
455 project.all_issue_custom_fields :
455 project.all_issue_custom_fields :
456 IssueCustomField.find(:all)
456 IssueCustomField.find(:all)
457 ).collect {|cf| QueryCustomFieldColumn.new(cf) }
457 ).collect {|cf| QueryCustomFieldColumn.new(cf) }
458
458
459 if User.current.allowed_to?(:view_time_entries, project, :global => true)
459 if User.current.allowed_to?(:view_time_entries, project, :global => true)
460 index = nil
460 index = nil
461 @available_columns.each_with_index {|column, i| index = i if column.name == :estimated_hours}
461 @available_columns.each_with_index {|column, i| index = i if column.name == :estimated_hours}
462 index = (index ? index + 1 : -1)
462 index = (index ? index + 1 : -1)
463 # insert the column after estimated_hours or at the end
463 # insert the column after estimated_hours or at the end
464 @available_columns.insert index, QueryColumn.new(:spent_hours,
464 @available_columns.insert index, QueryColumn.new(:spent_hours,
465 :sortable => "(SELECT COALESCE(SUM(hours), 0) FROM #{TimeEntry.table_name} WHERE #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id)",
465 :sortable => "(SELECT COALESCE(SUM(hours), 0) FROM #{TimeEntry.table_name} WHERE #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id)",
466 :default_order => 'desc',
466 :default_order => 'desc',
467 :caption => :label_spent_time
467 :caption => :label_spent_time
468 )
468 )
469 end
469 end
470
470
471 if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
471 if User.current.allowed_to?(:set_issues_private, nil, :global => true) ||
472 User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
472 User.current.allowed_to?(:set_own_issues_private, nil, :global => true)
473 @available_columns << QueryColumn.new(:is_private, :sortable => "#{Issue.table_name}.is_private")
473 @available_columns << QueryColumn.new(:is_private, :sortable => "#{Issue.table_name}.is_private")
474 end
474 end
475
475
476 disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')}
476 disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')}
477 @available_columns.reject! {|column|
477 @available_columns.reject! {|column|
478 disabled_fields.include?(column.name.to_s)
478 disabled_fields.include?(column.name.to_s)
479 }
479 }
480
480
481 @available_columns
481 @available_columns
482 end
482 end
483
483
484 def self.available_columns=(v)
484 def self.available_columns=(v)
485 self.available_columns = (v)
485 self.available_columns = (v)
486 end
486 end
487
487
488 def self.add_available_column(column)
488 def self.add_available_column(column)
489 self.available_columns << (column) if column.is_a?(QueryColumn)
489 self.available_columns << (column) if column.is_a?(QueryColumn)
490 end
490 end
491
491
492 # Returns an array of columns that can be used to group the results
492 # Returns an array of columns that can be used to group the results
493 def groupable_columns
493 def groupable_columns
494 available_columns.select {|c| c.groupable}
494 available_columns.select {|c| c.groupable}
495 end
495 end
496
496
497 # Returns a Hash of columns and the key for sorting
497 # Returns a Hash of columns and the key for sorting
498 def sortable_columns
498 def sortable_columns
499 {'id' => "#{Issue.table_name}.id"}.merge(available_columns.inject({}) {|h, column|
499 {'id' => "#{Issue.table_name}.id"}.merge(available_columns.inject({}) {|h, column|
500 h[column.name.to_s] = column.sortable
500 h[column.name.to_s] = column.sortable
501 h
501 h
502 })
502 })
503 end
503 end
504
504
505 def columns
505 def columns
506 # preserve the column_names order
506 # preserve the column_names order
507 (has_default_columns? ? default_columns_names : column_names).collect do |name|
507 (has_default_columns? ? default_columns_names : column_names).collect do |name|
508 available_columns.find { |col| col.name == name }
508 available_columns.find { |col| col.name == name }
509 end.compact
509 end.compact
510 end
510 end
511
511
512 def default_columns_names
512 def default_columns_names
513 @default_columns_names ||= begin
513 @default_columns_names ||= begin
514 default_columns = Setting.issue_list_default_columns.map(&:to_sym)
514 default_columns = Setting.issue_list_default_columns.map(&:to_sym)
515
515
516 project.present? ? default_columns : [:project] | default_columns
516 project.present? ? default_columns : [:project] | default_columns
517 end
517 end
518 end
518 end
519
519
520 def column_names=(names)
520 def column_names=(names)
521 if names
521 if names
522 names = names.select {|n| n.is_a?(Symbol) || !n.blank? }
522 names = names.select {|n| n.is_a?(Symbol) || !n.blank? }
523 names = names.collect {|n| n.is_a?(Symbol) ? n : n.to_sym }
523 names = names.collect {|n| n.is_a?(Symbol) ? n : n.to_sym }
524 # Set column_names to nil if default columns
524 # Set column_names to nil if default columns
525 if names == default_columns_names
525 if names == default_columns_names
526 names = nil
526 names = nil
527 end
527 end
528 end
528 end
529 write_attribute(:column_names, names)
529 write_attribute(:column_names, names)
530 end
530 end
531
531
532 def has_column?(column)
532 def has_column?(column)
533 column_names && column_names.include?(column.is_a?(QueryColumn) ? column.name : column)
533 column_names && column_names.include?(column.is_a?(QueryColumn) ? column.name : column)
534 end
534 end
535
535
536 def has_default_columns?
536 def has_default_columns?
537 column_names.nil? || column_names.empty?
537 column_names.nil? || column_names.empty?
538 end
538 end
539
539
540 def sort_criteria=(arg)
540 def sort_criteria=(arg)
541 c = []
541 c = []
542 if arg.is_a?(Hash)
542 if arg.is_a?(Hash)
543 arg = arg.keys.sort.collect {|k| arg[k]}
543 arg = arg.keys.sort.collect {|k| arg[k]}
544 end
544 end
545 c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, o == 'desc' ? o : 'asc']}
545 c = arg.select {|k,o| !k.to_s.blank?}.slice(0,3).collect {|k,o| [k.to_s, (o == 'desc' || o == false) ? 'desc' : 'asc']}
546 write_attribute(:sort_criteria, c)
546 write_attribute(:sort_criteria, c)
547 end
547 end
548
548
549 def sort_criteria
549 def sort_criteria
550 read_attribute(:sort_criteria) || []
550 read_attribute(:sort_criteria) || []
551 end
551 end
552
552
553 def sort_criteria_key(arg)
553 def sort_criteria_key(arg)
554 sort_criteria && sort_criteria[arg] && sort_criteria[arg].first
554 sort_criteria && sort_criteria[arg] && sort_criteria[arg].first
555 end
555 end
556
556
557 def sort_criteria_order(arg)
557 def sort_criteria_order(arg)
558 sort_criteria && sort_criteria[arg] && sort_criteria[arg].last
558 sort_criteria && sort_criteria[arg] && sort_criteria[arg].last
559 end
559 end
560
560
561 def sort_criteria_order_for(key)
562 sort_criteria.detect {|k, order| key.to_s == k}.try(:last)
563 end
564
561 # Returns the SQL sort order that should be prepended for grouping
565 # Returns the SQL sort order that should be prepended for grouping
562 def group_by_sort_order
566 def group_by_sort_order
563 if grouped? && (column = group_by_column)
567 if grouped? && (column = group_by_column)
568 order = sort_criteria_order_for(column.name) || column.default_order
564 column.sortable.is_a?(Array) ?
569 column.sortable.is_a?(Array) ?
565 column.sortable.collect {|s| "#{s} #{column.default_order}"}.join(',') :
570 column.sortable.collect {|s| "#{s} #{order}"}.join(',') :
566 "#{column.sortable} #{column.default_order}"
571 "#{column.sortable} #{order}"
567 end
572 end
568 end
573 end
569
574
570 # Returns true if the query is a grouped query
575 # Returns true if the query is a grouped query
571 def grouped?
576 def grouped?
572 !group_by_column.nil?
577 !group_by_column.nil?
573 end
578 end
574
579
575 def group_by_column
580 def group_by_column
576 groupable_columns.detect {|c| c.groupable && c.name.to_s == group_by}
581 groupable_columns.detect {|c| c.groupable && c.name.to_s == group_by}
577 end
582 end
578
583
579 def group_by_statement
584 def group_by_statement
580 group_by_column.try(:groupable)
585 group_by_column.try(:groupable)
581 end
586 end
582
587
583 def project_statement
588 def project_statement
584 project_clauses = []
589 project_clauses = []
585 if project && !project.descendants.active.empty?
590 if project && !project.descendants.active.empty?
586 ids = [project.id]
591 ids = [project.id]
587 if has_filter?("subproject_id")
592 if has_filter?("subproject_id")
588 case operator_for("subproject_id")
593 case operator_for("subproject_id")
589 when '='
594 when '='
590 # include the selected subprojects
595 # include the selected subprojects
591 ids += values_for("subproject_id").each(&:to_i)
596 ids += values_for("subproject_id").each(&:to_i)
592 when '!*'
597 when '!*'
593 # main project only
598 # main project only
594 else
599 else
595 # all subprojects
600 # all subprojects
596 ids += project.descendants.collect(&:id)
601 ids += project.descendants.collect(&:id)
597 end
602 end
598 elsif Setting.display_subprojects_issues?
603 elsif Setting.display_subprojects_issues?
599 ids += project.descendants.collect(&:id)
604 ids += project.descendants.collect(&:id)
600 end
605 end
601 project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',')
606 project_clauses << "#{Project.table_name}.id IN (%s)" % ids.join(',')
602 elsif project
607 elsif project
603 project_clauses << "#{Project.table_name}.id = %d" % project.id
608 project_clauses << "#{Project.table_name}.id = %d" % project.id
604 end
609 end
605 project_clauses.any? ? project_clauses.join(' AND ') : nil
610 project_clauses.any? ? project_clauses.join(' AND ') : nil
606 end
611 end
607
612
608 def statement
613 def statement
609 # filters clauses
614 # filters clauses
610 filters_clauses = []
615 filters_clauses = []
611 filters.each_key do |field|
616 filters.each_key do |field|
612 next if field == "subproject_id"
617 next if field == "subproject_id"
613 v = values_for(field).clone
618 v = values_for(field).clone
614 next unless v and !v.empty?
619 next unless v and !v.empty?
615 operator = operator_for(field)
620 operator = operator_for(field)
616
621
617 # "me" value subsitution
622 # "me" value subsitution
618 if %w(assigned_to_id author_id watcher_id).include?(field)
623 if %w(assigned_to_id author_id watcher_id).include?(field)
619 if v.delete("me")
624 if v.delete("me")
620 if User.current.logged?
625 if User.current.logged?
621 v.push(User.current.id.to_s)
626 v.push(User.current.id.to_s)
622 v += User.current.group_ids.map(&:to_s) if field == 'assigned_to_id'
627 v += User.current.group_ids.map(&:to_s) if field == 'assigned_to_id'
623 else
628 else
624 v.push("0")
629 v.push("0")
625 end
630 end
626 end
631 end
627 end
632 end
628
633
629 if field == 'project_id'
634 if field == 'project_id'
630 if v.delete('mine')
635 if v.delete('mine')
631 v += User.current.memberships.map(&:project_id).map(&:to_s)
636 v += User.current.memberships.map(&:project_id).map(&:to_s)
632 end
637 end
633 end
638 end
634
639
635 if field =~ /cf_(\d+)$/
640 if field =~ /cf_(\d+)$/
636 # custom field
641 # custom field
637 filters_clauses << sql_for_custom_field(field, operator, v, $1)
642 filters_clauses << sql_for_custom_field(field, operator, v, $1)
638 elsif respond_to?("sql_for_#{field}_field")
643 elsif respond_to?("sql_for_#{field}_field")
639 # specific statement
644 # specific statement
640 filters_clauses << send("sql_for_#{field}_field", field, operator, v)
645 filters_clauses << send("sql_for_#{field}_field", field, operator, v)
641 else
646 else
642 # regular field
647 # regular field
643 filters_clauses << '(' + sql_for_field(field, operator, v, Issue.table_name, field) + ')'
648 filters_clauses << '(' + sql_for_field(field, operator, v, Issue.table_name, field) + ')'
644 end
649 end
645 end if filters and valid?
650 end if filters and valid?
646
651
647 filters_clauses << project_statement
652 filters_clauses << project_statement
648 filters_clauses.reject!(&:blank?)
653 filters_clauses.reject!(&:blank?)
649
654
650 filters_clauses.any? ? filters_clauses.join(' AND ') : nil
655 filters_clauses.any? ? filters_clauses.join(' AND ') : nil
651 end
656 end
652
657
653 # Returns the issue count
658 # Returns the issue count
654 def issue_count
659 def issue_count
655 Issue.visible.count(:include => [:status, :project], :conditions => statement)
660 Issue.visible.count(:include => [:status, :project], :conditions => statement)
656 rescue ::ActiveRecord::StatementInvalid => e
661 rescue ::ActiveRecord::StatementInvalid => e
657 raise StatementInvalid.new(e.message)
662 raise StatementInvalid.new(e.message)
658 end
663 end
659
664
660 # Returns the issue count by group or nil if query is not grouped
665 # Returns the issue count by group or nil if query is not grouped
661 def issue_count_by_group
666 def issue_count_by_group
662 r = nil
667 r = nil
663 if grouped?
668 if grouped?
664 begin
669 begin
665 # Rails3 will raise an (unexpected) RecordNotFound if there's only a nil group value
670 # Rails3 will raise an (unexpected) RecordNotFound if there's only a nil group value
666 r = Issue.visible.count(:group => group_by_statement, :include => [:status, :project], :conditions => statement)
671 r = Issue.visible.count(:group => group_by_statement, :include => [:status, :project], :conditions => statement)
667 rescue ActiveRecord::RecordNotFound
672 rescue ActiveRecord::RecordNotFound
668 r = {nil => issue_count}
673 r = {nil => issue_count}
669 end
674 end
670 c = group_by_column
675 c = group_by_column
671 if c.is_a?(QueryCustomFieldColumn)
676 if c.is_a?(QueryCustomFieldColumn)
672 r = r.keys.inject({}) {|h, k| h[c.custom_field.cast_value(k)] = r[k]; h}
677 r = r.keys.inject({}) {|h, k| h[c.custom_field.cast_value(k)] = r[k]; h}
673 end
678 end
674 end
679 end
675 r
680 r
676 rescue ::ActiveRecord::StatementInvalid => e
681 rescue ::ActiveRecord::StatementInvalid => e
677 raise StatementInvalid.new(e.message)
682 raise StatementInvalid.new(e.message)
678 end
683 end
679
684
680 # Returns the issues
685 # Returns the issues
681 # Valid options are :order, :offset, :limit, :include, :conditions
686 # Valid options are :order, :offset, :limit, :include, :conditions
682 def issues(options={})
687 def issues(options={})
683 order_option = [group_by_sort_order, options[:order]].reject {|s| s.blank?}.join(',')
688 order_option = [group_by_sort_order, options[:order]].reject {|s| s.blank?}.join(',')
684 order_option = nil if order_option.blank?
689 order_option = nil if order_option.blank?
685
690
686 issues = Issue.visible.scoped(:conditions => options[:conditions]).find :all, :include => ([:status, :project] + (options[:include] || [])).uniq,
691 issues = Issue.visible.scoped(:conditions => options[:conditions]).find :all, :include => ([:status, :project] + (options[:include] || [])).uniq,
687 :conditions => statement,
692 :conditions => statement,
688 :order => order_option,
693 :order => order_option,
689 :joins => joins_for_order_statement(order_option),
694 :joins => joins_for_order_statement(order_option),
690 :limit => options[:limit],
695 :limit => options[:limit],
691 :offset => options[:offset]
696 :offset => options[:offset]
692
697
693 if has_column?(:spent_hours)
698 if has_column?(:spent_hours)
694 Issue.load_visible_spent_hours(issues)
699 Issue.load_visible_spent_hours(issues)
695 end
700 end
696 if has_column?(:relations)
701 if has_column?(:relations)
697 Issue.load_visible_relations(issues)
702 Issue.load_visible_relations(issues)
698 end
703 end
699 issues
704 issues
700 rescue ::ActiveRecord::StatementInvalid => e
705 rescue ::ActiveRecord::StatementInvalid => e
701 raise StatementInvalid.new(e.message)
706 raise StatementInvalid.new(e.message)
702 end
707 end
703
708
704 # Returns the issues ids
709 # Returns the issues ids
705 def issue_ids(options={})
710 def issue_ids(options={})
706 order_option = [group_by_sort_order, options[:order]].reject {|s| s.blank?}.join(',')
711 order_option = [group_by_sort_order, options[:order]].reject {|s| s.blank?}.join(',')
707 order_option = nil if order_option.blank?
712 order_option = nil if order_option.blank?
708
713
709 Issue.visible.scoped(:conditions => options[:conditions]).scoped(:include => ([:status, :project] + (options[:include] || [])).uniq,
714 Issue.visible.scoped(:conditions => options[:conditions]).scoped(:include => ([:status, :project] + (options[:include] || [])).uniq,
710 :conditions => statement,
715 :conditions => statement,
711 :order => order_option,
716 :order => order_option,
712 :joins => joins_for_order_statement(order_option),
717 :joins => joins_for_order_statement(order_option),
713 :limit => options[:limit],
718 :limit => options[:limit],
714 :offset => options[:offset]).find_ids
719 :offset => options[:offset]).find_ids
715 rescue ::ActiveRecord::StatementInvalid => e
720 rescue ::ActiveRecord::StatementInvalid => e
716 raise StatementInvalid.new(e.message)
721 raise StatementInvalid.new(e.message)
717 end
722 end
718
723
719 # Returns the journals
724 # Returns the journals
720 # Valid options are :order, :offset, :limit
725 # Valid options are :order, :offset, :limit
721 def journals(options={})
726 def journals(options={})
722 Journal.visible.find :all, :include => [:details, :user, {:issue => [:project, :author, :tracker, :status]}],
727 Journal.visible.find :all, :include => [:details, :user, {:issue => [:project, :author, :tracker, :status]}],
723 :conditions => statement,
728 :conditions => statement,
724 :order => options[:order],
729 :order => options[:order],
725 :limit => options[:limit],
730 :limit => options[:limit],
726 :offset => options[:offset]
731 :offset => options[:offset]
727 rescue ::ActiveRecord::StatementInvalid => e
732 rescue ::ActiveRecord::StatementInvalid => e
728 raise StatementInvalid.new(e.message)
733 raise StatementInvalid.new(e.message)
729 end
734 end
730
735
731 # Returns the versions
736 # Returns the versions
732 # Valid options are :conditions
737 # Valid options are :conditions
733 def versions(options={})
738 def versions(options={})
734 Version.visible.scoped(:conditions => options[:conditions]).find :all, :include => :project, :conditions => project_statement
739 Version.visible.scoped(:conditions => options[:conditions]).find :all, :include => :project, :conditions => project_statement
735 rescue ::ActiveRecord::StatementInvalid => e
740 rescue ::ActiveRecord::StatementInvalid => e
736 raise StatementInvalid.new(e.message)
741 raise StatementInvalid.new(e.message)
737 end
742 end
738
743
739 def sql_for_watcher_id_field(field, operator, value)
744 def sql_for_watcher_id_field(field, operator, value)
740 db_table = Watcher.table_name
745 db_table = Watcher.table_name
741 "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND " +
746 "#{Issue.table_name}.id #{ operator == '=' ? 'IN' : 'NOT IN' } (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='Issue' AND " +
742 sql_for_field(field, '=', value, db_table, 'user_id') + ')'
747 sql_for_field(field, '=', value, db_table, 'user_id') + ')'
743 end
748 end
744
749
745 def sql_for_member_of_group_field(field, operator, value)
750 def sql_for_member_of_group_field(field, operator, value)
746 if operator == '*' # Any group
751 if operator == '*' # Any group
747 groups = Group.all
752 groups = Group.all
748 operator = '=' # Override the operator since we want to find by assigned_to
753 operator = '=' # Override the operator since we want to find by assigned_to
749 elsif operator == "!*"
754 elsif operator == "!*"
750 groups = Group.all
755 groups = Group.all
751 operator = '!' # Override the operator since we want to find by assigned_to
756 operator = '!' # Override the operator since we want to find by assigned_to
752 else
757 else
753 groups = Group.find_all_by_id(value)
758 groups = Group.find_all_by_id(value)
754 end
759 end
755 groups ||= []
760 groups ||= []
756
761
757 members_of_groups = groups.inject([]) {|user_ids, group|
762 members_of_groups = groups.inject([]) {|user_ids, group|
758 if group && group.user_ids.present?
763 if group && group.user_ids.present?
759 user_ids << group.user_ids
764 user_ids << group.user_ids
760 end
765 end
761 user_ids.flatten.uniq.compact
766 user_ids.flatten.uniq.compact
762 }.sort.collect(&:to_s)
767 }.sort.collect(&:to_s)
763
768
764 '(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')'
769 '(' + sql_for_field("assigned_to_id", operator, members_of_groups, Issue.table_name, "assigned_to_id", false) + ')'
765 end
770 end
766
771
767 def sql_for_assigned_to_role_field(field, operator, value)
772 def sql_for_assigned_to_role_field(field, operator, value)
768 case operator
773 case operator
769 when "*", "!*" # Member / Not member
774 when "*", "!*" # Member / Not member
770 sw = operator == "!*" ? 'NOT' : ''
775 sw = operator == "!*" ? 'NOT' : ''
771 nl = operator == "!*" ? "#{Issue.table_name}.assigned_to_id IS NULL OR" : ''
776 nl = operator == "!*" ? "#{Issue.table_name}.assigned_to_id IS NULL OR" : ''
772 "(#{nl} #{Issue.table_name}.assigned_to_id #{sw} IN (SELECT DISTINCT #{Member.table_name}.user_id FROM #{Member.table_name}" +
777 "(#{nl} #{Issue.table_name}.assigned_to_id #{sw} IN (SELECT DISTINCT #{Member.table_name}.user_id FROM #{Member.table_name}" +
773 " WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id))"
778 " WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id))"
774 when "=", "!"
779 when "=", "!"
775 role_cond = value.any? ?
780 role_cond = value.any? ?
776 "#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")" :
781 "#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")" :
777 "1=0"
782 "1=0"
778
783
779 sw = operator == "!" ? 'NOT' : ''
784 sw = operator == "!" ? 'NOT' : ''
780 nl = operator == "!" ? "#{Issue.table_name}.assigned_to_id IS NULL OR" : ''
785 nl = operator == "!" ? "#{Issue.table_name}.assigned_to_id IS NULL OR" : ''
781 "(#{nl} #{Issue.table_name}.assigned_to_id #{sw} IN (SELECT DISTINCT #{Member.table_name}.user_id FROM #{Member.table_name}, #{MemberRole.table_name}" +
786 "(#{nl} #{Issue.table_name}.assigned_to_id #{sw} IN (SELECT DISTINCT #{Member.table_name}.user_id FROM #{Member.table_name}, #{MemberRole.table_name}" +
782 " WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id AND #{Member.table_name}.id = #{MemberRole.table_name}.member_id AND #{role_cond}))"
787 " WHERE #{Member.table_name}.project_id = #{Issue.table_name}.project_id AND #{Member.table_name}.id = #{MemberRole.table_name}.member_id AND #{role_cond}))"
783 end
788 end
784 end
789 end
785
790
786 def sql_for_is_private_field(field, operator, value)
791 def sql_for_is_private_field(field, operator, value)
787 op = (operator == "=" ? 'IN' : 'NOT IN')
792 op = (operator == "=" ? 'IN' : 'NOT IN')
788 va = value.map {|v| v == '0' ? connection.quoted_false : connection.quoted_true}.uniq.join(',')
793 va = value.map {|v| v == '0' ? connection.quoted_false : connection.quoted_true}.uniq.join(',')
789
794
790 "#{Issue.table_name}.is_private #{op} (#{va})"
795 "#{Issue.table_name}.is_private #{op} (#{va})"
791 end
796 end
792
797
793 def sql_for_relations(field, operator, value, options={})
798 def sql_for_relations(field, operator, value, options={})
794 relation_options = IssueRelation::TYPES[field]
799 relation_options = IssueRelation::TYPES[field]
795 return relation_options unless relation_options
800 return relation_options unless relation_options
796
801
797 relation_type = field
802 relation_type = field
798 join_column, target_join_column = "issue_from_id", "issue_to_id"
803 join_column, target_join_column = "issue_from_id", "issue_to_id"
799 if relation_options[:reverse] || options[:reverse]
804 if relation_options[:reverse] || options[:reverse]
800 relation_type = relation_options[:reverse] || relation_type
805 relation_type = relation_options[:reverse] || relation_type
801 join_column, target_join_column = target_join_column, join_column
806 join_column, target_join_column = target_join_column, join_column
802 end
807 end
803
808
804 sql = case operator
809 sql = case operator
805 when "*", "!*"
810 when "*", "!*"
806 op = (operator == "*" ? 'IN' : 'NOT IN')
811 op = (operator == "*" ? 'IN' : 'NOT IN')
807 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}')"
812 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}')"
808 when "=", "!"
813 when "=", "!"
809 op = (operator == "=" ? 'IN' : 'NOT IN')
814 op = (operator == "=" ? 'IN' : 'NOT IN')
810 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
815 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name} WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = #{value.first.to_i})"
811 when "=p", "=!p", "!p"
816 when "=p", "=!p", "!p"
812 op = (operator == "!p" ? 'NOT IN' : 'IN')
817 op = (operator == "!p" ? 'NOT IN' : 'IN')
813 comp = (operator == "=!p" ? '<>' : '=')
818 comp = (operator == "=!p" ? '<>' : '=')
814 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
819 "#{Issue.table_name}.id #{op} (SELECT DISTINCT #{IssueRelation.table_name}.#{join_column} FROM #{IssueRelation.table_name}, #{Issue.table_name} relissues WHERE #{IssueRelation.table_name}.relation_type = '#{connection.quote_string(relation_type)}' AND #{IssueRelation.table_name}.#{target_join_column} = relissues.id AND relissues.project_id #{comp} #{value.first.to_i})"
815 end
820 end
816
821
817 if relation_options[:sym] == field && !options[:reverse]
822 if relation_options[:sym] == field && !options[:reverse]
818 sqls = [sql, sql_for_relations(field, operator, value, :reverse => true)]
823 sqls = [sql, sql_for_relations(field, operator, value, :reverse => true)]
819 sqls.join(["!", "!*", "!p"].include?(operator) ? " AND " : " OR ")
824 sqls.join(["!", "!*", "!p"].include?(operator) ? " AND " : " OR ")
820 else
825 else
821 sql
826 sql
822 end
827 end
823 end
828 end
824
829
825 IssueRelation::TYPES.keys.each do |relation_type|
830 IssueRelation::TYPES.keys.each do |relation_type|
826 alias_method "sql_for_#{relation_type}_field".to_sym, :sql_for_relations
831 alias_method "sql_for_#{relation_type}_field".to_sym, :sql_for_relations
827 end
832 end
828
833
829 private
834 private
830
835
831 def sql_for_custom_field(field, operator, value, custom_field_id)
836 def sql_for_custom_field(field, operator, value, custom_field_id)
832 db_table = CustomValue.table_name
837 db_table = CustomValue.table_name
833 db_field = 'value'
838 db_field = 'value'
834 filter = @available_filters[field]
839 filter = @available_filters[field]
835 return nil unless filter
840 return nil unless filter
836 if filter[:format] == 'user'
841 if filter[:format] == 'user'
837 if value.delete('me')
842 if value.delete('me')
838 value.push User.current.id.to_s
843 value.push User.current.id.to_s
839 end
844 end
840 end
845 end
841 not_in = nil
846 not_in = nil
842 if operator == '!'
847 if operator == '!'
843 # Makes ! operator work for custom fields with multiple values
848 # Makes ! operator work for custom fields with multiple values
844 operator = '='
849 operator = '='
845 not_in = 'NOT'
850 not_in = 'NOT'
846 end
851 end
847 customized_key = "id"
852 customized_key = "id"
848 customized_class = Issue
853 customized_class = Issue
849 if field =~ /^(.+)\.cf_/
854 if field =~ /^(.+)\.cf_/
850 assoc = $1
855 assoc = $1
851 customized_key = "#{assoc}_id"
856 customized_key = "#{assoc}_id"
852 customized_class = Issue.reflect_on_association(assoc.to_sym).klass.base_class rescue nil
857 customized_class = Issue.reflect_on_association(assoc.to_sym).klass.base_class rescue nil
853 raise "Unknown Issue association #{assoc}" unless customized_class
858 raise "Unknown Issue association #{assoc}" unless customized_class
854 end
859 end
855 "#{Issue.table_name}.#{customized_key} #{not_in} IN (SELECT #{customized_class.table_name}.id FROM #{customized_class.table_name} LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='#{customized_class}' AND #{db_table}.customized_id=#{customized_class.table_name}.id AND #{db_table}.custom_field_id=#{custom_field_id} WHERE " +
860 "#{Issue.table_name}.#{customized_key} #{not_in} IN (SELECT #{customized_class.table_name}.id FROM #{customized_class.table_name} LEFT OUTER JOIN #{db_table} ON #{db_table}.customized_type='#{customized_class}' AND #{db_table}.customized_id=#{customized_class.table_name}.id AND #{db_table}.custom_field_id=#{custom_field_id} WHERE " +
856 sql_for_field(field, operator, value, db_table, db_field, true) + ')'
861 sql_for_field(field, operator, value, db_table, db_field, true) + ')'
857 end
862 end
858
863
859 # Helper method to generate the WHERE sql for a +field+, +operator+ and a +value+
864 # Helper method to generate the WHERE sql for a +field+, +operator+ and a +value+
860 def sql_for_field(field, operator, value, db_table, db_field, is_custom_filter=false)
865 def sql_for_field(field, operator, value, db_table, db_field, is_custom_filter=false)
861 sql = ''
866 sql = ''
862 case operator
867 case operator
863 when "="
868 when "="
864 if value.any?
869 if value.any?
865 case type_for(field)
870 case type_for(field)
866 when :date, :date_past
871 when :date, :date_past
867 sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), (Date.parse(value.first) rescue nil))
872 sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), (Date.parse(value.first) rescue nil))
868 when :integer
873 when :integer
869 if is_custom_filter
874 if is_custom_filter
870 sql = "(#{db_table}.#{db_field} <> '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) = #{value.first.to_i})"
875 sql = "(#{db_table}.#{db_field} <> '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) = #{value.first.to_i})"
871 else
876 else
872 sql = "#{db_table}.#{db_field} = #{value.first.to_i}"
877 sql = "#{db_table}.#{db_field} = #{value.first.to_i}"
873 end
878 end
874 when :float
879 when :float
875 if is_custom_filter
880 if is_custom_filter
876 sql = "(#{db_table}.#{db_field} <> '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5})"
881 sql = "(#{db_table}.#{db_field} <> '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5})"
877 else
882 else
878 sql = "#{db_table}.#{db_field} BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5}"
883 sql = "#{db_table}.#{db_field} BETWEEN #{value.first.to_f - 1e-5} AND #{value.first.to_f + 1e-5}"
879 end
884 end
880 else
885 else
881 sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
886 sql = "#{db_table}.#{db_field} IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + ")"
882 end
887 end
883 else
888 else
884 # IN an empty set
889 # IN an empty set
885 sql = "1=0"
890 sql = "1=0"
886 end
891 end
887 when "!"
892 when "!"
888 if value.any?
893 if value.any?
889 sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
894 sql = "(#{db_table}.#{db_field} IS NULL OR #{db_table}.#{db_field} NOT IN (" + value.collect{|val| "'#{connection.quote_string(val)}'"}.join(",") + "))"
890 else
895 else
891 # NOT IN an empty set
896 # NOT IN an empty set
892 sql = "1=1"
897 sql = "1=1"
893 end
898 end
894 when "!*"
899 when "!*"
895 sql = "#{db_table}.#{db_field} IS NULL"
900 sql = "#{db_table}.#{db_field} IS NULL"
896 sql << " OR #{db_table}.#{db_field} = ''" if is_custom_filter
901 sql << " OR #{db_table}.#{db_field} = ''" if is_custom_filter
897 when "*"
902 when "*"
898 sql = "#{db_table}.#{db_field} IS NOT NULL"
903 sql = "#{db_table}.#{db_field} IS NOT NULL"
899 sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
904 sql << " AND #{db_table}.#{db_field} <> ''" if is_custom_filter
900 when ">="
905 when ">="
901 if [:date, :date_past].include?(type_for(field))
906 if [:date, :date_past].include?(type_for(field))
902 sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), nil)
907 sql = date_clause(db_table, db_field, (Date.parse(value.first) rescue nil), nil)
903 else
908 else
904 if is_custom_filter
909 if is_custom_filter
905 sql = "(#{db_table}.#{db_field} <> '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) >= #{value.first.to_f})"
910 sql = "(#{db_table}.#{db_field} <> '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) >= #{value.first.to_f})"
906 else
911 else
907 sql = "#{db_table}.#{db_field} >= #{value.first.to_f}"
912 sql = "#{db_table}.#{db_field} >= #{value.first.to_f}"
908 end
913 end
909 end
914 end
910 when "<="
915 when "<="
911 if [:date, :date_past].include?(type_for(field))
916 if [:date, :date_past].include?(type_for(field))
912 sql = date_clause(db_table, db_field, nil, (Date.parse(value.first) rescue nil))
917 sql = date_clause(db_table, db_field, nil, (Date.parse(value.first) rescue nil))
913 else
918 else
914 if is_custom_filter
919 if is_custom_filter
915 sql = "(#{db_table}.#{db_field} <> '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) <= #{value.first.to_f})"
920 sql = "(#{db_table}.#{db_field} <> '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) <= #{value.first.to_f})"
916 else
921 else
917 sql = "#{db_table}.#{db_field} <= #{value.first.to_f}"
922 sql = "#{db_table}.#{db_field} <= #{value.first.to_f}"
918 end
923 end
919 end
924 end
920 when "><"
925 when "><"
921 if [:date, :date_past].include?(type_for(field))
926 if [:date, :date_past].include?(type_for(field))
922 sql = date_clause(db_table, db_field, (Date.parse(value[0]) rescue nil), (Date.parse(value[1]) rescue nil))
927 sql = date_clause(db_table, db_field, (Date.parse(value[0]) rescue nil), (Date.parse(value[1]) rescue nil))
923 else
928 else
924 if is_custom_filter
929 if is_custom_filter
925 sql = "(#{db_table}.#{db_field} <> '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) BETWEEN #{value[0].to_f} AND #{value[1].to_f})"
930 sql = "(#{db_table}.#{db_field} <> '' AND CAST(#{db_table}.#{db_field} AS decimal(60,3)) BETWEEN #{value[0].to_f} AND #{value[1].to_f})"
926 else
931 else
927 sql = "#{db_table}.#{db_field} BETWEEN #{value[0].to_f} AND #{value[1].to_f}"
932 sql = "#{db_table}.#{db_field} BETWEEN #{value[0].to_f} AND #{value[1].to_f}"
928 end
933 end
929 end
934 end
930 when "o"
935 when "o"
931 sql = "#{Issue.table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_false})" if field == "status_id"
936 sql = "#{Issue.table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_false})" if field == "status_id"
932 when "c"
937 when "c"
933 sql = "#{Issue.table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_true})" if field == "status_id"
938 sql = "#{Issue.table_name}.status_id IN (SELECT id FROM #{IssueStatus.table_name} WHERE is_closed=#{connection.quoted_true})" if field == "status_id"
934 when ">t-"
939 when ">t-"
935 sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0)
940 sql = relative_date_clause(db_table, db_field, - value.first.to_i, 0)
936 when "<t-"
941 when "<t-"
937 sql = relative_date_clause(db_table, db_field, nil, - value.first.to_i)
942 sql = relative_date_clause(db_table, db_field, nil, - value.first.to_i)
938 when "t-"
943 when "t-"
939 sql = relative_date_clause(db_table, db_field, - value.first.to_i, - value.first.to_i)
944 sql = relative_date_clause(db_table, db_field, - value.first.to_i, - value.first.to_i)
940 when ">t+"
945 when ">t+"
941 sql = relative_date_clause(db_table, db_field, value.first.to_i, nil)
946 sql = relative_date_clause(db_table, db_field, value.first.to_i, nil)
942 when "<t+"
947 when "<t+"
943 sql = relative_date_clause(db_table, db_field, 0, value.first.to_i)
948 sql = relative_date_clause(db_table, db_field, 0, value.first.to_i)
944 when "t+"
949 when "t+"
945 sql = relative_date_clause(db_table, db_field, value.first.to_i, value.first.to_i)
950 sql = relative_date_clause(db_table, db_field, value.first.to_i, value.first.to_i)
946 when "t"
951 when "t"
947 sql = relative_date_clause(db_table, db_field, 0, 0)
952 sql = relative_date_clause(db_table, db_field, 0, 0)
948 when "w"
953 when "w"
949 first_day_of_week = l(:general_first_day_of_week).to_i
954 first_day_of_week = l(:general_first_day_of_week).to_i
950 day_of_week = Date.today.cwday
955 day_of_week = Date.today.cwday
951 days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
956 days_ago = (day_of_week >= first_day_of_week ? day_of_week - first_day_of_week : day_of_week + 7 - first_day_of_week)
952 sql = relative_date_clause(db_table, db_field, - days_ago, - days_ago + 6)
957 sql = relative_date_clause(db_table, db_field, - days_ago, - days_ago + 6)
953 when "~"
958 when "~"
954 sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
959 sql = "LOWER(#{db_table}.#{db_field}) LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
955 when "!~"
960 when "!~"
956 sql = "LOWER(#{db_table}.#{db_field}) NOT LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
961 sql = "LOWER(#{db_table}.#{db_field}) NOT LIKE '%#{connection.quote_string(value.first.to_s.downcase)}%'"
957 else
962 else
958 raise "Unknown query operator #{operator}"
963 raise "Unknown query operator #{operator}"
959 end
964 end
960
965
961 return sql
966 return sql
962 end
967 end
963
968
964 def add_custom_fields_filters(custom_fields, assoc=nil)
969 def add_custom_fields_filters(custom_fields, assoc=nil)
965 return unless custom_fields.present?
970 return unless custom_fields.present?
966 @available_filters ||= {}
971 @available_filters ||= {}
967
972
968 custom_fields.select(&:is_filter?).each do |field|
973 custom_fields.select(&:is_filter?).each do |field|
969 case field.field_format
974 case field.field_format
970 when "text"
975 when "text"
971 options = { :type => :text, :order => 20 }
976 options = { :type => :text, :order => 20 }
972 when "list"
977 when "list"
973 options = { :type => :list_optional, :values => field.possible_values, :order => 20}
978 options = { :type => :list_optional, :values => field.possible_values, :order => 20}
974 when "date"
979 when "date"
975 options = { :type => :date, :order => 20 }
980 options = { :type => :date, :order => 20 }
976 when "bool"
981 when "bool"
977 options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
982 options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 }
978 when "int"
983 when "int"
979 options = { :type => :integer, :order => 20 }
984 options = { :type => :integer, :order => 20 }
980 when "float"
985 when "float"
981 options = { :type => :float, :order => 20 }
986 options = { :type => :float, :order => 20 }
982 when "user", "version"
987 when "user", "version"
983 next unless project
988 next unless project
984 values = field.possible_values_options(project)
989 values = field.possible_values_options(project)
985 if User.current.logged? && field.field_format == 'user'
990 if User.current.logged? && field.field_format == 'user'
986 values.unshift ["<< #{l(:label_me)} >>", "me"]
991 values.unshift ["<< #{l(:label_me)} >>", "me"]
987 end
992 end
988 options = { :type => :list_optional, :values => values, :order => 20}
993 options = { :type => :list_optional, :values => values, :order => 20}
989 else
994 else
990 options = { :type => :string, :order => 20 }
995 options = { :type => :string, :order => 20 }
991 end
996 end
992 filter_id = "cf_#{field.id}"
997 filter_id = "cf_#{field.id}"
993 filter_name = field.name
998 filter_name = field.name
994 if assoc.present?
999 if assoc.present?
995 filter_id = "#{assoc}.#{filter_id}"
1000 filter_id = "#{assoc}.#{filter_id}"
996 filter_name = l("label_attribute_of_#{assoc}", :name => filter_name)
1001 filter_name = l("label_attribute_of_#{assoc}", :name => filter_name)
997 end
1002 end
998 @available_filters[filter_id] = options.merge({
1003 @available_filters[filter_id] = options.merge({
999 :name => filter_name,
1004 :name => filter_name,
1000 :format => field.field_format,
1005 :format => field.field_format,
1001 :field => field
1006 :field => field
1002 })
1007 })
1003 end
1008 end
1004 end
1009 end
1005
1010
1006 def add_associations_custom_fields_filters(*associations)
1011 def add_associations_custom_fields_filters(*associations)
1007 fields_by_class = CustomField.where(:is_filter => true).group_by(&:class)
1012 fields_by_class = CustomField.where(:is_filter => true).group_by(&:class)
1008 associations.each do |assoc|
1013 associations.each do |assoc|
1009 association_klass = Issue.reflect_on_association(assoc).klass
1014 association_klass = Issue.reflect_on_association(assoc).klass
1010 fields_by_class.each do |field_class, fields|
1015 fields_by_class.each do |field_class, fields|
1011 if field_class.customized_class <= association_klass
1016 if field_class.customized_class <= association_klass
1012 add_custom_fields_filters(fields, assoc)
1017 add_custom_fields_filters(fields, assoc)
1013 end
1018 end
1014 end
1019 end
1015 end
1020 end
1016 end
1021 end
1017
1022
1018 # Returns a SQL clause for a date or datetime field.
1023 # Returns a SQL clause for a date or datetime field.
1019 def date_clause(table, field, from, to)
1024 def date_clause(table, field, from, to)
1020 s = []
1025 s = []
1021 if from
1026 if from
1022 from_yesterday = from - 1
1027 from_yesterday = from - 1
1023 from_yesterday_time = Time.local(from_yesterday.year, from_yesterday.month, from_yesterday.day)
1028 from_yesterday_time = Time.local(from_yesterday.year, from_yesterday.month, from_yesterday.day)
1024 if self.class.default_timezone == :utc
1029 if self.class.default_timezone == :utc
1025 from_yesterday_time = from_yesterday_time.utc
1030 from_yesterday_time = from_yesterday_time.utc
1026 end
1031 end
1027 s << ("#{table}.#{field} > '%s'" % [connection.quoted_date(from_yesterday_time.end_of_day)])
1032 s << ("#{table}.#{field} > '%s'" % [connection.quoted_date(from_yesterday_time.end_of_day)])
1028 end
1033 end
1029 if to
1034 if to
1030 to_time = Time.local(to.year, to.month, to.day)
1035 to_time = Time.local(to.year, to.month, to.day)
1031 if self.class.default_timezone == :utc
1036 if self.class.default_timezone == :utc
1032 to_time = to_time.utc
1037 to_time = to_time.utc
1033 end
1038 end
1034 s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to_time.end_of_day)])
1039 s << ("#{table}.#{field} <= '%s'" % [connection.quoted_date(to_time.end_of_day)])
1035 end
1040 end
1036 s.join(' AND ')
1041 s.join(' AND ')
1037 end
1042 end
1038
1043
1039 # Returns a SQL clause for a date or datetime field using relative dates.
1044 # Returns a SQL clause for a date or datetime field using relative dates.
1040 def relative_date_clause(table, field, days_from, days_to)
1045 def relative_date_clause(table, field, days_from, days_to)
1041 date_clause(table, field, (days_from ? Date.today + days_from : nil), (days_to ? Date.today + days_to : nil))
1046 date_clause(table, field, (days_from ? Date.today + days_from : nil), (days_to ? Date.today + days_to : nil))
1042 end
1047 end
1043
1048
1044 # Additional joins required for the given sort options
1049 # Additional joins required for the given sort options
1045 def joins_for_order_statement(order_options)
1050 def joins_for_order_statement(order_options)
1046 joins = []
1051 joins = []
1047
1052
1048 if order_options
1053 if order_options
1049 if order_options.include?('authors')
1054 if order_options.include?('authors')
1050 joins << "LEFT OUTER JOIN #{User.table_name} authors ON authors.id = #{Issue.table_name}.author_id"
1055 joins << "LEFT OUTER JOIN #{User.table_name} authors ON authors.id = #{Issue.table_name}.author_id"
1051 end
1056 end
1052 order_options.scan(/cf_\d+/).uniq.each do |name|
1057 order_options.scan(/cf_\d+/).uniq.each do |name|
1053 column = available_columns.detect {|c| c.name.to_s == name}
1058 column = available_columns.detect {|c| c.name.to_s == name}
1054 join = column && column.custom_field.join_for_order_statement
1059 join = column && column.custom_field.join_for_order_statement
1055 if join
1060 if join
1056 joins << join
1061 joins << join
1057 end
1062 end
1058 end
1063 end
1059 end
1064 end
1060
1065
1061 joins.any? ? joins.join(' ') : nil
1066 joins.any? ? joins.join(' ') : nil
1062 end
1067 end
1063 end
1068 end
@@ -1,3806 +1,3826
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 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
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19 require 'issues_controller'
19 require 'issues_controller'
20
20
21 class IssuesControllerTest < ActionController::TestCase
21 class IssuesControllerTest < ActionController::TestCase
22 fixtures :projects,
22 fixtures :projects,
23 :users,
23 :users,
24 :roles,
24 :roles,
25 :members,
25 :members,
26 :member_roles,
26 :member_roles,
27 :issues,
27 :issues,
28 :issue_statuses,
28 :issue_statuses,
29 :versions,
29 :versions,
30 :trackers,
30 :trackers,
31 :projects_trackers,
31 :projects_trackers,
32 :issue_categories,
32 :issue_categories,
33 :enabled_modules,
33 :enabled_modules,
34 :enumerations,
34 :enumerations,
35 :attachments,
35 :attachments,
36 :workflows,
36 :workflows,
37 :custom_fields,
37 :custom_fields,
38 :custom_values,
38 :custom_values,
39 :custom_fields_projects,
39 :custom_fields_projects,
40 :custom_fields_trackers,
40 :custom_fields_trackers,
41 :time_entries,
41 :time_entries,
42 :journals,
42 :journals,
43 :journal_details,
43 :journal_details,
44 :queries,
44 :queries,
45 :repositories,
45 :repositories,
46 :changesets
46 :changesets
47
47
48 include Redmine::I18n
48 include Redmine::I18n
49
49
50 def setup
50 def setup
51 @controller = IssuesController.new
51 @controller = IssuesController.new
52 @request = ActionController::TestRequest.new
52 @request = ActionController::TestRequest.new
53 @response = ActionController::TestResponse.new
53 @response = ActionController::TestResponse.new
54 User.current = nil
54 User.current = nil
55 end
55 end
56
56
57 def test_index
57 def test_index
58 with_settings :default_language => "en" do
58 with_settings :default_language => "en" do
59 get :index
59 get :index
60 assert_response :success
60 assert_response :success
61 assert_template 'index'
61 assert_template 'index'
62 assert_not_nil assigns(:issues)
62 assert_not_nil assigns(:issues)
63 assert_nil assigns(:project)
63 assert_nil assigns(:project)
64
64
65 # links to visible issues
65 # links to visible issues
66 assert_select 'a[href=/issues/1]', :text => /Can&#x27;t print recipes/
66 assert_select 'a[href=/issues/1]', :text => /Can&#x27;t print recipes/
67 assert_select 'a[href=/issues/5]', :text => /Subproject issue/
67 assert_select 'a[href=/issues/5]', :text => /Subproject issue/
68 # private projects hidden
68 # private projects hidden
69 assert_select 'a[href=/issues/6]', 0
69 assert_select 'a[href=/issues/6]', 0
70 assert_select 'a[href=/issues/4]', 0
70 assert_select 'a[href=/issues/4]', 0
71 # project column
71 # project column
72 assert_select 'th', :text => /Project/
72 assert_select 'th', :text => /Project/
73 end
73 end
74 end
74 end
75
75
76 def test_index_should_not_list_issues_when_module_disabled
76 def test_index_should_not_list_issues_when_module_disabled
77 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
77 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
78 get :index
78 get :index
79 assert_response :success
79 assert_response :success
80 assert_template 'index'
80 assert_template 'index'
81 assert_not_nil assigns(:issues)
81 assert_not_nil assigns(:issues)
82 assert_nil assigns(:project)
82 assert_nil assigns(:project)
83
83
84 assert_select 'a[href=/issues/1]', 0
84 assert_select 'a[href=/issues/1]', 0
85 assert_select 'a[href=/issues/5]', :text => /Subproject issue/
85 assert_select 'a[href=/issues/5]', :text => /Subproject issue/
86 end
86 end
87
87
88 def test_index_should_list_visible_issues_only
88 def test_index_should_list_visible_issues_only
89 get :index, :per_page => 100
89 get :index, :per_page => 100
90 assert_response :success
90 assert_response :success
91 assert_not_nil assigns(:issues)
91 assert_not_nil assigns(:issues)
92 assert_nil assigns(:issues).detect {|issue| !issue.visible?}
92 assert_nil assigns(:issues).detect {|issue| !issue.visible?}
93 end
93 end
94
94
95 def test_index_with_project
95 def test_index_with_project
96 Setting.display_subprojects_issues = 0
96 Setting.display_subprojects_issues = 0
97 get :index, :project_id => 1
97 get :index, :project_id => 1
98 assert_response :success
98 assert_response :success
99 assert_template 'index'
99 assert_template 'index'
100 assert_not_nil assigns(:issues)
100 assert_not_nil assigns(:issues)
101
101
102 assert_select 'a[href=/issues/1]', :text => /Can&#x27;t print recipes/
102 assert_select 'a[href=/issues/1]', :text => /Can&#x27;t print recipes/
103 assert_select 'a[href=/issues/5]', 0
103 assert_select 'a[href=/issues/5]', 0
104 end
104 end
105
105
106 def test_index_with_project_and_subprojects
106 def test_index_with_project_and_subprojects
107 Setting.display_subprojects_issues = 1
107 Setting.display_subprojects_issues = 1
108 get :index, :project_id => 1
108 get :index, :project_id => 1
109 assert_response :success
109 assert_response :success
110 assert_template 'index'
110 assert_template 'index'
111 assert_not_nil assigns(:issues)
111 assert_not_nil assigns(:issues)
112
112
113 assert_select 'a[href=/issues/1]', :text => /Can&#x27;t print recipes/
113 assert_select 'a[href=/issues/1]', :text => /Can&#x27;t print recipes/
114 assert_select 'a[href=/issues/5]', :text => /Subproject issue/
114 assert_select 'a[href=/issues/5]', :text => /Subproject issue/
115 assert_select 'a[href=/issues/6]', 0
115 assert_select 'a[href=/issues/6]', 0
116 end
116 end
117
117
118 def test_index_with_project_and_subprojects_should_show_private_subprojects_with_permission
118 def test_index_with_project_and_subprojects_should_show_private_subprojects_with_permission
119 @request.session[:user_id] = 2
119 @request.session[:user_id] = 2
120 Setting.display_subprojects_issues = 1
120 Setting.display_subprojects_issues = 1
121 get :index, :project_id => 1
121 get :index, :project_id => 1
122 assert_response :success
122 assert_response :success
123 assert_template 'index'
123 assert_template 'index'
124 assert_not_nil assigns(:issues)
124 assert_not_nil assigns(:issues)
125
125
126 assert_select 'a[href=/issues/1]', :text => /Can&#x27;t print recipes/
126 assert_select 'a[href=/issues/1]', :text => /Can&#x27;t print recipes/
127 assert_select 'a[href=/issues/5]', :text => /Subproject issue/
127 assert_select 'a[href=/issues/5]', :text => /Subproject issue/
128 assert_select 'a[href=/issues/6]', :text => /Issue of a private subproject/
128 assert_select 'a[href=/issues/6]', :text => /Issue of a private subproject/
129 end
129 end
130
130
131 def test_index_with_project_and_default_filter
131 def test_index_with_project_and_default_filter
132 get :index, :project_id => 1, :set_filter => 1
132 get :index, :project_id => 1, :set_filter => 1
133 assert_response :success
133 assert_response :success
134 assert_template 'index'
134 assert_template 'index'
135 assert_not_nil assigns(:issues)
135 assert_not_nil assigns(:issues)
136
136
137 query = assigns(:query)
137 query = assigns(:query)
138 assert_not_nil query
138 assert_not_nil query
139 # default filter
139 # default filter
140 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
140 assert_equal({'status_id' => {:operator => 'o', :values => ['']}}, query.filters)
141 end
141 end
142
142
143 def test_index_with_project_and_filter
143 def test_index_with_project_and_filter
144 get :index, :project_id => 1, :set_filter => 1,
144 get :index, :project_id => 1, :set_filter => 1,
145 :f => ['tracker_id'],
145 :f => ['tracker_id'],
146 :op => {'tracker_id' => '='},
146 :op => {'tracker_id' => '='},
147 :v => {'tracker_id' => ['1']}
147 :v => {'tracker_id' => ['1']}
148 assert_response :success
148 assert_response :success
149 assert_template 'index'
149 assert_template 'index'
150 assert_not_nil assigns(:issues)
150 assert_not_nil assigns(:issues)
151
151
152 query = assigns(:query)
152 query = assigns(:query)
153 assert_not_nil query
153 assert_not_nil query
154 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
154 assert_equal({'tracker_id' => {:operator => '=', :values => ['1']}}, query.filters)
155 end
155 end
156
156
157 def test_index_with_short_filters
157 def test_index_with_short_filters
158 to_test = {
158 to_test = {
159 'status_id' => {
159 'status_id' => {
160 'o' => { :op => 'o', :values => [''] },
160 'o' => { :op => 'o', :values => [''] },
161 'c' => { :op => 'c', :values => [''] },
161 'c' => { :op => 'c', :values => [''] },
162 '7' => { :op => '=', :values => ['7'] },
162 '7' => { :op => '=', :values => ['7'] },
163 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
163 '7|3|4' => { :op => '=', :values => ['7', '3', '4'] },
164 '=7' => { :op => '=', :values => ['7'] },
164 '=7' => { :op => '=', :values => ['7'] },
165 '!3' => { :op => '!', :values => ['3'] },
165 '!3' => { :op => '!', :values => ['3'] },
166 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
166 '!7|3|4' => { :op => '!', :values => ['7', '3', '4'] }},
167 'subject' => {
167 'subject' => {
168 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
168 'This is a subject' => { :op => '=', :values => ['This is a subject'] },
169 'o' => { :op => '=', :values => ['o'] },
169 'o' => { :op => '=', :values => ['o'] },
170 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
170 '~This is part of a subject' => { :op => '~', :values => ['This is part of a subject'] },
171 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
171 '!~This is part of a subject' => { :op => '!~', :values => ['This is part of a subject'] }},
172 'tracker_id' => {
172 'tracker_id' => {
173 '3' => { :op => '=', :values => ['3'] },
173 '3' => { :op => '=', :values => ['3'] },
174 '=3' => { :op => '=', :values => ['3'] }},
174 '=3' => { :op => '=', :values => ['3'] }},
175 'start_date' => {
175 'start_date' => {
176 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
176 '2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
177 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
177 '=2011-10-12' => { :op => '=', :values => ['2011-10-12'] },
178 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
178 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
179 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
179 '<=2011-10-12' => { :op => '<=', :values => ['2011-10-12'] },
180 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
180 '><2011-10-01|2011-10-30' => { :op => '><', :values => ['2011-10-01', '2011-10-30'] },
181 '<t+2' => { :op => '<t+', :values => ['2'] },
181 '<t+2' => { :op => '<t+', :values => ['2'] },
182 '>t+2' => { :op => '>t+', :values => ['2'] },
182 '>t+2' => { :op => '>t+', :values => ['2'] },
183 't+2' => { :op => 't+', :values => ['2'] },
183 't+2' => { :op => 't+', :values => ['2'] },
184 't' => { :op => 't', :values => [''] },
184 't' => { :op => 't', :values => [''] },
185 'w' => { :op => 'w', :values => [''] },
185 'w' => { :op => 'w', :values => [''] },
186 '>t-2' => { :op => '>t-', :values => ['2'] },
186 '>t-2' => { :op => '>t-', :values => ['2'] },
187 '<t-2' => { :op => '<t-', :values => ['2'] },
187 '<t-2' => { :op => '<t-', :values => ['2'] },
188 't-2' => { :op => 't-', :values => ['2'] }},
188 't-2' => { :op => 't-', :values => ['2'] }},
189 'created_on' => {
189 'created_on' => {
190 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
190 '>=2011-10-12' => { :op => '>=', :values => ['2011-10-12'] },
191 '<t-2' => { :op => '<t-', :values => ['2'] },
191 '<t-2' => { :op => '<t-', :values => ['2'] },
192 '>t-2' => { :op => '>t-', :values => ['2'] },
192 '>t-2' => { :op => '>t-', :values => ['2'] },
193 't-2' => { :op => 't-', :values => ['2'] }},
193 't-2' => { :op => 't-', :values => ['2'] }},
194 'cf_1' => {
194 'cf_1' => {
195 'c' => { :op => '=', :values => ['c'] },
195 'c' => { :op => '=', :values => ['c'] },
196 '!c' => { :op => '!', :values => ['c'] },
196 '!c' => { :op => '!', :values => ['c'] },
197 '!*' => { :op => '!*', :values => [''] },
197 '!*' => { :op => '!*', :values => [''] },
198 '*' => { :op => '*', :values => [''] }},
198 '*' => { :op => '*', :values => [''] }},
199 'estimated_hours' => {
199 'estimated_hours' => {
200 '=13.4' => { :op => '=', :values => ['13.4'] },
200 '=13.4' => { :op => '=', :values => ['13.4'] },
201 '>=45' => { :op => '>=', :values => ['45'] },
201 '>=45' => { :op => '>=', :values => ['45'] },
202 '<=125' => { :op => '<=', :values => ['125'] },
202 '<=125' => { :op => '<=', :values => ['125'] },
203 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
203 '><10.5|20.5' => { :op => '><', :values => ['10.5', '20.5'] },
204 '!*' => { :op => '!*', :values => [''] },
204 '!*' => { :op => '!*', :values => [''] },
205 '*' => { :op => '*', :values => [''] }}
205 '*' => { :op => '*', :values => [''] }}
206 }
206 }
207
207
208 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
208 default_filter = { 'status_id' => {:operator => 'o', :values => [''] }}
209
209
210 to_test.each do |field, expression_and_expected|
210 to_test.each do |field, expression_and_expected|
211 expression_and_expected.each do |filter_expression, expected|
211 expression_and_expected.each do |filter_expression, expected|
212
212
213 get :index, :set_filter => 1, field => filter_expression
213 get :index, :set_filter => 1, field => filter_expression
214
214
215 assert_response :success
215 assert_response :success
216 assert_template 'index'
216 assert_template 'index'
217 assert_not_nil assigns(:issues)
217 assert_not_nil assigns(:issues)
218
218
219 query = assigns(:query)
219 query = assigns(:query)
220 assert_not_nil query
220 assert_not_nil query
221 assert query.has_filter?(field)
221 assert query.has_filter?(field)
222 assert_equal(default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}}), query.filters)
222 assert_equal(default_filter.merge({field => {:operator => expected[:op], :values => expected[:values]}}), query.filters)
223 end
223 end
224 end
224 end
225 end
225 end
226
226
227 def test_index_with_project_and_empty_filters
227 def test_index_with_project_and_empty_filters
228 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
228 get :index, :project_id => 1, :set_filter => 1, :fields => ['']
229 assert_response :success
229 assert_response :success
230 assert_template 'index'
230 assert_template 'index'
231 assert_not_nil assigns(:issues)
231 assert_not_nil assigns(:issues)
232
232
233 query = assigns(:query)
233 query = assigns(:query)
234 assert_not_nil query
234 assert_not_nil query
235 # no filter
235 # no filter
236 assert_equal({}, query.filters)
236 assert_equal({}, query.filters)
237 end
237 end
238
238
239 def test_index_with_project_custom_field_filter
239 def test_index_with_project_custom_field_filter
240 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
240 field = ProjectCustomField.create!(:name => 'Client', :is_filter => true, :field_format => 'string')
241 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
241 CustomValue.create!(:custom_field => field, :customized => Project.find(3), :value => 'Foo')
242 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
242 CustomValue.create!(:custom_field => field, :customized => Project.find(5), :value => 'Foo')
243 filter_name = "project.cf_#{field.id}"
243 filter_name = "project.cf_#{field.id}"
244 @request.session[:user_id] = 1
244 @request.session[:user_id] = 1
245
245
246 get :index, :set_filter => 1,
246 get :index, :set_filter => 1,
247 :f => [filter_name],
247 :f => [filter_name],
248 :op => {filter_name => '='},
248 :op => {filter_name => '='},
249 :v => {filter_name => ['Foo']}
249 :v => {filter_name => ['Foo']}
250 assert_response :success
250 assert_response :success
251 assert_template 'index'
251 assert_template 'index'
252 assert_equal [3, 5], assigns(:issues).map(&:project_id).uniq.sort
252 assert_equal [3, 5], assigns(:issues).map(&:project_id).uniq.sort
253 end
253 end
254
254
255 def test_index_with_query
255 def test_index_with_query
256 get :index, :project_id => 1, :query_id => 5
256 get :index, :project_id => 1, :query_id => 5
257 assert_response :success
257 assert_response :success
258 assert_template 'index'
258 assert_template 'index'
259 assert_not_nil assigns(:issues)
259 assert_not_nil assigns(:issues)
260 assert_nil assigns(:issue_count_by_group)
260 assert_nil assigns(:issue_count_by_group)
261 end
261 end
262
262
263 def test_index_with_query_grouped_by_tracker
263 def test_index_with_query_grouped_by_tracker
264 get :index, :project_id => 1, :query_id => 6
264 get :index, :project_id => 1, :query_id => 6
265 assert_response :success
265 assert_response :success
266 assert_template 'index'
266 assert_template 'index'
267 assert_not_nil assigns(:issues)
267 assert_not_nil assigns(:issues)
268 assert_not_nil assigns(:issue_count_by_group)
268 assert_not_nil assigns(:issue_count_by_group)
269 end
269 end
270
270
271 def test_index_with_query_grouped_by_list_custom_field
271 def test_index_with_query_grouped_by_list_custom_field
272 get :index, :project_id => 1, :query_id => 9
272 get :index, :project_id => 1, :query_id => 9
273 assert_response :success
273 assert_response :success
274 assert_template 'index'
274 assert_template 'index'
275 assert_not_nil assigns(:issues)
275 assert_not_nil assigns(:issues)
276 assert_not_nil assigns(:issue_count_by_group)
276 assert_not_nil assigns(:issue_count_by_group)
277 end
277 end
278
278
279 def test_index_with_query_grouped_by_user_custom_field
279 def test_index_with_query_grouped_by_user_custom_field
280 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
280 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
281 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
281 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
282 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
282 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
283 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
283 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
284 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
284 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
285
285
286 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
286 get :index, :project_id => 1, :set_filter => 1, :group_by => "cf_#{cf.id}"
287 assert_response :success
287 assert_response :success
288
288
289 assert_select 'tr.group', 3
289 assert_select 'tr.group', 3
290 assert_select 'tr.group' do
290 assert_select 'tr.group' do
291 assert_select 'a', :text => 'John Smith'
291 assert_select 'a', :text => 'John Smith'
292 assert_select 'span.count', :text => '1'
292 assert_select 'span.count', :text => '1'
293 end
293 end
294 assert_select 'tr.group' do
294 assert_select 'tr.group' do
295 assert_select 'a', :text => 'Dave Lopper'
295 assert_select 'a', :text => 'Dave Lopper'
296 assert_select 'span.count', :text => '2'
296 assert_select 'span.count', :text => '2'
297 end
297 end
298 end
298 end
299
299
300 def test_index_with_query_grouped_by_tracker
301 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
302
303 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc'
304 assert_response :success
305
306 trackers = assigns(:issues).map(&:tracker).uniq
307 assert_equal [1, 2, 3], trackers.map(&:id)
308 end
309
310 def test_index_with_query_grouped_by_tracker_in_reverse_order
311 3.times {|i| Issue.generate!(:tracker_id => (i + 1))}
312
313 get :index, :set_filter => 1, :group_by => 'tracker', :sort => 'id:desc,tracker:desc'
314 assert_response :success
315
316 trackers = assigns(:issues).map(&:tracker).uniq
317 assert_equal [3, 2, 1], trackers.map(&:id)
318 end
319
300 def test_index_with_query_id_and_project_id_should_set_session_query
320 def test_index_with_query_id_and_project_id_should_set_session_query
301 get :index, :project_id => 1, :query_id => 4
321 get :index, :project_id => 1, :query_id => 4
302 assert_response :success
322 assert_response :success
303 assert_kind_of Hash, session[:query]
323 assert_kind_of Hash, session[:query]
304 assert_equal 4, session[:query][:id]
324 assert_equal 4, session[:query][:id]
305 assert_equal 1, session[:query][:project_id]
325 assert_equal 1, session[:query][:project_id]
306 end
326 end
307
327
308 def test_index_with_invalid_query_id_should_respond_404
328 def test_index_with_invalid_query_id_should_respond_404
309 get :index, :project_id => 1, :query_id => 999
329 get :index, :project_id => 1, :query_id => 999
310 assert_response 404
330 assert_response 404
311 end
331 end
312
332
313 def test_index_with_cross_project_query_in_session_should_show_project_issues
333 def test_index_with_cross_project_query_in_session_should_show_project_issues
314 q = Query.create!(:name => "test", :user_id => 2, :is_public => false, :project => nil)
334 q = Query.create!(:name => "test", :user_id => 2, :is_public => false, :project => nil)
315 @request.session[:query] = {:id => q.id, :project_id => 1}
335 @request.session[:query] = {:id => q.id, :project_id => 1}
316
336
317 with_settings :display_subprojects_issues => '0' do
337 with_settings :display_subprojects_issues => '0' do
318 get :index, :project_id => 1
338 get :index, :project_id => 1
319 end
339 end
320 assert_response :success
340 assert_response :success
321 assert_not_nil assigns(:query)
341 assert_not_nil assigns(:query)
322 assert_equal q.id, assigns(:query).id
342 assert_equal q.id, assigns(:query).id
323 assert_equal 1, assigns(:query).project_id
343 assert_equal 1, assigns(:query).project_id
324 assert_equal [1], assigns(:issues).map(&:project_id).uniq
344 assert_equal [1], assigns(:issues).map(&:project_id).uniq
325 end
345 end
326
346
327 def test_private_query_should_not_be_available_to_other_users
347 def test_private_query_should_not_be_available_to_other_users
328 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
348 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
329 @request.session[:user_id] = 3
349 @request.session[:user_id] = 3
330
350
331 get :index, :query_id => q.id
351 get :index, :query_id => q.id
332 assert_response 403
352 assert_response 403
333 end
353 end
334
354
335 def test_private_query_should_be_available_to_its_user
355 def test_private_query_should_be_available_to_its_user
336 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
356 q = Query.create!(:name => "private", :user => User.find(2), :is_public => false, :project => nil)
337 @request.session[:user_id] = 2
357 @request.session[:user_id] = 2
338
358
339 get :index, :query_id => q.id
359 get :index, :query_id => q.id
340 assert_response :success
360 assert_response :success
341 end
361 end
342
362
343 def test_public_query_should_be_available_to_other_users
363 def test_public_query_should_be_available_to_other_users
344 q = Query.create!(:name => "private", :user => User.find(2), :is_public => true, :project => nil)
364 q = Query.create!(:name => "private", :user => User.find(2), :is_public => true, :project => nil)
345 @request.session[:user_id] = 3
365 @request.session[:user_id] = 3
346
366
347 get :index, :query_id => q.id
367 get :index, :query_id => q.id
348 assert_response :success
368 assert_response :success
349 end
369 end
350
370
351 def test_index_should_omit_page_param_in_export_links
371 def test_index_should_omit_page_param_in_export_links
352 get :index, :page => 2
372 get :index, :page => 2
353 assert_response :success
373 assert_response :success
354 assert_select 'a.atom[href=/issues.atom]'
374 assert_select 'a.atom[href=/issues.atom]'
355 assert_select 'a.csv[href=/issues.csv]'
375 assert_select 'a.csv[href=/issues.csv]'
356 assert_select 'a.pdf[href=/issues.pdf]'
376 assert_select 'a.pdf[href=/issues.pdf]'
357 assert_select 'form#csv-export-form[action=/issues.csv]'
377 assert_select 'form#csv-export-form[action=/issues.csv]'
358 end
378 end
359
379
360 def test_index_csv
380 def test_index_csv
361 get :index, :format => 'csv'
381 get :index, :format => 'csv'
362 assert_response :success
382 assert_response :success
363 assert_not_nil assigns(:issues)
383 assert_not_nil assigns(:issues)
364 assert_equal 'text/csv; header=present', @response.content_type
384 assert_equal 'text/csv; header=present', @response.content_type
365 assert @response.body.starts_with?("#,")
385 assert @response.body.starts_with?("#,")
366 lines = @response.body.chomp.split("\n")
386 lines = @response.body.chomp.split("\n")
367 assert_equal assigns(:query).columns.size + 1, lines[0].split(',').size
387 assert_equal assigns(:query).columns.size + 1, lines[0].split(',').size
368 end
388 end
369
389
370 def test_index_csv_with_project
390 def test_index_csv_with_project
371 get :index, :project_id => 1, :format => 'csv'
391 get :index, :project_id => 1, :format => 'csv'
372 assert_response :success
392 assert_response :success
373 assert_not_nil assigns(:issues)
393 assert_not_nil assigns(:issues)
374 assert_equal 'text/csv; header=present', @response.content_type
394 assert_equal 'text/csv; header=present', @response.content_type
375 end
395 end
376
396
377 def test_index_csv_with_description
397 def test_index_csv_with_description
378 get :index, :format => 'csv', :description => '1'
398 get :index, :format => 'csv', :description => '1'
379 assert_response :success
399 assert_response :success
380 assert_not_nil assigns(:issues)
400 assert_not_nil assigns(:issues)
381 assert_equal 'text/csv; header=present', @response.content_type
401 assert_equal 'text/csv; header=present', @response.content_type
382 assert @response.body.starts_with?("#,")
402 assert @response.body.starts_with?("#,")
383 lines = @response.body.chomp.split("\n")
403 lines = @response.body.chomp.split("\n")
384 assert_equal assigns(:query).columns.size + 2, lines[0].split(',').size
404 assert_equal assigns(:query).columns.size + 2, lines[0].split(',').size
385 end
405 end
386
406
387 def test_index_csv_with_spent_time_column
407 def test_index_csv_with_spent_time_column
388 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :subject => 'test_index_csv_with_spent_time_column', :author_id => 2)
408 issue = Issue.create!(:project_id => 1, :tracker_id => 1, :subject => 'test_index_csv_with_spent_time_column', :author_id => 2)
389 TimeEntry.create!(:project => issue.project, :issue => issue, :hours => 7.33, :user => User.find(2), :spent_on => Date.today)
409 TimeEntry.create!(:project => issue.project, :issue => issue, :hours => 7.33, :user => User.find(2), :spent_on => Date.today)
390
410
391 get :index, :format => 'csv', :set_filter => '1', :c => %w(subject spent_hours)
411 get :index, :format => 'csv', :set_filter => '1', :c => %w(subject spent_hours)
392 assert_response :success
412 assert_response :success
393 assert_equal 'text/csv; header=present', @response.content_type
413 assert_equal 'text/csv; header=present', @response.content_type
394 lines = @response.body.chomp.split("\n")
414 lines = @response.body.chomp.split("\n")
395 assert_include "#{issue.id},#{issue.subject},7.33", lines
415 assert_include "#{issue.id},#{issue.subject},7.33", lines
396 end
416 end
397
417
398 def test_index_csv_with_all_columns
418 def test_index_csv_with_all_columns
399 get :index, :format => 'csv', :columns => 'all'
419 get :index, :format => 'csv', :columns => 'all'
400 assert_response :success
420 assert_response :success
401 assert_not_nil assigns(:issues)
421 assert_not_nil assigns(:issues)
402 assert_equal 'text/csv; header=present', @response.content_type
422 assert_equal 'text/csv; header=present', @response.content_type
403 assert @response.body.starts_with?("#,")
423 assert @response.body.starts_with?("#,")
404 lines = @response.body.chomp.split("\n")
424 lines = @response.body.chomp.split("\n")
405 assert_equal assigns(:query).available_columns.size + 1, lines[0].split(',').size
425 assert_equal assigns(:query).available_columns.size + 1, lines[0].split(',').size
406 end
426 end
407
427
408 def test_index_csv_with_multi_column_field
428 def test_index_csv_with_multi_column_field
409 CustomField.find(1).update_attribute :multiple, true
429 CustomField.find(1).update_attribute :multiple, true
410 issue = Issue.find(1)
430 issue = Issue.find(1)
411 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
431 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
412 issue.save!
432 issue.save!
413
433
414 get :index, :format => 'csv', :columns => 'all'
434 get :index, :format => 'csv', :columns => 'all'
415 assert_response :success
435 assert_response :success
416 lines = @response.body.chomp.split("\n")
436 lines = @response.body.chomp.split("\n")
417 assert lines.detect {|line| line.include?('"MySQL, Oracle"')}
437 assert lines.detect {|line| line.include?('"MySQL, Oracle"')}
418 end
438 end
419
439
420 def test_index_csv_big_5
440 def test_index_csv_big_5
421 with_settings :default_language => "zh-TW" do
441 with_settings :default_language => "zh-TW" do
422 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
442 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
423 str_big5 = "\xa4@\xa4\xeb"
443 str_big5 = "\xa4@\xa4\xeb"
424 if str_utf8.respond_to?(:force_encoding)
444 if str_utf8.respond_to?(:force_encoding)
425 str_utf8.force_encoding('UTF-8')
445 str_utf8.force_encoding('UTF-8')
426 str_big5.force_encoding('Big5')
446 str_big5.force_encoding('Big5')
427 end
447 end
428 issue = Issue.generate!(:subject => str_utf8)
448 issue = Issue.generate!(:subject => str_utf8)
429
449
430 get :index, :project_id => 1,
450 get :index, :project_id => 1,
431 :f => ['subject'],
451 :f => ['subject'],
432 :op => '=', :values => [str_utf8],
452 :op => '=', :values => [str_utf8],
433 :format => 'csv'
453 :format => 'csv'
434 assert_equal 'text/csv; header=present', @response.content_type
454 assert_equal 'text/csv; header=present', @response.content_type
435 lines = @response.body.chomp.split("\n")
455 lines = @response.body.chomp.split("\n")
436 s1 = "\xaa\xac\xbaA"
456 s1 = "\xaa\xac\xbaA"
437 if str_utf8.respond_to?(:force_encoding)
457 if str_utf8.respond_to?(:force_encoding)
438 s1.force_encoding('Big5')
458 s1.force_encoding('Big5')
439 end
459 end
440 assert lines[0].include?(s1)
460 assert lines[0].include?(s1)
441 assert lines[1].include?(str_big5)
461 assert lines[1].include?(str_big5)
442 end
462 end
443 end
463 end
444
464
445 def test_index_csv_cannot_convert_should_be_replaced_big_5
465 def test_index_csv_cannot_convert_should_be_replaced_big_5
446 with_settings :default_language => "zh-TW" do
466 with_settings :default_language => "zh-TW" do
447 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
467 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
448 if str_utf8.respond_to?(:force_encoding)
468 if str_utf8.respond_to?(:force_encoding)
449 str_utf8.force_encoding('UTF-8')
469 str_utf8.force_encoding('UTF-8')
450 end
470 end
451 issue = Issue.generate!(:subject => str_utf8)
471 issue = Issue.generate!(:subject => str_utf8)
452
472
453 get :index, :project_id => 1,
473 get :index, :project_id => 1,
454 :f => ['subject'],
474 :f => ['subject'],
455 :op => '=', :values => [str_utf8],
475 :op => '=', :values => [str_utf8],
456 :c => ['status', 'subject'],
476 :c => ['status', 'subject'],
457 :format => 'csv',
477 :format => 'csv',
458 :set_filter => 1
478 :set_filter => 1
459 assert_equal 'text/csv; header=present', @response.content_type
479 assert_equal 'text/csv; header=present', @response.content_type
460 lines = @response.body.chomp.split("\n")
480 lines = @response.body.chomp.split("\n")
461 s1 = "\xaa\xac\xbaA" # status
481 s1 = "\xaa\xac\xbaA" # status
462 if str_utf8.respond_to?(:force_encoding)
482 if str_utf8.respond_to?(:force_encoding)
463 s1.force_encoding('Big5')
483 s1.force_encoding('Big5')
464 end
484 end
465 assert lines[0].include?(s1)
485 assert lines[0].include?(s1)
466 s2 = lines[1].split(",")[2]
486 s2 = lines[1].split(",")[2]
467 if s1.respond_to?(:force_encoding)
487 if s1.respond_to?(:force_encoding)
468 s3 = "\xa5H?" # subject
488 s3 = "\xa5H?" # subject
469 s3.force_encoding('Big5')
489 s3.force_encoding('Big5')
470 assert_equal s3, s2
490 assert_equal s3, s2
471 elsif RUBY_PLATFORM == 'java'
491 elsif RUBY_PLATFORM == 'java'
472 assert_equal "??", s2
492 assert_equal "??", s2
473 else
493 else
474 assert_equal "\xa5H???", s2
494 assert_equal "\xa5H???", s2
475 end
495 end
476 end
496 end
477 end
497 end
478
498
479 def test_index_csv_tw
499 def test_index_csv_tw
480 with_settings :default_language => "zh-TW" do
500 with_settings :default_language => "zh-TW" do
481 str1 = "test_index_csv_tw"
501 str1 = "test_index_csv_tw"
482 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
502 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
483
503
484 get :index, :project_id => 1,
504 get :index, :project_id => 1,
485 :f => ['subject'],
505 :f => ['subject'],
486 :op => '=', :values => [str1],
506 :op => '=', :values => [str1],
487 :c => ['estimated_hours', 'subject'],
507 :c => ['estimated_hours', 'subject'],
488 :format => 'csv',
508 :format => 'csv',
489 :set_filter => 1
509 :set_filter => 1
490 assert_equal 'text/csv; header=present', @response.content_type
510 assert_equal 'text/csv; header=present', @response.content_type
491 lines = @response.body.chomp.split("\n")
511 lines = @response.body.chomp.split("\n")
492 assert_equal "#{issue.id},1234.50,#{str1}", lines[1]
512 assert_equal "#{issue.id},1234.50,#{str1}", lines[1]
493 end
513 end
494 end
514 end
495
515
496 def test_index_csv_fr
516 def test_index_csv_fr
497 with_settings :default_language => "fr" do
517 with_settings :default_language => "fr" do
498 str1 = "test_index_csv_fr"
518 str1 = "test_index_csv_fr"
499 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
519 issue = Issue.generate!(:subject => str1, :estimated_hours => '1234.5')
500
520
501 get :index, :project_id => 1,
521 get :index, :project_id => 1,
502 :f => ['subject'],
522 :f => ['subject'],
503 :op => '=', :values => [str1],
523 :op => '=', :values => [str1],
504 :c => ['estimated_hours', 'subject'],
524 :c => ['estimated_hours', 'subject'],
505 :format => 'csv',
525 :format => 'csv',
506 :set_filter => 1
526 :set_filter => 1
507 assert_equal 'text/csv; header=present', @response.content_type
527 assert_equal 'text/csv; header=present', @response.content_type
508 lines = @response.body.chomp.split("\n")
528 lines = @response.body.chomp.split("\n")
509 assert_equal "#{issue.id};1234,50;#{str1}", lines[1]
529 assert_equal "#{issue.id};1234,50;#{str1}", lines[1]
510 end
530 end
511 end
531 end
512
532
513 def test_index_pdf
533 def test_index_pdf
514 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
534 ["en", "zh", "zh-TW", "ja", "ko"].each do |lang|
515 with_settings :default_language => lang do
535 with_settings :default_language => lang do
516
536
517 get :index
537 get :index
518 assert_response :success
538 assert_response :success
519 assert_template 'index'
539 assert_template 'index'
520
540
521 if lang == "ja"
541 if lang == "ja"
522 if RUBY_PLATFORM != 'java'
542 if RUBY_PLATFORM != 'java'
523 assert_equal "CP932", l(:general_pdf_encoding)
543 assert_equal "CP932", l(:general_pdf_encoding)
524 end
544 end
525 if RUBY_PLATFORM == 'java' && l(:general_pdf_encoding) == "CP932"
545 if RUBY_PLATFORM == 'java' && l(:general_pdf_encoding) == "CP932"
526 next
546 next
527 end
547 end
528 end
548 end
529
549
530 get :index, :format => 'pdf'
550 get :index, :format => 'pdf'
531 assert_response :success
551 assert_response :success
532 assert_not_nil assigns(:issues)
552 assert_not_nil assigns(:issues)
533 assert_equal 'application/pdf', @response.content_type
553 assert_equal 'application/pdf', @response.content_type
534
554
535 get :index, :project_id => 1, :format => 'pdf'
555 get :index, :project_id => 1, :format => 'pdf'
536 assert_response :success
556 assert_response :success
537 assert_not_nil assigns(:issues)
557 assert_not_nil assigns(:issues)
538 assert_equal 'application/pdf', @response.content_type
558 assert_equal 'application/pdf', @response.content_type
539
559
540 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
560 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
541 assert_response :success
561 assert_response :success
542 assert_not_nil assigns(:issues)
562 assert_not_nil assigns(:issues)
543 assert_equal 'application/pdf', @response.content_type
563 assert_equal 'application/pdf', @response.content_type
544 end
564 end
545 end
565 end
546 end
566 end
547
567
548 def test_index_pdf_with_query_grouped_by_list_custom_field
568 def test_index_pdf_with_query_grouped_by_list_custom_field
549 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
569 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
550 assert_response :success
570 assert_response :success
551 assert_not_nil assigns(:issues)
571 assert_not_nil assigns(:issues)
552 assert_not_nil assigns(:issue_count_by_group)
572 assert_not_nil assigns(:issue_count_by_group)
553 assert_equal 'application/pdf', @response.content_type
573 assert_equal 'application/pdf', @response.content_type
554 end
574 end
555
575
556 def test_index_atom
576 def test_index_atom
557 get :index, :project_id => 'ecookbook', :format => 'atom'
577 get :index, :project_id => 'ecookbook', :format => 'atom'
558 assert_response :success
578 assert_response :success
559 assert_template 'common/feed'
579 assert_template 'common/feed'
560 assert_equal 'application/atom+xml', response.content_type
580 assert_equal 'application/atom+xml', response.content_type
561
581
562 assert_select 'feed' do
582 assert_select 'feed' do
563 assert_select 'link[rel=self][href=?]', 'http://test.host/projects/ecookbook/issues.atom'
583 assert_select 'link[rel=self][href=?]', 'http://test.host/projects/ecookbook/issues.atom'
564 assert_select 'link[rel=alternate][href=?]', 'http://test.host/projects/ecookbook/issues'
584 assert_select 'link[rel=alternate][href=?]', 'http://test.host/projects/ecookbook/issues'
565 assert_select 'entry link[href=?]', 'http://test.host/issues/1'
585 assert_select 'entry link[href=?]', 'http://test.host/issues/1'
566 end
586 end
567 end
587 end
568
588
569 def test_index_sort
589 def test_index_sort
570 get :index, :sort => 'tracker,id:desc'
590 get :index, :sort => 'tracker,id:desc'
571 assert_response :success
591 assert_response :success
572
592
573 sort_params = @request.session['issues_index_sort']
593 sort_params = @request.session['issues_index_sort']
574 assert sort_params.is_a?(String)
594 assert sort_params.is_a?(String)
575 assert_equal 'tracker,id:desc', sort_params
595 assert_equal 'tracker,id:desc', sort_params
576
596
577 issues = assigns(:issues)
597 issues = assigns(:issues)
578 assert_not_nil issues
598 assert_not_nil issues
579 assert !issues.empty?
599 assert !issues.empty?
580 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
600 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
581 end
601 end
582
602
583 def test_index_sort_by_field_not_included_in_columns
603 def test_index_sort_by_field_not_included_in_columns
584 Setting.issue_list_default_columns = %w(subject author)
604 Setting.issue_list_default_columns = %w(subject author)
585 get :index, :sort => 'tracker'
605 get :index, :sort => 'tracker'
586 end
606 end
587
607
588 def test_index_sort_by_assigned_to
608 def test_index_sort_by_assigned_to
589 get :index, :sort => 'assigned_to'
609 get :index, :sort => 'assigned_to'
590 assert_response :success
610 assert_response :success
591 assignees = assigns(:issues).collect(&:assigned_to).compact
611 assignees = assigns(:issues).collect(&:assigned_to).compact
592 assert_equal assignees.sort, assignees
612 assert_equal assignees.sort, assignees
593 end
613 end
594
614
595 def test_index_sort_by_assigned_to_desc
615 def test_index_sort_by_assigned_to_desc
596 get :index, :sort => 'assigned_to:desc'
616 get :index, :sort => 'assigned_to:desc'
597 assert_response :success
617 assert_response :success
598 assignees = assigns(:issues).collect(&:assigned_to).compact
618 assignees = assigns(:issues).collect(&:assigned_to).compact
599 assert_equal assignees.sort.reverse, assignees
619 assert_equal assignees.sort.reverse, assignees
600 end
620 end
601
621
602 def test_index_group_by_assigned_to
622 def test_index_group_by_assigned_to
603 get :index, :group_by => 'assigned_to', :sort => 'priority'
623 get :index, :group_by => 'assigned_to', :sort => 'priority'
604 assert_response :success
624 assert_response :success
605 end
625 end
606
626
607 def test_index_sort_by_author
627 def test_index_sort_by_author
608 get :index, :sort => 'author'
628 get :index, :sort => 'author'
609 assert_response :success
629 assert_response :success
610 authors = assigns(:issues).collect(&:author)
630 authors = assigns(:issues).collect(&:author)
611 assert_equal authors.sort, authors
631 assert_equal authors.sort, authors
612 end
632 end
613
633
614 def test_index_sort_by_author_desc
634 def test_index_sort_by_author_desc
615 get :index, :sort => 'author:desc'
635 get :index, :sort => 'author:desc'
616 assert_response :success
636 assert_response :success
617 authors = assigns(:issues).collect(&:author)
637 authors = assigns(:issues).collect(&:author)
618 assert_equal authors.sort.reverse, authors
638 assert_equal authors.sort.reverse, authors
619 end
639 end
620
640
621 def test_index_group_by_author
641 def test_index_group_by_author
622 get :index, :group_by => 'author', :sort => 'priority'
642 get :index, :group_by => 'author', :sort => 'priority'
623 assert_response :success
643 assert_response :success
624 end
644 end
625
645
626 def test_index_sort_by_spent_hours
646 def test_index_sort_by_spent_hours
627 get :index, :sort => 'spent_hours:desc'
647 get :index, :sort => 'spent_hours:desc'
628 assert_response :success
648 assert_response :success
629 hours = assigns(:issues).collect(&:spent_hours)
649 hours = assigns(:issues).collect(&:spent_hours)
630 assert_equal hours.sort.reverse, hours
650 assert_equal hours.sort.reverse, hours
631 end
651 end
632
652
633 def test_index_sort_by_user_custom_field
653 def test_index_sort_by_user_custom_field
634 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
654 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
635 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
655 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
636 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
656 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
637 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
657 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
638 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
658 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
639
659
640 get :index, :project_id => 1, :set_filter => 1, :sort => "cf_#{cf.id},id"
660 get :index, :project_id => 1, :set_filter => 1, :sort => "cf_#{cf.id},id"
641 assert_response :success
661 assert_response :success
642
662
643 assert_equal [2, 3, 1], assigns(:issues).select {|issue| issue.custom_field_value(cf).present?}.map(&:id)
663 assert_equal [2, 3, 1], assigns(:issues).select {|issue| issue.custom_field_value(cf).present?}.map(&:id)
644 end
664 end
645
665
646 def test_index_with_columns
666 def test_index_with_columns
647 columns = ['tracker', 'subject', 'assigned_to']
667 columns = ['tracker', 'subject', 'assigned_to']
648 get :index, :set_filter => 1, :c => columns
668 get :index, :set_filter => 1, :c => columns
649 assert_response :success
669 assert_response :success
650
670
651 # query should use specified columns
671 # query should use specified columns
652 query = assigns(:query)
672 query = assigns(:query)
653 assert_kind_of Query, query
673 assert_kind_of Query, query
654 assert_equal columns, query.column_names.map(&:to_s)
674 assert_equal columns, query.column_names.map(&:to_s)
655
675
656 # columns should be stored in session
676 # columns should be stored in session
657 assert_kind_of Hash, session[:query]
677 assert_kind_of Hash, session[:query]
658 assert_kind_of Array, session[:query][:column_names]
678 assert_kind_of Array, session[:query][:column_names]
659 assert_equal columns, session[:query][:column_names].map(&:to_s)
679 assert_equal columns, session[:query][:column_names].map(&:to_s)
660
680
661 # ensure only these columns are kept in the selected columns list
681 # ensure only these columns are kept in the selected columns list
662 assert_select 'select#selected_columns option' do
682 assert_select 'select#selected_columns option' do
663 assert_select 'option', 3
683 assert_select 'option', 3
664 assert_select 'option[value=tracker]'
684 assert_select 'option[value=tracker]'
665 assert_select 'option[value=project]', 0
685 assert_select 'option[value=project]', 0
666 end
686 end
667 end
687 end
668
688
669 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
689 def test_index_without_project_should_implicitly_add_project_column_to_default_columns
670 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
690 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
671 get :index, :set_filter => 1
691 get :index, :set_filter => 1
672
692
673 # query should use specified columns
693 # query should use specified columns
674 query = assigns(:query)
694 query = assigns(:query)
675 assert_kind_of Query, query
695 assert_kind_of Query, query
676 assert_equal [:project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
696 assert_equal [:project, :tracker, :subject, :assigned_to], query.columns.map(&:name)
677 end
697 end
678
698
679 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
699 def test_index_without_project_and_explicit_default_columns_should_not_add_project_column
680 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
700 Setting.issue_list_default_columns = ['tracker', 'subject', 'assigned_to']
681 columns = ['tracker', 'subject', 'assigned_to']
701 columns = ['tracker', 'subject', 'assigned_to']
682 get :index, :set_filter => 1, :c => columns
702 get :index, :set_filter => 1, :c => columns
683
703
684 # query should use specified columns
704 # query should use specified columns
685 query = assigns(:query)
705 query = assigns(:query)
686 assert_kind_of Query, query
706 assert_kind_of Query, query
687 assert_equal columns.map(&:to_sym), query.columns.map(&:name)
707 assert_equal columns.map(&:to_sym), query.columns.map(&:name)
688 end
708 end
689
709
690 def test_index_with_custom_field_column
710 def test_index_with_custom_field_column
691 columns = %w(tracker subject cf_2)
711 columns = %w(tracker subject cf_2)
692 get :index, :set_filter => 1, :c => columns
712 get :index, :set_filter => 1, :c => columns
693 assert_response :success
713 assert_response :success
694
714
695 # query should use specified columns
715 # query should use specified columns
696 query = assigns(:query)
716 query = assigns(:query)
697 assert_kind_of Query, query
717 assert_kind_of Query, query
698 assert_equal columns, query.column_names.map(&:to_s)
718 assert_equal columns, query.column_names.map(&:to_s)
699
719
700 assert_select 'table.issues td.cf_2.string'
720 assert_select 'table.issues td.cf_2.string'
701 end
721 end
702
722
703 def test_index_with_multi_custom_field_column
723 def test_index_with_multi_custom_field_column
704 field = CustomField.find(1)
724 field = CustomField.find(1)
705 field.update_attribute :multiple, true
725 field.update_attribute :multiple, true
706 issue = Issue.find(1)
726 issue = Issue.find(1)
707 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
727 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
708 issue.save!
728 issue.save!
709
729
710 get :index, :set_filter => 1, :c => %w(tracker subject cf_1)
730 get :index, :set_filter => 1, :c => %w(tracker subject cf_1)
711 assert_response :success
731 assert_response :success
712
732
713 assert_select 'table.issues td.cf_1', :text => 'MySQL, Oracle'
733 assert_select 'table.issues td.cf_1', :text => 'MySQL, Oracle'
714 end
734 end
715
735
716 def test_index_with_multi_user_custom_field_column
736 def test_index_with_multi_user_custom_field_column
717 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
737 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
718 :tracker_ids => [1], :is_for_all => true)
738 :tracker_ids => [1], :is_for_all => true)
719 issue = Issue.find(1)
739 issue = Issue.find(1)
720 issue.custom_field_values = {field.id => ['2', '3']}
740 issue.custom_field_values = {field.id => ['2', '3']}
721 issue.save!
741 issue.save!
722
742
723 get :index, :set_filter => 1, :c => ['tracker', 'subject', "cf_#{field.id}"]
743 get :index, :set_filter => 1, :c => ['tracker', 'subject', "cf_#{field.id}"]
724 assert_response :success
744 assert_response :success
725
745
726 assert_select "table.issues td.cf_#{field.id}" do
746 assert_select "table.issues td.cf_#{field.id}" do
727 assert_select 'a', 2
747 assert_select 'a', 2
728 assert_select 'a[href=?]', '/users/2', :text => 'John Smith'
748 assert_select 'a[href=?]', '/users/2', :text => 'John Smith'
729 assert_select 'a[href=?]', '/users/3', :text => 'Dave Lopper'
749 assert_select 'a[href=?]', '/users/3', :text => 'Dave Lopper'
730 end
750 end
731 end
751 end
732
752
733 def test_index_with_date_column
753 def test_index_with_date_column
734 with_settings :date_format => '%d/%m/%Y' do
754 with_settings :date_format => '%d/%m/%Y' do
735 Issue.find(1).update_attribute :start_date, '1987-08-24'
755 Issue.find(1).update_attribute :start_date, '1987-08-24'
736
756
737 get :index, :set_filter => 1, :c => %w(start_date)
757 get :index, :set_filter => 1, :c => %w(start_date)
738
758
739 assert_select "table.issues td.start_date", :text => '24/08/1987'
759 assert_select "table.issues td.start_date", :text => '24/08/1987'
740 end
760 end
741 end
761 end
742
762
743 def test_index_with_done_ratio_column
763 def test_index_with_done_ratio_column
744 Issue.find(1).update_attribute :done_ratio, 40
764 Issue.find(1).update_attribute :done_ratio, 40
745
765
746 get :index, :set_filter => 1, :c => %w(done_ratio)
766 get :index, :set_filter => 1, :c => %w(done_ratio)
747
767
748 assert_select 'table.issues td.done_ratio' do
768 assert_select 'table.issues td.done_ratio' do
749 assert_select 'table.progress' do
769 assert_select 'table.progress' do
750 assert_select 'td.closed[style=?]', 'width: 40%;'
770 assert_select 'td.closed[style=?]', 'width: 40%;'
751 end
771 end
752 end
772 end
753 end
773 end
754
774
755 def test_index_with_spent_hours_column
775 def test_index_with_spent_hours_column
756 get :index, :set_filter => 1, :c => %w(subject spent_hours)
776 get :index, :set_filter => 1, :c => %w(subject spent_hours)
757
777
758 assert_select 'table.issues tr#issue-3 td.spent_hours', :text => '1.00'
778 assert_select 'table.issues tr#issue-3 td.spent_hours', :text => '1.00'
759 end
779 end
760
780
761 def test_index_should_not_show_spent_hours_column_without_permission
781 def test_index_should_not_show_spent_hours_column_without_permission
762 Role.anonymous.remove_permission! :view_time_entries
782 Role.anonymous.remove_permission! :view_time_entries
763 get :index, :set_filter => 1, :c => %w(subject spent_hours)
783 get :index, :set_filter => 1, :c => %w(subject spent_hours)
764
784
765 assert_select 'td.spent_hours', 0
785 assert_select 'td.spent_hours', 0
766 end
786 end
767
787
768 def test_index_with_fixed_version_column
788 def test_index_with_fixed_version_column
769 get :index, :set_filter => 1, :c => %w(fixed_version)
789 get :index, :set_filter => 1, :c => %w(fixed_version)
770
790
771 assert_select 'table.issues td.fixed_version' do
791 assert_select 'table.issues td.fixed_version' do
772 assert_select 'a[href=?]', '/versions/2', :text => '1.0'
792 assert_select 'a[href=?]', '/versions/2', :text => '1.0'
773 end
793 end
774 end
794 end
775
795
776 def test_index_with_relations_column
796 def test_index_with_relations_column
777 IssueRelation.delete_all
797 IssueRelation.delete_all
778 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(7))
798 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(1), :issue_to => Issue.find(7))
779 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(8), :issue_to => Issue.find(1))
799 IssueRelation.create!(:relation_type => "relates", :issue_from => Issue.find(8), :issue_to => Issue.find(1))
780 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(1), :issue_to => Issue.find(11))
800 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(1), :issue_to => Issue.find(11))
781 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(12), :issue_to => Issue.find(2))
801 IssueRelation.create!(:relation_type => "blocks", :issue_from => Issue.find(12), :issue_to => Issue.find(2))
782
802
783 get :index, :set_filter => 1, :c => %w(subject relations)
803 get :index, :set_filter => 1, :c => %w(subject relations)
784 assert_response :success
804 assert_response :success
785 assert_select "tr#issue-1 td.relations" do
805 assert_select "tr#issue-1 td.relations" do
786 assert_select "span", 3
806 assert_select "span", 3
787 assert_select "span", :text => "Related to #7"
807 assert_select "span", :text => "Related to #7"
788 assert_select "span", :text => "Related to #8"
808 assert_select "span", :text => "Related to #8"
789 assert_select "span", :text => "Blocks #11"
809 assert_select "span", :text => "Blocks #11"
790 end
810 end
791 assert_select "tr#issue-2 td.relations" do
811 assert_select "tr#issue-2 td.relations" do
792 assert_select "span", 1
812 assert_select "span", 1
793 assert_select "span", :text => "Blocked by #12"
813 assert_select "span", :text => "Blocked by #12"
794 end
814 end
795 assert_select "tr#issue-3 td.relations" do
815 assert_select "tr#issue-3 td.relations" do
796 assert_select "span", 0
816 assert_select "span", 0
797 end
817 end
798
818
799 get :index, :set_filter => 1, :c => %w(relations), :format => 'csv'
819 get :index, :set_filter => 1, :c => %w(relations), :format => 'csv'
800 assert_response :success
820 assert_response :success
801 assert_equal 'text/csv; header=present', response.content_type
821 assert_equal 'text/csv; header=present', response.content_type
802 lines = response.body.chomp.split("\n")
822 lines = response.body.chomp.split("\n")
803 assert_include '1,"Related to #7, Related to #8, Blocks #11"', lines
823 assert_include '1,"Related to #7, Related to #8, Blocks #11"', lines
804 assert_include '2,Blocked by #12', lines
824 assert_include '2,Blocked by #12', lines
805 assert_include '3,""', lines
825 assert_include '3,""', lines
806
826
807 get :index, :set_filter => 1, :c => %w(subject relations), :format => 'pdf'
827 get :index, :set_filter => 1, :c => %w(subject relations), :format => 'pdf'
808 assert_response :success
828 assert_response :success
809 assert_equal 'application/pdf', response.content_type
829 assert_equal 'application/pdf', response.content_type
810 end
830 end
811
831
812 def test_index_send_html_if_query_is_invalid
832 def test_index_send_html_if_query_is_invalid
813 get :index, :f => ['start_date'], :op => {:start_date => '='}
833 get :index, :f => ['start_date'], :op => {:start_date => '='}
814 assert_equal 'text/html', @response.content_type
834 assert_equal 'text/html', @response.content_type
815 assert_template 'index'
835 assert_template 'index'
816 end
836 end
817
837
818 def test_index_send_nothing_if_query_is_invalid
838 def test_index_send_nothing_if_query_is_invalid
819 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
839 get :index, :f => ['start_date'], :op => {:start_date => '='}, :format => 'csv'
820 assert_equal 'text/csv', @response.content_type
840 assert_equal 'text/csv', @response.content_type
821 assert @response.body.blank?
841 assert @response.body.blank?
822 end
842 end
823
843
824 def test_show_by_anonymous
844 def test_show_by_anonymous
825 get :show, :id => 1
845 get :show, :id => 1
826 assert_response :success
846 assert_response :success
827 assert_template 'show'
847 assert_template 'show'
828 assert_equal Issue.find(1), assigns(:issue)
848 assert_equal Issue.find(1), assigns(:issue)
829
849
830 assert_select 'div.issue div.description', :text => /Unable to print recipes/
850 assert_select 'div.issue div.description', :text => /Unable to print recipes/
831
851
832 # anonymous role is allowed to add a note
852 # anonymous role is allowed to add a note
833 assert_select 'form#issue-form' do
853 assert_select 'form#issue-form' do
834 assert_select 'fieldset' do
854 assert_select 'fieldset' do
835 assert_select 'legend', :text => 'Notes'
855 assert_select 'legend', :text => 'Notes'
836 assert_select 'textarea[name=?]', 'issue[notes]'
856 assert_select 'textarea[name=?]', 'issue[notes]'
837 end
857 end
838 end
858 end
839
859
840 assert_select 'title', :text => "Bug #1: Can&#x27;t print recipes - eCookbook - Redmine"
860 assert_select 'title', :text => "Bug #1: Can&#x27;t print recipes - eCookbook - Redmine"
841 end
861 end
842
862
843 def test_show_by_manager
863 def test_show_by_manager
844 @request.session[:user_id] = 2
864 @request.session[:user_id] = 2
845 get :show, :id => 1
865 get :show, :id => 1
846 assert_response :success
866 assert_response :success
847
867
848 assert_select 'a', :text => /Quote/
868 assert_select 'a', :text => /Quote/
849
869
850 assert_select 'form#issue-form' do
870 assert_select 'form#issue-form' do
851 assert_select 'fieldset' do
871 assert_select 'fieldset' do
852 assert_select 'legend', :text => 'Change properties'
872 assert_select 'legend', :text => 'Change properties'
853 assert_select 'input[name=?]', 'issue[subject]'
873 assert_select 'input[name=?]', 'issue[subject]'
854 end
874 end
855 assert_select 'fieldset' do
875 assert_select 'fieldset' do
856 assert_select 'legend', :text => 'Log time'
876 assert_select 'legend', :text => 'Log time'
857 assert_select 'input[name=?]', 'time_entry[hours]'
877 assert_select 'input[name=?]', 'time_entry[hours]'
858 end
878 end
859 assert_select 'fieldset' do
879 assert_select 'fieldset' do
860 assert_select 'legend', :text => 'Notes'
880 assert_select 'legend', :text => 'Notes'
861 assert_select 'textarea[name=?]', 'issue[notes]'
881 assert_select 'textarea[name=?]', 'issue[notes]'
862 end
882 end
863 end
883 end
864 end
884 end
865
885
866 def test_show_should_display_update_form
886 def test_show_should_display_update_form
867 @request.session[:user_id] = 2
887 @request.session[:user_id] = 2
868 get :show, :id => 1
888 get :show, :id => 1
869 assert_response :success
889 assert_response :success
870
890
871 assert_tag 'form', :attributes => {:id => 'issue-form'}
891 assert_tag 'form', :attributes => {:id => 'issue-form'}
872 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
892 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
873 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
893 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
874 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
894 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
875 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
895 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
876 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
896 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
877 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
897 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
878 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
898 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
879 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
899 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
880 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
900 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
881 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
901 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
882 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
902 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
883 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
903 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
884 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
904 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
885 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
905 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
886 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
906 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
887 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
907 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
888 assert_tag 'textarea', :attributes => {:name => 'issue[notes]'}
908 assert_tag 'textarea', :attributes => {:name => 'issue[notes]'}
889 end
909 end
890
910
891 def test_show_should_display_update_form_with_minimal_permissions
911 def test_show_should_display_update_form_with_minimal_permissions
892 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
912 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
893 WorkflowTransition.delete_all :role_id => 1
913 WorkflowTransition.delete_all :role_id => 1
894
914
895 @request.session[:user_id] = 2
915 @request.session[:user_id] = 2
896 get :show, :id => 1
916 get :show, :id => 1
897 assert_response :success
917 assert_response :success
898
918
899 assert_tag 'form', :attributes => {:id => 'issue-form'}
919 assert_tag 'form', :attributes => {:id => 'issue-form'}
900 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
920 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
901 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
921 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
902 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
922 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
903 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
923 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
904 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
924 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
905 assert_no_tag 'select', :attributes => {:name => 'issue[status_id]'}
925 assert_no_tag 'select', :attributes => {:name => 'issue[status_id]'}
906 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
926 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
907 assert_no_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
927 assert_no_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
908 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
928 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
909 assert_no_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
929 assert_no_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
910 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
930 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
911 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
931 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
912 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
932 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
913 assert_no_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
933 assert_no_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
914 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
934 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
915 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
935 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
916 assert_tag 'textarea', :attributes => {:name => 'issue[notes]'}
936 assert_tag 'textarea', :attributes => {:name => 'issue[notes]'}
917 end
937 end
918
938
919 def test_show_should_display_update_form_with_workflow_permissions
939 def test_show_should_display_update_form_with_workflow_permissions
920 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
940 Role.find(1).update_attribute :permissions, [:view_issues, :add_issue_notes]
921
941
922 @request.session[:user_id] = 2
942 @request.session[:user_id] = 2
923 get :show, :id => 1
943 get :show, :id => 1
924 assert_response :success
944 assert_response :success
925
945
926 assert_tag 'form', :attributes => {:id => 'issue-form'}
946 assert_tag 'form', :attributes => {:id => 'issue-form'}
927 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
947 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
928 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
948 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
929 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
949 assert_no_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
930 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
950 assert_no_tag 'input', :attributes => {:name => 'issue[subject]'}
931 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
951 assert_no_tag 'textarea', :attributes => {:name => 'issue[description]'}
932 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
952 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
933 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
953 assert_no_tag 'select', :attributes => {:name => 'issue[priority_id]'}
934 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
954 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
935 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
955 assert_no_tag 'select', :attributes => {:name => 'issue[category_id]'}
936 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
956 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
937 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
957 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
938 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
958 assert_no_tag 'input', :attributes => {:name => 'issue[start_date]'}
939 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
959 assert_no_tag 'input', :attributes => {:name => 'issue[due_date]'}
940 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
960 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
941 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
961 assert_no_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]' }
942 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
962 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
943 assert_tag 'textarea', :attributes => {:name => 'issue[notes]'}
963 assert_tag 'textarea', :attributes => {:name => 'issue[notes]'}
944 end
964 end
945
965
946 def test_show_should_not_display_update_form_without_permissions
966 def test_show_should_not_display_update_form_without_permissions
947 Role.find(1).update_attribute :permissions, [:view_issues]
967 Role.find(1).update_attribute :permissions, [:view_issues]
948
968
949 @request.session[:user_id] = 2
969 @request.session[:user_id] = 2
950 get :show, :id => 1
970 get :show, :id => 1
951 assert_response :success
971 assert_response :success
952
972
953 assert_select 'form#issue-form', 0
973 assert_select 'form#issue-form', 0
954 end
974 end
955
975
956 def test_update_form_should_not_display_inactive_enumerations
976 def test_update_form_should_not_display_inactive_enumerations
957 assert !IssuePriority.find(15).active?
977 assert !IssuePriority.find(15).active?
958
978
959 @request.session[:user_id] = 2
979 @request.session[:user_id] = 2
960 get :show, :id => 1
980 get :show, :id => 1
961 assert_response :success
981 assert_response :success
962
982
963 assert_select 'form#issue-form' do
983 assert_select 'form#issue-form' do
964 assert_select 'select[name=?]', 'issue[priority_id]' do
984 assert_select 'select[name=?]', 'issue[priority_id]' do
965 assert_select 'option[value=4]'
985 assert_select 'option[value=4]'
966 assert_select 'option[value=15]', 0
986 assert_select 'option[value=15]', 0
967 end
987 end
968 end
988 end
969 end
989 end
970
990
971 def test_update_form_should_allow_attachment_upload
991 def test_update_form_should_allow_attachment_upload
972 @request.session[:user_id] = 2
992 @request.session[:user_id] = 2
973 get :show, :id => 1
993 get :show, :id => 1
974
994
975 assert_select 'form#issue-form[method=post][enctype=multipart/form-data]' do
995 assert_select 'form#issue-form[method=post][enctype=multipart/form-data]' do
976 assert_select 'input[type=file][name=?]', 'attachments[1][file]'
996 assert_select 'input[type=file][name=?]', 'attachments[1][file]'
977 end
997 end
978 end
998 end
979
999
980 def test_show_should_deny_anonymous_access_without_permission
1000 def test_show_should_deny_anonymous_access_without_permission
981 Role.anonymous.remove_permission!(:view_issues)
1001 Role.anonymous.remove_permission!(:view_issues)
982 get :show, :id => 1
1002 get :show, :id => 1
983 assert_response :redirect
1003 assert_response :redirect
984 end
1004 end
985
1005
986 def test_show_should_deny_anonymous_access_to_private_issue
1006 def test_show_should_deny_anonymous_access_to_private_issue
987 Issue.update_all(["is_private = ?", true], "id = 1")
1007 Issue.update_all(["is_private = ?", true], "id = 1")
988 get :show, :id => 1
1008 get :show, :id => 1
989 assert_response :redirect
1009 assert_response :redirect
990 end
1010 end
991
1011
992 def test_show_should_deny_non_member_access_without_permission
1012 def test_show_should_deny_non_member_access_without_permission
993 Role.non_member.remove_permission!(:view_issues)
1013 Role.non_member.remove_permission!(:view_issues)
994 @request.session[:user_id] = 9
1014 @request.session[:user_id] = 9
995 get :show, :id => 1
1015 get :show, :id => 1
996 assert_response 403
1016 assert_response 403
997 end
1017 end
998
1018
999 def test_show_should_deny_non_member_access_to_private_issue
1019 def test_show_should_deny_non_member_access_to_private_issue
1000 Issue.update_all(["is_private = ?", true], "id = 1")
1020 Issue.update_all(["is_private = ?", true], "id = 1")
1001 @request.session[:user_id] = 9
1021 @request.session[:user_id] = 9
1002 get :show, :id => 1
1022 get :show, :id => 1
1003 assert_response 403
1023 assert_response 403
1004 end
1024 end
1005
1025
1006 def test_show_should_deny_member_access_without_permission
1026 def test_show_should_deny_member_access_without_permission
1007 Role.find(1).remove_permission!(:view_issues)
1027 Role.find(1).remove_permission!(:view_issues)
1008 @request.session[:user_id] = 2
1028 @request.session[:user_id] = 2
1009 get :show, :id => 1
1029 get :show, :id => 1
1010 assert_response 403
1030 assert_response 403
1011 end
1031 end
1012
1032
1013 def test_show_should_deny_member_access_to_private_issue_without_permission
1033 def test_show_should_deny_member_access_to_private_issue_without_permission
1014 Issue.update_all(["is_private = ?", true], "id = 1")
1034 Issue.update_all(["is_private = ?", true], "id = 1")
1015 @request.session[:user_id] = 3
1035 @request.session[:user_id] = 3
1016 get :show, :id => 1
1036 get :show, :id => 1
1017 assert_response 403
1037 assert_response 403
1018 end
1038 end
1019
1039
1020 def test_show_should_allow_author_access_to_private_issue
1040 def test_show_should_allow_author_access_to_private_issue
1021 Issue.update_all(["is_private = ?, author_id = 3", true], "id = 1")
1041 Issue.update_all(["is_private = ?, author_id = 3", true], "id = 1")
1022 @request.session[:user_id] = 3
1042 @request.session[:user_id] = 3
1023 get :show, :id => 1
1043 get :show, :id => 1
1024 assert_response :success
1044 assert_response :success
1025 end
1045 end
1026
1046
1027 def test_show_should_allow_assignee_access_to_private_issue
1047 def test_show_should_allow_assignee_access_to_private_issue
1028 Issue.update_all(["is_private = ?, assigned_to_id = 3", true], "id = 1")
1048 Issue.update_all(["is_private = ?, assigned_to_id = 3", true], "id = 1")
1029 @request.session[:user_id] = 3
1049 @request.session[:user_id] = 3
1030 get :show, :id => 1
1050 get :show, :id => 1
1031 assert_response :success
1051 assert_response :success
1032 end
1052 end
1033
1053
1034 def test_show_should_allow_member_access_to_private_issue_with_permission
1054 def test_show_should_allow_member_access_to_private_issue_with_permission
1035 Issue.update_all(["is_private = ?", true], "id = 1")
1055 Issue.update_all(["is_private = ?", true], "id = 1")
1036 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
1056 User.find(3).roles_for_project(Project.find(1)).first.update_attribute :issues_visibility, 'all'
1037 @request.session[:user_id] = 3
1057 @request.session[:user_id] = 3
1038 get :show, :id => 1
1058 get :show, :id => 1
1039 assert_response :success
1059 assert_response :success
1040 end
1060 end
1041
1061
1042 def test_show_should_not_disclose_relations_to_invisible_issues
1062 def test_show_should_not_disclose_relations_to_invisible_issues
1043 Setting.cross_project_issue_relations = '1'
1063 Setting.cross_project_issue_relations = '1'
1044 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
1064 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
1045 # Relation to a private project issue
1065 # Relation to a private project issue
1046 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
1066 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
1047
1067
1048 get :show, :id => 1
1068 get :show, :id => 1
1049 assert_response :success
1069 assert_response :success
1050
1070
1051 assert_select 'div#relations' do
1071 assert_select 'div#relations' do
1052 assert_select 'a', :text => /#2$/
1072 assert_select 'a', :text => /#2$/
1053 assert_select 'a', :text => /#4$/, :count => 0
1073 assert_select 'a', :text => /#4$/, :count => 0
1054 end
1074 end
1055 end
1075 end
1056
1076
1057 def test_show_should_list_subtasks
1077 def test_show_should_list_subtasks
1058 Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1078 Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1059
1079
1060 get :show, :id => 1
1080 get :show, :id => 1
1061 assert_response :success
1081 assert_response :success
1062
1082
1063 assert_select 'div#issue_tree' do
1083 assert_select 'div#issue_tree' do
1064 assert_select 'td.subject', :text => /Child Issue/
1084 assert_select 'td.subject', :text => /Child Issue/
1065 end
1085 end
1066 end
1086 end
1067
1087
1068 def test_show_should_list_parents
1088 def test_show_should_list_parents
1069 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1089 issue = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :parent_issue_id => 1, :subject => 'Child Issue')
1070
1090
1071 get :show, :id => issue.id
1091 get :show, :id => issue.id
1072 assert_response :success
1092 assert_response :success
1073
1093
1074 assert_select 'div.subject' do
1094 assert_select 'div.subject' do
1075 assert_select 'h3', 'Child Issue'
1095 assert_select 'h3', 'Child Issue'
1076 assert_select 'a[href=/issues/1]'
1096 assert_select 'a[href=/issues/1]'
1077 end
1097 end
1078 end
1098 end
1079
1099
1080 def test_show_should_not_display_prev_next_links_without_query_in_session
1100 def test_show_should_not_display_prev_next_links_without_query_in_session
1081 get :show, :id => 1
1101 get :show, :id => 1
1082 assert_response :success
1102 assert_response :success
1083 assert_nil assigns(:prev_issue_id)
1103 assert_nil assigns(:prev_issue_id)
1084 assert_nil assigns(:next_issue_id)
1104 assert_nil assigns(:next_issue_id)
1085
1105
1086 assert_select 'div.next-prev-links', 0
1106 assert_select 'div.next-prev-links', 0
1087 end
1107 end
1088
1108
1089 def test_show_should_display_prev_next_links_with_query_in_session
1109 def test_show_should_display_prev_next_links_with_query_in_session
1090 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1110 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1091 @request.session['issues_index_sort'] = 'id'
1111 @request.session['issues_index_sort'] = 'id'
1092
1112
1093 with_settings :display_subprojects_issues => '0' do
1113 with_settings :display_subprojects_issues => '0' do
1094 get :show, :id => 3
1114 get :show, :id => 3
1095 end
1115 end
1096
1116
1097 assert_response :success
1117 assert_response :success
1098 # Previous and next issues for all projects
1118 # Previous and next issues for all projects
1099 assert_equal 2, assigns(:prev_issue_id)
1119 assert_equal 2, assigns(:prev_issue_id)
1100 assert_equal 5, assigns(:next_issue_id)
1120 assert_equal 5, assigns(:next_issue_id)
1101
1121
1102 count = Issue.open.visible.count
1122 count = Issue.open.visible.count
1103
1123
1104 assert_select 'div.next-prev-links' do
1124 assert_select 'div.next-prev-links' do
1105 assert_select 'a[href=/issues/2]', :text => /Previous/
1125 assert_select 'a[href=/issues/2]', :text => /Previous/
1106 assert_select 'a[href=/issues/5]', :text => /Next/
1126 assert_select 'a[href=/issues/5]', :text => /Next/
1107 assert_select 'span.position', :text => "3 of #{count}"
1127 assert_select 'span.position', :text => "3 of #{count}"
1108 end
1128 end
1109 end
1129 end
1110
1130
1111 def test_show_should_display_prev_next_links_with_saved_query_in_session
1131 def test_show_should_display_prev_next_links_with_saved_query_in_session
1112 query = Query.create!(:name => 'test', :is_public => true, :user_id => 1,
1132 query = Query.create!(:name => 'test', :is_public => true, :user_id => 1,
1113 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
1133 :filters => {'status_id' => {:values => ['5'], :operator => '='}},
1114 :sort_criteria => [['id', 'asc']])
1134 :sort_criteria => [['id', 'asc']])
1115 @request.session[:query] = {:id => query.id, :project_id => nil}
1135 @request.session[:query] = {:id => query.id, :project_id => nil}
1116
1136
1117 get :show, :id => 11
1137 get :show, :id => 11
1118
1138
1119 assert_response :success
1139 assert_response :success
1120 assert_equal query, assigns(:query)
1140 assert_equal query, assigns(:query)
1121 # Previous and next issues for all projects
1141 # Previous and next issues for all projects
1122 assert_equal 8, assigns(:prev_issue_id)
1142 assert_equal 8, assigns(:prev_issue_id)
1123 assert_equal 12, assigns(:next_issue_id)
1143 assert_equal 12, assigns(:next_issue_id)
1124
1144
1125 assert_select 'div.next-prev-links' do
1145 assert_select 'div.next-prev-links' do
1126 assert_select 'a[href=/issues/8]', :text => /Previous/
1146 assert_select 'a[href=/issues/8]', :text => /Previous/
1127 assert_select 'a[href=/issues/12]', :text => /Next/
1147 assert_select 'a[href=/issues/12]', :text => /Next/
1128 end
1148 end
1129 end
1149 end
1130
1150
1131 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
1151 def test_show_should_display_prev_next_links_with_query_and_sort_on_association
1132 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1152 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => nil}
1133
1153
1134 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
1154 %w(project tracker status priority author assigned_to category fixed_version).each do |assoc_sort|
1135 @request.session['issues_index_sort'] = assoc_sort
1155 @request.session['issues_index_sort'] = assoc_sort
1136
1156
1137 get :show, :id => 3
1157 get :show, :id => 3
1138 assert_response :success, "Wrong response status for #{assoc_sort} sort"
1158 assert_response :success, "Wrong response status for #{assoc_sort} sort"
1139
1159
1140 assert_select 'div.next-prev-links' do
1160 assert_select 'div.next-prev-links' do
1141 assert_select 'a', :text => /(Previous|Next)/
1161 assert_select 'a', :text => /(Previous|Next)/
1142 end
1162 end
1143 end
1163 end
1144 end
1164 end
1145
1165
1146 def test_show_should_display_prev_next_links_with_project_query_in_session
1166 def test_show_should_display_prev_next_links_with_project_query_in_session
1147 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1167 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1148 @request.session['issues_index_sort'] = 'id'
1168 @request.session['issues_index_sort'] = 'id'
1149
1169
1150 with_settings :display_subprojects_issues => '0' do
1170 with_settings :display_subprojects_issues => '0' do
1151 get :show, :id => 3
1171 get :show, :id => 3
1152 end
1172 end
1153
1173
1154 assert_response :success
1174 assert_response :success
1155 # Previous and next issues inside project
1175 # Previous and next issues inside project
1156 assert_equal 2, assigns(:prev_issue_id)
1176 assert_equal 2, assigns(:prev_issue_id)
1157 assert_equal 7, assigns(:next_issue_id)
1177 assert_equal 7, assigns(:next_issue_id)
1158
1178
1159 assert_select 'div.next-prev-links' do
1179 assert_select 'div.next-prev-links' do
1160 assert_select 'a[href=/issues/2]', :text => /Previous/
1180 assert_select 'a[href=/issues/2]', :text => /Previous/
1161 assert_select 'a[href=/issues/7]', :text => /Next/
1181 assert_select 'a[href=/issues/7]', :text => /Next/
1162 end
1182 end
1163 end
1183 end
1164
1184
1165 def test_show_should_not_display_prev_link_for_first_issue
1185 def test_show_should_not_display_prev_link_for_first_issue
1166 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1186 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'o'}}, :project_id => 1}
1167 @request.session['issues_index_sort'] = 'id'
1187 @request.session['issues_index_sort'] = 'id'
1168
1188
1169 with_settings :display_subprojects_issues => '0' do
1189 with_settings :display_subprojects_issues => '0' do
1170 get :show, :id => 1
1190 get :show, :id => 1
1171 end
1191 end
1172
1192
1173 assert_response :success
1193 assert_response :success
1174 assert_nil assigns(:prev_issue_id)
1194 assert_nil assigns(:prev_issue_id)
1175 assert_equal 2, assigns(:next_issue_id)
1195 assert_equal 2, assigns(:next_issue_id)
1176
1196
1177 assert_select 'div.next-prev-links' do
1197 assert_select 'div.next-prev-links' do
1178 assert_select 'a', :text => /Previous/, :count => 0
1198 assert_select 'a', :text => /Previous/, :count => 0
1179 assert_select 'a[href=/issues/2]', :text => /Next/
1199 assert_select 'a[href=/issues/2]', :text => /Next/
1180 end
1200 end
1181 end
1201 end
1182
1202
1183 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1203 def test_show_should_not_display_prev_next_links_for_issue_not_in_query_results
1184 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1204 @request.session[:query] = {:filters => {'status_id' => {:values => [''], :operator => 'c'}}, :project_id => 1}
1185 @request.session['issues_index_sort'] = 'id'
1205 @request.session['issues_index_sort'] = 'id'
1186
1206
1187 get :show, :id => 1
1207 get :show, :id => 1
1188
1208
1189 assert_response :success
1209 assert_response :success
1190 assert_nil assigns(:prev_issue_id)
1210 assert_nil assigns(:prev_issue_id)
1191 assert_nil assigns(:next_issue_id)
1211 assert_nil assigns(:next_issue_id)
1192
1212
1193 assert_select 'a', :text => /Previous/, :count => 0
1213 assert_select 'a', :text => /Previous/, :count => 0
1194 assert_select 'a', :text => /Next/, :count => 0
1214 assert_select 'a', :text => /Next/, :count => 0
1195 end
1215 end
1196
1216
1197 def test_show_show_should_display_prev_next_links_with_query_sort_by_user_custom_field
1217 def test_show_show_should_display_prev_next_links_with_query_sort_by_user_custom_field
1198 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
1218 cf = IssueCustomField.create!(:name => 'User', :is_for_all => true, :tracker_ids => [1,2,3], :field_format => 'user')
1199 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
1219 CustomValue.create!(:custom_field => cf, :customized => Issue.find(1), :value => '2')
1200 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
1220 CustomValue.create!(:custom_field => cf, :customized => Issue.find(2), :value => '3')
1201 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
1221 CustomValue.create!(:custom_field => cf, :customized => Issue.find(3), :value => '3')
1202 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
1222 CustomValue.create!(:custom_field => cf, :customized => Issue.find(5), :value => '')
1203
1223
1204 query = Query.create!(:name => 'test', :is_public => true, :user_id => 1, :filters => {},
1224 query = Query.create!(:name => 'test', :is_public => true, :user_id => 1, :filters => {},
1205 :sort_criteria => [["cf_#{cf.id}", 'asc'], ['id', 'asc']])
1225 :sort_criteria => [["cf_#{cf.id}", 'asc'], ['id', 'asc']])
1206 @request.session[:query] = {:id => query.id, :project_id => nil}
1226 @request.session[:query] = {:id => query.id, :project_id => nil}
1207
1227
1208 get :show, :id => 3
1228 get :show, :id => 3
1209 assert_response :success
1229 assert_response :success
1210
1230
1211 assert_equal 2, assigns(:prev_issue_id)
1231 assert_equal 2, assigns(:prev_issue_id)
1212 assert_equal 1, assigns(:next_issue_id)
1232 assert_equal 1, assigns(:next_issue_id)
1213
1233
1214 assert_select 'div.next-prev-links' do
1234 assert_select 'div.next-prev-links' do
1215 assert_select 'a[href=/issues/2]', :text => /Previous/
1235 assert_select 'a[href=/issues/2]', :text => /Previous/
1216 assert_select 'a[href=/issues/1]', :text => /Next/
1236 assert_select 'a[href=/issues/1]', :text => /Next/
1217 end
1237 end
1218 end
1238 end
1219
1239
1220 def test_show_should_display_link_to_the_assignee
1240 def test_show_should_display_link_to_the_assignee
1221 get :show, :id => 2
1241 get :show, :id => 2
1222 assert_response :success
1242 assert_response :success
1223 assert_select '.assigned-to' do
1243 assert_select '.assigned-to' do
1224 assert_select 'a[href=/users/3]'
1244 assert_select 'a[href=/users/3]'
1225 end
1245 end
1226 end
1246 end
1227
1247
1228 def test_show_should_display_visible_changesets_from_other_projects
1248 def test_show_should_display_visible_changesets_from_other_projects
1229 project = Project.find(2)
1249 project = Project.find(2)
1230 issue = project.issues.first
1250 issue = project.issues.first
1231 issue.changeset_ids = [102]
1251 issue.changeset_ids = [102]
1232 issue.save!
1252 issue.save!
1233 # changesets from other projects should be displayed even if repository
1253 # changesets from other projects should be displayed even if repository
1234 # is disabled on issue's project
1254 # is disabled on issue's project
1235 project.disable_module! :repository
1255 project.disable_module! :repository
1236
1256
1237 @request.session[:user_id] = 2
1257 @request.session[:user_id] = 2
1238 get :show, :id => issue.id
1258 get :show, :id => issue.id
1239
1259
1240 assert_select 'a[href=?]', '/projects/ecookbook/repository/revisions/3'
1260 assert_select 'a[href=?]', '/projects/ecookbook/repository/revisions/3'
1241 end
1261 end
1242
1262
1243 def test_show_should_display_watchers
1263 def test_show_should_display_watchers
1244 @request.session[:user_id] = 2
1264 @request.session[:user_id] = 2
1245 Issue.find(1).add_watcher User.find(2)
1265 Issue.find(1).add_watcher User.find(2)
1246
1266
1247 get :show, :id => 1
1267 get :show, :id => 1
1248 assert_select 'div#watchers ul' do
1268 assert_select 'div#watchers ul' do
1249 assert_select 'li' do
1269 assert_select 'li' do
1250 assert_select 'a[href=/users/2]'
1270 assert_select 'a[href=/users/2]'
1251 assert_select 'a img[alt=Delete]'
1271 assert_select 'a img[alt=Delete]'
1252 end
1272 end
1253 end
1273 end
1254 end
1274 end
1255
1275
1256 def test_show_should_display_watchers_with_gravatars
1276 def test_show_should_display_watchers_with_gravatars
1257 @request.session[:user_id] = 2
1277 @request.session[:user_id] = 2
1258 Issue.find(1).add_watcher User.find(2)
1278 Issue.find(1).add_watcher User.find(2)
1259
1279
1260 with_settings :gravatar_enabled => '1' do
1280 with_settings :gravatar_enabled => '1' do
1261 get :show, :id => 1
1281 get :show, :id => 1
1262 end
1282 end
1263
1283
1264 assert_select 'div#watchers ul' do
1284 assert_select 'div#watchers ul' do
1265 assert_select 'li' do
1285 assert_select 'li' do
1266 assert_select 'img.gravatar'
1286 assert_select 'img.gravatar'
1267 assert_select 'a[href=/users/2]'
1287 assert_select 'a[href=/users/2]'
1268 assert_select 'a img[alt=Delete]'
1288 assert_select 'a img[alt=Delete]'
1269 end
1289 end
1270 end
1290 end
1271 end
1291 end
1272
1292
1273 def test_show_with_thumbnails_enabled_should_display_thumbnails
1293 def test_show_with_thumbnails_enabled_should_display_thumbnails
1274 @request.session[:user_id] = 2
1294 @request.session[:user_id] = 2
1275
1295
1276 with_settings :thumbnails_enabled => '1' do
1296 with_settings :thumbnails_enabled => '1' do
1277 get :show, :id => 14
1297 get :show, :id => 14
1278 assert_response :success
1298 assert_response :success
1279 end
1299 end
1280
1300
1281 assert_select 'div.thumbnails' do
1301 assert_select 'div.thumbnails' do
1282 assert_select 'a[href=/attachments/16/testfile.png]' do
1302 assert_select 'a[href=/attachments/16/testfile.png]' do
1283 assert_select 'img[src=/attachments/thumbnail/16]'
1303 assert_select 'img[src=/attachments/thumbnail/16]'
1284 end
1304 end
1285 end
1305 end
1286 end
1306 end
1287
1307
1288 def test_show_with_thumbnails_disabled_should_not_display_thumbnails
1308 def test_show_with_thumbnails_disabled_should_not_display_thumbnails
1289 @request.session[:user_id] = 2
1309 @request.session[:user_id] = 2
1290
1310
1291 with_settings :thumbnails_enabled => '0' do
1311 with_settings :thumbnails_enabled => '0' do
1292 get :show, :id => 14
1312 get :show, :id => 14
1293 assert_response :success
1313 assert_response :success
1294 end
1314 end
1295
1315
1296 assert_select 'div.thumbnails', 0
1316 assert_select 'div.thumbnails', 0
1297 end
1317 end
1298
1318
1299 def test_show_with_multi_custom_field
1319 def test_show_with_multi_custom_field
1300 field = CustomField.find(1)
1320 field = CustomField.find(1)
1301 field.update_attribute :multiple, true
1321 field.update_attribute :multiple, true
1302 issue = Issue.find(1)
1322 issue = Issue.find(1)
1303 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1323 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
1304 issue.save!
1324 issue.save!
1305
1325
1306 get :show, :id => 1
1326 get :show, :id => 1
1307 assert_response :success
1327 assert_response :success
1308
1328
1309 assert_select 'td', :text => 'MySQL, Oracle'
1329 assert_select 'td', :text => 'MySQL, Oracle'
1310 end
1330 end
1311
1331
1312 def test_show_with_multi_user_custom_field
1332 def test_show_with_multi_user_custom_field
1313 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1333 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1314 :tracker_ids => [1], :is_for_all => true)
1334 :tracker_ids => [1], :is_for_all => true)
1315 issue = Issue.find(1)
1335 issue = Issue.find(1)
1316 issue.custom_field_values = {field.id => ['2', '3']}
1336 issue.custom_field_values = {field.id => ['2', '3']}
1317 issue.save!
1337 issue.save!
1318
1338
1319 get :show, :id => 1
1339 get :show, :id => 1
1320 assert_response :success
1340 assert_response :success
1321
1341
1322 # TODO: should display links
1342 # TODO: should display links
1323 assert_select 'td', :text => 'Dave Lopper, John Smith'
1343 assert_select 'td', :text => 'Dave Lopper, John Smith'
1324 end
1344 end
1325
1345
1326 def test_show_should_display_private_notes_with_permission_only
1346 def test_show_should_display_private_notes_with_permission_only
1327 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true, :user_id => 1)
1347 journal = Journal.create!(:journalized => Issue.find(2), :notes => 'Privates notes', :private_notes => true, :user_id => 1)
1328 @request.session[:user_id] = 2
1348 @request.session[:user_id] = 2
1329
1349
1330 get :show, :id => 2
1350 get :show, :id => 2
1331 assert_response :success
1351 assert_response :success
1332 assert_include journal, assigns(:journals)
1352 assert_include journal, assigns(:journals)
1333
1353
1334 Role.find(1).remove_permission! :view_private_notes
1354 Role.find(1).remove_permission! :view_private_notes
1335 get :show, :id => 2
1355 get :show, :id => 2
1336 assert_response :success
1356 assert_response :success
1337 assert_not_include journal, assigns(:journals)
1357 assert_not_include journal, assigns(:journals)
1338 end
1358 end
1339
1359
1340 def test_show_atom
1360 def test_show_atom
1341 get :show, :id => 2, :format => 'atom'
1361 get :show, :id => 2, :format => 'atom'
1342 assert_response :success
1362 assert_response :success
1343 assert_template 'journals/index'
1363 assert_template 'journals/index'
1344 # Inline image
1364 # Inline image
1345 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1365 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
1346 end
1366 end
1347
1367
1348 def test_show_export_to_pdf
1368 def test_show_export_to_pdf
1349 get :show, :id => 3, :format => 'pdf'
1369 get :show, :id => 3, :format => 'pdf'
1350 assert_response :success
1370 assert_response :success
1351 assert_equal 'application/pdf', @response.content_type
1371 assert_equal 'application/pdf', @response.content_type
1352 assert @response.body.starts_with?('%PDF')
1372 assert @response.body.starts_with?('%PDF')
1353 assert_not_nil assigns(:issue)
1373 assert_not_nil assigns(:issue)
1354 end
1374 end
1355
1375
1356 def test_show_export_to_pdf_with_ancestors
1376 def test_show_export_to_pdf_with_ancestors
1357 issue = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1377 issue = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1358
1378
1359 get :show, :id => issue.id, :format => 'pdf'
1379 get :show, :id => issue.id, :format => 'pdf'
1360 assert_response :success
1380 assert_response :success
1361 assert_equal 'application/pdf', @response.content_type
1381 assert_equal 'application/pdf', @response.content_type
1362 assert @response.body.starts_with?('%PDF')
1382 assert @response.body.starts_with?('%PDF')
1363 end
1383 end
1364
1384
1365 def test_show_export_to_pdf_with_descendants
1385 def test_show_export_to_pdf_with_descendants
1366 c1 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1386 c1 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1367 c2 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1387 c2 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => 1)
1368 c3 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => c1.id)
1388 c3 = Issue.generate!(:project_id => 1, :author_id => 2, :tracker_id => 1, :subject => 'child', :parent_issue_id => c1.id)
1369
1389
1370 get :show, :id => 1, :format => 'pdf'
1390 get :show, :id => 1, :format => 'pdf'
1371 assert_response :success
1391 assert_response :success
1372 assert_equal 'application/pdf', @response.content_type
1392 assert_equal 'application/pdf', @response.content_type
1373 assert @response.body.starts_with?('%PDF')
1393 assert @response.body.starts_with?('%PDF')
1374 end
1394 end
1375
1395
1376 def test_show_export_to_pdf_with_journals
1396 def test_show_export_to_pdf_with_journals
1377 get :show, :id => 1, :format => 'pdf'
1397 get :show, :id => 1, :format => 'pdf'
1378 assert_response :success
1398 assert_response :success
1379 assert_equal 'application/pdf', @response.content_type
1399 assert_equal 'application/pdf', @response.content_type
1380 assert @response.body.starts_with?('%PDF')
1400 assert @response.body.starts_with?('%PDF')
1381 end
1401 end
1382
1402
1383 def test_show_export_to_pdf_with_changesets
1403 def test_show_export_to_pdf_with_changesets
1384 Issue.find(3).changesets = Changeset.find_all_by_id(100, 101, 102)
1404 Issue.find(3).changesets = Changeset.find_all_by_id(100, 101, 102)
1385
1405
1386 get :show, :id => 3, :format => 'pdf'
1406 get :show, :id => 3, :format => 'pdf'
1387 assert_response :success
1407 assert_response :success
1388 assert_equal 'application/pdf', @response.content_type
1408 assert_equal 'application/pdf', @response.content_type
1389 assert @response.body.starts_with?('%PDF')
1409 assert @response.body.starts_with?('%PDF')
1390 end
1410 end
1391
1411
1392 def test_get_new
1412 def test_get_new
1393 @request.session[:user_id] = 2
1413 @request.session[:user_id] = 2
1394 get :new, :project_id => 1, :tracker_id => 1
1414 get :new, :project_id => 1, :tracker_id => 1
1395 assert_response :success
1415 assert_response :success
1396 assert_template 'new'
1416 assert_template 'new'
1397
1417
1398 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
1418 assert_tag 'input', :attributes => {:name => 'issue[is_private]'}
1399 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1419 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1400 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1420 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1401 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1421 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1402 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1422 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1403 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1423 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1404 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1424 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1405 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1425 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1406 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1426 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1407 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1427 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1408 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1428 assert_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1409 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1429 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1410 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1430 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1411 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1431 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1412 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1432 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1413 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1433 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1414
1434
1415 # Be sure we don't display inactive IssuePriorities
1435 # Be sure we don't display inactive IssuePriorities
1416 assert ! IssuePriority.find(15).active?
1436 assert ! IssuePriority.find(15).active?
1417 assert_no_tag :option, :attributes => {:value => '15'},
1437 assert_no_tag :option, :attributes => {:value => '15'},
1418 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1438 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
1419 end
1439 end
1420
1440
1421 def test_get_new_with_minimal_permissions
1441 def test_get_new_with_minimal_permissions
1422 Role.find(1).update_attribute :permissions, [:add_issues]
1442 Role.find(1).update_attribute :permissions, [:add_issues]
1423 WorkflowTransition.delete_all :role_id => 1
1443 WorkflowTransition.delete_all :role_id => 1
1424
1444
1425 @request.session[:user_id] = 2
1445 @request.session[:user_id] = 2
1426 get :new, :project_id => 1, :tracker_id => 1
1446 get :new, :project_id => 1, :tracker_id => 1
1427 assert_response :success
1447 assert_response :success
1428 assert_template 'new'
1448 assert_template 'new'
1429
1449
1430 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
1450 assert_no_tag 'input', :attributes => {:name => 'issue[is_private]'}
1431 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1451 assert_no_tag 'select', :attributes => {:name => 'issue[project_id]'}
1432 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1452 assert_tag 'select', :attributes => {:name => 'issue[tracker_id]'}
1433 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1453 assert_tag 'input', :attributes => {:name => 'issue[subject]'}
1434 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1454 assert_tag 'textarea', :attributes => {:name => 'issue[description]'}
1435 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1455 assert_tag 'select', :attributes => {:name => 'issue[status_id]'}
1436 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1456 assert_tag 'select', :attributes => {:name => 'issue[priority_id]'}
1437 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1457 assert_tag 'select', :attributes => {:name => 'issue[assigned_to_id]'}
1438 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1458 assert_tag 'select', :attributes => {:name => 'issue[category_id]'}
1439 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1459 assert_tag 'select', :attributes => {:name => 'issue[fixed_version_id]'}
1440 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1460 assert_no_tag 'input', :attributes => {:name => 'issue[parent_issue_id]'}
1441 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1461 assert_tag 'input', :attributes => {:name => 'issue[start_date]'}
1442 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1462 assert_tag 'input', :attributes => {:name => 'issue[due_date]'}
1443 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1463 assert_tag 'select', :attributes => {:name => 'issue[done_ratio]'}
1444 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1464 assert_tag 'input', :attributes => { :name => 'issue[custom_field_values][2]', :value => 'Default string' }
1445 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1465 assert_no_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]'}
1446 end
1466 end
1447
1467
1448 def test_get_new_with_list_custom_field
1468 def test_get_new_with_list_custom_field
1449 @request.session[:user_id] = 2
1469 @request.session[:user_id] = 2
1450 get :new, :project_id => 1, :tracker_id => 1
1470 get :new, :project_id => 1, :tracker_id => 1
1451 assert_response :success
1471 assert_response :success
1452 assert_template 'new'
1472 assert_template 'new'
1453
1473
1454 assert_select 'select.list_cf[name=?]', 'issue[custom_field_values][1]' do
1474 assert_select 'select.list_cf[name=?]', 'issue[custom_field_values][1]' do
1455 assert_select 'option', 4
1475 assert_select 'option', 4
1456 assert_select 'option[value=MySQL]', :text => 'MySQL'
1476 assert_select 'option[value=MySQL]', :text => 'MySQL'
1457 end
1477 end
1458 end
1478 end
1459
1479
1460 def test_get_new_with_multi_custom_field
1480 def test_get_new_with_multi_custom_field
1461 field = IssueCustomField.find(1)
1481 field = IssueCustomField.find(1)
1462 field.update_attribute :multiple, true
1482 field.update_attribute :multiple, true
1463
1483
1464 @request.session[:user_id] = 2
1484 @request.session[:user_id] = 2
1465 get :new, :project_id => 1, :tracker_id => 1
1485 get :new, :project_id => 1, :tracker_id => 1
1466 assert_response :success
1486 assert_response :success
1467 assert_template 'new'
1487 assert_template 'new'
1468
1488
1469 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
1489 assert_select 'select[name=?][multiple=multiple]', 'issue[custom_field_values][1][]' do
1470 assert_select 'option', 3
1490 assert_select 'option', 3
1471 assert_select 'option[value=MySQL]', :text => 'MySQL'
1491 assert_select 'option[value=MySQL]', :text => 'MySQL'
1472 end
1492 end
1473 assert_select 'input[name=?][type=hidden][value=?]', 'issue[custom_field_values][1][]', ''
1493 assert_select 'input[name=?][type=hidden][value=?]', 'issue[custom_field_values][1][]', ''
1474 end
1494 end
1475
1495
1476 def test_get_new_with_multi_user_custom_field
1496 def test_get_new_with_multi_user_custom_field
1477 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1497 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1478 :tracker_ids => [1], :is_for_all => true)
1498 :tracker_ids => [1], :is_for_all => true)
1479
1499
1480 @request.session[:user_id] = 2
1500 @request.session[:user_id] = 2
1481 get :new, :project_id => 1, :tracker_id => 1
1501 get :new, :project_id => 1, :tracker_id => 1
1482 assert_response :success
1502 assert_response :success
1483 assert_template 'new'
1503 assert_template 'new'
1484
1504
1485 assert_select 'select[name=?][multiple=multiple]', "issue[custom_field_values][#{field.id}][]" do
1505 assert_select 'select[name=?][multiple=multiple]', "issue[custom_field_values][#{field.id}][]" do
1486 assert_select 'option', Project.find(1).users.count
1506 assert_select 'option', Project.find(1).users.count
1487 assert_select 'option[value=2]', :text => 'John Smith'
1507 assert_select 'option[value=2]', :text => 'John Smith'
1488 end
1508 end
1489 assert_select 'input[name=?][type=hidden][value=?]', "issue[custom_field_values][#{field.id}][]", ''
1509 assert_select 'input[name=?][type=hidden][value=?]', "issue[custom_field_values][#{field.id}][]", ''
1490 end
1510 end
1491
1511
1492 def test_get_new_with_date_custom_field
1512 def test_get_new_with_date_custom_field
1493 field = IssueCustomField.create!(:name => 'Date', :field_format => 'date', :tracker_ids => [1], :is_for_all => true)
1513 field = IssueCustomField.create!(:name => 'Date', :field_format => 'date', :tracker_ids => [1], :is_for_all => true)
1494
1514
1495 @request.session[:user_id] = 2
1515 @request.session[:user_id] = 2
1496 get :new, :project_id => 1, :tracker_id => 1
1516 get :new, :project_id => 1, :tracker_id => 1
1497 assert_response :success
1517 assert_response :success
1498
1518
1499 assert_select 'input[name=?]', "issue[custom_field_values][#{field.id}]"
1519 assert_select 'input[name=?]', "issue[custom_field_values][#{field.id}]"
1500 end
1520 end
1501
1521
1502 def test_get_new_with_text_custom_field
1522 def test_get_new_with_text_custom_field
1503 field = IssueCustomField.create!(:name => 'Text', :field_format => 'text', :tracker_ids => [1], :is_for_all => true)
1523 field = IssueCustomField.create!(:name => 'Text', :field_format => 'text', :tracker_ids => [1], :is_for_all => true)
1504
1524
1505 @request.session[:user_id] = 2
1525 @request.session[:user_id] = 2
1506 get :new, :project_id => 1, :tracker_id => 1
1526 get :new, :project_id => 1, :tracker_id => 1
1507 assert_response :success
1527 assert_response :success
1508
1528
1509 assert_select 'textarea[name=?]', "issue[custom_field_values][#{field.id}]"
1529 assert_select 'textarea[name=?]', "issue[custom_field_values][#{field.id}]"
1510 end
1530 end
1511
1531
1512 def test_get_new_without_default_start_date_is_creation_date
1532 def test_get_new_without_default_start_date_is_creation_date
1513 Setting.default_issue_start_date_to_creation_date = 0
1533 Setting.default_issue_start_date_to_creation_date = 0
1514
1534
1515 @request.session[:user_id] = 2
1535 @request.session[:user_id] = 2
1516 get :new, :project_id => 1, :tracker_id => 1
1536 get :new, :project_id => 1, :tracker_id => 1
1517 assert_response :success
1537 assert_response :success
1518 assert_template 'new'
1538 assert_template 'new'
1519
1539
1520 assert_select 'input[name=?]', 'issue[start_date]'
1540 assert_select 'input[name=?]', 'issue[start_date]'
1521 assert_select 'input[name=?][value]', 'issue[start_date]', 0
1541 assert_select 'input[name=?][value]', 'issue[start_date]', 0
1522 end
1542 end
1523
1543
1524 def test_get_new_with_default_start_date_is_creation_date
1544 def test_get_new_with_default_start_date_is_creation_date
1525 Setting.default_issue_start_date_to_creation_date = 1
1545 Setting.default_issue_start_date_to_creation_date = 1
1526
1546
1527 @request.session[:user_id] = 2
1547 @request.session[:user_id] = 2
1528 get :new, :project_id => 1, :tracker_id => 1
1548 get :new, :project_id => 1, :tracker_id => 1
1529 assert_response :success
1549 assert_response :success
1530 assert_template 'new'
1550 assert_template 'new'
1531
1551
1532 assert_select 'input[name=?][value=?]', 'issue[start_date]', Date.today.to_s
1552 assert_select 'input[name=?][value=?]', 'issue[start_date]', Date.today.to_s
1533 end
1553 end
1534
1554
1535 def test_get_new_form_should_allow_attachment_upload
1555 def test_get_new_form_should_allow_attachment_upload
1536 @request.session[:user_id] = 2
1556 @request.session[:user_id] = 2
1537 get :new, :project_id => 1, :tracker_id => 1
1557 get :new, :project_id => 1, :tracker_id => 1
1538
1558
1539 assert_select 'form[id=issue-form][method=post][enctype=multipart/form-data]' do
1559 assert_select 'form[id=issue-form][method=post][enctype=multipart/form-data]' do
1540 assert_select 'input[name=?][type=file]', 'attachments[1][file]'
1560 assert_select 'input[name=?][type=file]', 'attachments[1][file]'
1541 assert_select 'input[name=?][maxlength=255]', 'attachments[1][description]'
1561 assert_select 'input[name=?][maxlength=255]', 'attachments[1][description]'
1542 end
1562 end
1543 end
1563 end
1544
1564
1545 def test_get_new_should_prefill_the_form_from_params
1565 def test_get_new_should_prefill_the_form_from_params
1546 @request.session[:user_id] = 2
1566 @request.session[:user_id] = 2
1547 get :new, :project_id => 1,
1567 get :new, :project_id => 1,
1548 :issue => {:tracker_id => 3, :description => 'Prefilled', :custom_field_values => {'2' => 'Custom field value'}}
1568 :issue => {:tracker_id => 3, :description => 'Prefilled', :custom_field_values => {'2' => 'Custom field value'}}
1549
1569
1550 issue = assigns(:issue)
1570 issue = assigns(:issue)
1551 assert_equal 3, issue.tracker_id
1571 assert_equal 3, issue.tracker_id
1552 assert_equal 'Prefilled', issue.description
1572 assert_equal 'Prefilled', issue.description
1553 assert_equal 'Custom field value', issue.custom_field_value(2)
1573 assert_equal 'Custom field value', issue.custom_field_value(2)
1554
1574
1555 assert_select 'select[name=?]', 'issue[tracker_id]' do
1575 assert_select 'select[name=?]', 'issue[tracker_id]' do
1556 assert_select 'option[value=3][selected=selected]'
1576 assert_select 'option[value=3][selected=selected]'
1557 end
1577 end
1558 assert_select 'textarea[name=?]', 'issue[description]', :text => /Prefilled/
1578 assert_select 'textarea[name=?]', 'issue[description]', :text => /Prefilled/
1559 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Custom field value'
1579 assert_select 'input[name=?][value=?]', 'issue[custom_field_values][2]', 'Custom field value'
1560 end
1580 end
1561
1581
1562 def test_get_new_should_mark_required_fields
1582 def test_get_new_should_mark_required_fields
1563 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1583 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1564 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1584 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1565 WorkflowPermission.delete_all
1585 WorkflowPermission.delete_all
1566 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
1586 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'required')
1567 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
1587 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
1568 @request.session[:user_id] = 2
1588 @request.session[:user_id] = 2
1569
1589
1570 get :new, :project_id => 1
1590 get :new, :project_id => 1
1571 assert_response :success
1591 assert_response :success
1572 assert_template 'new'
1592 assert_template 'new'
1573
1593
1574 assert_select 'label[for=issue_start_date]' do
1594 assert_select 'label[for=issue_start_date]' do
1575 assert_select 'span[class=required]', 0
1595 assert_select 'span[class=required]', 0
1576 end
1596 end
1577 assert_select 'label[for=issue_due_date]' do
1597 assert_select 'label[for=issue_due_date]' do
1578 assert_select 'span[class=required]'
1598 assert_select 'span[class=required]'
1579 end
1599 end
1580 assert_select 'label[for=?]', "issue_custom_field_values_#{cf1.id}" do
1600 assert_select 'label[for=?]', "issue_custom_field_values_#{cf1.id}" do
1581 assert_select 'span[class=required]', 0
1601 assert_select 'span[class=required]', 0
1582 end
1602 end
1583 assert_select 'label[for=?]', "issue_custom_field_values_#{cf2.id}" do
1603 assert_select 'label[for=?]', "issue_custom_field_values_#{cf2.id}" do
1584 assert_select 'span[class=required]'
1604 assert_select 'span[class=required]'
1585 end
1605 end
1586 end
1606 end
1587
1607
1588 def test_get_new_should_not_display_readonly_fields
1608 def test_get_new_should_not_display_readonly_fields
1589 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1609 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1590 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1610 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1591 WorkflowPermission.delete_all
1611 WorkflowPermission.delete_all
1592 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
1612 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
1593 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
1613 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 1, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
1594 @request.session[:user_id] = 2
1614 @request.session[:user_id] = 2
1595
1615
1596 get :new, :project_id => 1
1616 get :new, :project_id => 1
1597 assert_response :success
1617 assert_response :success
1598 assert_template 'new'
1618 assert_template 'new'
1599
1619
1600 assert_select 'input[name=?]', 'issue[start_date]'
1620 assert_select 'input[name=?]', 'issue[start_date]'
1601 assert_select 'input[name=?]', 'issue[due_date]', 0
1621 assert_select 'input[name=?]', 'issue[due_date]', 0
1602 assert_select 'input[name=?]', "issue[custom_field_values][#{cf1.id}]"
1622 assert_select 'input[name=?]', "issue[custom_field_values][#{cf1.id}]"
1603 assert_select 'input[name=?]', "issue[custom_field_values][#{cf2.id}]", 0
1623 assert_select 'input[name=?]', "issue[custom_field_values][#{cf2.id}]", 0
1604 end
1624 end
1605
1625
1606 def test_get_new_without_tracker_id
1626 def test_get_new_without_tracker_id
1607 @request.session[:user_id] = 2
1627 @request.session[:user_id] = 2
1608 get :new, :project_id => 1
1628 get :new, :project_id => 1
1609 assert_response :success
1629 assert_response :success
1610 assert_template 'new'
1630 assert_template 'new'
1611
1631
1612 issue = assigns(:issue)
1632 issue = assigns(:issue)
1613 assert_not_nil issue
1633 assert_not_nil issue
1614 assert_equal Project.find(1).trackers.first, issue.tracker
1634 assert_equal Project.find(1).trackers.first, issue.tracker
1615 end
1635 end
1616
1636
1617 def test_get_new_with_no_default_status_should_display_an_error
1637 def test_get_new_with_no_default_status_should_display_an_error
1618 @request.session[:user_id] = 2
1638 @request.session[:user_id] = 2
1619 IssueStatus.delete_all
1639 IssueStatus.delete_all
1620
1640
1621 get :new, :project_id => 1
1641 get :new, :project_id => 1
1622 assert_response 500
1642 assert_response 500
1623 assert_error_tag :content => /No default issue/
1643 assert_error_tag :content => /No default issue/
1624 end
1644 end
1625
1645
1626 def test_get_new_with_no_tracker_should_display_an_error
1646 def test_get_new_with_no_tracker_should_display_an_error
1627 @request.session[:user_id] = 2
1647 @request.session[:user_id] = 2
1628 Tracker.delete_all
1648 Tracker.delete_all
1629
1649
1630 get :new, :project_id => 1
1650 get :new, :project_id => 1
1631 assert_response 500
1651 assert_response 500
1632 assert_error_tag :content => /No tracker/
1652 assert_error_tag :content => /No tracker/
1633 end
1653 end
1634
1654
1635 def test_update_new_form
1655 def test_update_new_form
1636 @request.session[:user_id] = 2
1656 @request.session[:user_id] = 2
1637 xhr :post, :new, :project_id => 1,
1657 xhr :post, :new, :project_id => 1,
1638 :issue => {:tracker_id => 2,
1658 :issue => {:tracker_id => 2,
1639 :subject => 'This is the test_new issue',
1659 :subject => 'This is the test_new issue',
1640 :description => 'This is the description',
1660 :description => 'This is the description',
1641 :priority_id => 5}
1661 :priority_id => 5}
1642 assert_response :success
1662 assert_response :success
1643 assert_template 'update_form'
1663 assert_template 'update_form'
1644 assert_template 'form'
1664 assert_template 'form'
1645 assert_equal 'text/javascript', response.content_type
1665 assert_equal 'text/javascript', response.content_type
1646
1666
1647 issue = assigns(:issue)
1667 issue = assigns(:issue)
1648 assert_kind_of Issue, issue
1668 assert_kind_of Issue, issue
1649 assert_equal 1, issue.project_id
1669 assert_equal 1, issue.project_id
1650 assert_equal 2, issue.tracker_id
1670 assert_equal 2, issue.tracker_id
1651 assert_equal 'This is the test_new issue', issue.subject
1671 assert_equal 'This is the test_new issue', issue.subject
1652 end
1672 end
1653
1673
1654 def test_update_new_form_should_propose_transitions_based_on_initial_status
1674 def test_update_new_form_should_propose_transitions_based_on_initial_status
1655 @request.session[:user_id] = 2
1675 @request.session[:user_id] = 2
1656 WorkflowTransition.delete_all
1676 WorkflowTransition.delete_all
1657 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2)
1677 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 2)
1658 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 5)
1678 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 5)
1659 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4)
1679 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 5, :new_status_id => 4)
1660
1680
1661 xhr :post, :new, :project_id => 1,
1681 xhr :post, :new, :project_id => 1,
1662 :issue => {:tracker_id => 1,
1682 :issue => {:tracker_id => 1,
1663 :status_id => 5,
1683 :status_id => 5,
1664 :subject => 'This is an issue'}
1684 :subject => 'This is an issue'}
1665
1685
1666 assert_equal 5, assigns(:issue).status_id
1686 assert_equal 5, assigns(:issue).status_id
1667 assert_equal [1,2,5], assigns(:allowed_statuses).map(&:id).sort
1687 assert_equal [1,2,5], assigns(:allowed_statuses).map(&:id).sort
1668 end
1688 end
1669
1689
1670 def test_post_create
1690 def test_post_create
1671 @request.session[:user_id] = 2
1691 @request.session[:user_id] = 2
1672 assert_difference 'Issue.count' do
1692 assert_difference 'Issue.count' do
1673 post :create, :project_id => 1,
1693 post :create, :project_id => 1,
1674 :issue => {:tracker_id => 3,
1694 :issue => {:tracker_id => 3,
1675 :status_id => 2,
1695 :status_id => 2,
1676 :subject => 'This is the test_new issue',
1696 :subject => 'This is the test_new issue',
1677 :description => 'This is the description',
1697 :description => 'This is the description',
1678 :priority_id => 5,
1698 :priority_id => 5,
1679 :start_date => '2010-11-07',
1699 :start_date => '2010-11-07',
1680 :estimated_hours => '',
1700 :estimated_hours => '',
1681 :custom_field_values => {'2' => 'Value for field 2'}}
1701 :custom_field_values => {'2' => 'Value for field 2'}}
1682 end
1702 end
1683 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1703 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1684
1704
1685 issue = Issue.find_by_subject('This is the test_new issue')
1705 issue = Issue.find_by_subject('This is the test_new issue')
1686 assert_not_nil issue
1706 assert_not_nil issue
1687 assert_equal 2, issue.author_id
1707 assert_equal 2, issue.author_id
1688 assert_equal 3, issue.tracker_id
1708 assert_equal 3, issue.tracker_id
1689 assert_equal 2, issue.status_id
1709 assert_equal 2, issue.status_id
1690 assert_equal Date.parse('2010-11-07'), issue.start_date
1710 assert_equal Date.parse('2010-11-07'), issue.start_date
1691 assert_nil issue.estimated_hours
1711 assert_nil issue.estimated_hours
1692 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
1712 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
1693 assert_not_nil v
1713 assert_not_nil v
1694 assert_equal 'Value for field 2', v.value
1714 assert_equal 'Value for field 2', v.value
1695 end
1715 end
1696
1716
1697 def test_post_new_with_group_assignment
1717 def test_post_new_with_group_assignment
1698 group = Group.find(11)
1718 group = Group.find(11)
1699 project = Project.find(1)
1719 project = Project.find(1)
1700 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
1720 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
1701
1721
1702 with_settings :issue_group_assignment => '1' do
1722 with_settings :issue_group_assignment => '1' do
1703 @request.session[:user_id] = 2
1723 @request.session[:user_id] = 2
1704 assert_difference 'Issue.count' do
1724 assert_difference 'Issue.count' do
1705 post :create, :project_id => project.id,
1725 post :create, :project_id => project.id,
1706 :issue => {:tracker_id => 3,
1726 :issue => {:tracker_id => 3,
1707 :status_id => 1,
1727 :status_id => 1,
1708 :subject => 'This is the test_new_with_group_assignment issue',
1728 :subject => 'This is the test_new_with_group_assignment issue',
1709 :assigned_to_id => group.id}
1729 :assigned_to_id => group.id}
1710 end
1730 end
1711 end
1731 end
1712 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1732 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1713
1733
1714 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
1734 issue = Issue.find_by_subject('This is the test_new_with_group_assignment issue')
1715 assert_not_nil issue
1735 assert_not_nil issue
1716 assert_equal group, issue.assigned_to
1736 assert_equal group, issue.assigned_to
1717 end
1737 end
1718
1738
1719 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
1739 def test_post_create_without_start_date_and_default_start_date_is_not_creation_date
1720 Setting.default_issue_start_date_to_creation_date = 0
1740 Setting.default_issue_start_date_to_creation_date = 0
1721
1741
1722 @request.session[:user_id] = 2
1742 @request.session[:user_id] = 2
1723 assert_difference 'Issue.count' do
1743 assert_difference 'Issue.count' do
1724 post :create, :project_id => 1,
1744 post :create, :project_id => 1,
1725 :issue => {:tracker_id => 3,
1745 :issue => {:tracker_id => 3,
1726 :status_id => 2,
1746 :status_id => 2,
1727 :subject => 'This is the test_new issue',
1747 :subject => 'This is the test_new issue',
1728 :description => 'This is the description',
1748 :description => 'This is the description',
1729 :priority_id => 5,
1749 :priority_id => 5,
1730 :estimated_hours => '',
1750 :estimated_hours => '',
1731 :custom_field_values => {'2' => 'Value for field 2'}}
1751 :custom_field_values => {'2' => 'Value for field 2'}}
1732 end
1752 end
1733 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1753 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1734
1754
1735 issue = Issue.find_by_subject('This is the test_new issue')
1755 issue = Issue.find_by_subject('This is the test_new issue')
1736 assert_not_nil issue
1756 assert_not_nil issue
1737 assert_nil issue.start_date
1757 assert_nil issue.start_date
1738 end
1758 end
1739
1759
1740 def test_post_create_without_start_date_and_default_start_date_is_creation_date
1760 def test_post_create_without_start_date_and_default_start_date_is_creation_date
1741 Setting.default_issue_start_date_to_creation_date = 1
1761 Setting.default_issue_start_date_to_creation_date = 1
1742
1762
1743 @request.session[:user_id] = 2
1763 @request.session[:user_id] = 2
1744 assert_difference 'Issue.count' do
1764 assert_difference 'Issue.count' do
1745 post :create, :project_id => 1,
1765 post :create, :project_id => 1,
1746 :issue => {:tracker_id => 3,
1766 :issue => {:tracker_id => 3,
1747 :status_id => 2,
1767 :status_id => 2,
1748 :subject => 'This is the test_new issue',
1768 :subject => 'This is the test_new issue',
1749 :description => 'This is the description',
1769 :description => 'This is the description',
1750 :priority_id => 5,
1770 :priority_id => 5,
1751 :estimated_hours => '',
1771 :estimated_hours => '',
1752 :custom_field_values => {'2' => 'Value for field 2'}}
1772 :custom_field_values => {'2' => 'Value for field 2'}}
1753 end
1773 end
1754 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1774 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1755
1775
1756 issue = Issue.find_by_subject('This is the test_new issue')
1776 issue = Issue.find_by_subject('This is the test_new issue')
1757 assert_not_nil issue
1777 assert_not_nil issue
1758 assert_equal Date.today, issue.start_date
1778 assert_equal Date.today, issue.start_date
1759 end
1779 end
1760
1780
1761 def test_post_create_and_continue
1781 def test_post_create_and_continue
1762 @request.session[:user_id] = 2
1782 @request.session[:user_id] = 2
1763 assert_difference 'Issue.count' do
1783 assert_difference 'Issue.count' do
1764 post :create, :project_id => 1,
1784 post :create, :project_id => 1,
1765 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
1785 :issue => {:tracker_id => 3, :subject => 'This is first issue', :priority_id => 5},
1766 :continue => ''
1786 :continue => ''
1767 end
1787 end
1768
1788
1769 issue = Issue.first(:order => 'id DESC')
1789 issue = Issue.first(:order => 'id DESC')
1770 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
1790 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook', :issue => {:tracker_id => 3}
1771 assert_not_nil flash[:notice], "flash was not set"
1791 assert_not_nil flash[:notice], "flash was not set"
1772 assert_include %|<a href="/issues/#{issue.id}" title="This is first issue">##{issue.id}</a>|, flash[:notice], "issue link not found in the flash message"
1792 assert_include %|<a href="/issues/#{issue.id}" title="This is first issue">##{issue.id}</a>|, flash[:notice], "issue link not found in the flash message"
1773 end
1793 end
1774
1794
1775 def test_post_create_without_custom_fields_param
1795 def test_post_create_without_custom_fields_param
1776 @request.session[:user_id] = 2
1796 @request.session[:user_id] = 2
1777 assert_difference 'Issue.count' do
1797 assert_difference 'Issue.count' do
1778 post :create, :project_id => 1,
1798 post :create, :project_id => 1,
1779 :issue => {:tracker_id => 1,
1799 :issue => {:tracker_id => 1,
1780 :subject => 'This is the test_new issue',
1800 :subject => 'This is the test_new issue',
1781 :description => 'This is the description',
1801 :description => 'This is the description',
1782 :priority_id => 5}
1802 :priority_id => 5}
1783 end
1803 end
1784 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1804 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
1785 end
1805 end
1786
1806
1787 def test_post_create_with_multi_custom_field
1807 def test_post_create_with_multi_custom_field
1788 field = IssueCustomField.find_by_name('Database')
1808 field = IssueCustomField.find_by_name('Database')
1789 field.update_attribute(:multiple, true)
1809 field.update_attribute(:multiple, true)
1790
1810
1791 @request.session[:user_id] = 2
1811 @request.session[:user_id] = 2
1792 assert_difference 'Issue.count' do
1812 assert_difference 'Issue.count' do
1793 post :create, :project_id => 1,
1813 post :create, :project_id => 1,
1794 :issue => {:tracker_id => 1,
1814 :issue => {:tracker_id => 1,
1795 :subject => 'This is the test_new issue',
1815 :subject => 'This is the test_new issue',
1796 :description => 'This is the description',
1816 :description => 'This is the description',
1797 :priority_id => 5,
1817 :priority_id => 5,
1798 :custom_field_values => {'1' => ['', 'MySQL', 'Oracle']}}
1818 :custom_field_values => {'1' => ['', 'MySQL', 'Oracle']}}
1799 end
1819 end
1800 assert_response 302
1820 assert_response 302
1801 issue = Issue.first(:order => 'id DESC')
1821 issue = Issue.first(:order => 'id DESC')
1802 assert_equal ['MySQL', 'Oracle'], issue.custom_field_value(1).sort
1822 assert_equal ['MySQL', 'Oracle'], issue.custom_field_value(1).sort
1803 end
1823 end
1804
1824
1805 def test_post_create_with_empty_multi_custom_field
1825 def test_post_create_with_empty_multi_custom_field
1806 field = IssueCustomField.find_by_name('Database')
1826 field = IssueCustomField.find_by_name('Database')
1807 field.update_attribute(:multiple, true)
1827 field.update_attribute(:multiple, true)
1808
1828
1809 @request.session[:user_id] = 2
1829 @request.session[:user_id] = 2
1810 assert_difference 'Issue.count' do
1830 assert_difference 'Issue.count' do
1811 post :create, :project_id => 1,
1831 post :create, :project_id => 1,
1812 :issue => {:tracker_id => 1,
1832 :issue => {:tracker_id => 1,
1813 :subject => 'This is the test_new issue',
1833 :subject => 'This is the test_new issue',
1814 :description => 'This is the description',
1834 :description => 'This is the description',
1815 :priority_id => 5,
1835 :priority_id => 5,
1816 :custom_field_values => {'1' => ['']}}
1836 :custom_field_values => {'1' => ['']}}
1817 end
1837 end
1818 assert_response 302
1838 assert_response 302
1819 issue = Issue.first(:order => 'id DESC')
1839 issue = Issue.first(:order => 'id DESC')
1820 assert_equal [''], issue.custom_field_value(1).sort
1840 assert_equal [''], issue.custom_field_value(1).sort
1821 end
1841 end
1822
1842
1823 def test_post_create_with_multi_user_custom_field
1843 def test_post_create_with_multi_user_custom_field
1824 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1844 field = IssueCustomField.create!(:name => 'Multi user', :field_format => 'user', :multiple => true,
1825 :tracker_ids => [1], :is_for_all => true)
1845 :tracker_ids => [1], :is_for_all => true)
1826
1846
1827 @request.session[:user_id] = 2
1847 @request.session[:user_id] = 2
1828 assert_difference 'Issue.count' do
1848 assert_difference 'Issue.count' do
1829 post :create, :project_id => 1,
1849 post :create, :project_id => 1,
1830 :issue => {:tracker_id => 1,
1850 :issue => {:tracker_id => 1,
1831 :subject => 'This is the test_new issue',
1851 :subject => 'This is the test_new issue',
1832 :description => 'This is the description',
1852 :description => 'This is the description',
1833 :priority_id => 5,
1853 :priority_id => 5,
1834 :custom_field_values => {field.id.to_s => ['', '2', '3']}}
1854 :custom_field_values => {field.id.to_s => ['', '2', '3']}}
1835 end
1855 end
1836 assert_response 302
1856 assert_response 302
1837 issue = Issue.first(:order => 'id DESC')
1857 issue = Issue.first(:order => 'id DESC')
1838 assert_equal ['2', '3'], issue.custom_field_value(field).sort
1858 assert_equal ['2', '3'], issue.custom_field_value(field).sort
1839 end
1859 end
1840
1860
1841 def test_post_create_with_required_custom_field_and_without_custom_fields_param
1861 def test_post_create_with_required_custom_field_and_without_custom_fields_param
1842 field = IssueCustomField.find_by_name('Database')
1862 field = IssueCustomField.find_by_name('Database')
1843 field.update_attribute(:is_required, true)
1863 field.update_attribute(:is_required, true)
1844
1864
1845 @request.session[:user_id] = 2
1865 @request.session[:user_id] = 2
1846 assert_no_difference 'Issue.count' do
1866 assert_no_difference 'Issue.count' do
1847 post :create, :project_id => 1,
1867 post :create, :project_id => 1,
1848 :issue => {:tracker_id => 1,
1868 :issue => {:tracker_id => 1,
1849 :subject => 'This is the test_new issue',
1869 :subject => 'This is the test_new issue',
1850 :description => 'This is the description',
1870 :description => 'This is the description',
1851 :priority_id => 5}
1871 :priority_id => 5}
1852 end
1872 end
1853 assert_response :success
1873 assert_response :success
1854 assert_template 'new'
1874 assert_template 'new'
1855 issue = assigns(:issue)
1875 issue = assigns(:issue)
1856 assert_not_nil issue
1876 assert_not_nil issue
1857 assert_error_tag :content => /Database can&#x27;t be blank/
1877 assert_error_tag :content => /Database can&#x27;t be blank/
1858 end
1878 end
1859
1879
1860 def test_create_should_validate_required_fields
1880 def test_create_should_validate_required_fields
1861 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1881 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1862 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1882 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1863 WorkflowPermission.delete_all
1883 WorkflowPermission.delete_all
1864 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'required')
1884 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'required')
1865 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
1885 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'required')
1866 @request.session[:user_id] = 2
1886 @request.session[:user_id] = 2
1867
1887
1868 assert_no_difference 'Issue.count' do
1888 assert_no_difference 'Issue.count' do
1869 post :create, :project_id => 1, :issue => {
1889 post :create, :project_id => 1, :issue => {
1870 :tracker_id => 2,
1890 :tracker_id => 2,
1871 :status_id => 1,
1891 :status_id => 1,
1872 :subject => 'Test',
1892 :subject => 'Test',
1873 :start_date => '',
1893 :start_date => '',
1874 :due_date => '',
1894 :due_date => '',
1875 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ''}
1895 :custom_field_values => {cf1.id.to_s => '', cf2.id.to_s => ''}
1876 }
1896 }
1877 assert_response :success
1897 assert_response :success
1878 assert_template 'new'
1898 assert_template 'new'
1879 end
1899 end
1880
1900
1881 assert_error_tag :content => /Due date can&#x27;t be blank/i
1901 assert_error_tag :content => /Due date can&#x27;t be blank/i
1882 assert_error_tag :content => /Bar can&#x27;t be blank/i
1902 assert_error_tag :content => /Bar can&#x27;t be blank/i
1883 end
1903 end
1884
1904
1885 def test_create_should_ignore_readonly_fields
1905 def test_create_should_ignore_readonly_fields
1886 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1906 cf1 = IssueCustomField.create!(:name => 'Foo', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1887 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1907 cf2 = IssueCustomField.create!(:name => 'Bar', :field_format => 'string', :is_for_all => true, :tracker_ids => [1, 2])
1888 WorkflowPermission.delete_all
1908 WorkflowPermission.delete_all
1889 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
1909 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => 'due_date', :rule => 'readonly')
1890 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
1910 WorkflowPermission.create!(:old_status_id => 1, :tracker_id => 2, :role_id => 1, :field_name => cf2.id.to_s, :rule => 'readonly')
1891 @request.session[:user_id] = 2
1911 @request.session[:user_id] = 2
1892
1912
1893 assert_difference 'Issue.count' do
1913 assert_difference 'Issue.count' do
1894 post :create, :project_id => 1, :issue => {
1914 post :create, :project_id => 1, :issue => {
1895 :tracker_id => 2,
1915 :tracker_id => 2,
1896 :status_id => 1,
1916 :status_id => 1,
1897 :subject => 'Test',
1917 :subject => 'Test',
1898 :start_date => '2012-07-14',
1918 :start_date => '2012-07-14',
1899 :due_date => '2012-07-16',
1919 :due_date => '2012-07-16',
1900 :custom_field_values => {cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'}
1920 :custom_field_values => {cf1.id.to_s => 'value1', cf2.id.to_s => 'value2'}
1901 }
1921 }
1902 assert_response 302
1922 assert_response 302
1903 end
1923 end
1904
1924
1905 issue = Issue.first(:order => 'id DESC')
1925 issue = Issue.first(:order => 'id DESC')
1906 assert_equal Date.parse('2012-07-14'), issue.start_date
1926 assert_equal Date.parse('2012-07-14'), issue.start_date
1907 assert_nil issue.due_date
1927 assert_nil issue.due_date
1908 assert_equal 'value1', issue.custom_field_value(cf1)
1928 assert_equal 'value1', issue.custom_field_value(cf1)
1909 assert_nil issue.custom_field_value(cf2)
1929 assert_nil issue.custom_field_value(cf2)
1910 end
1930 end
1911
1931
1912 def test_post_create_with_watchers
1932 def test_post_create_with_watchers
1913 @request.session[:user_id] = 2
1933 @request.session[:user_id] = 2
1914 ActionMailer::Base.deliveries.clear
1934 ActionMailer::Base.deliveries.clear
1915
1935
1916 assert_difference 'Watcher.count', 2 do
1936 assert_difference 'Watcher.count', 2 do
1917 post :create, :project_id => 1,
1937 post :create, :project_id => 1,
1918 :issue => {:tracker_id => 1,
1938 :issue => {:tracker_id => 1,
1919 :subject => 'This is a new issue with watchers',
1939 :subject => 'This is a new issue with watchers',
1920 :description => 'This is the description',
1940 :description => 'This is the description',
1921 :priority_id => 5,
1941 :priority_id => 5,
1922 :watcher_user_ids => ['2', '3']}
1942 :watcher_user_ids => ['2', '3']}
1923 end
1943 end
1924 issue = Issue.find_by_subject('This is a new issue with watchers')
1944 issue = Issue.find_by_subject('This is a new issue with watchers')
1925 assert_not_nil issue
1945 assert_not_nil issue
1926 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1946 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1927
1947
1928 # Watchers added
1948 # Watchers added
1929 assert_equal [2, 3], issue.watcher_user_ids.sort
1949 assert_equal [2, 3], issue.watcher_user_ids.sort
1930 assert issue.watched_by?(User.find(3))
1950 assert issue.watched_by?(User.find(3))
1931 # Watchers notified
1951 # Watchers notified
1932 mail = ActionMailer::Base.deliveries.last
1952 mail = ActionMailer::Base.deliveries.last
1933 assert_not_nil mail
1953 assert_not_nil mail
1934 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
1954 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
1935 end
1955 end
1936
1956
1937 def test_post_create_subissue
1957 def test_post_create_subissue
1938 @request.session[:user_id] = 2
1958 @request.session[:user_id] = 2
1939
1959
1940 assert_difference 'Issue.count' do
1960 assert_difference 'Issue.count' do
1941 post :create, :project_id => 1,
1961 post :create, :project_id => 1,
1942 :issue => {:tracker_id => 1,
1962 :issue => {:tracker_id => 1,
1943 :subject => 'This is a child issue',
1963 :subject => 'This is a child issue',
1944 :parent_issue_id => '2'}
1964 :parent_issue_id => '2'}
1945 assert_response 302
1965 assert_response 302
1946 end
1966 end
1947 issue = Issue.order('id DESC').first
1967 issue = Issue.order('id DESC').first
1948 assert_equal Issue.find(2), issue.parent
1968 assert_equal Issue.find(2), issue.parent
1949 end
1969 end
1950
1970
1951 def test_post_create_subissue_with_sharp_parent_id
1971 def test_post_create_subissue_with_sharp_parent_id
1952 @request.session[:user_id] = 2
1972 @request.session[:user_id] = 2
1953
1973
1954 assert_difference 'Issue.count' do
1974 assert_difference 'Issue.count' do
1955 post :create, :project_id => 1,
1975 post :create, :project_id => 1,
1956 :issue => {:tracker_id => 1,
1976 :issue => {:tracker_id => 1,
1957 :subject => 'This is a child issue',
1977 :subject => 'This is a child issue',
1958 :parent_issue_id => '#2'}
1978 :parent_issue_id => '#2'}
1959 assert_response 302
1979 assert_response 302
1960 end
1980 end
1961 issue = Issue.order('id DESC').first
1981 issue = Issue.order('id DESC').first
1962 assert_equal Issue.find(2), issue.parent
1982 assert_equal Issue.find(2), issue.parent
1963 end
1983 end
1964
1984
1965 def test_post_create_subissue_with_non_visible_parent_id_should_not_validate
1985 def test_post_create_subissue_with_non_visible_parent_id_should_not_validate
1966 @request.session[:user_id] = 2
1986 @request.session[:user_id] = 2
1967
1987
1968 assert_no_difference 'Issue.count' do
1988 assert_no_difference 'Issue.count' do
1969 post :create, :project_id => 1,
1989 post :create, :project_id => 1,
1970 :issue => {:tracker_id => 1,
1990 :issue => {:tracker_id => 1,
1971 :subject => 'This is a child issue',
1991 :subject => 'This is a child issue',
1972 :parent_issue_id => '4'}
1992 :parent_issue_id => '4'}
1973
1993
1974 assert_response :success
1994 assert_response :success
1975 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '4'
1995 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '4'
1976 assert_error_tag :content => /Parent task is invalid/i
1996 assert_error_tag :content => /Parent task is invalid/i
1977 end
1997 end
1978 end
1998 end
1979
1999
1980 def test_post_create_subissue_with_non_numeric_parent_id_should_not_validate
2000 def test_post_create_subissue_with_non_numeric_parent_id_should_not_validate
1981 @request.session[:user_id] = 2
2001 @request.session[:user_id] = 2
1982
2002
1983 assert_no_difference 'Issue.count' do
2003 assert_no_difference 'Issue.count' do
1984 post :create, :project_id => 1,
2004 post :create, :project_id => 1,
1985 :issue => {:tracker_id => 1,
2005 :issue => {:tracker_id => 1,
1986 :subject => 'This is a child issue',
2006 :subject => 'This is a child issue',
1987 :parent_issue_id => '01ABC'}
2007 :parent_issue_id => '01ABC'}
1988
2008
1989 assert_response :success
2009 assert_response :success
1990 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '01ABC'
2010 assert_select 'input[name=?][value=?]', 'issue[parent_issue_id]', '01ABC'
1991 assert_error_tag :content => /Parent task is invalid/i
2011 assert_error_tag :content => /Parent task is invalid/i
1992 end
2012 end
1993 end
2013 end
1994
2014
1995 def test_post_create_private
2015 def test_post_create_private
1996 @request.session[:user_id] = 2
2016 @request.session[:user_id] = 2
1997
2017
1998 assert_difference 'Issue.count' do
2018 assert_difference 'Issue.count' do
1999 post :create, :project_id => 1,
2019 post :create, :project_id => 1,
2000 :issue => {:tracker_id => 1,
2020 :issue => {:tracker_id => 1,
2001 :subject => 'This is a private issue',
2021 :subject => 'This is a private issue',
2002 :is_private => '1'}
2022 :is_private => '1'}
2003 end
2023 end
2004 issue = Issue.first(:order => 'id DESC')
2024 issue = Issue.first(:order => 'id DESC')
2005 assert issue.is_private?
2025 assert issue.is_private?
2006 end
2026 end
2007
2027
2008 def test_post_create_private_with_set_own_issues_private_permission
2028 def test_post_create_private_with_set_own_issues_private_permission
2009 role = Role.find(1)
2029 role = Role.find(1)
2010 role.remove_permission! :set_issues_private
2030 role.remove_permission! :set_issues_private
2011 role.add_permission! :set_own_issues_private
2031 role.add_permission! :set_own_issues_private
2012
2032
2013 @request.session[:user_id] = 2
2033 @request.session[:user_id] = 2
2014
2034
2015 assert_difference 'Issue.count' do
2035 assert_difference 'Issue.count' do
2016 post :create, :project_id => 1,
2036 post :create, :project_id => 1,
2017 :issue => {:tracker_id => 1,
2037 :issue => {:tracker_id => 1,
2018 :subject => 'This is a private issue',
2038 :subject => 'This is a private issue',
2019 :is_private => '1'}
2039 :is_private => '1'}
2020 end
2040 end
2021 issue = Issue.first(:order => 'id DESC')
2041 issue = Issue.first(:order => 'id DESC')
2022 assert issue.is_private?
2042 assert issue.is_private?
2023 end
2043 end
2024
2044
2025 def test_post_create_should_send_a_notification
2045 def test_post_create_should_send_a_notification
2026 ActionMailer::Base.deliveries.clear
2046 ActionMailer::Base.deliveries.clear
2027 @request.session[:user_id] = 2
2047 @request.session[:user_id] = 2
2028 assert_difference 'Issue.count' do
2048 assert_difference 'Issue.count' do
2029 post :create, :project_id => 1,
2049 post :create, :project_id => 1,
2030 :issue => {:tracker_id => 3,
2050 :issue => {:tracker_id => 3,
2031 :subject => 'This is the test_new issue',
2051 :subject => 'This is the test_new issue',
2032 :description => 'This is the description',
2052 :description => 'This is the description',
2033 :priority_id => 5,
2053 :priority_id => 5,
2034 :estimated_hours => '',
2054 :estimated_hours => '',
2035 :custom_field_values => {'2' => 'Value for field 2'}}
2055 :custom_field_values => {'2' => 'Value for field 2'}}
2036 end
2056 end
2037 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2057 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
2038
2058
2039 assert_equal 1, ActionMailer::Base.deliveries.size
2059 assert_equal 1, ActionMailer::Base.deliveries.size
2040 end
2060 end
2041
2061
2042 def test_post_create_should_preserve_fields_values_on_validation_failure
2062 def test_post_create_should_preserve_fields_values_on_validation_failure
2043 @request.session[:user_id] = 2
2063 @request.session[:user_id] = 2
2044 post :create, :project_id => 1,
2064 post :create, :project_id => 1,
2045 :issue => {:tracker_id => 1,
2065 :issue => {:tracker_id => 1,
2046 # empty subject
2066 # empty subject
2047 :subject => '',
2067 :subject => '',
2048 :description => 'This is a description',
2068 :description => 'This is a description',
2049 :priority_id => 6,
2069 :priority_id => 6,
2050 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
2070 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
2051 assert_response :success
2071 assert_response :success
2052 assert_template 'new'
2072 assert_template 'new'
2053
2073
2054 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
2074 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
2055 :content => "\nThis is a description"
2075 :content => "\nThis is a description"
2056 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
2076 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
2057 :child => { :tag => 'option', :attributes => { :selected => 'selected',
2077 :child => { :tag => 'option', :attributes => { :selected => 'selected',
2058 :value => '6' },
2078 :value => '6' },
2059 :content => 'High' }
2079 :content => 'High' }
2060 # Custom fields
2080 # Custom fields
2061 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
2081 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
2062 :child => { :tag => 'option', :attributes => { :selected => 'selected',
2082 :child => { :tag => 'option', :attributes => { :selected => 'selected',
2063 :value => 'Oracle' },
2083 :value => 'Oracle' },
2064 :content => 'Oracle' }
2084 :content => 'Oracle' }
2065 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
2085 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
2066 :value => 'Value for field 2'}
2086 :value => 'Value for field 2'}
2067 end
2087 end
2068
2088
2069 def test_post_create_with_failure_should_preserve_watchers
2089 def test_post_create_with_failure_should_preserve_watchers
2070 assert !User.find(8).member_of?(Project.find(1))
2090 assert !User.find(8).member_of?(Project.find(1))
2071
2091
2072 @request.session[:user_id] = 2
2092 @request.session[:user_id] = 2
2073 post :create, :project_id => 1,
2093 post :create, :project_id => 1,
2074 :issue => {:tracker_id => 1,
2094 :issue => {:tracker_id => 1,
2075 :watcher_user_ids => ['3', '8']}
2095 :watcher_user_ids => ['3', '8']}
2076 assert_response :success
2096 assert_response :success
2077 assert_template 'new'
2097 assert_template 'new'
2078
2098
2079 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]', :value => '2', :checked => nil}
2099 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]', :value => '2', :checked => nil}
2080 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]', :value => '3', :checked => 'checked'}
2100 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]', :value => '3', :checked => 'checked'}
2081 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]', :value => '8', :checked => 'checked'}
2101 assert_tag 'input', :attributes => {:name => 'issue[watcher_user_ids][]', :value => '8', :checked => 'checked'}
2082 end
2102 end
2083
2103
2084 def test_post_create_should_ignore_non_safe_attributes
2104 def test_post_create_should_ignore_non_safe_attributes
2085 @request.session[:user_id] = 2
2105 @request.session[:user_id] = 2
2086 assert_nothing_raised do
2106 assert_nothing_raised do
2087 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
2107 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
2088 end
2108 end
2089 end
2109 end
2090
2110
2091 def test_post_create_with_attachment
2111 def test_post_create_with_attachment
2092 set_tmp_attachments_directory
2112 set_tmp_attachments_directory
2093 @request.session[:user_id] = 2
2113 @request.session[:user_id] = 2
2094
2114
2095 assert_difference 'Issue.count' do
2115 assert_difference 'Issue.count' do
2096 assert_difference 'Attachment.count' do
2116 assert_difference 'Attachment.count' do
2097 post :create, :project_id => 1,
2117 post :create, :project_id => 1,
2098 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2118 :issue => { :tracker_id => '1', :subject => 'With attachment' },
2099 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2119 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2100 end
2120 end
2101 end
2121 end
2102
2122
2103 issue = Issue.first(:order => 'id DESC')
2123 issue = Issue.first(:order => 'id DESC')
2104 attachment = Attachment.first(:order => 'id DESC')
2124 attachment = Attachment.first(:order => 'id DESC')
2105
2125
2106 assert_equal issue, attachment.container
2126 assert_equal issue, attachment.container
2107 assert_equal 2, attachment.author_id
2127 assert_equal 2, attachment.author_id
2108 assert_equal 'testfile.txt', attachment.filename
2128 assert_equal 'testfile.txt', attachment.filename
2109 assert_equal 'text/plain', attachment.content_type
2129 assert_equal 'text/plain', attachment.content_type
2110 assert_equal 'test file', attachment.description
2130 assert_equal 'test file', attachment.description
2111 assert_equal 59, attachment.filesize
2131 assert_equal 59, attachment.filesize
2112 assert File.exists?(attachment.diskfile)
2132 assert File.exists?(attachment.diskfile)
2113 assert_equal 59, File.size(attachment.diskfile)
2133 assert_equal 59, File.size(attachment.diskfile)
2114 end
2134 end
2115
2135
2116 def test_post_create_with_failure_should_save_attachments
2136 def test_post_create_with_failure_should_save_attachments
2117 set_tmp_attachments_directory
2137 set_tmp_attachments_directory
2118 @request.session[:user_id] = 2
2138 @request.session[:user_id] = 2
2119
2139
2120 assert_no_difference 'Issue.count' do
2140 assert_no_difference 'Issue.count' do
2121 assert_difference 'Attachment.count' do
2141 assert_difference 'Attachment.count' do
2122 post :create, :project_id => 1,
2142 post :create, :project_id => 1,
2123 :issue => { :tracker_id => '1', :subject => '' },
2143 :issue => { :tracker_id => '1', :subject => '' },
2124 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2144 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2125 assert_response :success
2145 assert_response :success
2126 assert_template 'new'
2146 assert_template 'new'
2127 end
2147 end
2128 end
2148 end
2129
2149
2130 attachment = Attachment.first(:order => 'id DESC')
2150 attachment = Attachment.first(:order => 'id DESC')
2131 assert_equal 'testfile.txt', attachment.filename
2151 assert_equal 'testfile.txt', attachment.filename
2132 assert File.exists?(attachment.diskfile)
2152 assert File.exists?(attachment.diskfile)
2133 assert_nil attachment.container
2153 assert_nil attachment.container
2134
2154
2135 assert_tag 'input', :attributes => {:name => 'attachments[p0][token]', :value => attachment.token}
2155 assert_tag 'input', :attributes => {:name => 'attachments[p0][token]', :value => attachment.token}
2136 assert_tag 'span', :content => /testfile.txt/
2156 assert_tag 'span', :content => /testfile.txt/
2137 end
2157 end
2138
2158
2139 def test_post_create_with_failure_should_keep_saved_attachments
2159 def test_post_create_with_failure_should_keep_saved_attachments
2140 set_tmp_attachments_directory
2160 set_tmp_attachments_directory
2141 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2161 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2142 @request.session[:user_id] = 2
2162 @request.session[:user_id] = 2
2143
2163
2144 assert_no_difference 'Issue.count' do
2164 assert_no_difference 'Issue.count' do
2145 assert_no_difference 'Attachment.count' do
2165 assert_no_difference 'Attachment.count' do
2146 post :create, :project_id => 1,
2166 post :create, :project_id => 1,
2147 :issue => { :tracker_id => '1', :subject => '' },
2167 :issue => { :tracker_id => '1', :subject => '' },
2148 :attachments => {'p0' => {'token' => attachment.token}}
2168 :attachments => {'p0' => {'token' => attachment.token}}
2149 assert_response :success
2169 assert_response :success
2150 assert_template 'new'
2170 assert_template 'new'
2151 end
2171 end
2152 end
2172 end
2153
2173
2154 assert_tag 'input', :attributes => {:name => 'attachments[p0][token]', :value => attachment.token}
2174 assert_tag 'input', :attributes => {:name => 'attachments[p0][token]', :value => attachment.token}
2155 assert_tag 'span', :content => /testfile.txt/
2175 assert_tag 'span', :content => /testfile.txt/
2156 end
2176 end
2157
2177
2158 def test_post_create_should_attach_saved_attachments
2178 def test_post_create_should_attach_saved_attachments
2159 set_tmp_attachments_directory
2179 set_tmp_attachments_directory
2160 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2180 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2161 @request.session[:user_id] = 2
2181 @request.session[:user_id] = 2
2162
2182
2163 assert_difference 'Issue.count' do
2183 assert_difference 'Issue.count' do
2164 assert_no_difference 'Attachment.count' do
2184 assert_no_difference 'Attachment.count' do
2165 post :create, :project_id => 1,
2185 post :create, :project_id => 1,
2166 :issue => { :tracker_id => '1', :subject => 'Saved attachments' },
2186 :issue => { :tracker_id => '1', :subject => 'Saved attachments' },
2167 :attachments => {'p0' => {'token' => attachment.token}}
2187 :attachments => {'p0' => {'token' => attachment.token}}
2168 assert_response 302
2188 assert_response 302
2169 end
2189 end
2170 end
2190 end
2171
2191
2172 issue = Issue.first(:order => 'id DESC')
2192 issue = Issue.first(:order => 'id DESC')
2173 assert_equal 1, issue.attachments.count
2193 assert_equal 1, issue.attachments.count
2174
2194
2175 attachment.reload
2195 attachment.reload
2176 assert_equal issue, attachment.container
2196 assert_equal issue, attachment.container
2177 end
2197 end
2178
2198
2179 context "without workflow privilege" do
2199 context "without workflow privilege" do
2180 setup do
2200 setup do
2181 WorkflowTransition.delete_all(["role_id = ?", Role.anonymous.id])
2201 WorkflowTransition.delete_all(["role_id = ?", Role.anonymous.id])
2182 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2202 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2183 end
2203 end
2184
2204
2185 context "#new" do
2205 context "#new" do
2186 should "propose default status only" do
2206 should "propose default status only" do
2187 get :new, :project_id => 1
2207 get :new, :project_id => 1
2188 assert_response :success
2208 assert_response :success
2189 assert_template 'new'
2209 assert_template 'new'
2190 assert_tag :tag => 'select',
2210 assert_tag :tag => 'select',
2191 :attributes => {:name => 'issue[status_id]'},
2211 :attributes => {:name => 'issue[status_id]'},
2192 :children => {:count => 1},
2212 :children => {:count => 1},
2193 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
2213 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
2194 end
2214 end
2195
2215
2196 should "accept default status" do
2216 should "accept default status" do
2197 assert_difference 'Issue.count' do
2217 assert_difference 'Issue.count' do
2198 post :create, :project_id => 1,
2218 post :create, :project_id => 1,
2199 :issue => {:tracker_id => 1,
2219 :issue => {:tracker_id => 1,
2200 :subject => 'This is an issue',
2220 :subject => 'This is an issue',
2201 :status_id => 1}
2221 :status_id => 1}
2202 end
2222 end
2203 issue = Issue.last(:order => 'id')
2223 issue = Issue.last(:order => 'id')
2204 assert_equal IssueStatus.default, issue.status
2224 assert_equal IssueStatus.default, issue.status
2205 end
2225 end
2206
2226
2207 should "ignore unauthorized status" do
2227 should "ignore unauthorized status" do
2208 assert_difference 'Issue.count' do
2228 assert_difference 'Issue.count' do
2209 post :create, :project_id => 1,
2229 post :create, :project_id => 1,
2210 :issue => {:tracker_id => 1,
2230 :issue => {:tracker_id => 1,
2211 :subject => 'This is an issue',
2231 :subject => 'This is an issue',
2212 :status_id => 3}
2232 :status_id => 3}
2213 end
2233 end
2214 issue = Issue.last(:order => 'id')
2234 issue = Issue.last(:order => 'id')
2215 assert_equal IssueStatus.default, issue.status
2235 assert_equal IssueStatus.default, issue.status
2216 end
2236 end
2217 end
2237 end
2218
2238
2219 context "#update" do
2239 context "#update" do
2220 should "ignore status change" do
2240 should "ignore status change" do
2221 assert_difference 'Journal.count' do
2241 assert_difference 'Journal.count' do
2222 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2242 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2223 end
2243 end
2224 assert_equal 1, Issue.find(1).status_id
2244 assert_equal 1, Issue.find(1).status_id
2225 end
2245 end
2226
2246
2227 should "ignore attributes changes" do
2247 should "ignore attributes changes" do
2228 assert_difference 'Journal.count' do
2248 assert_difference 'Journal.count' do
2229 put :update, :id => 1, :issue => {:subject => 'changed', :assigned_to_id => 2, :notes => 'just trying'}
2249 put :update, :id => 1, :issue => {:subject => 'changed', :assigned_to_id => 2, :notes => 'just trying'}
2230 end
2250 end
2231 issue = Issue.find(1)
2251 issue = Issue.find(1)
2232 assert_equal "Can't print recipes", issue.subject
2252 assert_equal "Can't print recipes", issue.subject
2233 assert_nil issue.assigned_to
2253 assert_nil issue.assigned_to
2234 end
2254 end
2235 end
2255 end
2236 end
2256 end
2237
2257
2238 context "with workflow privilege" do
2258 context "with workflow privilege" do
2239 setup do
2259 setup do
2240 WorkflowTransition.delete_all(["role_id = ?", Role.anonymous.id])
2260 WorkflowTransition.delete_all(["role_id = ?", Role.anonymous.id])
2241 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
2261 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
2242 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
2262 WorkflowTransition.create!(:role => Role.anonymous, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
2243 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2263 Role.anonymous.add_permission! :add_issues, :add_issue_notes
2244 end
2264 end
2245
2265
2246 context "#update" do
2266 context "#update" do
2247 should "accept authorized status" do
2267 should "accept authorized status" do
2248 assert_difference 'Journal.count' do
2268 assert_difference 'Journal.count' do
2249 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2269 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2250 end
2270 end
2251 assert_equal 3, Issue.find(1).status_id
2271 assert_equal 3, Issue.find(1).status_id
2252 end
2272 end
2253
2273
2254 should "ignore unauthorized status" do
2274 should "ignore unauthorized status" do
2255 assert_difference 'Journal.count' do
2275 assert_difference 'Journal.count' do
2256 put :update, :id => 1, :issue => {:status_id => 2, :notes => 'just trying'}
2276 put :update, :id => 1, :issue => {:status_id => 2, :notes => 'just trying'}
2257 end
2277 end
2258 assert_equal 1, Issue.find(1).status_id
2278 assert_equal 1, Issue.find(1).status_id
2259 end
2279 end
2260
2280
2261 should "accept authorized attributes changes" do
2281 should "accept authorized attributes changes" do
2262 assert_difference 'Journal.count' do
2282 assert_difference 'Journal.count' do
2263 put :update, :id => 1, :issue => {:assigned_to_id => 2, :notes => 'just trying'}
2283 put :update, :id => 1, :issue => {:assigned_to_id => 2, :notes => 'just trying'}
2264 end
2284 end
2265 issue = Issue.find(1)
2285 issue = Issue.find(1)
2266 assert_equal 2, issue.assigned_to_id
2286 assert_equal 2, issue.assigned_to_id
2267 end
2287 end
2268
2288
2269 should "ignore unauthorized attributes changes" do
2289 should "ignore unauthorized attributes changes" do
2270 assert_difference 'Journal.count' do
2290 assert_difference 'Journal.count' do
2271 put :update, :id => 1, :issue => {:subject => 'changed', :notes => 'just trying'}
2291 put :update, :id => 1, :issue => {:subject => 'changed', :notes => 'just trying'}
2272 end
2292 end
2273 issue = Issue.find(1)
2293 issue = Issue.find(1)
2274 assert_equal "Can't print recipes", issue.subject
2294 assert_equal "Can't print recipes", issue.subject
2275 end
2295 end
2276 end
2296 end
2277
2297
2278 context "and :edit_issues permission" do
2298 context "and :edit_issues permission" do
2279 setup do
2299 setup do
2280 Role.anonymous.add_permission! :add_issues, :edit_issues
2300 Role.anonymous.add_permission! :add_issues, :edit_issues
2281 end
2301 end
2282
2302
2283 should "accept authorized status" do
2303 should "accept authorized status" do
2284 assert_difference 'Journal.count' do
2304 assert_difference 'Journal.count' do
2285 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2305 put :update, :id => 1, :issue => {:status_id => 3, :notes => 'just trying'}
2286 end
2306 end
2287 assert_equal 3, Issue.find(1).status_id
2307 assert_equal 3, Issue.find(1).status_id
2288 end
2308 end
2289
2309
2290 should "ignore unauthorized status" do
2310 should "ignore unauthorized status" do
2291 assert_difference 'Journal.count' do
2311 assert_difference 'Journal.count' do
2292 put :update, :id => 1, :issue => {:status_id => 2, :notes => 'just trying'}
2312 put :update, :id => 1, :issue => {:status_id => 2, :notes => 'just trying'}
2293 end
2313 end
2294 assert_equal 1, Issue.find(1).status_id
2314 assert_equal 1, Issue.find(1).status_id
2295 end
2315 end
2296
2316
2297 should "accept authorized attributes changes" do
2317 should "accept authorized attributes changes" do
2298 assert_difference 'Journal.count' do
2318 assert_difference 'Journal.count' do
2299 put :update, :id => 1, :issue => {:subject => 'changed', :assigned_to_id => 2, :notes => 'just trying'}
2319 put :update, :id => 1, :issue => {:subject => 'changed', :assigned_to_id => 2, :notes => 'just trying'}
2300 end
2320 end
2301 issue = Issue.find(1)
2321 issue = Issue.find(1)
2302 assert_equal "changed", issue.subject
2322 assert_equal "changed", issue.subject
2303 assert_equal 2, issue.assigned_to_id
2323 assert_equal 2, issue.assigned_to_id
2304 end
2324 end
2305 end
2325 end
2306 end
2326 end
2307
2327
2308 def test_new_as_copy
2328 def test_new_as_copy
2309 @request.session[:user_id] = 2
2329 @request.session[:user_id] = 2
2310 get :new, :project_id => 1, :copy_from => 1
2330 get :new, :project_id => 1, :copy_from => 1
2311
2331
2312 assert_response :success
2332 assert_response :success
2313 assert_template 'new'
2333 assert_template 'new'
2314
2334
2315 assert_not_nil assigns(:issue)
2335 assert_not_nil assigns(:issue)
2316 orig = Issue.find(1)
2336 orig = Issue.find(1)
2317 assert_equal 1, assigns(:issue).project_id
2337 assert_equal 1, assigns(:issue).project_id
2318 assert_equal orig.subject, assigns(:issue).subject
2338 assert_equal orig.subject, assigns(:issue).subject
2319 assert assigns(:issue).copy?
2339 assert assigns(:issue).copy?
2320
2340
2321 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
2341 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
2322 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
2342 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
2323 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
2343 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
2324 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}, :content => 'eCookbook'}
2344 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}, :content => 'eCookbook'}
2325 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
2345 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
2326 :child => {:tag => 'option', :attributes => {:value => '2', :selected => nil}, :content => 'OnlineStore'}
2346 :child => {:tag => 'option', :attributes => {:value => '2', :selected => nil}, :content => 'OnlineStore'}
2327 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
2347 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
2328 end
2348 end
2329
2349
2330 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
2350 def test_new_as_copy_with_attachments_should_show_copy_attachments_checkbox
2331 @request.session[:user_id] = 2
2351 @request.session[:user_id] = 2
2332 issue = Issue.find(3)
2352 issue = Issue.find(3)
2333 assert issue.attachments.count > 0
2353 assert issue.attachments.count > 0
2334 get :new, :project_id => 1, :copy_from => 3
2354 get :new, :project_id => 1, :copy_from => 3
2335
2355
2336 assert_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
2356 assert_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
2337 end
2357 end
2338
2358
2339 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
2359 def test_new_as_copy_without_attachments_should_not_show_copy_attachments_checkbox
2340 @request.session[:user_id] = 2
2360 @request.session[:user_id] = 2
2341 issue = Issue.find(3)
2361 issue = Issue.find(3)
2342 issue.attachments.delete_all
2362 issue.attachments.delete_all
2343 get :new, :project_id => 1, :copy_from => 3
2363 get :new, :project_id => 1, :copy_from => 3
2344
2364
2345 assert_no_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
2365 assert_no_tag 'input', :attributes => {:name => 'copy_attachments', :type => 'checkbox', :checked => 'checked', :value => '1'}
2346 end
2366 end
2347
2367
2348 def test_new_as_copy_with_subtasks_should_show_copy_subtasks_checkbox
2368 def test_new_as_copy_with_subtasks_should_show_copy_subtasks_checkbox
2349 @request.session[:user_id] = 2
2369 @request.session[:user_id] = 2
2350 issue = Issue.generate_with_descendants!
2370 issue = Issue.generate_with_descendants!
2351 get :new, :project_id => 1, :copy_from => issue.id
2371 get :new, :project_id => 1, :copy_from => issue.id
2352
2372
2353 assert_select 'input[type=checkbox][name=copy_subtasks][checked=checked][value=1]'
2373 assert_select 'input[type=checkbox][name=copy_subtasks][checked=checked][value=1]'
2354 end
2374 end
2355
2375
2356 def test_new_as_copy_with_invalid_issue_should_respond_with_404
2376 def test_new_as_copy_with_invalid_issue_should_respond_with_404
2357 @request.session[:user_id] = 2
2377 @request.session[:user_id] = 2
2358 get :new, :project_id => 1, :copy_from => 99999
2378 get :new, :project_id => 1, :copy_from => 99999
2359 assert_response 404
2379 assert_response 404
2360 end
2380 end
2361
2381
2362 def test_create_as_copy_on_different_project
2382 def test_create_as_copy_on_different_project
2363 @request.session[:user_id] = 2
2383 @request.session[:user_id] = 2
2364 assert_difference 'Issue.count' do
2384 assert_difference 'Issue.count' do
2365 post :create, :project_id => 1, :copy_from => 1,
2385 post :create, :project_id => 1, :copy_from => 1,
2366 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
2386 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
2367
2387
2368 assert_not_nil assigns(:issue)
2388 assert_not_nil assigns(:issue)
2369 assert assigns(:issue).copy?
2389 assert assigns(:issue).copy?
2370 end
2390 end
2371 issue = Issue.first(:order => 'id DESC')
2391 issue = Issue.first(:order => 'id DESC')
2372 assert_redirected_to "/issues/#{issue.id}"
2392 assert_redirected_to "/issues/#{issue.id}"
2373
2393
2374 assert_equal 2, issue.project_id
2394 assert_equal 2, issue.project_id
2375 assert_equal 3, issue.tracker_id
2395 assert_equal 3, issue.tracker_id
2376 assert_equal 'Copy', issue.subject
2396 assert_equal 'Copy', issue.subject
2377 end
2397 end
2378
2398
2379 def test_create_as_copy_should_copy_attachments
2399 def test_create_as_copy_should_copy_attachments
2380 @request.session[:user_id] = 2
2400 @request.session[:user_id] = 2
2381 issue = Issue.find(3)
2401 issue = Issue.find(3)
2382 count = issue.attachments.count
2402 count = issue.attachments.count
2383 assert count > 0
2403 assert count > 0
2384
2404
2385 assert_difference 'Issue.count' do
2405 assert_difference 'Issue.count' do
2386 assert_difference 'Attachment.count', count do
2406 assert_difference 'Attachment.count', count do
2387 assert_no_difference 'Journal.count' do
2407 assert_no_difference 'Journal.count' do
2388 post :create, :project_id => 1, :copy_from => 3,
2408 post :create, :project_id => 1, :copy_from => 3,
2389 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
2409 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
2390 :copy_attachments => '1'
2410 :copy_attachments => '1'
2391 end
2411 end
2392 end
2412 end
2393 end
2413 end
2394 copy = Issue.first(:order => 'id DESC')
2414 copy = Issue.first(:order => 'id DESC')
2395 assert_equal count, copy.attachments.count
2415 assert_equal count, copy.attachments.count
2396 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
2416 assert_equal issue.attachments.map(&:filename).sort, copy.attachments.map(&:filename).sort
2397 end
2417 end
2398
2418
2399 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
2419 def test_create_as_copy_without_copy_attachments_option_should_not_copy_attachments
2400 @request.session[:user_id] = 2
2420 @request.session[:user_id] = 2
2401 issue = Issue.find(3)
2421 issue = Issue.find(3)
2402 count = issue.attachments.count
2422 count = issue.attachments.count
2403 assert count > 0
2423 assert count > 0
2404
2424
2405 assert_difference 'Issue.count' do
2425 assert_difference 'Issue.count' do
2406 assert_no_difference 'Attachment.count' do
2426 assert_no_difference 'Attachment.count' do
2407 assert_no_difference 'Journal.count' do
2427 assert_no_difference 'Journal.count' do
2408 post :create, :project_id => 1, :copy_from => 3,
2428 post :create, :project_id => 1, :copy_from => 3,
2409 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'}
2429 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'}
2410 end
2430 end
2411 end
2431 end
2412 end
2432 end
2413 copy = Issue.first(:order => 'id DESC')
2433 copy = Issue.first(:order => 'id DESC')
2414 assert_equal 0, copy.attachments.count
2434 assert_equal 0, copy.attachments.count
2415 end
2435 end
2416
2436
2417 def test_create_as_copy_with_attachments_should_add_new_files
2437 def test_create_as_copy_with_attachments_should_add_new_files
2418 @request.session[:user_id] = 2
2438 @request.session[:user_id] = 2
2419 issue = Issue.find(3)
2439 issue = Issue.find(3)
2420 count = issue.attachments.count
2440 count = issue.attachments.count
2421 assert count > 0
2441 assert count > 0
2422
2442
2423 assert_difference 'Issue.count' do
2443 assert_difference 'Issue.count' do
2424 assert_difference 'Attachment.count', count + 1 do
2444 assert_difference 'Attachment.count', count + 1 do
2425 assert_no_difference 'Journal.count' do
2445 assert_no_difference 'Journal.count' do
2426 post :create, :project_id => 1, :copy_from => 3,
2446 post :create, :project_id => 1, :copy_from => 3,
2427 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
2447 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with attachments'},
2428 :copy_attachments => '1',
2448 :copy_attachments => '1',
2429 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2449 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2430 end
2450 end
2431 end
2451 end
2432 end
2452 end
2433 copy = Issue.first(:order => 'id DESC')
2453 copy = Issue.first(:order => 'id DESC')
2434 assert_equal count + 1, copy.attachments.count
2454 assert_equal count + 1, copy.attachments.count
2435 end
2455 end
2436
2456
2437 def test_create_as_copy_should_add_relation_with_copied_issue
2457 def test_create_as_copy_should_add_relation_with_copied_issue
2438 @request.session[:user_id] = 2
2458 @request.session[:user_id] = 2
2439
2459
2440 assert_difference 'Issue.count' do
2460 assert_difference 'Issue.count' do
2441 assert_difference 'IssueRelation.count' do
2461 assert_difference 'IssueRelation.count' do
2442 post :create, :project_id => 1, :copy_from => 1,
2462 post :create, :project_id => 1, :copy_from => 1,
2443 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
2463 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
2444 end
2464 end
2445 end
2465 end
2446 copy = Issue.first(:order => 'id DESC')
2466 copy = Issue.first(:order => 'id DESC')
2447 assert_equal 1, copy.relations.size
2467 assert_equal 1, copy.relations.size
2448 end
2468 end
2449
2469
2450 def test_create_as_copy_should_copy_subtasks
2470 def test_create_as_copy_should_copy_subtasks
2451 @request.session[:user_id] = 2
2471 @request.session[:user_id] = 2
2452 issue = Issue.generate_with_descendants!
2472 issue = Issue.generate_with_descendants!
2453 count = issue.descendants.count
2473 count = issue.descendants.count
2454
2474
2455 assert_difference 'Issue.count', count+1 do
2475 assert_difference 'Issue.count', count+1 do
2456 assert_no_difference 'Journal.count' do
2476 assert_no_difference 'Journal.count' do
2457 post :create, :project_id => 1, :copy_from => issue.id,
2477 post :create, :project_id => 1, :copy_from => issue.id,
2458 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with subtasks'},
2478 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with subtasks'},
2459 :copy_subtasks => '1'
2479 :copy_subtasks => '1'
2460 end
2480 end
2461 end
2481 end
2462 copy = Issue.where(:parent_id => nil).first(:order => 'id DESC')
2482 copy = Issue.where(:parent_id => nil).first(:order => 'id DESC')
2463 assert_equal count, copy.descendants.count
2483 assert_equal count, copy.descendants.count
2464 assert_equal issue.descendants.map(&:subject).sort, copy.descendants.map(&:subject).sort
2484 assert_equal issue.descendants.map(&:subject).sort, copy.descendants.map(&:subject).sort
2465 end
2485 end
2466
2486
2467 def test_create_as_copy_without_copy_subtasks_option_should_not_copy_subtasks
2487 def test_create_as_copy_without_copy_subtasks_option_should_not_copy_subtasks
2468 @request.session[:user_id] = 2
2488 @request.session[:user_id] = 2
2469 issue = Issue.generate_with_descendants!
2489 issue = Issue.generate_with_descendants!
2470
2490
2471 assert_difference 'Issue.count', 1 do
2491 assert_difference 'Issue.count', 1 do
2472 assert_no_difference 'Journal.count' do
2492 assert_no_difference 'Journal.count' do
2473 post :create, :project_id => 1, :copy_from => 3,
2493 post :create, :project_id => 1, :copy_from => 3,
2474 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with subtasks'}
2494 :issue => {:project_id => '1', :tracker_id => '3', :status_id => '1', :subject => 'Copy with subtasks'}
2475 end
2495 end
2476 end
2496 end
2477 copy = Issue.where(:parent_id => nil).first(:order => 'id DESC')
2497 copy = Issue.where(:parent_id => nil).first(:order => 'id DESC')
2478 assert_equal 0, copy.descendants.count
2498 assert_equal 0, copy.descendants.count
2479 end
2499 end
2480
2500
2481 def test_create_as_copy_with_failure
2501 def test_create_as_copy_with_failure
2482 @request.session[:user_id] = 2
2502 @request.session[:user_id] = 2
2483 post :create, :project_id => 1, :copy_from => 1,
2503 post :create, :project_id => 1, :copy_from => 1,
2484 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
2504 :issue => {:project_id => '2', :tracker_id => '3', :status_id => '1', :subject => ''}
2485
2505
2486 assert_response :success
2506 assert_response :success
2487 assert_template 'new'
2507 assert_template 'new'
2488
2508
2489 assert_not_nil assigns(:issue)
2509 assert_not_nil assigns(:issue)
2490 assert assigns(:issue).copy?
2510 assert assigns(:issue).copy?
2491
2511
2492 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
2512 assert_tag 'form', :attributes => {:id => 'issue-form', :action => '/projects/ecookbook/issues'}
2493 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
2513 assert_tag 'select', :attributes => {:name => 'issue[project_id]'}
2494 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
2514 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
2495 :child => {:tag => 'option', :attributes => {:value => '1', :selected => nil}, :content => 'eCookbook'}
2515 :child => {:tag => 'option', :attributes => {:value => '1', :selected => nil}, :content => 'eCookbook'}
2496 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
2516 assert_tag 'select', :attributes => {:name => 'issue[project_id]'},
2497 :child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}, :content => 'OnlineStore'}
2517 :child => {:tag => 'option', :attributes => {:value => '2', :selected => 'selected'}, :content => 'OnlineStore'}
2498 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
2518 assert_tag 'input', :attributes => {:name => 'copy_from', :value => '1'}
2499 end
2519 end
2500
2520
2501 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
2521 def test_create_as_copy_on_project_without_permission_should_ignore_target_project
2502 @request.session[:user_id] = 2
2522 @request.session[:user_id] = 2
2503 assert !User.find(2).member_of?(Project.find(4))
2523 assert !User.find(2).member_of?(Project.find(4))
2504
2524
2505 assert_difference 'Issue.count' do
2525 assert_difference 'Issue.count' do
2506 post :create, :project_id => 1, :copy_from => 1,
2526 post :create, :project_id => 1, :copy_from => 1,
2507 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
2527 :issue => {:project_id => '4', :tracker_id => '3', :status_id => '1', :subject => 'Copy'}
2508 end
2528 end
2509 issue = Issue.first(:order => 'id DESC')
2529 issue = Issue.first(:order => 'id DESC')
2510 assert_equal 1, issue.project_id
2530 assert_equal 1, issue.project_id
2511 end
2531 end
2512
2532
2513 def test_get_edit
2533 def test_get_edit
2514 @request.session[:user_id] = 2
2534 @request.session[:user_id] = 2
2515 get :edit, :id => 1
2535 get :edit, :id => 1
2516 assert_response :success
2536 assert_response :success
2517 assert_template 'edit'
2537 assert_template 'edit'
2518 assert_not_nil assigns(:issue)
2538 assert_not_nil assigns(:issue)
2519 assert_equal Issue.find(1), assigns(:issue)
2539 assert_equal Issue.find(1), assigns(:issue)
2520
2540
2521 # Be sure we don't display inactive IssuePriorities
2541 # Be sure we don't display inactive IssuePriorities
2522 assert ! IssuePriority.find(15).active?
2542 assert ! IssuePriority.find(15).active?
2523 assert_no_tag :option, :attributes => {:value => '15'},
2543 assert_no_tag :option, :attributes => {:value => '15'},
2524 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
2544 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
2525 end
2545 end
2526
2546
2527 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
2547 def test_get_edit_should_display_the_time_entry_form_with_log_time_permission
2528 @request.session[:user_id] = 2
2548 @request.session[:user_id] = 2
2529 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
2549 Role.find_by_name('Manager').update_attribute :permissions, [:view_issues, :edit_issues, :log_time]
2530
2550
2531 get :edit, :id => 1
2551 get :edit, :id => 1
2532 assert_tag 'input', :attributes => {:name => 'time_entry[hours]'}
2552 assert_tag 'input', :attributes => {:name => 'time_entry[hours]'}
2533 end
2553 end
2534
2554
2535 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
2555 def test_get_edit_should_not_display_the_time_entry_form_without_log_time_permission
2536 @request.session[:user_id] = 2
2556 @request.session[:user_id] = 2
2537 Role.find_by_name('Manager').remove_permission! :log_time
2557 Role.find_by_name('Manager').remove_permission! :log_time
2538
2558
2539 get :edit, :id => 1
2559 get :edit, :id => 1
2540 assert_no_tag 'input', :attributes => {:name => 'time_entry[hours]'}
2560 assert_no_tag 'input', :attributes => {:name => 'time_entry[hours]'}
2541 end
2561 end
2542
2562
2543 def test_get_edit_with_params
2563 def test_get_edit_with_params
2544 @request.session[:user_id] = 2
2564 @request.session[:user_id] = 2
2545 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
2565 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 },
2546 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
2566 :time_entry => { :hours => '2.5', :comments => 'test_get_edit_with_params', :activity_id => TimeEntryActivity.first.id }
2547 assert_response :success
2567 assert_response :success
2548 assert_template 'edit'
2568 assert_template 'edit'
2549
2569
2550 issue = assigns(:issue)
2570 issue = assigns(:issue)
2551 assert_not_nil issue
2571 assert_not_nil issue
2552
2572
2553 assert_equal 5, issue.status_id
2573 assert_equal 5, issue.status_id
2554 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
2574 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
2555 :child => { :tag => 'option',
2575 :child => { :tag => 'option',
2556 :content => 'Closed',
2576 :content => 'Closed',
2557 :attributes => { :selected => 'selected' } }
2577 :attributes => { :selected => 'selected' } }
2558
2578
2559 assert_equal 7, issue.priority_id
2579 assert_equal 7, issue.priority_id
2560 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
2580 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
2561 :child => { :tag => 'option',
2581 :child => { :tag => 'option',
2562 :content => 'Urgent',
2582 :content => 'Urgent',
2563 :attributes => { :selected => 'selected' } }
2583 :attributes => { :selected => 'selected' } }
2564
2584
2565 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
2585 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => '2.5' }
2566 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
2586 assert_tag :select, :attributes => { :name => 'time_entry[activity_id]' },
2567 :child => { :tag => 'option',
2587 :child => { :tag => 'option',
2568 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
2588 :attributes => { :selected => 'selected', :value => TimeEntryActivity.first.id } }
2569 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
2589 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => 'test_get_edit_with_params' }
2570 end
2590 end
2571
2591
2572 def test_get_edit_with_multi_custom_field
2592 def test_get_edit_with_multi_custom_field
2573 field = CustomField.find(1)
2593 field = CustomField.find(1)
2574 field.update_attribute :multiple, true
2594 field.update_attribute :multiple, true
2575 issue = Issue.find(1)
2595 issue = Issue.find(1)
2576 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
2596 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
2577 issue.save!
2597 issue.save!
2578
2598
2579 @request.session[:user_id] = 2
2599 @request.session[:user_id] = 2
2580 get :edit, :id => 1
2600 get :edit, :id => 1
2581 assert_response :success
2601 assert_response :success
2582 assert_template 'edit'
2602 assert_template 'edit'
2583
2603
2584 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]', :multiple => 'multiple'}
2604 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]', :multiple => 'multiple'}
2585 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
2605 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
2586 :child => {:tag => 'option', :attributes => {:value => 'MySQL', :selected => 'selected'}}
2606 :child => {:tag => 'option', :attributes => {:value => 'MySQL', :selected => 'selected'}}
2587 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
2607 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
2588 :child => {:tag => 'option', :attributes => {:value => 'PostgreSQL', :selected => nil}}
2608 :child => {:tag => 'option', :attributes => {:value => 'PostgreSQL', :selected => nil}}
2589 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
2609 assert_tag 'select', :attributes => {:name => 'issue[custom_field_values][1][]'},
2590 :child => {:tag => 'option', :attributes => {:value => 'Oracle', :selected => 'selected'}}
2610 :child => {:tag => 'option', :attributes => {:value => 'Oracle', :selected => 'selected'}}
2591 end
2611 end
2592
2612
2593 def test_update_edit_form
2613 def test_update_edit_form
2594 @request.session[:user_id] = 2
2614 @request.session[:user_id] = 2
2595 xhr :put, :new, :project_id => 1,
2615 xhr :put, :new, :project_id => 1,
2596 :id => 1,
2616 :id => 1,
2597 :issue => {:tracker_id => 2,
2617 :issue => {:tracker_id => 2,
2598 :subject => 'This is the test_new issue',
2618 :subject => 'This is the test_new issue',
2599 :description => 'This is the description',
2619 :description => 'This is the description',
2600 :priority_id => 5}
2620 :priority_id => 5}
2601 assert_response :success
2621 assert_response :success
2602 assert_equal 'text/javascript', response.content_type
2622 assert_equal 'text/javascript', response.content_type
2603 assert_template 'update_form'
2623 assert_template 'update_form'
2604 assert_template 'form'
2624 assert_template 'form'
2605
2625
2606 issue = assigns(:issue)
2626 issue = assigns(:issue)
2607 assert_kind_of Issue, issue
2627 assert_kind_of Issue, issue
2608 assert_equal 1, issue.id
2628 assert_equal 1, issue.id
2609 assert_equal 1, issue.project_id
2629 assert_equal 1, issue.project_id
2610 assert_equal 2, issue.tracker_id
2630 assert_equal 2, issue.tracker_id
2611 assert_equal 'This is the test_new issue', issue.subject
2631 assert_equal 'This is the test_new issue', issue.subject
2612 end
2632 end
2613
2633
2614 def test_update_edit_form_should_keep_issue_author
2634 def test_update_edit_form_should_keep_issue_author
2615 @request.session[:user_id] = 3
2635 @request.session[:user_id] = 3
2616 xhr :put, :new, :project_id => 1, :id => 1, :issue => {:subject => 'Changed'}
2636 xhr :put, :new, :project_id => 1, :id => 1, :issue => {:subject => 'Changed'}
2617 assert_response :success
2637 assert_response :success
2618 assert_equal 'text/javascript', response.content_type
2638 assert_equal 'text/javascript', response.content_type
2619
2639
2620 issue = assigns(:issue)
2640 issue = assigns(:issue)
2621 assert_equal User.find(2), issue.author
2641 assert_equal User.find(2), issue.author
2622 assert_equal 2, issue.author_id
2642 assert_equal 2, issue.author_id
2623 assert_not_equal User.current, issue.author
2643 assert_not_equal User.current, issue.author
2624 end
2644 end
2625
2645
2626 def test_update_edit_form_should_propose_transitions_based_on_initial_status
2646 def test_update_edit_form_should_propose_transitions_based_on_initial_status
2627 @request.session[:user_id] = 2
2647 @request.session[:user_id] = 2
2628 WorkflowTransition.delete_all
2648 WorkflowTransition.delete_all
2629 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
2649 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
2630 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
2650 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
2631 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 5, :new_status_id => 4)
2651 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 5, :new_status_id => 4)
2632
2652
2633 xhr :put, :new, :project_id => 1,
2653 xhr :put, :new, :project_id => 1,
2634 :id => 2,
2654 :id => 2,
2635 :issue => {:tracker_id => 2,
2655 :issue => {:tracker_id => 2,
2636 :status_id => 5,
2656 :status_id => 5,
2637 :subject => 'This is an issue'}
2657 :subject => 'This is an issue'}
2638
2658
2639 assert_equal 5, assigns(:issue).status_id
2659 assert_equal 5, assigns(:issue).status_id
2640 assert_equal [1,2,5], assigns(:allowed_statuses).map(&:id).sort
2660 assert_equal [1,2,5], assigns(:allowed_statuses).map(&:id).sort
2641 end
2661 end
2642
2662
2643 def test_update_edit_form_with_project_change
2663 def test_update_edit_form_with_project_change
2644 @request.session[:user_id] = 2
2664 @request.session[:user_id] = 2
2645 xhr :put, :new, :project_id => 1,
2665 xhr :put, :new, :project_id => 1,
2646 :id => 1,
2666 :id => 1,
2647 :issue => {:project_id => 2,
2667 :issue => {:project_id => 2,
2648 :tracker_id => 2,
2668 :tracker_id => 2,
2649 :subject => 'This is the test_new issue',
2669 :subject => 'This is the test_new issue',
2650 :description => 'This is the description',
2670 :description => 'This is the description',
2651 :priority_id => 5}
2671 :priority_id => 5}
2652 assert_response :success
2672 assert_response :success
2653 assert_template 'form'
2673 assert_template 'form'
2654
2674
2655 issue = assigns(:issue)
2675 issue = assigns(:issue)
2656 assert_kind_of Issue, issue
2676 assert_kind_of Issue, issue
2657 assert_equal 1, issue.id
2677 assert_equal 1, issue.id
2658 assert_equal 2, issue.project_id
2678 assert_equal 2, issue.project_id
2659 assert_equal 2, issue.tracker_id
2679 assert_equal 2, issue.tracker_id
2660 assert_equal 'This is the test_new issue', issue.subject
2680 assert_equal 'This is the test_new issue', issue.subject
2661 end
2681 end
2662
2682
2663 def test_put_update_without_custom_fields_param
2683 def test_put_update_without_custom_fields_param
2664 @request.session[:user_id] = 2
2684 @request.session[:user_id] = 2
2665 ActionMailer::Base.deliveries.clear
2685 ActionMailer::Base.deliveries.clear
2666
2686
2667 issue = Issue.find(1)
2687 issue = Issue.find(1)
2668 assert_equal '125', issue.custom_value_for(2).value
2688 assert_equal '125', issue.custom_value_for(2).value
2669 old_subject = issue.subject
2689 old_subject = issue.subject
2670 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
2690 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
2671
2691
2672 assert_difference('Journal.count') do
2692 assert_difference('Journal.count') do
2673 assert_difference('JournalDetail.count', 2) do
2693 assert_difference('JournalDetail.count', 2) do
2674 put :update, :id => 1, :issue => {:subject => new_subject,
2694 put :update, :id => 1, :issue => {:subject => new_subject,
2675 :priority_id => '6',
2695 :priority_id => '6',
2676 :category_id => '1' # no change
2696 :category_id => '1' # no change
2677 }
2697 }
2678 end
2698 end
2679 end
2699 end
2680 assert_redirected_to :action => 'show', :id => '1'
2700 assert_redirected_to :action => 'show', :id => '1'
2681 issue.reload
2701 issue.reload
2682 assert_equal new_subject, issue.subject
2702 assert_equal new_subject, issue.subject
2683 # Make sure custom fields were not cleared
2703 # Make sure custom fields were not cleared
2684 assert_equal '125', issue.custom_value_for(2).value
2704 assert_equal '125', issue.custom_value_for(2).value
2685
2705
2686 mail = ActionMailer::Base.deliveries.last
2706 mail = ActionMailer::Base.deliveries.last
2687 assert_not_nil mail
2707 assert_not_nil mail
2688 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2708 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2689 assert_mail_body_match "Subject changed from #{old_subject} to #{new_subject}", mail
2709 assert_mail_body_match "Subject changed from #{old_subject} to #{new_subject}", mail
2690 end
2710 end
2691
2711
2692 def test_put_update_with_project_change
2712 def test_put_update_with_project_change
2693 @request.session[:user_id] = 2
2713 @request.session[:user_id] = 2
2694 ActionMailer::Base.deliveries.clear
2714 ActionMailer::Base.deliveries.clear
2695
2715
2696 assert_difference('Journal.count') do
2716 assert_difference('Journal.count') do
2697 assert_difference('JournalDetail.count', 3) do
2717 assert_difference('JournalDetail.count', 3) do
2698 put :update, :id => 1, :issue => {:project_id => '2',
2718 put :update, :id => 1, :issue => {:project_id => '2',
2699 :tracker_id => '1', # no change
2719 :tracker_id => '1', # no change
2700 :priority_id => '6',
2720 :priority_id => '6',
2701 :category_id => '3'
2721 :category_id => '3'
2702 }
2722 }
2703 end
2723 end
2704 end
2724 end
2705 assert_redirected_to :action => 'show', :id => '1'
2725 assert_redirected_to :action => 'show', :id => '1'
2706 issue = Issue.find(1)
2726 issue = Issue.find(1)
2707 assert_equal 2, issue.project_id
2727 assert_equal 2, issue.project_id
2708 assert_equal 1, issue.tracker_id
2728 assert_equal 1, issue.tracker_id
2709 assert_equal 6, issue.priority_id
2729 assert_equal 6, issue.priority_id
2710 assert_equal 3, issue.category_id
2730 assert_equal 3, issue.category_id
2711
2731
2712 mail = ActionMailer::Base.deliveries.last
2732 mail = ActionMailer::Base.deliveries.last
2713 assert_not_nil mail
2733 assert_not_nil mail
2714 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2734 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2715 assert_mail_body_match "Project changed from eCookbook to OnlineStore", mail
2735 assert_mail_body_match "Project changed from eCookbook to OnlineStore", mail
2716 end
2736 end
2717
2737
2718 def test_put_update_with_tracker_change
2738 def test_put_update_with_tracker_change
2719 @request.session[:user_id] = 2
2739 @request.session[:user_id] = 2
2720 ActionMailer::Base.deliveries.clear
2740 ActionMailer::Base.deliveries.clear
2721
2741
2722 assert_difference('Journal.count') do
2742 assert_difference('Journal.count') do
2723 assert_difference('JournalDetail.count', 2) do
2743 assert_difference('JournalDetail.count', 2) do
2724 put :update, :id => 1, :issue => {:project_id => '1',
2744 put :update, :id => 1, :issue => {:project_id => '1',
2725 :tracker_id => '2',
2745 :tracker_id => '2',
2726 :priority_id => '6'
2746 :priority_id => '6'
2727 }
2747 }
2728 end
2748 end
2729 end
2749 end
2730 assert_redirected_to :action => 'show', :id => '1'
2750 assert_redirected_to :action => 'show', :id => '1'
2731 issue = Issue.find(1)
2751 issue = Issue.find(1)
2732 assert_equal 1, issue.project_id
2752 assert_equal 1, issue.project_id
2733 assert_equal 2, issue.tracker_id
2753 assert_equal 2, issue.tracker_id
2734 assert_equal 6, issue.priority_id
2754 assert_equal 6, issue.priority_id
2735 assert_equal 1, issue.category_id
2755 assert_equal 1, issue.category_id
2736
2756
2737 mail = ActionMailer::Base.deliveries.last
2757 mail = ActionMailer::Base.deliveries.last
2738 assert_not_nil mail
2758 assert_not_nil mail
2739 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2759 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
2740 assert_mail_body_match "Tracker changed from Bug to Feature request", mail
2760 assert_mail_body_match "Tracker changed from Bug to Feature request", mail
2741 end
2761 end
2742
2762
2743 def test_put_update_with_custom_field_change
2763 def test_put_update_with_custom_field_change
2744 @request.session[:user_id] = 2
2764 @request.session[:user_id] = 2
2745 issue = Issue.find(1)
2765 issue = Issue.find(1)
2746 assert_equal '125', issue.custom_value_for(2).value
2766 assert_equal '125', issue.custom_value_for(2).value
2747
2767
2748 assert_difference('Journal.count') do
2768 assert_difference('Journal.count') do
2749 assert_difference('JournalDetail.count', 3) do
2769 assert_difference('JournalDetail.count', 3) do
2750 put :update, :id => 1, :issue => {:subject => 'Custom field change',
2770 put :update, :id => 1, :issue => {:subject => 'Custom field change',
2751 :priority_id => '6',
2771 :priority_id => '6',
2752 :category_id => '1', # no change
2772 :category_id => '1', # no change
2753 :custom_field_values => { '2' => 'New custom value' }
2773 :custom_field_values => { '2' => 'New custom value' }
2754 }
2774 }
2755 end
2775 end
2756 end
2776 end
2757 assert_redirected_to :action => 'show', :id => '1'
2777 assert_redirected_to :action => 'show', :id => '1'
2758 issue.reload
2778 issue.reload
2759 assert_equal 'New custom value', issue.custom_value_for(2).value
2779 assert_equal 'New custom value', issue.custom_value_for(2).value
2760
2780
2761 mail = ActionMailer::Base.deliveries.last
2781 mail = ActionMailer::Base.deliveries.last
2762 assert_not_nil mail
2782 assert_not_nil mail
2763 assert_mail_body_match "Searchable field changed from 125 to New custom value", mail
2783 assert_mail_body_match "Searchable field changed from 125 to New custom value", mail
2764 end
2784 end
2765
2785
2766 def test_put_update_with_multi_custom_field_change
2786 def test_put_update_with_multi_custom_field_change
2767 field = CustomField.find(1)
2787 field = CustomField.find(1)
2768 field.update_attribute :multiple, true
2788 field.update_attribute :multiple, true
2769 issue = Issue.find(1)
2789 issue = Issue.find(1)
2770 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
2790 issue.custom_field_values = {1 => ['MySQL', 'Oracle']}
2771 issue.save!
2791 issue.save!
2772
2792
2773 @request.session[:user_id] = 2
2793 @request.session[:user_id] = 2
2774 assert_difference('Journal.count') do
2794 assert_difference('Journal.count') do
2775 assert_difference('JournalDetail.count', 3) do
2795 assert_difference('JournalDetail.count', 3) do
2776 put :update, :id => 1,
2796 put :update, :id => 1,
2777 :issue => {
2797 :issue => {
2778 :subject => 'Custom field change',
2798 :subject => 'Custom field change',
2779 :custom_field_values => { '1' => ['', 'Oracle', 'PostgreSQL'] }
2799 :custom_field_values => { '1' => ['', 'Oracle', 'PostgreSQL'] }
2780 }
2800 }
2781 end
2801 end
2782 end
2802 end
2783 assert_redirected_to :action => 'show', :id => '1'
2803 assert_redirected_to :action => 'show', :id => '1'
2784 assert_equal ['Oracle', 'PostgreSQL'], Issue.find(1).custom_field_value(1).sort
2804 assert_equal ['Oracle', 'PostgreSQL'], Issue.find(1).custom_field_value(1).sort
2785 end
2805 end
2786
2806
2787 def test_put_update_with_status_and_assignee_change
2807 def test_put_update_with_status_and_assignee_change
2788 issue = Issue.find(1)
2808 issue = Issue.find(1)
2789 assert_equal 1, issue.status_id
2809 assert_equal 1, issue.status_id
2790 @request.session[:user_id] = 2
2810 @request.session[:user_id] = 2
2791 assert_difference('TimeEntry.count', 0) do
2811 assert_difference('TimeEntry.count', 0) do
2792 put :update,
2812 put :update,
2793 :id => 1,
2813 :id => 1,
2794 :issue => { :status_id => 2, :assigned_to_id => 3, :notes => 'Assigned to dlopper' },
2814 :issue => { :status_id => 2, :assigned_to_id => 3, :notes => 'Assigned to dlopper' },
2795 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
2815 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
2796 end
2816 end
2797 assert_redirected_to :action => 'show', :id => '1'
2817 assert_redirected_to :action => 'show', :id => '1'
2798 issue.reload
2818 issue.reload
2799 assert_equal 2, issue.status_id
2819 assert_equal 2, issue.status_id
2800 j = Journal.find(:first, :order => 'id DESC')
2820 j = Journal.find(:first, :order => 'id DESC')
2801 assert_equal 'Assigned to dlopper', j.notes
2821 assert_equal 'Assigned to dlopper', j.notes
2802 assert_equal 2, j.details.size
2822 assert_equal 2, j.details.size
2803
2823
2804 mail = ActionMailer::Base.deliveries.last
2824 mail = ActionMailer::Base.deliveries.last
2805 assert_mail_body_match "Status changed from New to Assigned", mail
2825 assert_mail_body_match "Status changed from New to Assigned", mail
2806 # subject should contain the new status
2826 # subject should contain the new status
2807 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
2827 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
2808 end
2828 end
2809
2829
2810 def test_put_update_with_note_only
2830 def test_put_update_with_note_only
2811 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
2831 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
2812 # anonymous user
2832 # anonymous user
2813 put :update,
2833 put :update,
2814 :id => 1,
2834 :id => 1,
2815 :issue => { :notes => notes }
2835 :issue => { :notes => notes }
2816 assert_redirected_to :action => 'show', :id => '1'
2836 assert_redirected_to :action => 'show', :id => '1'
2817 j = Journal.find(:first, :order => 'id DESC')
2837 j = Journal.find(:first, :order => 'id DESC')
2818 assert_equal notes, j.notes
2838 assert_equal notes, j.notes
2819 assert_equal 0, j.details.size
2839 assert_equal 0, j.details.size
2820 assert_equal User.anonymous, j.user
2840 assert_equal User.anonymous, j.user
2821
2841
2822 mail = ActionMailer::Base.deliveries.last
2842 mail = ActionMailer::Base.deliveries.last
2823 assert_mail_body_match notes, mail
2843 assert_mail_body_match notes, mail
2824 end
2844 end
2825
2845
2826 def test_put_update_with_private_note_only
2846 def test_put_update_with_private_note_only
2827 notes = 'Private note'
2847 notes = 'Private note'
2828 @request.session[:user_id] = 2
2848 @request.session[:user_id] = 2
2829
2849
2830 assert_difference 'Journal.count' do
2850 assert_difference 'Journal.count' do
2831 put :update, :id => 1, :issue => {:notes => notes, :private_notes => '1'}
2851 put :update, :id => 1, :issue => {:notes => notes, :private_notes => '1'}
2832 assert_redirected_to :action => 'show', :id => '1'
2852 assert_redirected_to :action => 'show', :id => '1'
2833 end
2853 end
2834
2854
2835 j = Journal.order('id DESC').first
2855 j = Journal.order('id DESC').first
2836 assert_equal notes, j.notes
2856 assert_equal notes, j.notes
2837 assert_equal true, j.private_notes
2857 assert_equal true, j.private_notes
2838 end
2858 end
2839
2859
2840 def test_put_update_with_private_note_and_changes
2860 def test_put_update_with_private_note_and_changes
2841 notes = 'Private note'
2861 notes = 'Private note'
2842 @request.session[:user_id] = 2
2862 @request.session[:user_id] = 2
2843
2863
2844 assert_difference 'Journal.count', 2 do
2864 assert_difference 'Journal.count', 2 do
2845 put :update, :id => 1, :issue => {:subject => 'New subject', :notes => notes, :private_notes => '1'}
2865 put :update, :id => 1, :issue => {:subject => 'New subject', :notes => notes, :private_notes => '1'}
2846 assert_redirected_to :action => 'show', :id => '1'
2866 assert_redirected_to :action => 'show', :id => '1'
2847 end
2867 end
2848
2868
2849 j = Journal.order('id DESC').first
2869 j = Journal.order('id DESC').first
2850 assert_equal notes, j.notes
2870 assert_equal notes, j.notes
2851 assert_equal true, j.private_notes
2871 assert_equal true, j.private_notes
2852 assert_equal 0, j.details.count
2872 assert_equal 0, j.details.count
2853
2873
2854 j = Journal.order('id DESC').offset(1).first
2874 j = Journal.order('id DESC').offset(1).first
2855 assert_nil j.notes
2875 assert_nil j.notes
2856 assert_equal false, j.private_notes
2876 assert_equal false, j.private_notes
2857 assert_equal 1, j.details.count
2877 assert_equal 1, j.details.count
2858 end
2878 end
2859
2879
2860 def test_put_update_with_note_and_spent_time
2880 def test_put_update_with_note_and_spent_time
2861 @request.session[:user_id] = 2
2881 @request.session[:user_id] = 2
2862 spent_hours_before = Issue.find(1).spent_hours
2882 spent_hours_before = Issue.find(1).spent_hours
2863 assert_difference('TimeEntry.count') do
2883 assert_difference('TimeEntry.count') do
2864 put :update,
2884 put :update,
2865 :id => 1,
2885 :id => 1,
2866 :issue => { :notes => '2.5 hours added' },
2886 :issue => { :notes => '2.5 hours added' },
2867 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
2887 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
2868 end
2888 end
2869 assert_redirected_to :action => 'show', :id => '1'
2889 assert_redirected_to :action => 'show', :id => '1'
2870
2890
2871 issue = Issue.find(1)
2891 issue = Issue.find(1)
2872
2892
2873 j = Journal.find(:first, :order => 'id DESC')
2893 j = Journal.find(:first, :order => 'id DESC')
2874 assert_equal '2.5 hours added', j.notes
2894 assert_equal '2.5 hours added', j.notes
2875 assert_equal 0, j.details.size
2895 assert_equal 0, j.details.size
2876
2896
2877 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
2897 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
2878 assert_not_nil t
2898 assert_not_nil t
2879 assert_equal 2.5, t.hours
2899 assert_equal 2.5, t.hours
2880 assert_equal spent_hours_before + 2.5, issue.spent_hours
2900 assert_equal spent_hours_before + 2.5, issue.spent_hours
2881 end
2901 end
2882
2902
2883 def test_put_update_with_attachment_only
2903 def test_put_update_with_attachment_only
2884 set_tmp_attachments_directory
2904 set_tmp_attachments_directory
2885
2905
2886 # Delete all fixtured journals, a race condition can occur causing the wrong
2906 # Delete all fixtured journals, a race condition can occur causing the wrong
2887 # journal to get fetched in the next find.
2907 # journal to get fetched in the next find.
2888 Journal.delete_all
2908 Journal.delete_all
2889
2909
2890 # anonymous user
2910 # anonymous user
2891 assert_difference 'Attachment.count' do
2911 assert_difference 'Attachment.count' do
2892 put :update, :id => 1,
2912 put :update, :id => 1,
2893 :issue => {:notes => ''},
2913 :issue => {:notes => ''},
2894 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2914 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2895 end
2915 end
2896
2916
2897 assert_redirected_to :action => 'show', :id => '1'
2917 assert_redirected_to :action => 'show', :id => '1'
2898 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
2918 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
2899 assert j.notes.blank?
2919 assert j.notes.blank?
2900 assert_equal 1, j.details.size
2920 assert_equal 1, j.details.size
2901 assert_equal 'testfile.txt', j.details.first.value
2921 assert_equal 'testfile.txt', j.details.first.value
2902 assert_equal User.anonymous, j.user
2922 assert_equal User.anonymous, j.user
2903
2923
2904 attachment = Attachment.first(:order => 'id DESC')
2924 attachment = Attachment.first(:order => 'id DESC')
2905 assert_equal Issue.find(1), attachment.container
2925 assert_equal Issue.find(1), attachment.container
2906 assert_equal User.anonymous, attachment.author
2926 assert_equal User.anonymous, attachment.author
2907 assert_equal 'testfile.txt', attachment.filename
2927 assert_equal 'testfile.txt', attachment.filename
2908 assert_equal 'text/plain', attachment.content_type
2928 assert_equal 'text/plain', attachment.content_type
2909 assert_equal 'test file', attachment.description
2929 assert_equal 'test file', attachment.description
2910 assert_equal 59, attachment.filesize
2930 assert_equal 59, attachment.filesize
2911 assert File.exists?(attachment.diskfile)
2931 assert File.exists?(attachment.diskfile)
2912 assert_equal 59, File.size(attachment.diskfile)
2932 assert_equal 59, File.size(attachment.diskfile)
2913
2933
2914 mail = ActionMailer::Base.deliveries.last
2934 mail = ActionMailer::Base.deliveries.last
2915 assert_mail_body_match 'testfile.txt', mail
2935 assert_mail_body_match 'testfile.txt', mail
2916 end
2936 end
2917
2937
2918 def test_put_update_with_failure_should_save_attachments
2938 def test_put_update_with_failure_should_save_attachments
2919 set_tmp_attachments_directory
2939 set_tmp_attachments_directory
2920 @request.session[:user_id] = 2
2940 @request.session[:user_id] = 2
2921
2941
2922 assert_no_difference 'Journal.count' do
2942 assert_no_difference 'Journal.count' do
2923 assert_difference 'Attachment.count' do
2943 assert_difference 'Attachment.count' do
2924 put :update, :id => 1,
2944 put :update, :id => 1,
2925 :issue => { :subject => '' },
2945 :issue => { :subject => '' },
2926 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2946 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain'), 'description' => 'test file'}}
2927 assert_response :success
2947 assert_response :success
2928 assert_template 'edit'
2948 assert_template 'edit'
2929 end
2949 end
2930 end
2950 end
2931
2951
2932 attachment = Attachment.first(:order => 'id DESC')
2952 attachment = Attachment.first(:order => 'id DESC')
2933 assert_equal 'testfile.txt', attachment.filename
2953 assert_equal 'testfile.txt', attachment.filename
2934 assert File.exists?(attachment.diskfile)
2954 assert File.exists?(attachment.diskfile)
2935 assert_nil attachment.container
2955 assert_nil attachment.container
2936
2956
2937 assert_tag 'input', :attributes => {:name => 'attachments[p0][token]', :value => attachment.token}
2957 assert_tag 'input', :attributes => {:name => 'attachments[p0][token]', :value => attachment.token}
2938 assert_tag 'span', :content => /testfile.txt/
2958 assert_tag 'span', :content => /testfile.txt/
2939 end
2959 end
2940
2960
2941 def test_put_update_with_failure_should_keep_saved_attachments
2961 def test_put_update_with_failure_should_keep_saved_attachments
2942 set_tmp_attachments_directory
2962 set_tmp_attachments_directory
2943 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2963 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2944 @request.session[:user_id] = 2
2964 @request.session[:user_id] = 2
2945
2965
2946 assert_no_difference 'Journal.count' do
2966 assert_no_difference 'Journal.count' do
2947 assert_no_difference 'Attachment.count' do
2967 assert_no_difference 'Attachment.count' do
2948 put :update, :id => 1,
2968 put :update, :id => 1,
2949 :issue => { :subject => '' },
2969 :issue => { :subject => '' },
2950 :attachments => {'p0' => {'token' => attachment.token}}
2970 :attachments => {'p0' => {'token' => attachment.token}}
2951 assert_response :success
2971 assert_response :success
2952 assert_template 'edit'
2972 assert_template 'edit'
2953 end
2973 end
2954 end
2974 end
2955
2975
2956 assert_tag 'input', :attributes => {:name => 'attachments[p0][token]', :value => attachment.token}
2976 assert_tag 'input', :attributes => {:name => 'attachments[p0][token]', :value => attachment.token}
2957 assert_tag 'span', :content => /testfile.txt/
2977 assert_tag 'span', :content => /testfile.txt/
2958 end
2978 end
2959
2979
2960 def test_put_update_should_attach_saved_attachments
2980 def test_put_update_should_attach_saved_attachments
2961 set_tmp_attachments_directory
2981 set_tmp_attachments_directory
2962 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2982 attachment = Attachment.create!(:file => uploaded_test_file("testfile.txt", "text/plain"), :author_id => 2)
2963 @request.session[:user_id] = 2
2983 @request.session[:user_id] = 2
2964
2984
2965 assert_difference 'Journal.count' do
2985 assert_difference 'Journal.count' do
2966 assert_difference 'JournalDetail.count' do
2986 assert_difference 'JournalDetail.count' do
2967 assert_no_difference 'Attachment.count' do
2987 assert_no_difference 'Attachment.count' do
2968 put :update, :id => 1,
2988 put :update, :id => 1,
2969 :issue => {:notes => 'Attachment added'},
2989 :issue => {:notes => 'Attachment added'},
2970 :attachments => {'p0' => {'token' => attachment.token}}
2990 :attachments => {'p0' => {'token' => attachment.token}}
2971 assert_redirected_to '/issues/1'
2991 assert_redirected_to '/issues/1'
2972 end
2992 end
2973 end
2993 end
2974 end
2994 end
2975
2995
2976 attachment.reload
2996 attachment.reload
2977 assert_equal Issue.find(1), attachment.container
2997 assert_equal Issue.find(1), attachment.container
2978
2998
2979 journal = Journal.first(:order => 'id DESC')
2999 journal = Journal.first(:order => 'id DESC')
2980 assert_equal 1, journal.details.size
3000 assert_equal 1, journal.details.size
2981 assert_equal 'testfile.txt', journal.details.first.value
3001 assert_equal 'testfile.txt', journal.details.first.value
2982 end
3002 end
2983
3003
2984 def test_put_update_with_attachment_that_fails_to_save
3004 def test_put_update_with_attachment_that_fails_to_save
2985 set_tmp_attachments_directory
3005 set_tmp_attachments_directory
2986
3006
2987 # Delete all fixtured journals, a race condition can occur causing the wrong
3007 # Delete all fixtured journals, a race condition can occur causing the wrong
2988 # journal to get fetched in the next find.
3008 # journal to get fetched in the next find.
2989 Journal.delete_all
3009 Journal.delete_all
2990
3010
2991 # Mock out the unsaved attachment
3011 # Mock out the unsaved attachment
2992 Attachment.any_instance.stubs(:create).returns(Attachment.new)
3012 Attachment.any_instance.stubs(:create).returns(Attachment.new)
2993
3013
2994 # anonymous user
3014 # anonymous user
2995 put :update,
3015 put :update,
2996 :id => 1,
3016 :id => 1,
2997 :issue => {:notes => ''},
3017 :issue => {:notes => ''},
2998 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
3018 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
2999 assert_redirected_to :action => 'show', :id => '1'
3019 assert_redirected_to :action => 'show', :id => '1'
3000 assert_equal '1 file(s) could not be saved.', flash[:warning]
3020 assert_equal '1 file(s) could not be saved.', flash[:warning]
3001 end
3021 end
3002
3022
3003 def test_put_update_with_no_change
3023 def test_put_update_with_no_change
3004 issue = Issue.find(1)
3024 issue = Issue.find(1)
3005 issue.journals.clear
3025 issue.journals.clear
3006 ActionMailer::Base.deliveries.clear
3026 ActionMailer::Base.deliveries.clear
3007
3027
3008 put :update,
3028 put :update,
3009 :id => 1,
3029 :id => 1,
3010 :issue => {:notes => ''}
3030 :issue => {:notes => ''}
3011 assert_redirected_to :action => 'show', :id => '1'
3031 assert_redirected_to :action => 'show', :id => '1'
3012
3032
3013 issue.reload
3033 issue.reload
3014 assert issue.journals.empty?
3034 assert issue.journals.empty?
3015 # No email should be sent
3035 # No email should be sent
3016 assert ActionMailer::Base.deliveries.empty?
3036 assert ActionMailer::Base.deliveries.empty?
3017 end
3037 end
3018
3038
3019 def test_put_update_should_send_a_notification
3039 def test_put_update_should_send_a_notification
3020 @request.session[:user_id] = 2
3040 @request.session[:user_id] = 2
3021 ActionMailer::Base.deliveries.clear
3041 ActionMailer::Base.deliveries.clear
3022 issue = Issue.find(1)
3042 issue = Issue.find(1)
3023 old_subject = issue.subject
3043 old_subject = issue.subject
3024 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
3044 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
3025
3045
3026 put :update, :id => 1, :issue => {:subject => new_subject,
3046 put :update, :id => 1, :issue => {:subject => new_subject,
3027 :priority_id => '6',
3047 :priority_id => '6',
3028 :category_id => '1' # no change
3048 :category_id => '1' # no change
3029 }
3049 }
3030 assert_equal 1, ActionMailer::Base.deliveries.size
3050 assert_equal 1, ActionMailer::Base.deliveries.size
3031 end
3051 end
3032
3052
3033 def test_put_update_with_invalid_spent_time_hours_only
3053 def test_put_update_with_invalid_spent_time_hours_only
3034 @request.session[:user_id] = 2
3054 @request.session[:user_id] = 2
3035 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3055 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3036
3056
3037 assert_no_difference('Journal.count') do
3057 assert_no_difference('Journal.count') do
3038 put :update,
3058 put :update,
3039 :id => 1,
3059 :id => 1,
3040 :issue => {:notes => notes},
3060 :issue => {:notes => notes},
3041 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
3061 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
3042 end
3062 end
3043 assert_response :success
3063 assert_response :success
3044 assert_template 'edit'
3064 assert_template 'edit'
3045
3065
3046 assert_error_tag :descendant => {:content => /Activity can&#x27;t be blank/}
3066 assert_error_tag :descendant => {:content => /Activity can&#x27;t be blank/}
3047 assert_tag :textarea, :attributes => { :name => 'issue[notes]' }, :content => "\n"+notes
3067 assert_tag :textarea, :attributes => { :name => 'issue[notes]' }, :content => "\n"+notes
3048 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
3068 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
3049 end
3069 end
3050
3070
3051 def test_put_update_with_invalid_spent_time_comments_only
3071 def test_put_update_with_invalid_spent_time_comments_only
3052 @request.session[:user_id] = 2
3072 @request.session[:user_id] = 2
3053 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3073 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
3054
3074
3055 assert_no_difference('Journal.count') do
3075 assert_no_difference('Journal.count') do
3056 put :update,
3076 put :update,
3057 :id => 1,
3077 :id => 1,
3058 :issue => {:notes => notes},
3078 :issue => {:notes => notes},
3059 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
3079 :time_entry => {"comments"=>"this is my comment", "activity_id"=>"", "hours"=>""}
3060 end
3080 end
3061 assert_response :success
3081 assert_response :success
3062 assert_template 'edit'
3082 assert_template 'edit'
3063
3083
3064 assert_error_tag :descendant => {:content => /Activity can&#x27;t be blank/}
3084 assert_error_tag :descendant => {:content => /Activity can&#x27;t be blank/}
3065 assert_error_tag :descendant => {:content => /Hours can&#x27;t be blank/}
3085 assert_error_tag :descendant => {:content => /Hours can&#x27;t be blank/}
3066 assert_tag :textarea, :attributes => { :name => 'issue[notes]' }, :content => "\n"+notes
3086 assert_tag :textarea, :attributes => { :name => 'issue[notes]' }, :content => "\n"+notes
3067 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
3087 assert_tag :input, :attributes => { :name => 'time_entry[comments]', :value => "this is my comment" }
3068 end
3088 end
3069
3089
3070 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
3090 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
3071 issue = Issue.find(2)
3091 issue = Issue.find(2)
3072 @request.session[:user_id] = 2
3092 @request.session[:user_id] = 2
3073
3093
3074 put :update,
3094 put :update,
3075 :id => issue.id,
3095 :id => issue.id,
3076 :issue => {
3096 :issue => {
3077 :fixed_version_id => 4
3097 :fixed_version_id => 4
3078 }
3098 }
3079
3099
3080 assert_response :redirect
3100 assert_response :redirect
3081 issue.reload
3101 issue.reload
3082 assert_equal 4, issue.fixed_version_id
3102 assert_equal 4, issue.fixed_version_id
3083 assert_not_equal issue.project_id, issue.fixed_version.project_id
3103 assert_not_equal issue.project_id, issue.fixed_version.project_id
3084 end
3104 end
3085
3105
3086 def test_put_update_should_redirect_back_using_the_back_url_parameter
3106 def test_put_update_should_redirect_back_using_the_back_url_parameter
3087 issue = Issue.find(2)
3107 issue = Issue.find(2)
3088 @request.session[:user_id] = 2
3108 @request.session[:user_id] = 2
3089
3109
3090 put :update,
3110 put :update,
3091 :id => issue.id,
3111 :id => issue.id,
3092 :issue => {
3112 :issue => {
3093 :fixed_version_id => 4
3113 :fixed_version_id => 4
3094 },
3114 },
3095 :back_url => '/issues'
3115 :back_url => '/issues'
3096
3116
3097 assert_response :redirect
3117 assert_response :redirect
3098 assert_redirected_to '/issues'
3118 assert_redirected_to '/issues'
3099 end
3119 end
3100
3120
3101 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
3121 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
3102 issue = Issue.find(2)
3122 issue = Issue.find(2)
3103 @request.session[:user_id] = 2
3123 @request.session[:user_id] = 2
3104
3124
3105 put :update,
3125 put :update,
3106 :id => issue.id,
3126 :id => issue.id,
3107 :issue => {
3127 :issue => {
3108 :fixed_version_id => 4
3128 :fixed_version_id => 4
3109 },
3129 },
3110 :back_url => 'http://google.com'
3130 :back_url => 'http://google.com'
3111
3131
3112 assert_response :redirect
3132 assert_response :redirect
3113 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
3133 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
3114 end
3134 end
3115
3135
3116 def test_get_bulk_edit
3136 def test_get_bulk_edit
3117 @request.session[:user_id] = 2
3137 @request.session[:user_id] = 2
3118 get :bulk_edit, :ids => [1, 2]
3138 get :bulk_edit, :ids => [1, 2]
3119 assert_response :success
3139 assert_response :success
3120 assert_template 'bulk_edit'
3140 assert_template 'bulk_edit'
3121
3141
3122 assert_tag :select, :attributes => {:name => 'issue[project_id]'}
3142 assert_tag :select, :attributes => {:name => 'issue[project_id]'}
3123 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
3143 assert_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
3124
3144
3125 # Project specific custom field, date type
3145 # Project specific custom field, date type
3126 field = CustomField.find(9)
3146 field = CustomField.find(9)
3127 assert !field.is_for_all?
3147 assert !field.is_for_all?
3128 assert_equal 'date', field.field_format
3148 assert_equal 'date', field.field_format
3129 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
3149 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
3130
3150
3131 # System wide custom field
3151 # System wide custom field
3132 assert CustomField.find(1).is_for_all?
3152 assert CustomField.find(1).is_for_all?
3133 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
3153 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
3134
3154
3135 # Be sure we don't display inactive IssuePriorities
3155 # Be sure we don't display inactive IssuePriorities
3136 assert ! IssuePriority.find(15).active?
3156 assert ! IssuePriority.find(15).active?
3137 assert_no_tag :option, :attributes => {:value => '15'},
3157 assert_no_tag :option, :attributes => {:value => '15'},
3138 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
3158 :parent => {:tag => 'select', :attributes => {:id => 'issue_priority_id'} }
3139 end
3159 end
3140
3160
3141 def test_get_bulk_edit_on_different_projects
3161 def test_get_bulk_edit_on_different_projects
3142 @request.session[:user_id] = 2
3162 @request.session[:user_id] = 2
3143 get :bulk_edit, :ids => [1, 2, 6]
3163 get :bulk_edit, :ids => [1, 2, 6]
3144 assert_response :success
3164 assert_response :success
3145 assert_template 'bulk_edit'
3165 assert_template 'bulk_edit'
3146
3166
3147 # Can not set issues from different projects as children of an issue
3167 # Can not set issues from different projects as children of an issue
3148 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
3168 assert_no_tag :input, :attributes => {:name => 'issue[parent_issue_id]'}
3149
3169
3150 # Project specific custom field, date type
3170 # Project specific custom field, date type
3151 field = CustomField.find(9)
3171 field = CustomField.find(9)
3152 assert !field.is_for_all?
3172 assert !field.is_for_all?
3153 assert !field.project_ids.include?(Issue.find(6).project_id)
3173 assert !field.project_ids.include?(Issue.find(6).project_id)
3154 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
3174 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
3155 end
3175 end
3156
3176
3157 def test_get_bulk_edit_with_user_custom_field
3177 def test_get_bulk_edit_with_user_custom_field
3158 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
3178 field = IssueCustomField.create!(:name => 'Tester', :field_format => 'user', :is_for_all => true)
3159
3179
3160 @request.session[:user_id] = 2
3180 @request.session[:user_id] = 2
3161 get :bulk_edit, :ids => [1, 2]
3181 get :bulk_edit, :ids => [1, 2]
3162 assert_response :success
3182 assert_response :success
3163 assert_template 'bulk_edit'
3183 assert_template 'bulk_edit'
3164
3184
3165 assert_tag :select,
3185 assert_tag :select,
3166 :attributes => {:name => "issue[custom_field_values][#{field.id}]", :class => 'user_cf'},
3186 :attributes => {:name => "issue[custom_field_values][#{field.id}]", :class => 'user_cf'},
3167 :children => {
3187 :children => {
3168 :only => {:tag => 'option'},
3188 :only => {:tag => 'option'},
3169 :count => Project.find(1).users.count + 2 # "no change" + "none" options
3189 :count => Project.find(1).users.count + 2 # "no change" + "none" options
3170 }
3190 }
3171 end
3191 end
3172
3192
3173 def test_get_bulk_edit_with_version_custom_field
3193 def test_get_bulk_edit_with_version_custom_field
3174 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
3194 field = IssueCustomField.create!(:name => 'Affected version', :field_format => 'version', :is_for_all => true)
3175
3195
3176 @request.session[:user_id] = 2
3196 @request.session[:user_id] = 2
3177 get :bulk_edit, :ids => [1, 2]
3197 get :bulk_edit, :ids => [1, 2]
3178 assert_response :success
3198 assert_response :success
3179 assert_template 'bulk_edit'
3199 assert_template 'bulk_edit'
3180
3200
3181 assert_tag :select,
3201 assert_tag :select,
3182 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
3202 :attributes => {:name => "issue[custom_field_values][#{field.id}]"},
3183 :children => {
3203 :children => {
3184 :only => {:tag => 'option'},
3204 :only => {:tag => 'option'},
3185 :count => Project.find(1).shared_versions.count + 2 # "no change" + "none" options
3205 :count => Project.find(1).shared_versions.count + 2 # "no change" + "none" options
3186 }
3206 }
3187 end
3207 end
3188
3208
3189 def test_get_bulk_edit_with_multi_custom_field
3209 def test_get_bulk_edit_with_multi_custom_field
3190 field = CustomField.find(1)
3210 field = CustomField.find(1)
3191 field.update_attribute :multiple, true
3211 field.update_attribute :multiple, true
3192
3212
3193 @request.session[:user_id] = 2
3213 @request.session[:user_id] = 2
3194 get :bulk_edit, :ids => [1, 2]
3214 get :bulk_edit, :ids => [1, 2]
3195 assert_response :success
3215 assert_response :success
3196 assert_template 'bulk_edit'
3216 assert_template 'bulk_edit'
3197
3217
3198 assert_tag :select,
3218 assert_tag :select,
3199 :attributes => {:name => "issue[custom_field_values][1][]"},
3219 :attributes => {:name => "issue[custom_field_values][1][]"},
3200 :children => {
3220 :children => {
3201 :only => {:tag => 'option'},
3221 :only => {:tag => 'option'},
3202 :count => field.possible_values.size + 1 # "none" options
3222 :count => field.possible_values.size + 1 # "none" options
3203 }
3223 }
3204 end
3224 end
3205
3225
3206 def test_bulk_edit_should_only_propose_statuses_allowed_for_all_issues
3226 def test_bulk_edit_should_only_propose_statuses_allowed_for_all_issues
3207 WorkflowTransition.delete_all
3227 WorkflowTransition.delete_all
3208 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 1)
3228 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 1)
3209 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
3229 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 3)
3210 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
3230 WorkflowTransition.create!(:role_id => 1, :tracker_id => 1, :old_status_id => 1, :new_status_id => 4)
3211 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
3231 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 1)
3212 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3)
3232 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 3)
3213 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
3233 WorkflowTransition.create!(:role_id => 1, :tracker_id => 2, :old_status_id => 2, :new_status_id => 5)
3214 @request.session[:user_id] = 2
3234 @request.session[:user_id] = 2
3215 get :bulk_edit, :ids => [1, 2]
3235 get :bulk_edit, :ids => [1, 2]
3216
3236
3217 assert_response :success
3237 assert_response :success
3218 statuses = assigns(:available_statuses)
3238 statuses = assigns(:available_statuses)
3219 assert_not_nil statuses
3239 assert_not_nil statuses
3220 assert_equal [1, 3], statuses.map(&:id).sort
3240 assert_equal [1, 3], statuses.map(&:id).sort
3221
3241
3222 assert_tag 'select', :attributes => {:name => 'issue[status_id]'},
3242 assert_tag 'select', :attributes => {:name => 'issue[status_id]'},
3223 :children => {:count => 3} # 2 statuses + "no change" option
3243 :children => {:count => 3} # 2 statuses + "no change" option
3224 end
3244 end
3225
3245
3226 def test_bulk_edit_should_propose_target_project_open_shared_versions
3246 def test_bulk_edit_should_propose_target_project_open_shared_versions
3227 @request.session[:user_id] = 2
3247 @request.session[:user_id] = 2
3228 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3248 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3229 assert_response :success
3249 assert_response :success
3230 assert_template 'bulk_edit'
3250 assert_template 'bulk_edit'
3231 assert_equal Project.find(1).shared_versions.open.all.sort, assigns(:versions).sort
3251 assert_equal Project.find(1).shared_versions.open.all.sort, assigns(:versions).sort
3232 assert_tag 'select',
3252 assert_tag 'select',
3233 :attributes => {:name => 'issue[fixed_version_id]'},
3253 :attributes => {:name => 'issue[fixed_version_id]'},
3234 :descendant => {:tag => 'option', :content => '2.0'}
3254 :descendant => {:tag => 'option', :content => '2.0'}
3235 end
3255 end
3236
3256
3237 def test_bulk_edit_should_propose_target_project_categories
3257 def test_bulk_edit_should_propose_target_project_categories
3238 @request.session[:user_id] = 2
3258 @request.session[:user_id] = 2
3239 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3259 post :bulk_edit, :ids => [1, 2, 6], :issue => {:project_id => 1}
3240 assert_response :success
3260 assert_response :success
3241 assert_template 'bulk_edit'
3261 assert_template 'bulk_edit'
3242 assert_equal Project.find(1).issue_categories.sort, assigns(:categories).sort
3262 assert_equal Project.find(1).issue_categories.sort, assigns(:categories).sort
3243 assert_tag 'select',
3263 assert_tag 'select',
3244 :attributes => {:name => 'issue[category_id]'},
3264 :attributes => {:name => 'issue[category_id]'},
3245 :descendant => {:tag => 'option', :content => 'Recipes'}
3265 :descendant => {:tag => 'option', :content => 'Recipes'}
3246 end
3266 end
3247
3267
3248 def test_bulk_update
3268 def test_bulk_update
3249 @request.session[:user_id] = 2
3269 @request.session[:user_id] = 2
3250 # update issues priority
3270 # update issues priority
3251 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
3271 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
3252 :issue => {:priority_id => 7,
3272 :issue => {:priority_id => 7,
3253 :assigned_to_id => '',
3273 :assigned_to_id => '',
3254 :custom_field_values => {'2' => ''}}
3274 :custom_field_values => {'2' => ''}}
3255
3275
3256 assert_response 302
3276 assert_response 302
3257 # check that the issues were updated
3277 # check that the issues were updated
3258 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
3278 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
3259
3279
3260 issue = Issue.find(1)
3280 issue = Issue.find(1)
3261 journal = issue.journals.find(:first, :order => 'created_on DESC')
3281 journal = issue.journals.find(:first, :order => 'created_on DESC')
3262 assert_equal '125', issue.custom_value_for(2).value
3282 assert_equal '125', issue.custom_value_for(2).value
3263 assert_equal 'Bulk editing', journal.notes
3283 assert_equal 'Bulk editing', journal.notes
3264 assert_equal 1, journal.details.size
3284 assert_equal 1, journal.details.size
3265 end
3285 end
3266
3286
3267 def test_bulk_update_with_group_assignee
3287 def test_bulk_update_with_group_assignee
3268 group = Group.find(11)
3288 group = Group.find(11)
3269 project = Project.find(1)
3289 project = Project.find(1)
3270 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
3290 project.members << Member.new(:principal => group, :roles => [Role.givable.first])
3271
3291
3272 @request.session[:user_id] = 2
3292 @request.session[:user_id] = 2
3273 # update issues assignee
3293 # update issues assignee
3274 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
3294 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
3275 :issue => {:priority_id => '',
3295 :issue => {:priority_id => '',
3276 :assigned_to_id => group.id,
3296 :assigned_to_id => group.id,
3277 :custom_field_values => {'2' => ''}}
3297 :custom_field_values => {'2' => ''}}
3278
3298
3279 assert_response 302
3299 assert_response 302
3280 assert_equal [group, group], Issue.find_all_by_id([1, 2]).collect {|i| i.assigned_to}
3300 assert_equal [group, group], Issue.find_all_by_id([1, 2]).collect {|i| i.assigned_to}
3281 end
3301 end
3282
3302
3283 def test_bulk_update_on_different_projects
3303 def test_bulk_update_on_different_projects
3284 @request.session[:user_id] = 2
3304 @request.session[:user_id] = 2
3285 # update issues priority
3305 # update issues priority
3286 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
3306 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
3287 :issue => {:priority_id => 7,
3307 :issue => {:priority_id => 7,
3288 :assigned_to_id => '',
3308 :assigned_to_id => '',
3289 :custom_field_values => {'2' => ''}}
3309 :custom_field_values => {'2' => ''}}
3290
3310
3291 assert_response 302
3311 assert_response 302
3292 # check that the issues were updated
3312 # check that the issues were updated
3293 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
3313 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
3294
3314
3295 issue = Issue.find(1)
3315 issue = Issue.find(1)
3296 journal = issue.journals.find(:first, :order => 'created_on DESC')
3316 journal = issue.journals.find(:first, :order => 'created_on DESC')
3297 assert_equal '125', issue.custom_value_for(2).value
3317 assert_equal '125', issue.custom_value_for(2).value
3298 assert_equal 'Bulk editing', journal.notes
3318 assert_equal 'Bulk editing', journal.notes
3299 assert_equal 1, journal.details.size
3319 assert_equal 1, journal.details.size
3300 end
3320 end
3301
3321
3302 def test_bulk_update_on_different_projects_without_rights
3322 def test_bulk_update_on_different_projects_without_rights
3303 @request.session[:user_id] = 3
3323 @request.session[:user_id] = 3
3304 user = User.find(3)
3324 user = User.find(3)
3305 action = { :controller => "issues", :action => "bulk_update" }
3325 action = { :controller => "issues", :action => "bulk_update" }
3306 assert user.allowed_to?(action, Issue.find(1).project)
3326 assert user.allowed_to?(action, Issue.find(1).project)
3307 assert ! user.allowed_to?(action, Issue.find(6).project)
3327 assert ! user.allowed_to?(action, Issue.find(6).project)
3308 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
3328 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
3309 :issue => {:priority_id => 7,
3329 :issue => {:priority_id => 7,
3310 :assigned_to_id => '',
3330 :assigned_to_id => '',
3311 :custom_field_values => {'2' => ''}}
3331 :custom_field_values => {'2' => ''}}
3312 assert_response 403
3332 assert_response 403
3313 assert_not_equal "Bulk should fail", Journal.last.notes
3333 assert_not_equal "Bulk should fail", Journal.last.notes
3314 end
3334 end
3315
3335
3316 def test_bullk_update_should_send_a_notification
3336 def test_bullk_update_should_send_a_notification
3317 @request.session[:user_id] = 2
3337 @request.session[:user_id] = 2
3318 ActionMailer::Base.deliveries.clear
3338 ActionMailer::Base.deliveries.clear
3319 post(:bulk_update,
3339 post(:bulk_update,
3320 {
3340 {
3321 :ids => [1, 2],
3341 :ids => [1, 2],
3322 :notes => 'Bulk editing',
3342 :notes => 'Bulk editing',
3323 :issue => {
3343 :issue => {
3324 :priority_id => 7,
3344 :priority_id => 7,
3325 :assigned_to_id => '',
3345 :assigned_to_id => '',
3326 :custom_field_values => {'2' => ''}
3346 :custom_field_values => {'2' => ''}
3327 }
3347 }
3328 })
3348 })
3329
3349
3330 assert_response 302
3350 assert_response 302
3331 assert_equal 2, ActionMailer::Base.deliveries.size
3351 assert_equal 2, ActionMailer::Base.deliveries.size
3332 end
3352 end
3333
3353
3334 def test_bulk_update_project
3354 def test_bulk_update_project
3335 @request.session[:user_id] = 2
3355 @request.session[:user_id] = 2
3336 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
3356 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}
3337 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
3357 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
3338 # Issues moved to project 2
3358 # Issues moved to project 2
3339 assert_equal 2, Issue.find(1).project_id
3359 assert_equal 2, Issue.find(1).project_id
3340 assert_equal 2, Issue.find(2).project_id
3360 assert_equal 2, Issue.find(2).project_id
3341 # No tracker change
3361 # No tracker change
3342 assert_equal 1, Issue.find(1).tracker_id
3362 assert_equal 1, Issue.find(1).tracker_id
3343 assert_equal 2, Issue.find(2).tracker_id
3363 assert_equal 2, Issue.find(2).tracker_id
3344 end
3364 end
3345
3365
3346 def test_bulk_update_project_on_single_issue_should_follow_when_needed
3366 def test_bulk_update_project_on_single_issue_should_follow_when_needed
3347 @request.session[:user_id] = 2
3367 @request.session[:user_id] = 2
3348 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
3368 post :bulk_update, :id => 1, :issue => {:project_id => '2'}, :follow => '1'
3349 assert_redirected_to '/issues/1'
3369 assert_redirected_to '/issues/1'
3350 end
3370 end
3351
3371
3352 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
3372 def test_bulk_update_project_on_multiple_issues_should_follow_when_needed
3353 @request.session[:user_id] = 2
3373 @request.session[:user_id] = 2
3354 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
3374 post :bulk_update, :id => [1, 2], :issue => {:project_id => '2'}, :follow => '1'
3355 assert_redirected_to '/projects/onlinestore/issues'
3375 assert_redirected_to '/projects/onlinestore/issues'
3356 end
3376 end
3357
3377
3358 def test_bulk_update_tracker
3378 def test_bulk_update_tracker
3359 @request.session[:user_id] = 2
3379 @request.session[:user_id] = 2
3360 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
3380 post :bulk_update, :ids => [1, 2], :issue => {:tracker_id => '2'}
3361 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
3381 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
3362 assert_equal 2, Issue.find(1).tracker_id
3382 assert_equal 2, Issue.find(1).tracker_id
3363 assert_equal 2, Issue.find(2).tracker_id
3383 assert_equal 2, Issue.find(2).tracker_id
3364 end
3384 end
3365
3385
3366 def test_bulk_update_status
3386 def test_bulk_update_status
3367 @request.session[:user_id] = 2
3387 @request.session[:user_id] = 2
3368 # update issues priority
3388 # update issues priority
3369 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
3389 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
3370 :issue => {:priority_id => '',
3390 :issue => {:priority_id => '',
3371 :assigned_to_id => '',
3391 :assigned_to_id => '',
3372 :status_id => '5'}
3392 :status_id => '5'}
3373
3393
3374 assert_response 302
3394 assert_response 302
3375 issue = Issue.find(1)
3395 issue = Issue.find(1)
3376 assert issue.closed?
3396 assert issue.closed?
3377 end
3397 end
3378
3398
3379 def test_bulk_update_priority
3399 def test_bulk_update_priority
3380 @request.session[:user_id] = 2
3400 @request.session[:user_id] = 2
3381 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
3401 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
3382
3402
3383 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
3403 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
3384 assert_equal 6, Issue.find(1).priority_id
3404 assert_equal 6, Issue.find(1).priority_id
3385 assert_equal 6, Issue.find(2).priority_id
3405 assert_equal 6, Issue.find(2).priority_id
3386 end
3406 end
3387
3407
3388 def test_bulk_update_with_notes
3408 def test_bulk_update_with_notes
3389 @request.session[:user_id] = 2
3409 @request.session[:user_id] = 2
3390 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
3410 post :bulk_update, :ids => [1, 2], :notes => 'Moving two issues'
3391
3411
3392 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
3412 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
3393 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
3413 assert_equal 'Moving two issues', Issue.find(1).journals.sort_by(&:id).last.notes
3394 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
3414 assert_equal 'Moving two issues', Issue.find(2).journals.sort_by(&:id).last.notes
3395 end
3415 end
3396
3416
3397 def test_bulk_update_parent_id
3417 def test_bulk_update_parent_id
3398 @request.session[:user_id] = 2
3418 @request.session[:user_id] = 2
3399 post :bulk_update, :ids => [1, 3],
3419 post :bulk_update, :ids => [1, 3],
3400 :notes => 'Bulk editing parent',
3420 :notes => 'Bulk editing parent',
3401 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
3421 :issue => {:priority_id => '', :assigned_to_id => '', :status_id => '', :parent_issue_id => '2'}
3402
3422
3403 assert_response 302
3423 assert_response 302
3404 parent = Issue.find(2)
3424 parent = Issue.find(2)
3405 assert_equal parent.id, Issue.find(1).parent_id
3425 assert_equal parent.id, Issue.find(1).parent_id
3406 assert_equal parent.id, Issue.find(3).parent_id
3426 assert_equal parent.id, Issue.find(3).parent_id
3407 assert_equal [1, 3], parent.children.collect(&:id).sort
3427 assert_equal [1, 3], parent.children.collect(&:id).sort
3408 end
3428 end
3409
3429
3410 def test_bulk_update_custom_field
3430 def test_bulk_update_custom_field
3411 @request.session[:user_id] = 2
3431 @request.session[:user_id] = 2
3412 # update issues priority
3432 # update issues priority
3413 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
3433 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
3414 :issue => {:priority_id => '',
3434 :issue => {:priority_id => '',
3415 :assigned_to_id => '',
3435 :assigned_to_id => '',
3416 :custom_field_values => {'2' => '777'}}
3436 :custom_field_values => {'2' => '777'}}
3417
3437
3418 assert_response 302
3438 assert_response 302
3419
3439
3420 issue = Issue.find(1)
3440 issue = Issue.find(1)
3421 journal = issue.journals.find(:first, :order => 'created_on DESC')
3441 journal = issue.journals.find(:first, :order => 'created_on DESC')
3422 assert_equal '777', issue.custom_value_for(2).value
3442 assert_equal '777', issue.custom_value_for(2).value
3423 assert_equal 1, journal.details.size
3443 assert_equal 1, journal.details.size
3424 assert_equal '125', journal.details.first.old_value
3444 assert_equal '125', journal.details.first.old_value
3425 assert_equal '777', journal.details.first.value
3445 assert_equal '777', journal.details.first.value
3426 end
3446 end
3427
3447
3428 def test_bulk_update_custom_field_to_blank
3448 def test_bulk_update_custom_field_to_blank
3429 @request.session[:user_id] = 2
3449 @request.session[:user_id] = 2
3430 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing custom field',
3450 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing custom field',
3431 :issue => {:priority_id => '',
3451 :issue => {:priority_id => '',
3432 :assigned_to_id => '',
3452 :assigned_to_id => '',
3433 :custom_field_values => {'1' => '__none__'}}
3453 :custom_field_values => {'1' => '__none__'}}
3434 assert_response 302
3454 assert_response 302
3435 assert_equal '', Issue.find(1).custom_field_value(1)
3455 assert_equal '', Issue.find(1).custom_field_value(1)
3436 assert_equal '', Issue.find(3).custom_field_value(1)
3456 assert_equal '', Issue.find(3).custom_field_value(1)
3437 end
3457 end
3438
3458
3439 def test_bulk_update_multi_custom_field
3459 def test_bulk_update_multi_custom_field
3440 field = CustomField.find(1)
3460 field = CustomField.find(1)
3441 field.update_attribute :multiple, true
3461 field.update_attribute :multiple, true
3442
3462
3443 @request.session[:user_id] = 2
3463 @request.session[:user_id] = 2
3444 post :bulk_update, :ids => [1, 2, 3], :notes => 'Bulk editing multi custom field',
3464 post :bulk_update, :ids => [1, 2, 3], :notes => 'Bulk editing multi custom field',
3445 :issue => {:priority_id => '',
3465 :issue => {:priority_id => '',
3446 :assigned_to_id => '',
3466 :assigned_to_id => '',
3447 :custom_field_values => {'1' => ['MySQL', 'Oracle']}}
3467 :custom_field_values => {'1' => ['MySQL', 'Oracle']}}
3448
3468
3449 assert_response 302
3469 assert_response 302
3450
3470
3451 assert_equal ['MySQL', 'Oracle'], Issue.find(1).custom_field_value(1).sort
3471 assert_equal ['MySQL', 'Oracle'], Issue.find(1).custom_field_value(1).sort
3452 assert_equal ['MySQL', 'Oracle'], Issue.find(3).custom_field_value(1).sort
3472 assert_equal ['MySQL', 'Oracle'], Issue.find(3).custom_field_value(1).sort
3453 # the custom field is not associated with the issue tracker
3473 # the custom field is not associated with the issue tracker
3454 assert_nil Issue.find(2).custom_field_value(1)
3474 assert_nil Issue.find(2).custom_field_value(1)
3455 end
3475 end
3456
3476
3457 def test_bulk_update_multi_custom_field_to_blank
3477 def test_bulk_update_multi_custom_field_to_blank
3458 field = CustomField.find(1)
3478 field = CustomField.find(1)
3459 field.update_attribute :multiple, true
3479 field.update_attribute :multiple, true
3460
3480
3461 @request.session[:user_id] = 2
3481 @request.session[:user_id] = 2
3462 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing multi custom field',
3482 post :bulk_update, :ids => [1, 3], :notes => 'Bulk editing multi custom field',
3463 :issue => {:priority_id => '',
3483 :issue => {:priority_id => '',
3464 :assigned_to_id => '',
3484 :assigned_to_id => '',
3465 :custom_field_values => {'1' => ['__none__']}}
3485 :custom_field_values => {'1' => ['__none__']}}
3466 assert_response 302
3486 assert_response 302
3467 assert_equal [''], Issue.find(1).custom_field_value(1)
3487 assert_equal [''], Issue.find(1).custom_field_value(1)
3468 assert_equal [''], Issue.find(3).custom_field_value(1)
3488 assert_equal [''], Issue.find(3).custom_field_value(1)
3469 end
3489 end
3470
3490
3471 def test_bulk_update_unassign
3491 def test_bulk_update_unassign
3472 assert_not_nil Issue.find(2).assigned_to
3492 assert_not_nil Issue.find(2).assigned_to
3473 @request.session[:user_id] = 2
3493 @request.session[:user_id] = 2
3474 # unassign issues
3494 # unassign issues
3475 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
3495 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
3476 assert_response 302
3496 assert_response 302
3477 # check that the issues were updated
3497 # check that the issues were updated
3478 assert_nil Issue.find(2).assigned_to
3498 assert_nil Issue.find(2).assigned_to
3479 end
3499 end
3480
3500
3481 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
3501 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
3482 @request.session[:user_id] = 2
3502 @request.session[:user_id] = 2
3483
3503
3484 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
3504 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
3485
3505
3486 assert_response :redirect
3506 assert_response :redirect
3487 issues = Issue.find([1,2])
3507 issues = Issue.find([1,2])
3488 issues.each do |issue|
3508 issues.each do |issue|
3489 assert_equal 4, issue.fixed_version_id
3509 assert_equal 4, issue.fixed_version_id
3490 assert_not_equal issue.project_id, issue.fixed_version.project_id
3510 assert_not_equal issue.project_id, issue.fixed_version.project_id
3491 end
3511 end
3492 end
3512 end
3493
3513
3494 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
3514 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
3495 @request.session[:user_id] = 2
3515 @request.session[:user_id] = 2
3496 post :bulk_update, :ids => [1,2], :back_url => '/issues'
3516 post :bulk_update, :ids => [1,2], :back_url => '/issues'
3497
3517
3498 assert_response :redirect
3518 assert_response :redirect
3499 assert_redirected_to '/issues'
3519 assert_redirected_to '/issues'
3500 end
3520 end
3501
3521
3502 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
3522 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
3503 @request.session[:user_id] = 2
3523 @request.session[:user_id] = 2
3504 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
3524 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
3505
3525
3506 assert_response :redirect
3526 assert_response :redirect
3507 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
3527 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
3508 end
3528 end
3509
3529
3510 def test_bulk_update_with_failure_should_set_flash
3530 def test_bulk_update_with_failure_should_set_flash
3511 @request.session[:user_id] = 2
3531 @request.session[:user_id] = 2
3512 Issue.update_all("subject = ''", "id = 2") # Make it invalid
3532 Issue.update_all("subject = ''", "id = 2") # Make it invalid
3513 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
3533 post :bulk_update, :ids => [1, 2], :issue => {:priority_id => 6}
3514
3534
3515 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
3535 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => 'ecookbook'
3516 assert_equal 'Failed to save 1 issue(s) on 2 selected: #2.', flash[:error]
3536 assert_equal 'Failed to save 1 issue(s) on 2 selected: #2.', flash[:error]
3517 end
3537 end
3518
3538
3519 def test_get_bulk_copy
3539 def test_get_bulk_copy
3520 @request.session[:user_id] = 2
3540 @request.session[:user_id] = 2
3521 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
3541 get :bulk_edit, :ids => [1, 2, 3], :copy => '1'
3522 assert_response :success
3542 assert_response :success
3523 assert_template 'bulk_edit'
3543 assert_template 'bulk_edit'
3524
3544
3525 issues = assigns(:issues)
3545 issues = assigns(:issues)
3526 assert_not_nil issues
3546 assert_not_nil issues
3527 assert_equal [1, 2, 3], issues.map(&:id).sort
3547 assert_equal [1, 2, 3], issues.map(&:id).sort
3528
3548
3529 assert_select 'input[name=copy_attachments]'
3549 assert_select 'input[name=copy_attachments]'
3530 end
3550 end
3531
3551
3532 def test_bulk_copy_to_another_project
3552 def test_bulk_copy_to_another_project
3533 @request.session[:user_id] = 2
3553 @request.session[:user_id] = 2
3534 assert_difference 'Issue.count', 2 do
3554 assert_difference 'Issue.count', 2 do
3535 assert_no_difference 'Project.find(1).issues.count' do
3555 assert_no_difference 'Project.find(1).issues.count' do
3536 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
3556 post :bulk_update, :ids => [1, 2], :issue => {:project_id => '2'}, :copy => '1'
3537 end
3557 end
3538 end
3558 end
3539 assert_redirected_to '/projects/ecookbook/issues'
3559 assert_redirected_to '/projects/ecookbook/issues'
3540
3560
3541 copies = Issue.all(:order => 'id DESC', :limit => issues.size)
3561 copies = Issue.all(:order => 'id DESC', :limit => issues.size)
3542 copies.each do |copy|
3562 copies.each do |copy|
3543 assert_equal 2, copy.project_id
3563 assert_equal 2, copy.project_id
3544 end
3564 end
3545 end
3565 end
3546
3566
3547 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
3567 def test_bulk_copy_should_allow_not_changing_the_issue_attributes
3548 @request.session[:user_id] = 2
3568 @request.session[:user_id] = 2
3549 issues = [
3569 issues = [
3550 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1, :priority_id => 2, :subject => 'issue 1', :author_id => 1, :assigned_to_id => nil),
3570 Issue.create!(:project_id => 1, :tracker_id => 1, :status_id => 1, :priority_id => 2, :subject => 'issue 1', :author_id => 1, :assigned_to_id => nil),
3551 Issue.create!(:project_id => 2, :tracker_id => 3, :status_id => 2, :priority_id => 1, :subject => 'issue 2', :author_id => 2, :assigned_to_id => 3)
3571 Issue.create!(:project_id => 2, :tracker_id => 3, :status_id => 2, :priority_id => 1, :subject => 'issue 2', :author_id => 2, :assigned_to_id => 3)
3552 ]
3572 ]
3553
3573
3554 assert_difference 'Issue.count', issues.size do
3574 assert_difference 'Issue.count', issues.size do
3555 post :bulk_update, :ids => issues.map(&:id), :copy => '1',
3575 post :bulk_update, :ids => issues.map(&:id), :copy => '1',
3556 :issue => {
3576 :issue => {
3557 :project_id => '', :tracker_id => '', :assigned_to_id => '',
3577 :project_id => '', :tracker_id => '', :assigned_to_id => '',
3558 :status_id => '', :start_date => '', :due_date => ''
3578 :status_id => '', :start_date => '', :due_date => ''
3559 }
3579 }
3560 end
3580 end
3561
3581
3562 copies = Issue.all(:order => 'id DESC', :limit => issues.size)
3582 copies = Issue.all(:order => 'id DESC', :limit => issues.size)
3563 issues.each do |orig|
3583 issues.each do |orig|
3564 copy = copies.detect {|c| c.subject == orig.subject}
3584 copy = copies.detect {|c| c.subject == orig.subject}
3565 assert_not_nil copy
3585 assert_not_nil copy
3566 assert_equal orig.project_id, copy.project_id
3586 assert_equal orig.project_id, copy.project_id
3567 assert_equal orig.tracker_id, copy.tracker_id
3587 assert_equal orig.tracker_id, copy.tracker_id
3568 assert_equal orig.status_id, copy.status_id
3588 assert_equal orig.status_id, copy.status_id
3569 assert_equal orig.assigned_to_id, copy.assigned_to_id
3589 assert_equal orig.assigned_to_id, copy.assigned_to_id
3570 assert_equal orig.priority_id, copy.priority_id
3590 assert_equal orig.priority_id, copy.priority_id
3571 end
3591 end
3572 end
3592 end
3573
3593
3574 def test_bulk_copy_should_allow_changing_the_issue_attributes
3594 def test_bulk_copy_should_allow_changing_the_issue_attributes
3575 # Fixes random test failure with Mysql
3595 # Fixes random test failure with Mysql
3576 # where Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
3596 # where Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
3577 # doesn't return the expected results
3597 # doesn't return the expected results
3578 Issue.delete_all("project_id=2")
3598 Issue.delete_all("project_id=2")
3579
3599
3580 @request.session[:user_id] = 2
3600 @request.session[:user_id] = 2
3581 assert_difference 'Issue.count', 2 do
3601 assert_difference 'Issue.count', 2 do
3582 assert_no_difference 'Project.find(1).issues.count' do
3602 assert_no_difference 'Project.find(1).issues.count' do
3583 post :bulk_update, :ids => [1, 2], :copy => '1',
3603 post :bulk_update, :ids => [1, 2], :copy => '1',
3584 :issue => {
3604 :issue => {
3585 :project_id => '2', :tracker_id => '', :assigned_to_id => '4',
3605 :project_id => '2', :tracker_id => '', :assigned_to_id => '4',
3586 :status_id => '1', :start_date => '2009-12-01', :due_date => '2009-12-31'
3606 :status_id => '1', :start_date => '2009-12-01', :due_date => '2009-12-31'
3587 }
3607 }
3588 end
3608 end
3589 end
3609 end
3590
3610
3591 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
3611 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
3592 assert_equal 2, copied_issues.size
3612 assert_equal 2, copied_issues.size
3593 copied_issues.each do |issue|
3613 copied_issues.each do |issue|
3594 assert_equal 2, issue.project_id, "Project is incorrect"
3614 assert_equal 2, issue.project_id, "Project is incorrect"
3595 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
3615 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
3596 assert_equal 1, issue.status_id, "Status is incorrect"
3616 assert_equal 1, issue.status_id, "Status is incorrect"
3597 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
3617 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
3598 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
3618 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
3599 end
3619 end
3600 end
3620 end
3601
3621
3602 def test_bulk_copy_should_allow_adding_a_note
3622 def test_bulk_copy_should_allow_adding_a_note
3603 @request.session[:user_id] = 2
3623 @request.session[:user_id] = 2
3604 assert_difference 'Issue.count', 1 do
3624 assert_difference 'Issue.count', 1 do
3605 post :bulk_update, :ids => [1], :copy => '1',
3625 post :bulk_update, :ids => [1], :copy => '1',
3606 :notes => 'Copying one issue',
3626 :notes => 'Copying one issue',
3607 :issue => {
3627 :issue => {
3608 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
3628 :project_id => '', :tracker_id => '', :assigned_to_id => '4',
3609 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
3629 :status_id => '3', :start_date => '2009-12-01', :due_date => '2009-12-31'
3610 }
3630 }
3611 end
3631 end
3612
3632
3613 issue = Issue.first(:order => 'id DESC')
3633 issue = Issue.first(:order => 'id DESC')
3614 assert_equal 1, issue.journals.size
3634 assert_equal 1, issue.journals.size
3615 journal = issue.journals.first
3635 journal = issue.journals.first
3616 assert_equal 0, journal.details.size
3636 assert_equal 0, journal.details.size
3617 assert_equal 'Copying one issue', journal.notes
3637 assert_equal 'Copying one issue', journal.notes
3618 end
3638 end
3619
3639
3620 def test_bulk_copy_should_allow_not_copying_the_attachments
3640 def test_bulk_copy_should_allow_not_copying_the_attachments
3621 attachment_count = Issue.find(3).attachments.size
3641 attachment_count = Issue.find(3).attachments.size
3622 assert attachment_count > 0
3642 assert attachment_count > 0
3623 @request.session[:user_id] = 2
3643 @request.session[:user_id] = 2
3624
3644
3625 assert_difference 'Issue.count', 1 do
3645 assert_difference 'Issue.count', 1 do
3626 assert_no_difference 'Attachment.count' do
3646 assert_no_difference 'Attachment.count' do
3627 post :bulk_update, :ids => [3], :copy => '1',
3647 post :bulk_update, :ids => [3], :copy => '1',
3628 :issue => {
3648 :issue => {
3629 :project_id => ''
3649 :project_id => ''
3630 }
3650 }
3631 end
3651 end
3632 end
3652 end
3633 end
3653 end
3634
3654
3635 def test_bulk_copy_should_allow_copying_the_attachments
3655 def test_bulk_copy_should_allow_copying_the_attachments
3636 attachment_count = Issue.find(3).attachments.size
3656 attachment_count = Issue.find(3).attachments.size
3637 assert attachment_count > 0
3657 assert attachment_count > 0
3638 @request.session[:user_id] = 2
3658 @request.session[:user_id] = 2
3639
3659
3640 assert_difference 'Issue.count', 1 do
3660 assert_difference 'Issue.count', 1 do
3641 assert_difference 'Attachment.count', attachment_count do
3661 assert_difference 'Attachment.count', attachment_count do
3642 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '1',
3662 post :bulk_update, :ids => [3], :copy => '1', :copy_attachments => '1',
3643 :issue => {
3663 :issue => {
3644 :project_id => ''
3664 :project_id => ''
3645 }
3665 }
3646 end
3666 end
3647 end
3667 end
3648 end
3668 end
3649
3669
3650 def test_bulk_copy_should_add_relations_with_copied_issues
3670 def test_bulk_copy_should_add_relations_with_copied_issues
3651 @request.session[:user_id] = 2
3671 @request.session[:user_id] = 2
3652
3672
3653 assert_difference 'Issue.count', 2 do
3673 assert_difference 'Issue.count', 2 do
3654 assert_difference 'IssueRelation.count', 2 do
3674 assert_difference 'IssueRelation.count', 2 do
3655 post :bulk_update, :ids => [1, 3], :copy => '1',
3675 post :bulk_update, :ids => [1, 3], :copy => '1',
3656 :issue => {
3676 :issue => {
3657 :project_id => '1'
3677 :project_id => '1'
3658 }
3678 }
3659 end
3679 end
3660 end
3680 end
3661 end
3681 end
3662
3682
3663 def test_bulk_copy_should_allow_not_copying_the_subtasks
3683 def test_bulk_copy_should_allow_not_copying_the_subtasks
3664 issue = Issue.generate_with_descendants!
3684 issue = Issue.generate_with_descendants!
3665 @request.session[:user_id] = 2
3685 @request.session[:user_id] = 2
3666
3686
3667 assert_difference 'Issue.count', 1 do
3687 assert_difference 'Issue.count', 1 do
3668 post :bulk_update, :ids => [issue.id], :copy => '1',
3688 post :bulk_update, :ids => [issue.id], :copy => '1',
3669 :issue => {
3689 :issue => {
3670 :project_id => ''
3690 :project_id => ''
3671 }
3691 }
3672 end
3692 end
3673 end
3693 end
3674
3694
3675 def test_bulk_copy_should_allow_copying_the_subtasks
3695 def test_bulk_copy_should_allow_copying_the_subtasks
3676 issue = Issue.generate_with_descendants!
3696 issue = Issue.generate_with_descendants!
3677 count = issue.descendants.count
3697 count = issue.descendants.count
3678 @request.session[:user_id] = 2
3698 @request.session[:user_id] = 2
3679
3699
3680 assert_difference 'Issue.count', count+1 do
3700 assert_difference 'Issue.count', count+1 do
3681 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '1',
3701 post :bulk_update, :ids => [issue.id], :copy => '1', :copy_subtasks => '1',
3682 :issue => {
3702 :issue => {
3683 :project_id => ''
3703 :project_id => ''
3684 }
3704 }
3685 end
3705 end
3686 copy = Issue.where(:parent_id => nil).order("id DESC").first
3706 copy = Issue.where(:parent_id => nil).order("id DESC").first
3687 assert_equal count, copy.descendants.count
3707 assert_equal count, copy.descendants.count
3688 end
3708 end
3689
3709
3690 def test_bulk_copy_should_not_copy_selected_subtasks_twice
3710 def test_bulk_copy_should_not_copy_selected_subtasks_twice
3691 issue = Issue.generate_with_descendants!
3711 issue = Issue.generate_with_descendants!
3692 count = issue.descendants.count
3712 count = issue.descendants.count
3693 @request.session[:user_id] = 2
3713 @request.session[:user_id] = 2
3694
3714
3695 assert_difference 'Issue.count', count+1 do
3715 assert_difference 'Issue.count', count+1 do
3696 post :bulk_update, :ids => issue.self_and_descendants.map(&:id), :copy => '1', :copy_subtasks => '1',
3716 post :bulk_update, :ids => issue.self_and_descendants.map(&:id), :copy => '1', :copy_subtasks => '1',
3697 :issue => {
3717 :issue => {
3698 :project_id => ''
3718 :project_id => ''
3699 }
3719 }
3700 end
3720 end
3701 copy = Issue.where(:parent_id => nil).order("id DESC").first
3721 copy = Issue.where(:parent_id => nil).order("id DESC").first
3702 assert_equal count, copy.descendants.count
3722 assert_equal count, copy.descendants.count
3703 end
3723 end
3704
3724
3705 def test_bulk_copy_to_another_project_should_follow_when_needed
3725 def test_bulk_copy_to_another_project_should_follow_when_needed
3706 @request.session[:user_id] = 2
3726 @request.session[:user_id] = 2
3707 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
3727 post :bulk_update, :ids => [1], :copy => '1', :issue => {:project_id => 2}, :follow => '1'
3708 issue = Issue.first(:order => 'id DESC')
3728 issue = Issue.first(:order => 'id DESC')
3709 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
3729 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
3710 end
3730 end
3711
3731
3712 def test_destroy_issue_with_no_time_entries
3732 def test_destroy_issue_with_no_time_entries
3713 assert_nil TimeEntry.find_by_issue_id(2)
3733 assert_nil TimeEntry.find_by_issue_id(2)
3714 @request.session[:user_id] = 2
3734 @request.session[:user_id] = 2
3715
3735
3716 assert_difference 'Issue.count', -1 do
3736 assert_difference 'Issue.count', -1 do
3717 delete :destroy, :id => 2
3737 delete :destroy, :id => 2
3718 end
3738 end
3719 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
3739 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
3720 assert_nil Issue.find_by_id(2)
3740 assert_nil Issue.find_by_id(2)
3721 end
3741 end
3722
3742
3723 def test_destroy_issues_with_time_entries
3743 def test_destroy_issues_with_time_entries
3724 @request.session[:user_id] = 2
3744 @request.session[:user_id] = 2
3725
3745
3726 assert_no_difference 'Issue.count' do
3746 assert_no_difference 'Issue.count' do
3727 delete :destroy, :ids => [1, 3]
3747 delete :destroy, :ids => [1, 3]
3728 end
3748 end
3729 assert_response :success
3749 assert_response :success
3730 assert_template 'destroy'
3750 assert_template 'destroy'
3731 assert_not_nil assigns(:hours)
3751 assert_not_nil assigns(:hours)
3732 assert Issue.find_by_id(1) && Issue.find_by_id(3)
3752 assert Issue.find_by_id(1) && Issue.find_by_id(3)
3733 assert_tag 'form',
3753 assert_tag 'form',
3734 :descendant => {:tag => 'input', :attributes => {:name => '_method', :value => 'delete'}}
3754 :descendant => {:tag => 'input', :attributes => {:name => '_method', :value => 'delete'}}
3735 end
3755 end
3736
3756
3737 def test_destroy_issues_and_destroy_time_entries
3757 def test_destroy_issues_and_destroy_time_entries
3738 @request.session[:user_id] = 2
3758 @request.session[:user_id] = 2
3739
3759
3740 assert_difference 'Issue.count', -2 do
3760 assert_difference 'Issue.count', -2 do
3741 assert_difference 'TimeEntry.count', -3 do
3761 assert_difference 'TimeEntry.count', -3 do
3742 delete :destroy, :ids => [1, 3], :todo => 'destroy'
3762 delete :destroy, :ids => [1, 3], :todo => 'destroy'
3743 end
3763 end
3744 end
3764 end
3745 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
3765 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
3746 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
3766 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
3747 assert_nil TimeEntry.find_by_id([1, 2])
3767 assert_nil TimeEntry.find_by_id([1, 2])
3748 end
3768 end
3749
3769
3750 def test_destroy_issues_and_assign_time_entries_to_project
3770 def test_destroy_issues_and_assign_time_entries_to_project
3751 @request.session[:user_id] = 2
3771 @request.session[:user_id] = 2
3752
3772
3753 assert_difference 'Issue.count', -2 do
3773 assert_difference 'Issue.count', -2 do
3754 assert_no_difference 'TimeEntry.count' do
3774 assert_no_difference 'TimeEntry.count' do
3755 delete :destroy, :ids => [1, 3], :todo => 'nullify'
3775 delete :destroy, :ids => [1, 3], :todo => 'nullify'
3756 end
3776 end
3757 end
3777 end
3758 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
3778 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
3759 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
3779 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
3760 assert_nil TimeEntry.find(1).issue_id
3780 assert_nil TimeEntry.find(1).issue_id
3761 assert_nil TimeEntry.find(2).issue_id
3781 assert_nil TimeEntry.find(2).issue_id
3762 end
3782 end
3763
3783
3764 def test_destroy_issues_and_reassign_time_entries_to_another_issue
3784 def test_destroy_issues_and_reassign_time_entries_to_another_issue
3765 @request.session[:user_id] = 2
3785 @request.session[:user_id] = 2
3766
3786
3767 assert_difference 'Issue.count', -2 do
3787 assert_difference 'Issue.count', -2 do
3768 assert_no_difference 'TimeEntry.count' do
3788 assert_no_difference 'TimeEntry.count' do
3769 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
3789 delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
3770 end
3790 end
3771 end
3791 end
3772 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
3792 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
3773 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
3793 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
3774 assert_equal 2, TimeEntry.find(1).issue_id
3794 assert_equal 2, TimeEntry.find(1).issue_id
3775 assert_equal 2, TimeEntry.find(2).issue_id
3795 assert_equal 2, TimeEntry.find(2).issue_id
3776 end
3796 end
3777
3797
3778 def test_destroy_issues_from_different_projects
3798 def test_destroy_issues_from_different_projects
3779 @request.session[:user_id] = 2
3799 @request.session[:user_id] = 2
3780
3800
3781 assert_difference 'Issue.count', -3 do
3801 assert_difference 'Issue.count', -3 do
3782 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
3802 delete :destroy, :ids => [1, 2, 6], :todo => 'destroy'
3783 end
3803 end
3784 assert_redirected_to :controller => 'issues', :action => 'index'
3804 assert_redirected_to :controller => 'issues', :action => 'index'
3785 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
3805 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
3786 end
3806 end
3787
3807
3788 def test_destroy_parent_and_child_issues
3808 def test_destroy_parent_and_child_issues
3789 parent = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Parent Issue')
3809 parent = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Parent Issue')
3790 child = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Child Issue', :parent_issue_id => parent.id)
3810 child = Issue.create!(:project_id => 1, :author_id => 1, :tracker_id => 1, :subject => 'Child Issue', :parent_issue_id => parent.id)
3791 assert child.is_descendant_of?(parent.reload)
3811 assert child.is_descendant_of?(parent.reload)
3792
3812
3793 @request.session[:user_id] = 2
3813 @request.session[:user_id] = 2
3794 assert_difference 'Issue.count', -2 do
3814 assert_difference 'Issue.count', -2 do
3795 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
3815 delete :destroy, :ids => [parent.id, child.id], :todo => 'destroy'
3796 end
3816 end
3797 assert_response 302
3817 assert_response 302
3798 end
3818 end
3799
3819
3800 def test_default_search_scope
3820 def test_default_search_scope
3801 get :index
3821 get :index
3802 assert_tag :div, :attributes => {:id => 'quick-search'},
3822 assert_tag :div, :attributes => {:id => 'quick-search'},
3803 :child => {:tag => 'form',
3823 :child => {:tag => 'form',
3804 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
3824 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
3805 end
3825 end
3806 end
3826 end
General Comments 0
You need to be logged in to leave comments. Login now