##// END OF EJS Templates
Cleaned up the IssueController redirects to use the back_url like the other actions....
Eric Davis -
r3201:1827d609a1b4
parent child
Show More
@@ -1,571 +1,571
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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
19 menu_item :new_issue, :only => :new
20 default_search_scope :issues
20 default_search_scope :issues
21
21
22 before_filter :find_issue, :only => [:show, :edit, :reply]
22 before_filter :find_issue, :only => [:show, :edit, :reply]
23 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
23 before_filter :find_issues, :only => [:bulk_edit, :move, :destroy]
24 before_filter :find_project, :only => [:new, :update_form, :preview]
24 before_filter :find_project, :only => [:new, :update_form, :preview]
25 before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :context_menu]
25 before_filter :authorize, :except => [:index, :changes, :gantt, :calendar, :preview, :context_menu]
26 before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
26 before_filter :find_optional_project, :only => [:index, :changes, :gantt, :calendar]
27 accept_key_auth :index, :show, :changes
27 accept_key_auth :index, :show, :changes
28
28
29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
29 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
30
30
31 helper :journals
31 helper :journals
32 helper :projects
32 helper :projects
33 include ProjectsHelper
33 include ProjectsHelper
34 helper :custom_fields
34 helper :custom_fields
35 include CustomFieldsHelper
35 include CustomFieldsHelper
36 helper :issue_relations
36 helper :issue_relations
37 include IssueRelationsHelper
37 include IssueRelationsHelper
38 helper :watchers
38 helper :watchers
39 include WatchersHelper
39 include WatchersHelper
40 helper :attachments
40 helper :attachments
41 include AttachmentsHelper
41 include AttachmentsHelper
42 helper :queries
42 helper :queries
43 helper :sort
43 helper :sort
44 include SortHelper
44 include SortHelper
45 include IssuesHelper
45 include IssuesHelper
46 helper :timelog
46 helper :timelog
47 include Redmine::Export::PDF
47 include Redmine::Export::PDF
48
48
49 verify :method => [:post, :delete],
49 verify :method => [:post, :delete],
50 :only => :destroy,
50 :only => :destroy,
51 :render => { :nothing => true, :status => :method_not_allowed }
51 :render => { :nothing => true, :status => :method_not_allowed }
52
52
53 def index
53 def index
54 retrieve_query
54 retrieve_query
55 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
55 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
56 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
56 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
57
57
58 if @query.valid?
58 if @query.valid?
59 limit = per_page_option
59 limit = per_page_option
60 respond_to do |format|
60 respond_to do |format|
61 format.html { }
61 format.html { }
62 format.xml { }
62 format.xml { }
63 format.atom { limit = Setting.feeds_limit.to_i }
63 format.atom { limit = Setting.feeds_limit.to_i }
64 format.csv { limit = Setting.issues_export_limit.to_i }
64 format.csv { limit = Setting.issues_export_limit.to_i }
65 format.pdf { limit = Setting.issues_export_limit.to_i }
65 format.pdf { limit = Setting.issues_export_limit.to_i }
66 end
66 end
67
67
68 @issue_count = @query.issue_count
68 @issue_count = @query.issue_count
69 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
69 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
70 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
70 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
71 :order => sort_clause,
71 :order => sort_clause,
72 :offset => @issue_pages.current.offset,
72 :offset => @issue_pages.current.offset,
73 :limit => limit)
73 :limit => limit)
74 @issue_count_by_group = @query.issue_count_by_group
74 @issue_count_by_group = @query.issue_count_by_group
75
75
76 respond_to do |format|
76 respond_to do |format|
77 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
77 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
78 format.xml { render :layout => false }
78 format.xml { render :layout => false }
79 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
79 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
80 format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
80 format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
81 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
81 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
82 end
82 end
83 else
83 else
84 # Send html if the query is not valid
84 # Send html if the query is not valid
85 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
85 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
86 end
86 end
87 rescue ActiveRecord::RecordNotFound
87 rescue ActiveRecord::RecordNotFound
88 render_404
88 render_404
89 end
89 end
90
90
91 def changes
91 def changes
92 retrieve_query
92 retrieve_query
93 sort_init 'id', 'desc'
93 sort_init 'id', 'desc'
94 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
94 sort_update({'id' => "#{Issue.table_name}.id"}.merge(@query.available_columns.inject({}) {|h, c| h[c.name.to_s] = c.sortable; h}))
95
95
96 if @query.valid?
96 if @query.valid?
97 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
97 @journals = @query.journals(:order => "#{Journal.table_name}.created_on DESC",
98 :limit => 25)
98 :limit => 25)
99 end
99 end
100 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
100 @title = (@project ? @project.name : Setting.app_title) + ": " + (@query.new_record? ? l(:label_changes_details) : @query.name)
101 render :layout => false, :content_type => 'application/atom+xml'
101 render :layout => false, :content_type => 'application/atom+xml'
102 rescue ActiveRecord::RecordNotFound
102 rescue ActiveRecord::RecordNotFound
103 render_404
103 render_404
104 end
104 end
105
105
106 def show
106 def show
107 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
107 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
108 @journals.each_with_index {|j,i| j.indice = i+1}
108 @journals.each_with_index {|j,i| j.indice = i+1}
109 @journals.reverse! if User.current.wants_comments_in_reverse_order?
109 @journals.reverse! if User.current.wants_comments_in_reverse_order?
110 @changesets = @issue.changesets
110 @changesets = @issue.changesets
111 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
111 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
112 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
112 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
113 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
113 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
114 @priorities = IssuePriority.all
114 @priorities = IssuePriority.all
115 @time_entry = TimeEntry.new
115 @time_entry = TimeEntry.new
116 respond_to do |format|
116 respond_to do |format|
117 format.html { render :template => 'issues/show.rhtml' }
117 format.html { render :template => 'issues/show.rhtml' }
118 format.xml { render :layout => false }
118 format.xml { render :layout => false }
119 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
119 format.atom { render :action => 'changes', :layout => false, :content_type => 'application/atom+xml' }
120 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
120 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
121 end
121 end
122 end
122 end
123
123
124 # Add a new issue
124 # Add a new issue
125 # The new issue will be created from an existing one if copy_from parameter is given
125 # The new issue will be created from an existing one if copy_from parameter is given
126 def new
126 def new
127 @issue = Issue.new
127 @issue = Issue.new
128 @issue.copy_from(params[:copy_from]) if params[:copy_from]
128 @issue.copy_from(params[:copy_from]) if params[:copy_from]
129 @issue.project = @project
129 @issue.project = @project
130 # Tracker must be set before custom field values
130 # Tracker must be set before custom field values
131 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
131 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
132 if @issue.tracker.nil?
132 if @issue.tracker.nil?
133 render_error l(:error_no_tracker_in_project)
133 render_error l(:error_no_tracker_in_project)
134 return
134 return
135 end
135 end
136 if params[:issue].is_a?(Hash)
136 if params[:issue].is_a?(Hash)
137 @issue.safe_attributes = params[:issue]
137 @issue.safe_attributes = params[:issue]
138 @issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
138 @issue.watcher_user_ids = params[:issue]['watcher_user_ids'] if User.current.allowed_to?(:add_issue_watchers, @project)
139 end
139 end
140 @issue.author = User.current
140 @issue.author = User.current
141
141
142 default_status = IssueStatus.default
142 default_status = IssueStatus.default
143 unless default_status
143 unless default_status
144 render_error l(:error_no_default_issue_status)
144 render_error l(:error_no_default_issue_status)
145 return
145 return
146 end
146 end
147 @issue.status = default_status
147 @issue.status = default_status
148 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
148 @allowed_statuses = ([default_status] + default_status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
149
149
150 if request.get? || request.xhr?
150 if request.get? || request.xhr?
151 @issue.start_date ||= Date.today
151 @issue.start_date ||= Date.today
152 else
152 else
153 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
153 requested_status = IssueStatus.find_by_id(params[:issue][:status_id])
154 # Check that the user is allowed to apply the requested status
154 # Check that the user is allowed to apply the requested status
155 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
155 @issue.status = (@allowed_statuses.include? requested_status) ? requested_status : default_status
156 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
156 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
157 if @issue.save
157 if @issue.save
158 attach_files(@issue, params[:attachments])
158 attach_files(@issue, params[:attachments])
159 flash[:notice] = l(:notice_successful_create)
159 flash[:notice] = l(:notice_successful_create)
160 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
160 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
161 respond_to do |format|
161 respond_to do |format|
162 format.html {
162 format.html {
163 redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
163 redirect_to(params[:continue] ? { :action => 'new', :tracker_id => @issue.tracker } :
164 { :action => 'show', :id => @issue })
164 { :action => 'show', :id => @issue })
165 }
165 }
166 format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
166 format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
167 end
167 end
168 return
168 return
169 else
169 else
170 respond_to do |format|
170 respond_to do |format|
171 format.html { }
171 format.html { }
172 format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
172 format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
173 end
173 end
174 end
174 end
175 end
175 end
176 @priorities = IssuePriority.all
176 @priorities = IssuePriority.all
177 render :layout => !request.xhr?
177 render :layout => !request.xhr?
178 end
178 end
179
179
180 # Attributes that can be updated on workflow transition (without :edit permission)
180 # Attributes that can be updated on workflow transition (without :edit permission)
181 # TODO: make it configurable (at least per role)
181 # TODO: make it configurable (at least per role)
182 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
182 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
183
183
184 def edit
184 def edit
185 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
185 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
186 @priorities = IssuePriority.all
186 @priorities = IssuePriority.all
187 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
187 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
188 @time_entry = TimeEntry.new
188 @time_entry = TimeEntry.new
189
189
190 @notes = params[:notes]
190 @notes = params[:notes]
191 journal = @issue.init_journal(User.current, @notes)
191 journal = @issue.init_journal(User.current, @notes)
192 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
192 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
193 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
193 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
194 attrs = params[:issue].dup
194 attrs = params[:issue].dup
195 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
195 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
196 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
196 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
197 @issue.safe_attributes = attrs
197 @issue.safe_attributes = attrs
198 end
198 end
199
199
200 if request.get?
200 if request.get?
201 # nop
201 # nop
202 else
202 else
203 @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
203 @time_entry = TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => Date.today)
204 @time_entry.attributes = params[:time_entry]
204 @time_entry.attributes = params[:time_entry]
205 if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.valid?
205 if (@time_entry.hours.nil? || @time_entry.valid?) && @issue.valid?
206 attachments = attach_files(@issue, params[:attachments])
206 attachments = attach_files(@issue, params[:attachments])
207 attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
207 attachments.each {|a| journal.details << JournalDetail.new(:property => 'attachment', :prop_key => a.id, :value => a.filename)}
208 call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
208 call_hook(:controller_issues_edit_before_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
209 if @issue.save
209 if @issue.save
210 # Log spend time
210 # Log spend time
211 if User.current.allowed_to?(:log_time, @project)
211 if User.current.allowed_to?(:log_time, @project)
212 @time_entry.save
212 @time_entry.save
213 end
213 end
214 if !journal.new_record?
214 if !journal.new_record?
215 # Only send notification if something was actually changed
215 # Only send notification if something was actually changed
216 flash[:notice] = l(:notice_successful_update)
216 flash[:notice] = l(:notice_successful_update)
217 end
217 end
218 call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
218 call_hook(:controller_issues_edit_after_save, { :params => params, :issue => @issue, :time_entry => @time_entry, :journal => journal})
219 respond_to do |format|
219 respond_to do |format|
220 format.html { redirect_to(params[:back_to] || {:action => 'show', :id => @issue}) }
220 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
221 format.xml { head :ok }
221 format.xml { head :ok }
222 end
222 end
223 return
223 return
224 end
224 end
225 end
225 end
226 # failure
226 # failure
227 respond_to do |format|
227 respond_to do |format|
228 format.html { }
228 format.html { }
229 format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
229 format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
230 end
230 end
231 end
231 end
232 rescue ActiveRecord::StaleObjectError
232 rescue ActiveRecord::StaleObjectError
233 # Optimistic locking exception
233 # Optimistic locking exception
234 flash.now[:error] = l(:notice_locking_conflict)
234 flash.now[:error] = l(:notice_locking_conflict)
235 # Remove the previously added attachments if issue was not updated
235 # Remove the previously added attachments if issue was not updated
236 attachments.each(&:destroy)
236 attachments.each(&:destroy)
237 end
237 end
238
238
239 def reply
239 def reply
240 journal = Journal.find(params[:journal_id]) if params[:journal_id]
240 journal = Journal.find(params[:journal_id]) if params[:journal_id]
241 if journal
241 if journal
242 user = journal.user
242 user = journal.user
243 text = journal.notes
243 text = journal.notes
244 else
244 else
245 user = @issue.author
245 user = @issue.author
246 text = @issue.description
246 text = @issue.description
247 end
247 end
248 content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
248 content = "#{ll(Setting.default_language, :text_user_wrote, user)}\\n> "
249 content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
249 content << text.to_s.strip.gsub(%r{<pre>((.|\s)*?)</pre>}m, '[...]').gsub('"', '\"').gsub(/(\r?\n|\r\n?)/, "\\n> ") + "\\n\\n"
250 render(:update) { |page|
250 render(:update) { |page|
251 page.<< "$('notes').value = \"#{content}\";"
251 page.<< "$('notes').value = \"#{content}\";"
252 page.show 'update'
252 page.show 'update'
253 page << "Form.Element.focus('notes');"
253 page << "Form.Element.focus('notes');"
254 page << "Element.scrollTo('update');"
254 page << "Element.scrollTo('update');"
255 page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
255 page << "$('notes').scrollTop = $('notes').scrollHeight - $('notes').clientHeight;"
256 }
256 }
257 end
257 end
258
258
259 # Bulk edit a set of issues
259 # Bulk edit a set of issues
260 def bulk_edit
260 def bulk_edit
261 if request.post?
261 if request.post?
262 tracker = params[:tracker_id].blank? ? nil : @project.trackers.find_by_id(params[:tracker_id])
262 tracker = params[:tracker_id].blank? ? nil : @project.trackers.find_by_id(params[:tracker_id])
263 status = params[:status_id].blank? ? nil : IssueStatus.find_by_id(params[:status_id])
263 status = params[:status_id].blank? ? nil : IssueStatus.find_by_id(params[:status_id])
264 priority = params[:priority_id].blank? ? nil : IssuePriority.find_by_id(params[:priority_id])
264 priority = params[:priority_id].blank? ? nil : IssuePriority.find_by_id(params[:priority_id])
265 assigned_to = (params[:assigned_to_id].blank? || params[:assigned_to_id] == 'none') ? nil : User.find_by_id(params[:assigned_to_id])
265 assigned_to = (params[:assigned_to_id].blank? || params[:assigned_to_id] == 'none') ? nil : User.find_by_id(params[:assigned_to_id])
266 category = (params[:category_id].blank? || params[:category_id] == 'none') ? nil : @project.issue_categories.find_by_id(params[:category_id])
266 category = (params[:category_id].blank? || params[:category_id] == 'none') ? nil : @project.issue_categories.find_by_id(params[:category_id])
267 fixed_version = (params[:fixed_version_id].blank? || params[:fixed_version_id] == 'none') ? nil : @project.shared_versions.find_by_id(params[:fixed_version_id])
267 fixed_version = (params[:fixed_version_id].blank? || params[:fixed_version_id] == 'none') ? nil : @project.shared_versions.find_by_id(params[:fixed_version_id])
268 custom_field_values = params[:custom_field_values] ? params[:custom_field_values].reject {|k,v| v.blank?} : nil
268 custom_field_values = params[:custom_field_values] ? params[:custom_field_values].reject {|k,v| v.blank?} : nil
269
269
270 unsaved_issue_ids = []
270 unsaved_issue_ids = []
271 @issues.each do |issue|
271 @issues.each do |issue|
272 journal = issue.init_journal(User.current, params[:notes])
272 journal = issue.init_journal(User.current, params[:notes])
273 issue.tracker = tracker if tracker
273 issue.tracker = tracker if tracker
274 issue.priority = priority if priority
274 issue.priority = priority if priority
275 issue.assigned_to = assigned_to if assigned_to || params[:assigned_to_id] == 'none'
275 issue.assigned_to = assigned_to if assigned_to || params[:assigned_to_id] == 'none'
276 issue.category = category if category || params[:category_id] == 'none'
276 issue.category = category if category || params[:category_id] == 'none'
277 issue.fixed_version = fixed_version if fixed_version || params[:fixed_version_id] == 'none'
277 issue.fixed_version = fixed_version if fixed_version || params[:fixed_version_id] == 'none'
278 issue.start_date = params[:start_date] unless params[:start_date].blank?
278 issue.start_date = params[:start_date] unless params[:start_date].blank?
279 issue.due_date = params[:due_date] unless params[:due_date].blank?
279 issue.due_date = params[:due_date] unless params[:due_date].blank?
280 issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
280 issue.done_ratio = params[:done_ratio] unless params[:done_ratio].blank?
281 issue.custom_field_values = custom_field_values if custom_field_values && !custom_field_values.empty?
281 issue.custom_field_values = custom_field_values if custom_field_values && !custom_field_values.empty?
282 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
282 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
283 # Don't save any change to the issue if the user is not authorized to apply the requested status
283 # Don't save any change to the issue if the user is not authorized to apply the requested status
284 unless (status.nil? || (issue.new_statuses_allowed_to(User.current).include?(status) && issue.status = status)) && issue.save
284 unless (status.nil? || (issue.new_statuses_allowed_to(User.current).include?(status) && issue.status = status)) && issue.save
285 # Keep unsaved issue ids to display them in flash error
285 # Keep unsaved issue ids to display them in flash error
286 unsaved_issue_ids << issue.id
286 unsaved_issue_ids << issue.id
287 end
287 end
288 end
288 end
289 if unsaved_issue_ids.empty?
289 if unsaved_issue_ids.empty?
290 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
290 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
291 else
291 else
292 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
292 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
293 :total => @issues.size,
293 :total => @issues.size,
294 :ids => '#' + unsaved_issue_ids.join(', #'))
294 :ids => '#' + unsaved_issue_ids.join(', #'))
295 end
295 end
296 redirect_to(params[:back_to] || {:controller => 'issues', :action => 'index', :project_id => @project})
296 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
297 return
297 return
298 end
298 end
299 @available_statuses = Workflow.available_statuses(@project)
299 @available_statuses = Workflow.available_statuses(@project)
300 @custom_fields = @project.all_issue_custom_fields
300 @custom_fields = @project.all_issue_custom_fields
301 end
301 end
302
302
303 def move
303 def move
304 @copy = params[:copy_options] && params[:copy_options][:copy]
304 @copy = params[:copy_options] && params[:copy_options][:copy]
305 @allowed_projects = []
305 @allowed_projects = []
306 # find projects to which the user is allowed to move the issue
306 # find projects to which the user is allowed to move the issue
307 if User.current.admin?
307 if User.current.admin?
308 # admin is allowed to move issues to any active (visible) project
308 # admin is allowed to move issues to any active (visible) project
309 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current))
309 @allowed_projects = Project.find(:all, :conditions => Project.visible_by(User.current))
310 else
310 else
311 User.current.memberships.each {|m| @allowed_projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
311 User.current.memberships.each {|m| @allowed_projects << m.project if m.roles.detect {|r| r.allowed_to?(:move_issues)}}
312 end
312 end
313 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
313 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:new_project_id]} if params[:new_project_id]
314 @target_project ||= @project
314 @target_project ||= @project
315 @trackers = @target_project.trackers
315 @trackers = @target_project.trackers
316 @available_statuses = Workflow.available_statuses(@project)
316 @available_statuses = Workflow.available_statuses(@project)
317 if request.post?
317 if request.post?
318 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
318 new_tracker = params[:new_tracker_id].blank? ? nil : @target_project.trackers.find_by_id(params[:new_tracker_id])
319 unsaved_issue_ids = []
319 unsaved_issue_ids = []
320 moved_issues = []
320 moved_issues = []
321 @issues.each do |issue|
321 @issues.each do |issue|
322 changed_attributes = {}
322 changed_attributes = {}
323 [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute|
323 [:assigned_to_id, :status_id, :start_date, :due_date].each do |valid_attribute|
324 unless params[valid_attribute].blank?
324 unless params[valid_attribute].blank?
325 changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute])
325 changed_attributes[valid_attribute] = (params[valid_attribute] == 'none' ? nil : params[valid_attribute])
326 end
326 end
327 end
327 end
328 issue.init_journal(User.current)
328 issue.init_journal(User.current)
329 if r = issue.move_to(@target_project, new_tracker, {:copy => @copy, :attributes => changed_attributes})
329 if r = issue.move_to(@target_project, new_tracker, {:copy => @copy, :attributes => changed_attributes})
330 moved_issues << r
330 moved_issues << r
331 else
331 else
332 unsaved_issue_ids << issue.id
332 unsaved_issue_ids << issue.id
333 end
333 end
334 end
334 end
335 if unsaved_issue_ids.empty?
335 if unsaved_issue_ids.empty?
336 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
336 flash[:notice] = l(:notice_successful_update) unless @issues.empty?
337 else
337 else
338 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
338 flash[:error] = l(:notice_failed_to_save_issues, :count => unsaved_issue_ids.size,
339 :total => @issues.size,
339 :total => @issues.size,
340 :ids => '#' + unsaved_issue_ids.join(', #'))
340 :ids => '#' + unsaved_issue_ids.join(', #'))
341 end
341 end
342 if params[:follow]
342 if params[:follow]
343 if @issues.size == 1 && moved_issues.size == 1
343 if @issues.size == 1 && moved_issues.size == 1
344 redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
344 redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
345 else
345 else
346 redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project)
346 redirect_to :controller => 'issues', :action => 'index', :project_id => (@target_project || @project)
347 end
347 end
348 else
348 else
349 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
349 redirect_to :controller => 'issues', :action => 'index', :project_id => @project
350 end
350 end
351 return
351 return
352 end
352 end
353 render :layout => false if request.xhr?
353 render :layout => false if request.xhr?
354 end
354 end
355
355
356 def destroy
356 def destroy
357 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
357 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
358 if @hours > 0
358 if @hours > 0
359 case params[:todo]
359 case params[:todo]
360 when 'destroy'
360 when 'destroy'
361 # nothing to do
361 # nothing to do
362 when 'nullify'
362 when 'nullify'
363 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
363 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
364 when 'reassign'
364 when 'reassign'
365 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
365 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
366 if reassign_to.nil?
366 if reassign_to.nil?
367 flash.now[:error] = l(:error_issue_not_found_in_project)
367 flash.now[:error] = l(:error_issue_not_found_in_project)
368 return
368 return
369 else
369 else
370 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
370 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
371 end
371 end
372 else
372 else
373 unless params[:format] == 'xml'
373 unless params[:format] == 'xml'
374 # display the destroy form if it's a user request
374 # display the destroy form if it's a user request
375 return
375 return
376 end
376 end
377 end
377 end
378 end
378 end
379 @issues.each(&:destroy)
379 @issues.each(&:destroy)
380 respond_to do |format|
380 respond_to do |format|
381 format.html { redirect_to :action => 'index', :project_id => @project }
381 format.html { redirect_to :action => 'index', :project_id => @project }
382 format.xml { head :ok }
382 format.xml { head :ok }
383 end
383 end
384 end
384 end
385
385
386 def gantt
386 def gantt
387 @gantt = Redmine::Helpers::Gantt.new(params)
387 @gantt = Redmine::Helpers::Gantt.new(params)
388 retrieve_query
388 retrieve_query
389 if @query.valid?
389 if @query.valid?
390 events = []
390 events = []
391 # Issues that have start and due dates
391 # Issues that have start and due dates
392 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
392 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
393 :order => "start_date, due_date",
393 :order => "start_date, due_date",
394 :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
394 :conditions => ["(((start_date>=? and start_date<=?) or (due_date>=? and due_date<=?) or (start_date<? and due_date>?)) and start_date is not null and due_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
395 )
395 )
396 # Issues that don't have a due date but that are assigned to a version with a date
396 # Issues that don't have a due date but that are assigned to a version with a date
397 events += @query.issues(:include => [:tracker, :assigned_to, :priority, :fixed_version],
397 events += @query.issues(:include => [:tracker, :assigned_to, :priority, :fixed_version],
398 :order => "start_date, effective_date",
398 :order => "start_date, effective_date",
399 :conditions => ["(((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date<? and effective_date>?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
399 :conditions => ["(((start_date>=? and start_date<=?) or (effective_date>=? and effective_date<=?) or (start_date<? and effective_date>?)) and start_date is not null and due_date is null and effective_date is not null)", @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to, @gantt.date_from, @gantt.date_to]
400 )
400 )
401 # Versions
401 # Versions
402 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
402 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @gantt.date_from, @gantt.date_to])
403
403
404 @gantt.events = events
404 @gantt.events = events
405 end
405 end
406
406
407 basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
407 basename = (@project ? "#{@project.identifier}-" : '') + 'gantt'
408
408
409 respond_to do |format|
409 respond_to do |format|
410 format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
410 format.html { render :template => "issues/gantt.rhtml", :layout => !request.xhr? }
411 format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
411 format.png { send_data(@gantt.to_image, :disposition => 'inline', :type => 'image/png', :filename => "#{basename}.png") } if @gantt.respond_to?('to_image')
412 format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
412 format.pdf { send_data(gantt_to_pdf(@gantt, @project), :type => 'application/pdf', :filename => "#{basename}.pdf") }
413 end
413 end
414 end
414 end
415
415
416 def calendar
416 def calendar
417 if params[:year] and params[:year].to_i > 1900
417 if params[:year] and params[:year].to_i > 1900
418 @year = params[:year].to_i
418 @year = params[:year].to_i
419 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
419 if params[:month] and params[:month].to_i > 0 and params[:month].to_i < 13
420 @month = params[:month].to_i
420 @month = params[:month].to_i
421 end
421 end
422 end
422 end
423 @year ||= Date.today.year
423 @year ||= Date.today.year
424 @month ||= Date.today.month
424 @month ||= Date.today.month
425
425
426 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
426 @calendar = Redmine::Helpers::Calendar.new(Date.civil(@year, @month, 1), current_language, :month)
427 retrieve_query
427 retrieve_query
428 if @query.valid?
428 if @query.valid?
429 events = []
429 events = []
430 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
430 events += @query.issues(:include => [:tracker, :assigned_to, :priority],
431 :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
431 :conditions => ["((start_date BETWEEN ? AND ?) OR (due_date BETWEEN ? AND ?))", @calendar.startdt, @calendar.enddt, @calendar.startdt, @calendar.enddt]
432 )
432 )
433 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
433 events += @query.versions(:conditions => ["effective_date BETWEEN ? AND ?", @calendar.startdt, @calendar.enddt])
434
434
435 @calendar.events = events
435 @calendar.events = events
436 end
436 end
437
437
438 render :layout => false if request.xhr?
438 render :layout => false if request.xhr?
439 end
439 end
440
440
441 def context_menu
441 def context_menu
442 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
442 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
443 if (@issues.size == 1)
443 if (@issues.size == 1)
444 @issue = @issues.first
444 @issue = @issues.first
445 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
445 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
446 end
446 end
447 projects = @issues.collect(&:project).compact.uniq
447 projects = @issues.collect(&:project).compact.uniq
448 @project = projects.first if projects.size == 1
448 @project = projects.first if projects.size == 1
449
449
450 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
450 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
451 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
451 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
452 :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
452 :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
453 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
453 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
454 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
454 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
455 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
455 :delete => (@project && User.current.allowed_to?(:delete_issues, @project))
456 }
456 }
457 if @project
457 if @project
458 @assignables = @project.assignable_users
458 @assignables = @project.assignable_users
459 @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
459 @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
460 @trackers = @project.trackers
460 @trackers = @project.trackers
461 end
461 end
462
462
463 @priorities = IssuePriority.all.reverse
463 @priorities = IssuePriority.all.reverse
464 @statuses = IssueStatus.find(:all, :order => 'position')
464 @statuses = IssueStatus.find(:all, :order => 'position')
465 @back = params[:back_url] || request.env['HTTP_REFERER']
465 @back = params[:back_url] || request.env['HTTP_REFERER']
466
466
467 render :layout => false
467 render :layout => false
468 end
468 end
469
469
470 def update_form
470 def update_form
471 if params[:id].blank?
471 if params[:id].blank?
472 @issue = Issue.new
472 @issue = Issue.new
473 @issue.project = @project
473 @issue.project = @project
474 else
474 else
475 @issue = @project.issues.visible.find(params[:id])
475 @issue = @project.issues.visible.find(params[:id])
476 end
476 end
477 @issue.attributes = params[:issue]
477 @issue.attributes = params[:issue]
478 @allowed_statuses = ([@issue.status] + @issue.status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
478 @allowed_statuses = ([@issue.status] + @issue.status.find_new_statuses_allowed_to(User.current.roles_for_project(@project), @issue.tracker)).uniq
479 @priorities = IssuePriority.all
479 @priorities = IssuePriority.all
480
480
481 render :partial => 'attributes'
481 render :partial => 'attributes'
482 end
482 end
483
483
484 def preview
484 def preview
485 @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
485 @issue = @project.issues.find_by_id(params[:id]) unless params[:id].blank?
486 @attachements = @issue.attachments if @issue
486 @attachements = @issue.attachments if @issue
487 @text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
487 @text = params[:notes] || (params[:issue] ? params[:issue][:description] : nil)
488 render :partial => 'common/preview'
488 render :partial => 'common/preview'
489 end
489 end
490
490
491 private
491 private
492 def find_issue
492 def find_issue
493 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
493 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
494 @project = @issue.project
494 @project = @issue.project
495 rescue ActiveRecord::RecordNotFound
495 rescue ActiveRecord::RecordNotFound
496 render_404
496 render_404
497 end
497 end
498
498
499 # Filter for bulk operations
499 # Filter for bulk operations
500 def find_issues
500 def find_issues
501 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
501 @issues = Issue.find_all_by_id(params[:id] || params[:ids])
502 raise ActiveRecord::RecordNotFound if @issues.empty?
502 raise ActiveRecord::RecordNotFound if @issues.empty?
503 projects = @issues.collect(&:project).compact.uniq
503 projects = @issues.collect(&:project).compact.uniq
504 if projects.size == 1
504 if projects.size == 1
505 @project = projects.first
505 @project = projects.first
506 else
506 else
507 # TODO: let users bulk edit/move/destroy issues from different projects
507 # TODO: let users bulk edit/move/destroy issues from different projects
508 render_error 'Can not bulk edit/move/destroy issues from different projects'
508 render_error 'Can not bulk edit/move/destroy issues from different projects'
509 return false
509 return false
510 end
510 end
511 rescue ActiveRecord::RecordNotFound
511 rescue ActiveRecord::RecordNotFound
512 render_404
512 render_404
513 end
513 end
514
514
515 def find_project
515 def find_project
516 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
516 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
517 @project = Project.find(project_id)
517 @project = Project.find(project_id)
518 rescue ActiveRecord::RecordNotFound
518 rescue ActiveRecord::RecordNotFound
519 render_404
519 render_404
520 end
520 end
521
521
522 def find_optional_project
522 def find_optional_project
523 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
523 @project = Project.find(params[:project_id]) unless params[:project_id].blank?
524 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
524 allowed = User.current.allowed_to?({:controller => params[:controller], :action => params[:action]}, @project, :global => true)
525 allowed ? true : deny_access
525 allowed ? true : deny_access
526 rescue ActiveRecord::RecordNotFound
526 rescue ActiveRecord::RecordNotFound
527 render_404
527 render_404
528 end
528 end
529
529
530 # Retrieve query from session or build a new query
530 # Retrieve query from session or build a new query
531 def retrieve_query
531 def retrieve_query
532 if !params[:query_id].blank?
532 if !params[:query_id].blank?
533 cond = "project_id IS NULL"
533 cond = "project_id IS NULL"
534 cond << " OR project_id = #{@project.id}" if @project
534 cond << " OR project_id = #{@project.id}" if @project
535 @query = Query.find(params[:query_id], :conditions => cond)
535 @query = Query.find(params[:query_id], :conditions => cond)
536 @query.project = @project
536 @query.project = @project
537 session[:query] = {:id => @query.id, :project_id => @query.project_id}
537 session[:query] = {:id => @query.id, :project_id => @query.project_id}
538 sort_clear
538 sort_clear
539 else
539 else
540 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
540 if params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
541 # Give it a name, required to be valid
541 # Give it a name, required to be valid
542 @query = Query.new(:name => "_")
542 @query = Query.new(:name => "_")
543 @query.project = @project
543 @query.project = @project
544 if params[:fields] and params[:fields].is_a? Array
544 if params[:fields] and params[:fields].is_a? Array
545 params[:fields].each do |field|
545 params[:fields].each do |field|
546 @query.add_filter(field, params[:operators][field], params[:values][field])
546 @query.add_filter(field, params[:operators][field], params[:values][field])
547 end
547 end
548 else
548 else
549 @query.available_filters.keys.each do |field|
549 @query.available_filters.keys.each do |field|
550 @query.add_short_filter(field, params[field]) if params[field]
550 @query.add_short_filter(field, params[field]) if params[field]
551 end
551 end
552 end
552 end
553 @query.group_by = params[:group_by]
553 @query.group_by = params[:group_by]
554 @query.column_names = params[:query] && params[:query][:column_names]
554 @query.column_names = params[:query] && params[:query][:column_names]
555 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
555 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
556 else
556 else
557 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
557 @query = Query.find_by_id(session[:query][:id]) if session[:query][:id]
558 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
558 @query ||= Query.new(:name => "_", :project => @project, :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
559 @query.project = @project
559 @query.project = @project
560 end
560 end
561 end
561 end
562 end
562 end
563
563
564 # Rescues an invalid query statement. Just in case...
564 # Rescues an invalid query statement. Just in case...
565 def query_statement_invalid(exception)
565 def query_statement_invalid(exception)
566 logger.error "Query::StatementInvalid: #{exception.message}" if logger
566 logger.error "Query::StatementInvalid: #{exception.message}" if logger
567 session.delete(:query)
567 session.delete(:query)
568 sort_clear
568 sort_clear
569 render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
569 render_error "An error occurred while executing the query and has been logged. Please report this error to your Redmine administrator."
570 end
570 end
571 end
571 end
@@ -1,113 +1,113
1 <ul>
1 <ul>
2 <%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %>
2 <%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %>
3
3
4 <% if !@issue.nil? -%>
4 <% if !@issue.nil? -%>
5 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
5 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
6 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
6 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
7 <li class="folder">
7 <li class="folder">
8 <a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
8 <a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
9 <ul>
9 <ul>
10 <% @statuses.each do |s| -%>
10 <% @statuses.each do |s| -%>
11 <li><%= context_menu_link s.name, {:controller => 'issues', :action => 'edit', :id => @issue, :issue => {:status_id => s}, :back_to => @back}, :method => :post,
11 <li><%= context_menu_link s.name, {:controller => 'issues', :action => 'edit', :id => @issue, :issue => {:status_id => s}, :back_url => @back}, :method => :post,
12 :selected => (s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li>
12 :selected => (s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li>
13 <% end -%>
13 <% end -%>
14 </ul>
14 </ul>
15 </li>
15 </li>
16 <% else %>
16 <% else %>
17 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
17 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
18 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
18 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
19 <% end %>
19 <% end %>
20
20
21 <% unless @trackers.nil? %>
21 <% unless @trackers.nil? %>
22 <li class="folder">
22 <li class="folder">
23 <a href="#" class="submenu"><%= l(:field_tracker) %></a>
23 <a href="#" class="submenu"><%= l(:field_tracker) %></a>
24 <ul>
24 <ul>
25 <% @trackers.each do |t| -%>
25 <% @trackers.each do |t| -%>
26 <li><%= context_menu_link t.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'tracker_id' => t, :back_to => @back}, :method => :post,
26 <li><%= context_menu_link t.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'tracker_id' => t, :back_url => @back}, :method => :post,
27 :selected => (@issue && t == @issue.tracker), :disabled => !@can[:edit] %></li>
27 :selected => (@issue && t == @issue.tracker), :disabled => !@can[:edit] %></li>
28 <% end -%>
28 <% end -%>
29 </ul>
29 </ul>
30 </li>
30 </li>
31 <% end %>
31 <% end %>
32 <li class="folder">
32 <li class="folder">
33 <a href="#" class="submenu"><%= l(:field_priority) %></a>
33 <a href="#" class="submenu"><%= l(:field_priority) %></a>
34 <ul>
34 <ul>
35 <% @priorities.each do |p| -%>
35 <% @priorities.each do |p| -%>
36 <li><%= context_menu_link p.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'priority_id' => p, :back_to => @back}, :method => :post,
36 <li><%= context_menu_link p.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'priority_id' => p, :back_url => @back}, :method => :post,
37 :selected => (@issue && p == @issue.priority), :disabled => !@can[:edit] %></li>
37 :selected => (@issue && p == @issue.priority), :disabled => !@can[:edit] %></li>
38 <% end -%>
38 <% end -%>
39 </ul>
39 </ul>
40 </li>
40 </li>
41 <% unless @project.nil? || @project.shared_versions.open.empty? -%>
41 <% unless @project.nil? || @project.shared_versions.open.empty? -%>
42 <li class="folder">
42 <li class="folder">
43 <a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
43 <a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
44 <ul>
44 <ul>
45 <% @project.shared_versions.open.sort.each do |v| -%>
45 <% @project.shared_versions.open.sort.each do |v| -%>
46 <li><%= context_menu_link format_version_name(v), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'fixed_version_id' => v, :back_to => @back}, :method => :post,
46 <li><%= context_menu_link format_version_name(v), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'fixed_version_id' => v, :back_url => @back}, :method => :post,
47 :selected => (@issue && v == @issue.fixed_version), :disabled => !@can[:update] %></li>
47 :selected => (@issue && v == @issue.fixed_version), :disabled => !@can[:update] %></li>
48 <% end -%>
48 <% end -%>
49 <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'fixed_version_id' => 'none', :back_to => @back}, :method => :post,
49 <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'fixed_version_id' => 'none', :back_url => @back}, :method => :post,
50 :selected => (@issue && @issue.fixed_version.nil?), :disabled => !@can[:update] %></li>
50 :selected => (@issue && @issue.fixed_version.nil?), :disabled => !@can[:update] %></li>
51 </ul>
51 </ul>
52 </li>
52 </li>
53 <% end %>
53 <% end %>
54 <% unless @assignables.nil? || @assignables.empty? -%>
54 <% unless @assignables.nil? || @assignables.empty? -%>
55 <li class="folder">
55 <li class="folder">
56 <a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
56 <a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
57 <ul>
57 <ul>
58 <% @assignables.each do |u| -%>
58 <% @assignables.each do |u| -%>
59 <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'assigned_to_id' => u, :back_to => @back}, :method => :post,
59 <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'assigned_to_id' => u, :back_url => @back}, :method => :post,
60 :selected => (@issue && u == @issue.assigned_to), :disabled => !@can[:update] %></li>
60 :selected => (@issue && u == @issue.assigned_to), :disabled => !@can[:update] %></li>
61 <% end -%>
61 <% end -%>
62 <li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'assigned_to_id' => 'none', :back_to => @back}, :method => :post,
62 <li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'assigned_to_id' => 'none', :back_url => @back}, :method => :post,
63 :selected => (@issue && @issue.assigned_to.nil?), :disabled => !@can[:update] %></li>
63 :selected => (@issue && @issue.assigned_to.nil?), :disabled => !@can[:update] %></li>
64 </ul>
64 </ul>
65 </li>
65 </li>
66 <% end %>
66 <% end %>
67 <% unless @project.nil? || @project.issue_categories.empty? -%>
67 <% unless @project.nil? || @project.issue_categories.empty? -%>
68 <li class="folder">
68 <li class="folder">
69 <a href="#" class="submenu"><%= l(:field_category) %></a>
69 <a href="#" class="submenu"><%= l(:field_category) %></a>
70 <ul>
70 <ul>
71 <% @project.issue_categories.each do |u| -%>
71 <% @project.issue_categories.each do |u| -%>
72 <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'category_id' => u, :back_to => @back}, :method => :post,
72 <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'category_id' => u, :back_url => @back}, :method => :post,
73 :selected => (@issue && u == @issue.category), :disabled => !@can[:update] %></li>
73 :selected => (@issue && u == @issue.category), :disabled => !@can[:update] %></li>
74 <% end -%>
74 <% end -%>
75 <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'category_id' => 'none', :back_to => @back}, :method => :post,
75 <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'category_id' => 'none', :back_url => @back}, :method => :post,
76 :selected => (@issue && @issue.category.nil?), :disabled => !@can[:update] %></li>
76 :selected => (@issue && @issue.category.nil?), :disabled => !@can[:update] %></li>
77 </ul>
77 </ul>
78 </li>
78 </li>
79 <% end -%>
79 <% end -%>
80 <% if Issue.use_field_for_done_ratio? %>
80 <% if Issue.use_field_for_done_ratio? %>
81 <li class="folder">
81 <li class="folder">
82 <a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
82 <a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
83 <ul>
83 <ul>
84 <% (0..10).map{|x|x*10}.each do |p| -%>
84 <% (0..10).map{|x|x*10}.each do |p| -%>
85 <li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'done_ratio' => p, :back_to => @back}, :method => :post,
85 <li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), 'done_ratio' => p, :back_url => @back}, :method => :post,
86 :selected => (@issue && p == @issue.done_ratio), :disabled => !@can[:edit] %></li>
86 :selected => (@issue && p == @issue.done_ratio), :disabled => !@can[:edit] %></li>
87 <% end -%>
87 <% end -%>
88 </ul>
88 </ul>
89 </li>
89 </li>
90 <% end %>
90 <% end %>
91 <% if !@issue.nil? %>
91 <% if !@issue.nil? %>
92 <% if @can[:log_time] -%>
92 <% if @can[:log_time] -%>
93 <li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue},
93 <li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue},
94 :class => 'icon-time-add' %></li>
94 :class => 'icon-time-add' %></li>
95 <% end %>
95 <% end %>
96 <% if User.current.logged? %>
96 <% if User.current.logged? %>
97 <li><%= watcher_link(@issue, User.current) %></li>
97 <li><%= watcher_link(@issue, User.current) %></li>
98 <% end %>
98 <% end %>
99 <% end %>
99 <% end %>
100
100
101 <% if @issue.present? %>
101 <% if @issue.present? %>
102 <li><%= context_menu_link l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
102 <li><%= context_menu_link l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
103 :class => 'icon-duplicate', :disabled => !@can[:copy] %></li>
103 :class => 'icon-duplicate', :disabled => !@can[:copy] %></li>
104 <% end %>
104 <% end %>
105 <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'move', :ids => @issues.collect(&:id), :copy_options => {:copy => 't'}},
105 <li><%= context_menu_link l(:button_copy), {:controller => 'issues', :action => 'move', :ids => @issues.collect(&:id), :copy_options => {:copy => 't'}},
106 :class => 'icon-copy', :disabled => !@can[:move] %></li>
106 :class => 'icon-copy', :disabled => !@can[:move] %></li>
107 <li><%= context_menu_link l(:button_move), {:controller => 'issues', :action => 'move', :ids => @issues.collect(&:id)},
107 <li><%= context_menu_link l(:button_move), {:controller => 'issues', :action => 'move', :ids => @issues.collect(&:id)},
108 :class => 'icon-move', :disabled => !@can[:move] %></li>
108 :class => 'icon-move', :disabled => !@can[:move] %></li>
109 <li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id)},
109 <li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id)},
110 :method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li>
110 :method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li>
111
111
112 <%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
112 <%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
113 </ul>
113 </ul>
@@ -1,1291 +1,1337
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'issues_controller'
19 require 'issues_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class IssuesController; def rescue_action(e) raise e end; end
22 class IssuesController; def rescue_action(e) raise e end; end
23
23
24 class IssuesControllerTest < ActionController::TestCase
24 class IssuesControllerTest < ActionController::TestCase
25 fixtures :projects,
25 fixtures :projects,
26 :users,
26 :users,
27 :roles,
27 :roles,
28 :members,
28 :members,
29 :member_roles,
29 :member_roles,
30 :issues,
30 :issues,
31 :issue_statuses,
31 :issue_statuses,
32 :versions,
32 :versions,
33 :trackers,
33 :trackers,
34 :projects_trackers,
34 :projects_trackers,
35 :issue_categories,
35 :issue_categories,
36 :enabled_modules,
36 :enabled_modules,
37 :enumerations,
37 :enumerations,
38 :attachments,
38 :attachments,
39 :workflows,
39 :workflows,
40 :custom_fields,
40 :custom_fields,
41 :custom_values,
41 :custom_values,
42 :custom_fields_projects,
42 :custom_fields_projects,
43 :custom_fields_trackers,
43 :custom_fields_trackers,
44 :time_entries,
44 :time_entries,
45 :journals,
45 :journals,
46 :journal_details,
46 :journal_details,
47 :queries
47 :queries
48
48
49 def setup
49 def setup
50 @controller = IssuesController.new
50 @controller = IssuesController.new
51 @request = ActionController::TestRequest.new
51 @request = ActionController::TestRequest.new
52 @response = ActionController::TestResponse.new
52 @response = ActionController::TestResponse.new
53 User.current = nil
53 User.current = nil
54 end
54 end
55
55
56 def test_index_routing
56 def test_index_routing
57 assert_routing(
57 assert_routing(
58 {:method => :get, :path => '/issues'},
58 {:method => :get, :path => '/issues'},
59 :controller => 'issues', :action => 'index'
59 :controller => 'issues', :action => 'index'
60 )
60 )
61 end
61 end
62
62
63 def test_index
63 def test_index
64 Setting.default_language = 'en'
64 Setting.default_language = 'en'
65
65
66 get :index
66 get :index
67 assert_response :success
67 assert_response :success
68 assert_template 'index.rhtml'
68 assert_template 'index.rhtml'
69 assert_not_nil assigns(:issues)
69 assert_not_nil assigns(:issues)
70 assert_nil assigns(:project)
70 assert_nil assigns(:project)
71 assert_tag :tag => 'a', :content => /Can't print recipes/
71 assert_tag :tag => 'a', :content => /Can't print recipes/
72 assert_tag :tag => 'a', :content => /Subproject issue/
72 assert_tag :tag => 'a', :content => /Subproject issue/
73 # private projects hidden
73 # private projects hidden
74 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
74 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
75 assert_no_tag :tag => 'a', :content => /Issue on project 2/
75 assert_no_tag :tag => 'a', :content => /Issue on project 2/
76 # project column
76 # project column
77 assert_tag :tag => 'th', :content => /Project/
77 assert_tag :tag => 'th', :content => /Project/
78 end
78 end
79
79
80 def test_index_should_not_list_issues_when_module_disabled
80 def test_index_should_not_list_issues_when_module_disabled
81 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
81 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
82 get :index
82 get :index
83 assert_response :success
83 assert_response :success
84 assert_template 'index.rhtml'
84 assert_template 'index.rhtml'
85 assert_not_nil assigns(:issues)
85 assert_not_nil assigns(:issues)
86 assert_nil assigns(:project)
86 assert_nil assigns(:project)
87 assert_no_tag :tag => 'a', :content => /Can't print recipes/
87 assert_no_tag :tag => 'a', :content => /Can't print recipes/
88 assert_tag :tag => 'a', :content => /Subproject issue/
88 assert_tag :tag => 'a', :content => /Subproject issue/
89 end
89 end
90
90
91 def test_index_with_project_routing
91 def test_index_with_project_routing
92 assert_routing(
92 assert_routing(
93 {:method => :get, :path => '/projects/23/issues'},
93 {:method => :get, :path => '/projects/23/issues'},
94 :controller => 'issues', :action => 'index', :project_id => '23'
94 :controller => 'issues', :action => 'index', :project_id => '23'
95 )
95 )
96 end
96 end
97
97
98 def test_index_should_not_list_issues_when_module_disabled
98 def test_index_should_not_list_issues_when_module_disabled
99 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
99 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
100 get :index
100 get :index
101 assert_response :success
101 assert_response :success
102 assert_template 'index.rhtml'
102 assert_template 'index.rhtml'
103 assert_not_nil assigns(:issues)
103 assert_not_nil assigns(:issues)
104 assert_nil assigns(:project)
104 assert_nil assigns(:project)
105 assert_no_tag :tag => 'a', :content => /Can't print recipes/
105 assert_no_tag :tag => 'a', :content => /Can't print recipes/
106 assert_tag :tag => 'a', :content => /Subproject issue/
106 assert_tag :tag => 'a', :content => /Subproject issue/
107 end
107 end
108
108
109 def test_index_with_project_routing
109 def test_index_with_project_routing
110 assert_routing(
110 assert_routing(
111 {:method => :get, :path => 'projects/23/issues'},
111 {:method => :get, :path => 'projects/23/issues'},
112 :controller => 'issues', :action => 'index', :project_id => '23'
112 :controller => 'issues', :action => 'index', :project_id => '23'
113 )
113 )
114 end
114 end
115
115
116 def test_index_with_project
116 def test_index_with_project
117 Setting.display_subprojects_issues = 0
117 Setting.display_subprojects_issues = 0
118 get :index, :project_id => 1
118 get :index, :project_id => 1
119 assert_response :success
119 assert_response :success
120 assert_template 'index.rhtml'
120 assert_template 'index.rhtml'
121 assert_not_nil assigns(:issues)
121 assert_not_nil assigns(:issues)
122 assert_tag :tag => 'a', :content => /Can't print recipes/
122 assert_tag :tag => 'a', :content => /Can't print recipes/
123 assert_no_tag :tag => 'a', :content => /Subproject issue/
123 assert_no_tag :tag => 'a', :content => /Subproject issue/
124 end
124 end
125
125
126 def test_index_with_project_and_subprojects
126 def test_index_with_project_and_subprojects
127 Setting.display_subprojects_issues = 1
127 Setting.display_subprojects_issues = 1
128 get :index, :project_id => 1
128 get :index, :project_id => 1
129 assert_response :success
129 assert_response :success
130 assert_template 'index.rhtml'
130 assert_template 'index.rhtml'
131 assert_not_nil assigns(:issues)
131 assert_not_nil assigns(:issues)
132 assert_tag :tag => 'a', :content => /Can't print recipes/
132 assert_tag :tag => 'a', :content => /Can't print recipes/
133 assert_tag :tag => 'a', :content => /Subproject issue/
133 assert_tag :tag => 'a', :content => /Subproject issue/
134 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
134 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
135 end
135 end
136
136
137 def test_index_with_project_and_subprojects_should_show_private_subprojects
137 def test_index_with_project_and_subprojects_should_show_private_subprojects
138 @request.session[:user_id] = 2
138 @request.session[:user_id] = 2
139 Setting.display_subprojects_issues = 1
139 Setting.display_subprojects_issues = 1
140 get :index, :project_id => 1
140 get :index, :project_id => 1
141 assert_response :success
141 assert_response :success
142 assert_template 'index.rhtml'
142 assert_template 'index.rhtml'
143 assert_not_nil assigns(:issues)
143 assert_not_nil assigns(:issues)
144 assert_tag :tag => 'a', :content => /Can't print recipes/
144 assert_tag :tag => 'a', :content => /Can't print recipes/
145 assert_tag :tag => 'a', :content => /Subproject issue/
145 assert_tag :tag => 'a', :content => /Subproject issue/
146 assert_tag :tag => 'a', :content => /Issue of a private subproject/
146 assert_tag :tag => 'a', :content => /Issue of a private subproject/
147 end
147 end
148
148
149 def test_index_with_project_routing_formatted
149 def test_index_with_project_routing_formatted
150 assert_routing(
150 assert_routing(
151 {:method => :get, :path => 'projects/23/issues.pdf'},
151 {:method => :get, :path => 'projects/23/issues.pdf'},
152 :controller => 'issues', :action => 'index', :project_id => '23', :format => 'pdf'
152 :controller => 'issues', :action => 'index', :project_id => '23', :format => 'pdf'
153 )
153 )
154 assert_routing(
154 assert_routing(
155 {:method => :get, :path => 'projects/23/issues.atom'},
155 {:method => :get, :path => 'projects/23/issues.atom'},
156 :controller => 'issues', :action => 'index', :project_id => '23', :format => 'atom'
156 :controller => 'issues', :action => 'index', :project_id => '23', :format => 'atom'
157 )
157 )
158 end
158 end
159
159
160 def test_index_with_project_and_filter
160 def test_index_with_project_and_filter
161 get :index, :project_id => 1, :set_filter => 1
161 get :index, :project_id => 1, :set_filter => 1
162 assert_response :success
162 assert_response :success
163 assert_template 'index.rhtml'
163 assert_template 'index.rhtml'
164 assert_not_nil assigns(:issues)
164 assert_not_nil assigns(:issues)
165 end
165 end
166
166
167 def test_index_with_query
167 def test_index_with_query
168 get :index, :project_id => 1, :query_id => 5
168 get :index, :project_id => 1, :query_id => 5
169 assert_response :success
169 assert_response :success
170 assert_template 'index.rhtml'
170 assert_template 'index.rhtml'
171 assert_not_nil assigns(:issues)
171 assert_not_nil assigns(:issues)
172 assert_nil assigns(:issue_count_by_group)
172 assert_nil assigns(:issue_count_by_group)
173 end
173 end
174
174
175 def test_index_with_query_grouped_by_tracker
175 def test_index_with_query_grouped_by_tracker
176 get :index, :project_id => 1, :query_id => 6
176 get :index, :project_id => 1, :query_id => 6
177 assert_response :success
177 assert_response :success
178 assert_template 'index.rhtml'
178 assert_template 'index.rhtml'
179 assert_not_nil assigns(:issues)
179 assert_not_nil assigns(:issues)
180 assert_not_nil assigns(:issue_count_by_group)
180 assert_not_nil assigns(:issue_count_by_group)
181 end
181 end
182
182
183 def test_index_with_query_grouped_by_list_custom_field
183 def test_index_with_query_grouped_by_list_custom_field
184 get :index, :project_id => 1, :query_id => 9
184 get :index, :project_id => 1, :query_id => 9
185 assert_response :success
185 assert_response :success
186 assert_template 'index.rhtml'
186 assert_template 'index.rhtml'
187 assert_not_nil assigns(:issues)
187 assert_not_nil assigns(:issues)
188 assert_not_nil assigns(:issue_count_by_group)
188 assert_not_nil assigns(:issue_count_by_group)
189 end
189 end
190
190
191 def test_index_sort_by_field_not_included_in_columns
191 def test_index_sort_by_field_not_included_in_columns
192 Setting.issue_list_default_columns = %w(subject author)
192 Setting.issue_list_default_columns = %w(subject author)
193 get :index, :sort => 'tracker'
193 get :index, :sort => 'tracker'
194 end
194 end
195
195
196 def test_index_csv_with_project
196 def test_index_csv_with_project
197 Setting.default_language = 'en'
197 Setting.default_language = 'en'
198
198
199 get :index, :format => 'csv'
199 get :index, :format => 'csv'
200 assert_response :success
200 assert_response :success
201 assert_not_nil assigns(:issues)
201 assert_not_nil assigns(:issues)
202 assert_equal 'text/csv', @response.content_type
202 assert_equal 'text/csv', @response.content_type
203 assert @response.body.starts_with?("#,")
203 assert @response.body.starts_with?("#,")
204
204
205 get :index, :project_id => 1, :format => 'csv'
205 get :index, :project_id => 1, :format => 'csv'
206 assert_response :success
206 assert_response :success
207 assert_not_nil assigns(:issues)
207 assert_not_nil assigns(:issues)
208 assert_equal 'text/csv', @response.content_type
208 assert_equal 'text/csv', @response.content_type
209 end
209 end
210
210
211 def test_index_formatted
211 def test_index_formatted
212 assert_routing(
212 assert_routing(
213 {:method => :get, :path => 'issues.pdf'},
213 {:method => :get, :path => 'issues.pdf'},
214 :controller => 'issues', :action => 'index', :format => 'pdf'
214 :controller => 'issues', :action => 'index', :format => 'pdf'
215 )
215 )
216 assert_routing(
216 assert_routing(
217 {:method => :get, :path => 'issues.atom'},
217 {:method => :get, :path => 'issues.atom'},
218 :controller => 'issues', :action => 'index', :format => 'atom'
218 :controller => 'issues', :action => 'index', :format => 'atom'
219 )
219 )
220 end
220 end
221
221
222 def test_index_pdf
222 def test_index_pdf
223 get :index, :format => 'pdf'
223 get :index, :format => 'pdf'
224 assert_response :success
224 assert_response :success
225 assert_not_nil assigns(:issues)
225 assert_not_nil assigns(:issues)
226 assert_equal 'application/pdf', @response.content_type
226 assert_equal 'application/pdf', @response.content_type
227
227
228 get :index, :project_id => 1, :format => 'pdf'
228 get :index, :project_id => 1, :format => 'pdf'
229 assert_response :success
229 assert_response :success
230 assert_not_nil assigns(:issues)
230 assert_not_nil assigns(:issues)
231 assert_equal 'application/pdf', @response.content_type
231 assert_equal 'application/pdf', @response.content_type
232
232
233 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
233 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
234 assert_response :success
234 assert_response :success
235 assert_not_nil assigns(:issues)
235 assert_not_nil assigns(:issues)
236 assert_equal 'application/pdf', @response.content_type
236 assert_equal 'application/pdf', @response.content_type
237 end
237 end
238
238
239 def test_index_sort
239 def test_index_sort
240 get :index, :sort => 'tracker,id:desc'
240 get :index, :sort => 'tracker,id:desc'
241 assert_response :success
241 assert_response :success
242
242
243 sort_params = @request.session['issues_index_sort']
243 sort_params = @request.session['issues_index_sort']
244 assert sort_params.is_a?(String)
244 assert sort_params.is_a?(String)
245 assert_equal 'tracker,id:desc', sort_params
245 assert_equal 'tracker,id:desc', sort_params
246
246
247 issues = assigns(:issues)
247 issues = assigns(:issues)
248 assert_not_nil issues
248 assert_not_nil issues
249 assert !issues.empty?
249 assert !issues.empty?
250 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
250 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
251 end
251 end
252
252
253 def test_index_with_columns
253 def test_index_with_columns
254 columns = ['tracker', 'subject', 'assigned_to']
254 columns = ['tracker', 'subject', 'assigned_to']
255 get :index, :set_filter => 1, :query => { 'column_names' => columns}
255 get :index, :set_filter => 1, :query => { 'column_names' => columns}
256 assert_response :success
256 assert_response :success
257
257
258 # query should use specified columns
258 # query should use specified columns
259 query = assigns(:query)
259 query = assigns(:query)
260 assert_kind_of Query, query
260 assert_kind_of Query, query
261 assert_equal columns, query.column_names.map(&:to_s)
261 assert_equal columns, query.column_names.map(&:to_s)
262
262
263 # columns should be stored in session
263 # columns should be stored in session
264 assert_kind_of Hash, session[:query]
264 assert_kind_of Hash, session[:query]
265 assert_kind_of Array, session[:query][:column_names]
265 assert_kind_of Array, session[:query][:column_names]
266 assert_equal columns, session[:query][:column_names].map(&:to_s)
266 assert_equal columns, session[:query][:column_names].map(&:to_s)
267 end
267 end
268
268
269 def test_gantt
269 def test_gantt
270 get :gantt, :project_id => 1
270 get :gantt, :project_id => 1
271 assert_response :success
271 assert_response :success
272 assert_template 'gantt.rhtml'
272 assert_template 'gantt.rhtml'
273 assert_not_nil assigns(:gantt)
273 assert_not_nil assigns(:gantt)
274 events = assigns(:gantt).events
274 events = assigns(:gantt).events
275 assert_not_nil events
275 assert_not_nil events
276 # Issue with start and due dates
276 # Issue with start and due dates
277 i = Issue.find(1)
277 i = Issue.find(1)
278 assert_not_nil i.due_date
278 assert_not_nil i.due_date
279 assert events.include?(Issue.find(1))
279 assert events.include?(Issue.find(1))
280 # Issue with without due date but targeted to a version with date
280 # Issue with without due date but targeted to a version with date
281 i = Issue.find(2)
281 i = Issue.find(2)
282 assert_nil i.due_date
282 assert_nil i.due_date
283 assert events.include?(i)
283 assert events.include?(i)
284 end
284 end
285
285
286 def test_cross_project_gantt
286 def test_cross_project_gantt
287 get :gantt
287 get :gantt
288 assert_response :success
288 assert_response :success
289 assert_template 'gantt.rhtml'
289 assert_template 'gantt.rhtml'
290 assert_not_nil assigns(:gantt)
290 assert_not_nil assigns(:gantt)
291 events = assigns(:gantt).events
291 events = assigns(:gantt).events
292 assert_not_nil events
292 assert_not_nil events
293 end
293 end
294
294
295 def test_gantt_export_to_pdf
295 def test_gantt_export_to_pdf
296 get :gantt, :project_id => 1, :format => 'pdf'
296 get :gantt, :project_id => 1, :format => 'pdf'
297 assert_response :success
297 assert_response :success
298 assert_equal 'application/pdf', @response.content_type
298 assert_equal 'application/pdf', @response.content_type
299 assert @response.body.starts_with?('%PDF')
299 assert @response.body.starts_with?('%PDF')
300 assert_not_nil assigns(:gantt)
300 assert_not_nil assigns(:gantt)
301 end
301 end
302
302
303 def test_cross_project_gantt_export_to_pdf
303 def test_cross_project_gantt_export_to_pdf
304 get :gantt, :format => 'pdf'
304 get :gantt, :format => 'pdf'
305 assert_response :success
305 assert_response :success
306 assert_equal 'application/pdf', @response.content_type
306 assert_equal 'application/pdf', @response.content_type
307 assert @response.body.starts_with?('%PDF')
307 assert @response.body.starts_with?('%PDF')
308 assert_not_nil assigns(:gantt)
308 assert_not_nil assigns(:gantt)
309 end
309 end
310
310
311 if Object.const_defined?(:Magick)
311 if Object.const_defined?(:Magick)
312 def test_gantt_image
312 def test_gantt_image
313 get :gantt, :project_id => 1, :format => 'png'
313 get :gantt, :project_id => 1, :format => 'png'
314 assert_response :success
314 assert_response :success
315 assert_equal 'image/png', @response.content_type
315 assert_equal 'image/png', @response.content_type
316 end
316 end
317 else
317 else
318 puts "RMagick not installed. Skipping tests !!!"
318 puts "RMagick not installed. Skipping tests !!!"
319 end
319 end
320
320
321 def test_calendar
321 def test_calendar
322 get :calendar, :project_id => 1
322 get :calendar, :project_id => 1
323 assert_response :success
323 assert_response :success
324 assert_template 'calendar'
324 assert_template 'calendar'
325 assert_not_nil assigns(:calendar)
325 assert_not_nil assigns(:calendar)
326 end
326 end
327
327
328 def test_cross_project_calendar
328 def test_cross_project_calendar
329 get :calendar
329 get :calendar
330 assert_response :success
330 assert_response :success
331 assert_template 'calendar'
331 assert_template 'calendar'
332 assert_not_nil assigns(:calendar)
332 assert_not_nil assigns(:calendar)
333 end
333 end
334
334
335 def test_changes
335 def test_changes
336 get :changes, :project_id => 1
336 get :changes, :project_id => 1
337 assert_response :success
337 assert_response :success
338 assert_not_nil assigns(:journals)
338 assert_not_nil assigns(:journals)
339 assert_equal 'application/atom+xml', @response.content_type
339 assert_equal 'application/atom+xml', @response.content_type
340 end
340 end
341
341
342 def test_show_routing
342 def test_show_routing
343 assert_routing(
343 assert_routing(
344 {:method => :get, :path => '/issues/64'},
344 {:method => :get, :path => '/issues/64'},
345 :controller => 'issues', :action => 'show', :id => '64'
345 :controller => 'issues', :action => 'show', :id => '64'
346 )
346 )
347 end
347 end
348
348
349 def test_show_routing_formatted
349 def test_show_routing_formatted
350 assert_routing(
350 assert_routing(
351 {:method => :get, :path => '/issues/2332.pdf'},
351 {:method => :get, :path => '/issues/2332.pdf'},
352 :controller => 'issues', :action => 'show', :id => '2332', :format => 'pdf'
352 :controller => 'issues', :action => 'show', :id => '2332', :format => 'pdf'
353 )
353 )
354 assert_routing(
354 assert_routing(
355 {:method => :get, :path => '/issues/23123.atom'},
355 {:method => :get, :path => '/issues/23123.atom'},
356 :controller => 'issues', :action => 'show', :id => '23123', :format => 'atom'
356 :controller => 'issues', :action => 'show', :id => '23123', :format => 'atom'
357 )
357 )
358 end
358 end
359
359
360 def test_show_by_anonymous
360 def test_show_by_anonymous
361 get :show, :id => 1
361 get :show, :id => 1
362 assert_response :success
362 assert_response :success
363 assert_template 'show.rhtml'
363 assert_template 'show.rhtml'
364 assert_not_nil assigns(:issue)
364 assert_not_nil assigns(:issue)
365 assert_equal Issue.find(1), assigns(:issue)
365 assert_equal Issue.find(1), assigns(:issue)
366
366
367 # anonymous role is allowed to add a note
367 # anonymous role is allowed to add a note
368 assert_tag :tag => 'form',
368 assert_tag :tag => 'form',
369 :descendant => { :tag => 'fieldset',
369 :descendant => { :tag => 'fieldset',
370 :child => { :tag => 'legend',
370 :child => { :tag => 'legend',
371 :content => /Notes/ } }
371 :content => /Notes/ } }
372 end
372 end
373
373
374 def test_show_by_manager
374 def test_show_by_manager
375 @request.session[:user_id] = 2
375 @request.session[:user_id] = 2
376 get :show, :id => 1
376 get :show, :id => 1
377 assert_response :success
377 assert_response :success
378
378
379 assert_tag :tag => 'form',
379 assert_tag :tag => 'form',
380 :descendant => { :tag => 'fieldset',
380 :descendant => { :tag => 'fieldset',
381 :child => { :tag => 'legend',
381 :child => { :tag => 'legend',
382 :content => /Change properties/ } },
382 :content => /Change properties/ } },
383 :descendant => { :tag => 'fieldset',
383 :descendant => { :tag => 'fieldset',
384 :child => { :tag => 'legend',
384 :child => { :tag => 'legend',
385 :content => /Log time/ } },
385 :content => /Log time/ } },
386 :descendant => { :tag => 'fieldset',
386 :descendant => { :tag => 'fieldset',
387 :child => { :tag => 'legend',
387 :child => { :tag => 'legend',
388 :content => /Notes/ } }
388 :content => /Notes/ } }
389 end
389 end
390
390
391 def test_show_should_deny_anonymous_access_without_permission
391 def test_show_should_deny_anonymous_access_without_permission
392 Role.anonymous.remove_permission!(:view_issues)
392 Role.anonymous.remove_permission!(:view_issues)
393 get :show, :id => 1
393 get :show, :id => 1
394 assert_response :redirect
394 assert_response :redirect
395 end
395 end
396
396
397 def test_show_should_deny_non_member_access_without_permission
397 def test_show_should_deny_non_member_access_without_permission
398 Role.non_member.remove_permission!(:view_issues)
398 Role.non_member.remove_permission!(:view_issues)
399 @request.session[:user_id] = 9
399 @request.session[:user_id] = 9
400 get :show, :id => 1
400 get :show, :id => 1
401 assert_response 403
401 assert_response 403
402 end
402 end
403
403
404 def test_show_should_deny_member_access_without_permission
404 def test_show_should_deny_member_access_without_permission
405 Role.find(1).remove_permission!(:view_issues)
405 Role.find(1).remove_permission!(:view_issues)
406 @request.session[:user_id] = 2
406 @request.session[:user_id] = 2
407 get :show, :id => 1
407 get :show, :id => 1
408 assert_response 403
408 assert_response 403
409 end
409 end
410
410
411 def test_show_should_not_disclose_relations_to_invisible_issues
411 def test_show_should_not_disclose_relations_to_invisible_issues
412 Setting.cross_project_issue_relations = '1'
412 Setting.cross_project_issue_relations = '1'
413 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
413 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
414 # Relation to a private project issue
414 # Relation to a private project issue
415 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
415 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
416
416
417 get :show, :id => 1
417 get :show, :id => 1
418 assert_response :success
418 assert_response :success
419
419
420 assert_tag :div, :attributes => { :id => 'relations' },
420 assert_tag :div, :attributes => { :id => 'relations' },
421 :descendant => { :tag => 'a', :content => /#2$/ }
421 :descendant => { :tag => 'a', :content => /#2$/ }
422 assert_no_tag :div, :attributes => { :id => 'relations' },
422 assert_no_tag :div, :attributes => { :id => 'relations' },
423 :descendant => { :tag => 'a', :content => /#4$/ }
423 :descendant => { :tag => 'a', :content => /#4$/ }
424 end
424 end
425
425
426 def test_show_atom
426 def test_show_atom
427 get :show, :id => 2, :format => 'atom'
427 get :show, :id => 2, :format => 'atom'
428 assert_response :success
428 assert_response :success
429 assert_template 'changes.rxml'
429 assert_template 'changes.rxml'
430 # Inline image
430 # Inline image
431 assert @response.body.include?("&lt;img src=\"http://test.host/attachments/download/10\" alt=\"\" /&gt;"), "Body did not match. Body: #{@response.body}"
431 assert @response.body.include?("&lt;img src=\"http://test.host/attachments/download/10\" alt=\"\" /&gt;"), "Body did not match. Body: #{@response.body}"
432 end
432 end
433
433
434 def test_new_routing
434 def test_new_routing
435 assert_routing(
435 assert_routing(
436 {:method => :get, :path => '/projects/1/issues/new'},
436 {:method => :get, :path => '/projects/1/issues/new'},
437 :controller => 'issues', :action => 'new', :project_id => '1'
437 :controller => 'issues', :action => 'new', :project_id => '1'
438 )
438 )
439 assert_recognizes(
439 assert_recognizes(
440 {:controller => 'issues', :action => 'new', :project_id => '1'},
440 {:controller => 'issues', :action => 'new', :project_id => '1'},
441 {:method => :post, :path => '/projects/1/issues'}
441 {:method => :post, :path => '/projects/1/issues'}
442 )
442 )
443 end
443 end
444
444
445 def test_show_export_to_pdf
445 def test_show_export_to_pdf
446 get :show, :id => 3, :format => 'pdf'
446 get :show, :id => 3, :format => 'pdf'
447 assert_response :success
447 assert_response :success
448 assert_equal 'application/pdf', @response.content_type
448 assert_equal 'application/pdf', @response.content_type
449 assert @response.body.starts_with?('%PDF')
449 assert @response.body.starts_with?('%PDF')
450 assert_not_nil assigns(:issue)
450 assert_not_nil assigns(:issue)
451 end
451 end
452
452
453 def test_get_new
453 def test_get_new
454 @request.session[:user_id] = 2
454 @request.session[:user_id] = 2
455 get :new, :project_id => 1, :tracker_id => 1
455 get :new, :project_id => 1, :tracker_id => 1
456 assert_response :success
456 assert_response :success
457 assert_template 'new'
457 assert_template 'new'
458
458
459 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
459 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
460 :value => 'Default string' }
460 :value => 'Default string' }
461 end
461 end
462
462
463 def test_get_new_without_tracker_id
463 def test_get_new_without_tracker_id
464 @request.session[:user_id] = 2
464 @request.session[:user_id] = 2
465 get :new, :project_id => 1
465 get :new, :project_id => 1
466 assert_response :success
466 assert_response :success
467 assert_template 'new'
467 assert_template 'new'
468
468
469 issue = assigns(:issue)
469 issue = assigns(:issue)
470 assert_not_nil issue
470 assert_not_nil issue
471 assert_equal Project.find(1).trackers.first, issue.tracker
471 assert_equal Project.find(1).trackers.first, issue.tracker
472 end
472 end
473
473
474 def test_get_new_with_no_default_status_should_display_an_error
474 def test_get_new_with_no_default_status_should_display_an_error
475 @request.session[:user_id] = 2
475 @request.session[:user_id] = 2
476 IssueStatus.delete_all
476 IssueStatus.delete_all
477
477
478 get :new, :project_id => 1
478 get :new, :project_id => 1
479 assert_response 500
479 assert_response 500
480 assert_not_nil flash[:error]
480 assert_not_nil flash[:error]
481 assert_tag :tag => 'div', :attributes => { :class => /error/ },
481 assert_tag :tag => 'div', :attributes => { :class => /error/ },
482 :content => /No default issue/
482 :content => /No default issue/
483 end
483 end
484
484
485 def test_get_new_with_no_tracker_should_display_an_error
485 def test_get_new_with_no_tracker_should_display_an_error
486 @request.session[:user_id] = 2
486 @request.session[:user_id] = 2
487 Tracker.delete_all
487 Tracker.delete_all
488
488
489 get :new, :project_id => 1
489 get :new, :project_id => 1
490 assert_response 500
490 assert_response 500
491 assert_not_nil flash[:error]
491 assert_not_nil flash[:error]
492 assert_tag :tag => 'div', :attributes => { :class => /error/ },
492 assert_tag :tag => 'div', :attributes => { :class => /error/ },
493 :content => /No tracker/
493 :content => /No tracker/
494 end
494 end
495
495
496 def test_update_new_form
496 def test_update_new_form
497 @request.session[:user_id] = 2
497 @request.session[:user_id] = 2
498 xhr :post, :update_form, :project_id => 1,
498 xhr :post, :update_form, :project_id => 1,
499 :issue => {:tracker_id => 2,
499 :issue => {:tracker_id => 2,
500 :subject => 'This is the test_new issue',
500 :subject => 'This is the test_new issue',
501 :description => 'This is the description',
501 :description => 'This is the description',
502 :priority_id => 5}
502 :priority_id => 5}
503 assert_response :success
503 assert_response :success
504 assert_template 'attributes'
504 assert_template 'attributes'
505
505
506 issue = assigns(:issue)
506 issue = assigns(:issue)
507 assert_kind_of Issue, issue
507 assert_kind_of Issue, issue
508 assert_equal 1, issue.project_id
508 assert_equal 1, issue.project_id
509 assert_equal 2, issue.tracker_id
509 assert_equal 2, issue.tracker_id
510 assert_equal 'This is the test_new issue', issue.subject
510 assert_equal 'This is the test_new issue', issue.subject
511 end
511 end
512
512
513 def test_post_new
513 def test_post_new
514 @request.session[:user_id] = 2
514 @request.session[:user_id] = 2
515 assert_difference 'Issue.count' do
515 assert_difference 'Issue.count' do
516 post :new, :project_id => 1,
516 post :new, :project_id => 1,
517 :issue => {:tracker_id => 3,
517 :issue => {:tracker_id => 3,
518 :subject => 'This is the test_new issue',
518 :subject => 'This is the test_new issue',
519 :description => 'This is the description',
519 :description => 'This is the description',
520 :priority_id => 5,
520 :priority_id => 5,
521 :estimated_hours => '',
521 :estimated_hours => '',
522 :custom_field_values => {'2' => 'Value for field 2'}}
522 :custom_field_values => {'2' => 'Value for field 2'}}
523 end
523 end
524 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
524 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
525
525
526 issue = Issue.find_by_subject('This is the test_new issue')
526 issue = Issue.find_by_subject('This is the test_new issue')
527 assert_not_nil issue
527 assert_not_nil issue
528 assert_equal 2, issue.author_id
528 assert_equal 2, issue.author_id
529 assert_equal 3, issue.tracker_id
529 assert_equal 3, issue.tracker_id
530 assert_nil issue.estimated_hours
530 assert_nil issue.estimated_hours
531 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
531 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
532 assert_not_nil v
532 assert_not_nil v
533 assert_equal 'Value for field 2', v.value
533 assert_equal 'Value for field 2', v.value
534 end
534 end
535
535
536 def test_post_new_and_continue
536 def test_post_new_and_continue
537 @request.session[:user_id] = 2
537 @request.session[:user_id] = 2
538 post :new, :project_id => 1,
538 post :new, :project_id => 1,
539 :issue => {:tracker_id => 3,
539 :issue => {:tracker_id => 3,
540 :subject => 'This is first issue',
540 :subject => 'This is first issue',
541 :priority_id => 5},
541 :priority_id => 5},
542 :continue => ''
542 :continue => ''
543 assert_redirected_to :controller => 'issues', :action => 'new', :tracker_id => 3
543 assert_redirected_to :controller => 'issues', :action => 'new', :tracker_id => 3
544 end
544 end
545
545
546 def test_post_new_without_custom_fields_param
546 def test_post_new_without_custom_fields_param
547 @request.session[:user_id] = 2
547 @request.session[:user_id] = 2
548 assert_difference 'Issue.count' do
548 assert_difference 'Issue.count' do
549 post :new, :project_id => 1,
549 post :new, :project_id => 1,
550 :issue => {:tracker_id => 1,
550 :issue => {:tracker_id => 1,
551 :subject => 'This is the test_new issue',
551 :subject => 'This is the test_new issue',
552 :description => 'This is the description',
552 :description => 'This is the description',
553 :priority_id => 5}
553 :priority_id => 5}
554 end
554 end
555 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
555 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
556 end
556 end
557
557
558 def test_post_new_with_required_custom_field_and_without_custom_fields_param
558 def test_post_new_with_required_custom_field_and_without_custom_fields_param
559 field = IssueCustomField.find_by_name('Database')
559 field = IssueCustomField.find_by_name('Database')
560 field.update_attribute(:is_required, true)
560 field.update_attribute(:is_required, true)
561
561
562 @request.session[:user_id] = 2
562 @request.session[:user_id] = 2
563 post :new, :project_id => 1,
563 post :new, :project_id => 1,
564 :issue => {:tracker_id => 1,
564 :issue => {:tracker_id => 1,
565 :subject => 'This is the test_new issue',
565 :subject => 'This is the test_new issue',
566 :description => 'This is the description',
566 :description => 'This is the description',
567 :priority_id => 5}
567 :priority_id => 5}
568 assert_response :success
568 assert_response :success
569 assert_template 'new'
569 assert_template 'new'
570 issue = assigns(:issue)
570 issue = assigns(:issue)
571 assert_not_nil issue
571 assert_not_nil issue
572 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
572 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
573 end
573 end
574
574
575 def test_post_new_with_watchers
575 def test_post_new_with_watchers
576 @request.session[:user_id] = 2
576 @request.session[:user_id] = 2
577 ActionMailer::Base.deliveries.clear
577 ActionMailer::Base.deliveries.clear
578
578
579 assert_difference 'Watcher.count', 2 do
579 assert_difference 'Watcher.count', 2 do
580 post :new, :project_id => 1,
580 post :new, :project_id => 1,
581 :issue => {:tracker_id => 1,
581 :issue => {:tracker_id => 1,
582 :subject => 'This is a new issue with watchers',
582 :subject => 'This is a new issue with watchers',
583 :description => 'This is the description',
583 :description => 'This is the description',
584 :priority_id => 5,
584 :priority_id => 5,
585 :watcher_user_ids => ['2', '3']}
585 :watcher_user_ids => ['2', '3']}
586 end
586 end
587 issue = Issue.find_by_subject('This is a new issue with watchers')
587 issue = Issue.find_by_subject('This is a new issue with watchers')
588 assert_not_nil issue
588 assert_not_nil issue
589 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
589 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
590
590
591 # Watchers added
591 # Watchers added
592 assert_equal [2, 3], issue.watcher_user_ids.sort
592 assert_equal [2, 3], issue.watcher_user_ids.sort
593 assert issue.watched_by?(User.find(3))
593 assert issue.watched_by?(User.find(3))
594 # Watchers notified
594 # Watchers notified
595 mail = ActionMailer::Base.deliveries.last
595 mail = ActionMailer::Base.deliveries.last
596 assert_kind_of TMail::Mail, mail
596 assert_kind_of TMail::Mail, mail
597 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
597 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
598 end
598 end
599
599
600 def test_post_new_should_send_a_notification
600 def test_post_new_should_send_a_notification
601 ActionMailer::Base.deliveries.clear
601 ActionMailer::Base.deliveries.clear
602 @request.session[:user_id] = 2
602 @request.session[:user_id] = 2
603 assert_difference 'Issue.count' do
603 assert_difference 'Issue.count' do
604 post :new, :project_id => 1,
604 post :new, :project_id => 1,
605 :issue => {:tracker_id => 3,
605 :issue => {:tracker_id => 3,
606 :subject => 'This is the test_new issue',
606 :subject => 'This is the test_new issue',
607 :description => 'This is the description',
607 :description => 'This is the description',
608 :priority_id => 5,
608 :priority_id => 5,
609 :estimated_hours => '',
609 :estimated_hours => '',
610 :custom_field_values => {'2' => 'Value for field 2'}}
610 :custom_field_values => {'2' => 'Value for field 2'}}
611 end
611 end
612 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
612 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
613
613
614 assert_equal 1, ActionMailer::Base.deliveries.size
614 assert_equal 1, ActionMailer::Base.deliveries.size
615 end
615 end
616
616
617 def test_post_should_preserve_fields_values_on_validation_failure
617 def test_post_should_preserve_fields_values_on_validation_failure
618 @request.session[:user_id] = 2
618 @request.session[:user_id] = 2
619 post :new, :project_id => 1,
619 post :new, :project_id => 1,
620 :issue => {:tracker_id => 1,
620 :issue => {:tracker_id => 1,
621 # empty subject
621 # empty subject
622 :subject => '',
622 :subject => '',
623 :description => 'This is a description',
623 :description => 'This is a description',
624 :priority_id => 6,
624 :priority_id => 6,
625 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
625 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
626 assert_response :success
626 assert_response :success
627 assert_template 'new'
627 assert_template 'new'
628
628
629 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
629 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
630 :content => 'This is a description'
630 :content => 'This is a description'
631 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
631 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
632 :child => { :tag => 'option', :attributes => { :selected => 'selected',
632 :child => { :tag => 'option', :attributes => { :selected => 'selected',
633 :value => '6' },
633 :value => '6' },
634 :content => 'High' }
634 :content => 'High' }
635 # Custom fields
635 # Custom fields
636 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
636 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
637 :child => { :tag => 'option', :attributes => { :selected => 'selected',
637 :child => { :tag => 'option', :attributes => { :selected => 'selected',
638 :value => 'Oracle' },
638 :value => 'Oracle' },
639 :content => 'Oracle' }
639 :content => 'Oracle' }
640 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
640 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
641 :value => 'Value for field 2'}
641 :value => 'Value for field 2'}
642 end
642 end
643
643
644 def test_post_new_should_ignore_non_safe_attributes
644 def test_post_new_should_ignore_non_safe_attributes
645 @request.session[:user_id] = 2
645 @request.session[:user_id] = 2
646 assert_nothing_raised do
646 assert_nothing_raised do
647 post :new, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
647 post :new, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
648 end
648 end
649 end
649 end
650
650
651 def test_copy_routing
651 def test_copy_routing
652 assert_routing(
652 assert_routing(
653 {:method => :get, :path => '/projects/world_domination/issues/567/copy'},
653 {:method => :get, :path => '/projects/world_domination/issues/567/copy'},
654 :controller => 'issues', :action => 'new', :project_id => 'world_domination', :copy_from => '567'
654 :controller => 'issues', :action => 'new', :project_id => 'world_domination', :copy_from => '567'
655 )
655 )
656 end
656 end
657
657
658 def test_copy_issue
658 def test_copy_issue
659 @request.session[:user_id] = 2
659 @request.session[:user_id] = 2
660 get :new, :project_id => 1, :copy_from => 1
660 get :new, :project_id => 1, :copy_from => 1
661 assert_template 'new'
661 assert_template 'new'
662 assert_not_nil assigns(:issue)
662 assert_not_nil assigns(:issue)
663 orig = Issue.find(1)
663 orig = Issue.find(1)
664 assert_equal orig.subject, assigns(:issue).subject
664 assert_equal orig.subject, assigns(:issue).subject
665 end
665 end
666
666
667 def test_edit_routing
667 def test_edit_routing
668 assert_routing(
668 assert_routing(
669 {:method => :get, :path => '/issues/1/edit'},
669 {:method => :get, :path => '/issues/1/edit'},
670 :controller => 'issues', :action => 'edit', :id => '1'
670 :controller => 'issues', :action => 'edit', :id => '1'
671 )
671 )
672 assert_recognizes( #TODO: use a PUT on the issue URI isntead, need to adjust form
672 assert_recognizes( #TODO: use a PUT on the issue URI isntead, need to adjust form
673 {:controller => 'issues', :action => 'edit', :id => '1'},
673 {:controller => 'issues', :action => 'edit', :id => '1'},
674 {:method => :post, :path => '/issues/1/edit'}
674 {:method => :post, :path => '/issues/1/edit'}
675 )
675 )
676 end
676 end
677
677
678 def test_get_edit
678 def test_get_edit
679 @request.session[:user_id] = 2
679 @request.session[:user_id] = 2
680 get :edit, :id => 1
680 get :edit, :id => 1
681 assert_response :success
681 assert_response :success
682 assert_template 'edit'
682 assert_template 'edit'
683 assert_not_nil assigns(:issue)
683 assert_not_nil assigns(:issue)
684 assert_equal Issue.find(1), assigns(:issue)
684 assert_equal Issue.find(1), assigns(:issue)
685 end
685 end
686
686
687 def test_get_edit_with_params
687 def test_get_edit_with_params
688 @request.session[:user_id] = 2
688 @request.session[:user_id] = 2
689 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
689 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
690 assert_response :success
690 assert_response :success
691 assert_template 'edit'
691 assert_template 'edit'
692
692
693 issue = assigns(:issue)
693 issue = assigns(:issue)
694 assert_not_nil issue
694 assert_not_nil issue
695
695
696 assert_equal 5, issue.status_id
696 assert_equal 5, issue.status_id
697 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
697 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
698 :child => { :tag => 'option',
698 :child => { :tag => 'option',
699 :content => 'Closed',
699 :content => 'Closed',
700 :attributes => { :selected => 'selected' } }
700 :attributes => { :selected => 'selected' } }
701
701
702 assert_equal 7, issue.priority_id
702 assert_equal 7, issue.priority_id
703 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
703 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
704 :child => { :tag => 'option',
704 :child => { :tag => 'option',
705 :content => 'Urgent',
705 :content => 'Urgent',
706 :attributes => { :selected => 'selected' } }
706 :attributes => { :selected => 'selected' } }
707 end
707 end
708
708
709 def test_update_edit_form
709 def test_update_edit_form
710 @request.session[:user_id] = 2
710 @request.session[:user_id] = 2
711 xhr :post, :update_form, :project_id => 1,
711 xhr :post, :update_form, :project_id => 1,
712 :id => 1,
712 :id => 1,
713 :issue => {:tracker_id => 2,
713 :issue => {:tracker_id => 2,
714 :subject => 'This is the test_new issue',
714 :subject => 'This is the test_new issue',
715 :description => 'This is the description',
715 :description => 'This is the description',
716 :priority_id => 5}
716 :priority_id => 5}
717 assert_response :success
717 assert_response :success
718 assert_template 'attributes'
718 assert_template 'attributes'
719
719
720 issue = assigns(:issue)
720 issue = assigns(:issue)
721 assert_kind_of Issue, issue
721 assert_kind_of Issue, issue
722 assert_equal 1, issue.id
722 assert_equal 1, issue.id
723 assert_equal 1, issue.project_id
723 assert_equal 1, issue.project_id
724 assert_equal 2, issue.tracker_id
724 assert_equal 2, issue.tracker_id
725 assert_equal 'This is the test_new issue', issue.subject
725 assert_equal 'This is the test_new issue', issue.subject
726 end
726 end
727
727
728 def test_reply_routing
728 def test_reply_routing
729 assert_routing(
729 assert_routing(
730 {:method => :post, :path => '/issues/1/quoted'},
730 {:method => :post, :path => '/issues/1/quoted'},
731 :controller => 'issues', :action => 'reply', :id => '1'
731 :controller => 'issues', :action => 'reply', :id => '1'
732 )
732 )
733 end
733 end
734
734
735 def test_reply_to_issue
735 def test_reply_to_issue
736 @request.session[:user_id] = 2
736 @request.session[:user_id] = 2
737 get :reply, :id => 1
737 get :reply, :id => 1
738 assert_response :success
738 assert_response :success
739 assert_select_rjs :show, "update"
739 assert_select_rjs :show, "update"
740 end
740 end
741
741
742 def test_reply_to_note
742 def test_reply_to_note
743 @request.session[:user_id] = 2
743 @request.session[:user_id] = 2
744 get :reply, :id => 1, :journal_id => 2
744 get :reply, :id => 1, :journal_id => 2
745 assert_response :success
745 assert_response :success
746 assert_select_rjs :show, "update"
746 assert_select_rjs :show, "update"
747 end
747 end
748
748
749 def test_post_edit_without_custom_fields_param
749 def test_post_edit_without_custom_fields_param
750 @request.session[:user_id] = 2
750 @request.session[:user_id] = 2
751 ActionMailer::Base.deliveries.clear
751 ActionMailer::Base.deliveries.clear
752
752
753 issue = Issue.find(1)
753 issue = Issue.find(1)
754 assert_equal '125', issue.custom_value_for(2).value
754 assert_equal '125', issue.custom_value_for(2).value
755 old_subject = issue.subject
755 old_subject = issue.subject
756 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
756 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
757
757
758 assert_difference('Journal.count') do
758 assert_difference('Journal.count') do
759 assert_difference('JournalDetail.count', 2) do
759 assert_difference('JournalDetail.count', 2) do
760 post :edit, :id => 1, :issue => {:subject => new_subject,
760 post :edit, :id => 1, :issue => {:subject => new_subject,
761 :priority_id => '6',
761 :priority_id => '6',
762 :category_id => '1' # no change
762 :category_id => '1' # no change
763 }
763 }
764 end
764 end
765 end
765 end
766 assert_redirected_to :action => 'show', :id => '1'
766 assert_redirected_to :action => 'show', :id => '1'
767 issue.reload
767 issue.reload
768 assert_equal new_subject, issue.subject
768 assert_equal new_subject, issue.subject
769 # Make sure custom fields were not cleared
769 # Make sure custom fields were not cleared
770 assert_equal '125', issue.custom_value_for(2).value
770 assert_equal '125', issue.custom_value_for(2).value
771
771
772 mail = ActionMailer::Base.deliveries.last
772 mail = ActionMailer::Base.deliveries.last
773 assert_kind_of TMail::Mail, mail
773 assert_kind_of TMail::Mail, mail
774 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
774 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
775 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
775 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
776 end
776 end
777
777
778 def test_post_edit_with_custom_field_change
778 def test_post_edit_with_custom_field_change
779 @request.session[:user_id] = 2
779 @request.session[:user_id] = 2
780 issue = Issue.find(1)
780 issue = Issue.find(1)
781 assert_equal '125', issue.custom_value_for(2).value
781 assert_equal '125', issue.custom_value_for(2).value
782
782
783 assert_difference('Journal.count') do
783 assert_difference('Journal.count') do
784 assert_difference('JournalDetail.count', 3) do
784 assert_difference('JournalDetail.count', 3) do
785 post :edit, :id => 1, :issue => {:subject => 'Custom field change',
785 post :edit, :id => 1, :issue => {:subject => 'Custom field change',
786 :priority_id => '6',
786 :priority_id => '6',
787 :category_id => '1', # no change
787 :category_id => '1', # no change
788 :custom_field_values => { '2' => 'New custom value' }
788 :custom_field_values => { '2' => 'New custom value' }
789 }
789 }
790 end
790 end
791 end
791 end
792 assert_redirected_to :action => 'show', :id => '1'
792 assert_redirected_to :action => 'show', :id => '1'
793 issue.reload
793 issue.reload
794 assert_equal 'New custom value', issue.custom_value_for(2).value
794 assert_equal 'New custom value', issue.custom_value_for(2).value
795
795
796 mail = ActionMailer::Base.deliveries.last
796 mail = ActionMailer::Base.deliveries.last
797 assert_kind_of TMail::Mail, mail
797 assert_kind_of TMail::Mail, mail
798 assert mail.body.include?("Searchable field changed from 125 to New custom value")
798 assert mail.body.include?("Searchable field changed from 125 to New custom value")
799 end
799 end
800
800
801 def test_post_edit_with_status_and_assignee_change
801 def test_post_edit_with_status_and_assignee_change
802 issue = Issue.find(1)
802 issue = Issue.find(1)
803 assert_equal 1, issue.status_id
803 assert_equal 1, issue.status_id
804 @request.session[:user_id] = 2
804 @request.session[:user_id] = 2
805 assert_difference('TimeEntry.count', 0) do
805 assert_difference('TimeEntry.count', 0) do
806 post :edit,
806 post :edit,
807 :id => 1,
807 :id => 1,
808 :issue => { :status_id => 2, :assigned_to_id => 3 },
808 :issue => { :status_id => 2, :assigned_to_id => 3 },
809 :notes => 'Assigned to dlopper',
809 :notes => 'Assigned to dlopper',
810 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
810 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
811 end
811 end
812 assert_redirected_to :action => 'show', :id => '1'
812 assert_redirected_to :action => 'show', :id => '1'
813 issue.reload
813 issue.reload
814 assert_equal 2, issue.status_id
814 assert_equal 2, issue.status_id
815 j = Journal.find(:first, :order => 'id DESC')
815 j = Journal.find(:first, :order => 'id DESC')
816 assert_equal 'Assigned to dlopper', j.notes
816 assert_equal 'Assigned to dlopper', j.notes
817 assert_equal 2, j.details.size
817 assert_equal 2, j.details.size
818
818
819 mail = ActionMailer::Base.deliveries.last
819 mail = ActionMailer::Base.deliveries.last
820 assert mail.body.include?("Status changed from New to Assigned")
820 assert mail.body.include?("Status changed from New to Assigned")
821 # subject should contain the new status
821 # subject should contain the new status
822 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
822 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
823 end
823 end
824
824
825 def test_post_edit_with_note_only
825 def test_post_edit_with_note_only
826 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
826 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
827 # anonymous user
827 # anonymous user
828 post :edit,
828 post :edit,
829 :id => 1,
829 :id => 1,
830 :notes => notes
830 :notes => notes
831 assert_redirected_to :action => 'show', :id => '1'
831 assert_redirected_to :action => 'show', :id => '1'
832 j = Journal.find(:first, :order => 'id DESC')
832 j = Journal.find(:first, :order => 'id DESC')
833 assert_equal notes, j.notes
833 assert_equal notes, j.notes
834 assert_equal 0, j.details.size
834 assert_equal 0, j.details.size
835 assert_equal User.anonymous, j.user
835 assert_equal User.anonymous, j.user
836
836
837 mail = ActionMailer::Base.deliveries.last
837 mail = ActionMailer::Base.deliveries.last
838 assert mail.body.include?(notes)
838 assert mail.body.include?(notes)
839 end
839 end
840
840
841 def test_post_edit_with_note_and_spent_time
841 def test_post_edit_with_note_and_spent_time
842 @request.session[:user_id] = 2
842 @request.session[:user_id] = 2
843 spent_hours_before = Issue.find(1).spent_hours
843 spent_hours_before = Issue.find(1).spent_hours
844 assert_difference('TimeEntry.count') do
844 assert_difference('TimeEntry.count') do
845 post :edit,
845 post :edit,
846 :id => 1,
846 :id => 1,
847 :notes => '2.5 hours added',
847 :notes => '2.5 hours added',
848 :time_entry => { :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first }
848 :time_entry => { :hours => '2.5', :comments => '', :activity_id => TimeEntryActivity.first }
849 end
849 end
850 assert_redirected_to :action => 'show', :id => '1'
850 assert_redirected_to :action => 'show', :id => '1'
851
851
852 issue = Issue.find(1)
852 issue = Issue.find(1)
853
853
854 j = Journal.find(:first, :order => 'id DESC')
854 j = Journal.find(:first, :order => 'id DESC')
855 assert_equal '2.5 hours added', j.notes
855 assert_equal '2.5 hours added', j.notes
856 assert_equal 0, j.details.size
856 assert_equal 0, j.details.size
857
857
858 t = issue.time_entries.find(:first, :order => 'id DESC')
858 t = issue.time_entries.find(:first, :order => 'id DESC')
859 assert_not_nil t
859 assert_not_nil t
860 assert_equal 2.5, t.hours
860 assert_equal 2.5, t.hours
861 assert_equal spent_hours_before + 2.5, issue.spent_hours
861 assert_equal spent_hours_before + 2.5, issue.spent_hours
862 end
862 end
863
863
864 def test_post_edit_with_attachment_only
864 def test_post_edit_with_attachment_only
865 set_tmp_attachments_directory
865 set_tmp_attachments_directory
866
866
867 # Delete all fixtured journals, a race condition can occur causing the wrong
867 # Delete all fixtured journals, a race condition can occur causing the wrong
868 # journal to get fetched in the next find.
868 # journal to get fetched in the next find.
869 Journal.delete_all
869 Journal.delete_all
870
870
871 # anonymous user
871 # anonymous user
872 post :edit,
872 post :edit,
873 :id => 1,
873 :id => 1,
874 :notes => '',
874 :notes => '',
875 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
875 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
876 assert_redirected_to :action => 'show', :id => '1'
876 assert_redirected_to :action => 'show', :id => '1'
877 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
877 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
878 assert j.notes.blank?
878 assert j.notes.blank?
879 assert_equal 1, j.details.size
879 assert_equal 1, j.details.size
880 assert_equal 'testfile.txt', j.details.first.value
880 assert_equal 'testfile.txt', j.details.first.value
881 assert_equal User.anonymous, j.user
881 assert_equal User.anonymous, j.user
882
882
883 mail = ActionMailer::Base.deliveries.last
883 mail = ActionMailer::Base.deliveries.last
884 assert mail.body.include?('testfile.txt')
884 assert mail.body.include?('testfile.txt')
885 end
885 end
886
886
887 def test_post_edit_with_no_change
887 def test_post_edit_with_no_change
888 issue = Issue.find(1)
888 issue = Issue.find(1)
889 issue.journals.clear
889 issue.journals.clear
890 ActionMailer::Base.deliveries.clear
890 ActionMailer::Base.deliveries.clear
891
891
892 post :edit,
892 post :edit,
893 :id => 1,
893 :id => 1,
894 :notes => ''
894 :notes => ''
895 assert_redirected_to :action => 'show', :id => '1'
895 assert_redirected_to :action => 'show', :id => '1'
896
896
897 issue.reload
897 issue.reload
898 assert issue.journals.empty?
898 assert issue.journals.empty?
899 # No email should be sent
899 # No email should be sent
900 assert ActionMailer::Base.deliveries.empty?
900 assert ActionMailer::Base.deliveries.empty?
901 end
901 end
902
902
903 def test_post_edit_should_send_a_notification
903 def test_post_edit_should_send_a_notification
904 @request.session[:user_id] = 2
904 @request.session[:user_id] = 2
905 ActionMailer::Base.deliveries.clear
905 ActionMailer::Base.deliveries.clear
906 issue = Issue.find(1)
906 issue = Issue.find(1)
907 old_subject = issue.subject
907 old_subject = issue.subject
908 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
908 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
909
909
910 post :edit, :id => 1, :issue => {:subject => new_subject,
910 post :edit, :id => 1, :issue => {:subject => new_subject,
911 :priority_id => '6',
911 :priority_id => '6',
912 :category_id => '1' # no change
912 :category_id => '1' # no change
913 }
913 }
914 assert_equal 1, ActionMailer::Base.deliveries.size
914 assert_equal 1, ActionMailer::Base.deliveries.size
915 end
915 end
916
916
917 def test_post_edit_with_invalid_spent_time
917 def test_post_edit_with_invalid_spent_time
918 @request.session[:user_id] = 2
918 @request.session[:user_id] = 2
919 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
919 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
920
920
921 assert_no_difference('Journal.count') do
921 assert_no_difference('Journal.count') do
922 post :edit,
922 post :edit,
923 :id => 1,
923 :id => 1,
924 :notes => notes,
924 :notes => notes,
925 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
925 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
926 end
926 end
927 assert_response :success
927 assert_response :success
928 assert_template 'edit'
928 assert_template 'edit'
929
929
930 assert_tag :textarea, :attributes => { :name => 'notes' },
930 assert_tag :textarea, :attributes => { :name => 'notes' },
931 :content => notes
931 :content => notes
932 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
932 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
933 end
933 end
934
934
935 def test_post_edit_should_allow_fixed_version_to_be_set_to_a_subproject
935 def test_post_edit_should_allow_fixed_version_to_be_set_to_a_subproject
936 issue = Issue.find(2)
936 issue = Issue.find(2)
937 @request.session[:user_id] = 2
937 @request.session[:user_id] = 2
938
938
939 post :edit,
939 post :edit,
940 :id => issue.id,
940 :id => issue.id,
941 :issue => {
941 :issue => {
942 :fixed_version_id => 4
942 :fixed_version_id => 4
943 }
943 }
944
944
945 assert_response :redirect
945 assert_response :redirect
946 issue.reload
946 issue.reload
947 assert_equal 4, issue.fixed_version_id
947 assert_equal 4, issue.fixed_version_id
948 assert_not_equal issue.project_id, issue.fixed_version.project_id
948 assert_not_equal issue.project_id, issue.fixed_version.project_id
949 end
949 end
950
951 def test_post_edit_should_redirect_back_using_the_back_url_parameter
952 issue = Issue.find(2)
953 @request.session[:user_id] = 2
954
955 post :edit,
956 :id => issue.id,
957 :issue => {
958 :fixed_version_id => 4
959 },
960 :back_url => '/issues'
961
962 assert_response :redirect
963 assert_redirected_to '/issues'
964 end
965
966 def test_post_edit_should_not_redirect_back_using_the_back_url_parameter_off_the_host
967 issue = Issue.find(2)
968 @request.session[:user_id] = 2
969
970 post :edit,
971 :id => issue.id,
972 :issue => {
973 :fixed_version_id => 4
974 },
975 :back_url => 'http://google.com'
976
977 assert_response :redirect
978 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
979 end
950
980
951 def test_get_bulk_edit
981 def test_get_bulk_edit
952 @request.session[:user_id] = 2
982 @request.session[:user_id] = 2
953 get :bulk_edit, :ids => [1, 2]
983 get :bulk_edit, :ids => [1, 2]
954 assert_response :success
984 assert_response :success
955 assert_template 'bulk_edit'
985 assert_template 'bulk_edit'
956
986
957 # Project specific custom field, date type
987 # Project specific custom field, date type
958 field = CustomField.find(9)
988 field = CustomField.find(9)
959 assert !field.is_for_all?
989 assert !field.is_for_all?
960 assert_equal 'date', field.field_format
990 assert_equal 'date', field.field_format
961 assert_tag :input, :attributes => {:name => 'custom_field_values[9]'}
991 assert_tag :input, :attributes => {:name => 'custom_field_values[9]'}
962
992
963 # System wide custom field
993 # System wide custom field
964 assert CustomField.find(1).is_for_all?
994 assert CustomField.find(1).is_for_all?
965 assert_tag :select, :attributes => {:name => 'custom_field_values[1]'}
995 assert_tag :select, :attributes => {:name => 'custom_field_values[1]'}
966 end
996 end
967
997
968 def test_bulk_edit
998 def test_bulk_edit
969 @request.session[:user_id] = 2
999 @request.session[:user_id] = 2
970 # update issues priority
1000 # update issues priority
971 post :bulk_edit, :ids => [1, 2], :priority_id => 7,
1001 post :bulk_edit, :ids => [1, 2], :priority_id => 7,
972 :assigned_to_id => '',
1002 :assigned_to_id => '',
973 :custom_field_values => {'2' => ''},
1003 :custom_field_values => {'2' => ''},
974 :notes => 'Bulk editing'
1004 :notes => 'Bulk editing'
975 assert_response 302
1005 assert_response 302
976 # check that the issues were updated
1006 # check that the issues were updated
977 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
1007 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
978
1008
979 issue = Issue.find(1)
1009 issue = Issue.find(1)
980 journal = issue.journals.find(:first, :order => 'created_on DESC')
1010 journal = issue.journals.find(:first, :order => 'created_on DESC')
981 assert_equal '125', issue.custom_value_for(2).value
1011 assert_equal '125', issue.custom_value_for(2).value
982 assert_equal 'Bulk editing', journal.notes
1012 assert_equal 'Bulk editing', journal.notes
983 assert_equal 1, journal.details.size
1013 assert_equal 1, journal.details.size
984 end
1014 end
985
1015
986 def test_bullk_edit_should_send_a_notification
1016 def test_bullk_edit_should_send_a_notification
987 @request.session[:user_id] = 2
1017 @request.session[:user_id] = 2
988 ActionMailer::Base.deliveries.clear
1018 ActionMailer::Base.deliveries.clear
989 post(:bulk_edit,
1019 post(:bulk_edit,
990 {
1020 {
991 :ids => [1, 2],
1021 :ids => [1, 2],
992 :priority_id => 7,
1022 :priority_id => 7,
993 :assigned_to_id => '',
1023 :assigned_to_id => '',
994 :custom_field_values => {'2' => ''},
1024 :custom_field_values => {'2' => ''},
995 :notes => 'Bulk editing'
1025 :notes => 'Bulk editing'
996 })
1026 })
997
1027
998 assert_response 302
1028 assert_response 302
999 assert_equal 2, ActionMailer::Base.deliveries.size
1029 assert_equal 2, ActionMailer::Base.deliveries.size
1000 end
1030 end
1001
1031
1002 def test_bulk_edit_status
1032 def test_bulk_edit_status
1003 @request.session[:user_id] = 2
1033 @request.session[:user_id] = 2
1004 # update issues priority
1034 # update issues priority
1005 post :bulk_edit, :ids => [1, 2], :priority_id => '',
1035 post :bulk_edit, :ids => [1, 2], :priority_id => '',
1006 :assigned_to_id => '',
1036 :assigned_to_id => '',
1007 :status_id => '5',
1037 :status_id => '5',
1008 :notes => 'Bulk editing status'
1038 :notes => 'Bulk editing status'
1009 assert_response 302
1039 assert_response 302
1010 issue = Issue.find(1)
1040 issue = Issue.find(1)
1011 assert issue.closed?
1041 assert issue.closed?
1012 end
1042 end
1013
1043
1014 def test_bulk_edit_custom_field
1044 def test_bulk_edit_custom_field
1015 @request.session[:user_id] = 2
1045 @request.session[:user_id] = 2
1016 # update issues priority
1046 # update issues priority
1017 post :bulk_edit, :ids => [1, 2], :priority_id => '',
1047 post :bulk_edit, :ids => [1, 2], :priority_id => '',
1018 :assigned_to_id => '',
1048 :assigned_to_id => '',
1019 :custom_field_values => {'2' => '777'},
1049 :custom_field_values => {'2' => '777'},
1020 :notes => 'Bulk editing custom field'
1050 :notes => 'Bulk editing custom field'
1021 assert_response 302
1051 assert_response 302
1022
1052
1023 issue = Issue.find(1)
1053 issue = Issue.find(1)
1024 journal = issue.journals.find(:first, :order => 'created_on DESC')
1054 journal = issue.journals.find(:first, :order => 'created_on DESC')
1025 assert_equal '777', issue.custom_value_for(2).value
1055 assert_equal '777', issue.custom_value_for(2).value
1026 assert_equal 1, journal.details.size
1056 assert_equal 1, journal.details.size
1027 assert_equal '125', journal.details.first.old_value
1057 assert_equal '125', journal.details.first.old_value
1028 assert_equal '777', journal.details.first.value
1058 assert_equal '777', journal.details.first.value
1029 end
1059 end
1030
1060
1031 def test_bulk_unassign
1061 def test_bulk_unassign
1032 assert_not_nil Issue.find(2).assigned_to
1062 assert_not_nil Issue.find(2).assigned_to
1033 @request.session[:user_id] = 2
1063 @request.session[:user_id] = 2
1034 # unassign issues
1064 # unassign issues
1035 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
1065 post :bulk_edit, :ids => [1, 2], :notes => 'Bulk unassigning', :assigned_to_id => 'none'
1036 assert_response 302
1066 assert_response 302
1037 # check that the issues were updated
1067 # check that the issues were updated
1038 assert_nil Issue.find(2).assigned_to
1068 assert_nil Issue.find(2).assigned_to
1039 end
1069 end
1040
1070
1041 def test_post_bulk_edit_should_allow_fixed_version_to_be_set_to_a_subproject
1071 def test_post_bulk_edit_should_allow_fixed_version_to_be_set_to_a_subproject
1042 @request.session[:user_id] = 2
1072 @request.session[:user_id] = 2
1043
1073
1044 post :bulk_edit,
1074 post :bulk_edit,
1045 :ids => [1,2],
1075 :ids => [1,2],
1046 :fixed_version_id => 4
1076 :fixed_version_id => 4
1047
1077
1048 assert_response :redirect
1078 assert_response :redirect
1049 issues = Issue.find([1,2])
1079 issues = Issue.find([1,2])
1050 issues.each do |issue|
1080 issues.each do |issue|
1051 assert_equal 4, issue.fixed_version_id
1081 assert_equal 4, issue.fixed_version_id
1052 assert_not_equal issue.project_id, issue.fixed_version.project_id
1082 assert_not_equal issue.project_id, issue.fixed_version.project_id
1053 end
1083 end
1054 end
1084 end
1055
1085
1086 def test_post_bulk_edit_should_redirect_back_using_the_back_url_parameter
1087 @request.session[:user_id] = 2
1088 post :bulk_edit, :ids => [1,2], :back_url => '/issues'
1089
1090 assert_response :redirect
1091 assert_redirected_to '/issues'
1092 end
1093
1094 def test_post_bulk_edit_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1095 @request.session[:user_id] = 2
1096 post :bulk_edit, :ids => [1,2], :back_url => 'http://google.com'
1097
1098 assert_response :redirect
1099 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
1100 end
1101
1056 def test_move_routing
1102 def test_move_routing
1057 assert_routing(
1103 assert_routing(
1058 {:method => :get, :path => '/issues/1/move'},
1104 {:method => :get, :path => '/issues/1/move'},
1059 :controller => 'issues', :action => 'move', :id => '1'
1105 :controller => 'issues', :action => 'move', :id => '1'
1060 )
1106 )
1061 assert_recognizes(
1107 assert_recognizes(
1062 {:controller => 'issues', :action => 'move', :id => '1'},
1108 {:controller => 'issues', :action => 'move', :id => '1'},
1063 {:method => :post, :path => '/issues/1/move'}
1109 {:method => :post, :path => '/issues/1/move'}
1064 )
1110 )
1065 end
1111 end
1066
1112
1067 def test_move_one_issue_to_another_project
1113 def test_move_one_issue_to_another_project
1068 @request.session[:user_id] = 2
1114 @request.session[:user_id] = 2
1069 post :move, :id => 1, :new_project_id => 2, :tracker_id => '', :assigned_to_id => '', :status_id => '', :start_date => '', :due_date => ''
1115 post :move, :id => 1, :new_project_id => 2, :tracker_id => '', :assigned_to_id => '', :status_id => '', :start_date => '', :due_date => ''
1070 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1116 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1071 assert_equal 2, Issue.find(1).project_id
1117 assert_equal 2, Issue.find(1).project_id
1072 end
1118 end
1073
1119
1074 def test_move_one_issue_to_another_project_should_follow_when_needed
1120 def test_move_one_issue_to_another_project_should_follow_when_needed
1075 @request.session[:user_id] = 2
1121 @request.session[:user_id] = 2
1076 post :move, :id => 1, :new_project_id => 2, :follow => '1'
1122 post :move, :id => 1, :new_project_id => 2, :follow => '1'
1077 assert_redirected_to '/issues/1'
1123 assert_redirected_to '/issues/1'
1078 end
1124 end
1079
1125
1080 def test_bulk_move_to_another_project
1126 def test_bulk_move_to_another_project
1081 @request.session[:user_id] = 2
1127 @request.session[:user_id] = 2
1082 post :move, :ids => [1, 2], :new_project_id => 2
1128 post :move, :ids => [1, 2], :new_project_id => 2
1083 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1129 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1084 # Issues moved to project 2
1130 # Issues moved to project 2
1085 assert_equal 2, Issue.find(1).project_id
1131 assert_equal 2, Issue.find(1).project_id
1086 assert_equal 2, Issue.find(2).project_id
1132 assert_equal 2, Issue.find(2).project_id
1087 # No tracker change
1133 # No tracker change
1088 assert_equal 1, Issue.find(1).tracker_id
1134 assert_equal 1, Issue.find(1).tracker_id
1089 assert_equal 2, Issue.find(2).tracker_id
1135 assert_equal 2, Issue.find(2).tracker_id
1090 end
1136 end
1091
1137
1092 def test_bulk_move_to_another_tracker
1138 def test_bulk_move_to_another_tracker
1093 @request.session[:user_id] = 2
1139 @request.session[:user_id] = 2
1094 post :move, :ids => [1, 2], :new_tracker_id => 2
1140 post :move, :ids => [1, 2], :new_tracker_id => 2
1095 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1141 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1096 assert_equal 2, Issue.find(1).tracker_id
1142 assert_equal 2, Issue.find(1).tracker_id
1097 assert_equal 2, Issue.find(2).tracker_id
1143 assert_equal 2, Issue.find(2).tracker_id
1098 end
1144 end
1099
1145
1100 def test_bulk_copy_to_another_project
1146 def test_bulk_copy_to_another_project
1101 @request.session[:user_id] = 2
1147 @request.session[:user_id] = 2
1102 assert_difference 'Issue.count', 2 do
1148 assert_difference 'Issue.count', 2 do
1103 assert_no_difference 'Project.find(1).issues.count' do
1149 assert_no_difference 'Project.find(1).issues.count' do
1104 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}
1150 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}
1105 end
1151 end
1106 end
1152 end
1107 assert_redirected_to 'projects/ecookbook/issues'
1153 assert_redirected_to 'projects/ecookbook/issues'
1108 end
1154 end
1109
1155
1110 context "#move via bulk copy" do
1156 context "#move via bulk copy" do
1111 should "allow not changing the issue's attributes" do
1157 should "allow not changing the issue's attributes" do
1112 @request.session[:user_id] = 2
1158 @request.session[:user_id] = 2
1113 issue_before_move = Issue.find(1)
1159 issue_before_move = Issue.find(1)
1114 assert_difference 'Issue.count', 1 do
1160 assert_difference 'Issue.count', 1 do
1115 assert_no_difference 'Project.find(1).issues.count' do
1161 assert_no_difference 'Project.find(1).issues.count' do
1116 post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :new_tracker_id => '', :assigned_to_id => '', :status_id => '', :start_date => '', :due_date => ''
1162 post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :new_tracker_id => '', :assigned_to_id => '', :status_id => '', :start_date => '', :due_date => ''
1117 end
1163 end
1118 end
1164 end
1119 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
1165 issue_after_move = Issue.first(:order => 'id desc', :conditions => {:project_id => 2})
1120 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
1166 assert_equal issue_before_move.tracker_id, issue_after_move.tracker_id
1121 assert_equal issue_before_move.status_id, issue_after_move.status_id
1167 assert_equal issue_before_move.status_id, issue_after_move.status_id
1122 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
1168 assert_equal issue_before_move.assigned_to_id, issue_after_move.assigned_to_id
1123 end
1169 end
1124
1170
1125 should "allow changing the issue's attributes" do
1171 should "allow changing the issue's attributes" do
1126 @request.session[:user_id] = 2
1172 @request.session[:user_id] = 2
1127 assert_difference 'Issue.count', 2 do
1173 assert_difference 'Issue.count', 2 do
1128 assert_no_difference 'Project.find(1).issues.count' do
1174 assert_no_difference 'Project.find(1).issues.count' do
1129 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}, :new_tracker_id => '', :assigned_to_id => 4, :status_id => 3, :start_date => '2009-12-01', :due_date => '2009-12-31'
1175 post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'}, :new_tracker_id => '', :assigned_to_id => 4, :status_id => 3, :start_date => '2009-12-01', :due_date => '2009-12-31'
1130 end
1176 end
1131 end
1177 end
1132
1178
1133 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
1179 copied_issues = Issue.all(:limit => 2, :order => 'id desc', :conditions => {:project_id => 2})
1134 assert_equal 2, copied_issues.size
1180 assert_equal 2, copied_issues.size
1135 copied_issues.each do |issue|
1181 copied_issues.each do |issue|
1136 assert_equal 2, issue.project_id, "Project is incorrect"
1182 assert_equal 2, issue.project_id, "Project is incorrect"
1137 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
1183 assert_equal 4, issue.assigned_to_id, "Assigned to is incorrect"
1138 assert_equal 3, issue.status_id, "Status is incorrect"
1184 assert_equal 3, issue.status_id, "Status is incorrect"
1139 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
1185 assert_equal '2009-12-01', issue.start_date.to_s, "Start date is incorrect"
1140 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
1186 assert_equal '2009-12-31', issue.due_date.to_s, "Due date is incorrect"
1141 end
1187 end
1142 end
1188 end
1143 end
1189 end
1144
1190
1145 def test_copy_to_another_project_should_follow_when_needed
1191 def test_copy_to_another_project_should_follow_when_needed
1146 @request.session[:user_id] = 2
1192 @request.session[:user_id] = 2
1147 post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :follow => '1'
1193 post :move, :ids => [1], :new_project_id => 2, :copy_options => {:copy => '1'}, :follow => '1'
1148 issue = Issue.first(:order => 'id DESC')
1194 issue = Issue.first(:order => 'id DESC')
1149 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1195 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
1150 end
1196 end
1151
1197
1152 def test_context_menu_one_issue
1198 def test_context_menu_one_issue
1153 @request.session[:user_id] = 2
1199 @request.session[:user_id] = 2
1154 get :context_menu, :ids => [1]
1200 get :context_menu, :ids => [1]
1155 assert_response :success
1201 assert_response :success
1156 assert_template 'context_menu'
1202 assert_template 'context_menu'
1157 assert_tag :tag => 'a', :content => 'Edit',
1203 assert_tag :tag => 'a', :content => 'Edit',
1158 :attributes => { :href => '/issues/1/edit',
1204 :attributes => { :href => '/issues/1/edit',
1159 :class => 'icon-edit' }
1205 :class => 'icon-edit' }
1160 assert_tag :tag => 'a', :content => 'Closed',
1206 assert_tag :tag => 'a', :content => 'Closed',
1161 :attributes => { :href => '/issues/1/edit?issue%5Bstatus_id%5D=5',
1207 :attributes => { :href => '/issues/1/edit?issue%5Bstatus_id%5D=5',
1162 :class => '' }
1208 :class => '' }
1163 assert_tag :tag => 'a', :content => 'Immediate',
1209 assert_tag :tag => 'a', :content => 'Immediate',
1164 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;priority_id=8',
1210 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;priority_id=8',
1165 :class => '' }
1211 :class => '' }
1166 # Versions
1212 # Versions
1167 assert_tag :tag => 'a', :content => '2.0',
1213 assert_tag :tag => 'a', :content => '2.0',
1168 :attributes => { :href => '/issues/bulk_edit?fixed_version_id=3&amp;ids%5B%5D=1',
1214 :attributes => { :href => '/issues/bulk_edit?fixed_version_id=3&amp;ids%5B%5D=1',
1169 :class => '' }
1215 :class => '' }
1170 assert_tag :tag => 'a', :content => 'eCookbook Subproject 1 - 2.0',
1216 assert_tag :tag => 'a', :content => 'eCookbook Subproject 1 - 2.0',
1171 :attributes => { :href => '/issues/bulk_edit?fixed_version_id=4&amp;ids%5B%5D=1',
1217 :attributes => { :href => '/issues/bulk_edit?fixed_version_id=4&amp;ids%5B%5D=1',
1172 :class => '' }
1218 :class => '' }
1173
1219
1174 assert_tag :tag => 'a', :content => 'Dave Lopper',
1220 assert_tag :tag => 'a', :content => 'Dave Lopper',
1175 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1',
1221 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1',
1176 :class => '' }
1222 :class => '' }
1177 assert_tag :tag => 'a', :content => 'Duplicate',
1223 assert_tag :tag => 'a', :content => 'Duplicate',
1178 :attributes => { :href => '/projects/ecookbook/issues/1/copy',
1224 :attributes => { :href => '/projects/ecookbook/issues/1/copy',
1179 :class => 'icon-duplicate' }
1225 :class => 'icon-duplicate' }
1180 assert_tag :tag => 'a', :content => 'Copy',
1226 assert_tag :tag => 'a', :content => 'Copy',
1181 :attributes => { :href => '/issues/move?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1',
1227 :attributes => { :href => '/issues/move?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1',
1182 :class => 'icon-copy' }
1228 :class => 'icon-copy' }
1183 assert_tag :tag => 'a', :content => 'Move',
1229 assert_tag :tag => 'a', :content => 'Move',
1184 :attributes => { :href => '/issues/move?ids%5B%5D=1',
1230 :attributes => { :href => '/issues/move?ids%5B%5D=1',
1185 :class => 'icon-move' }
1231 :class => 'icon-move' }
1186 assert_tag :tag => 'a', :content => 'Delete',
1232 assert_tag :tag => 'a', :content => 'Delete',
1187 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
1233 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
1188 :class => 'icon-del' }
1234 :class => 'icon-del' }
1189 end
1235 end
1190
1236
1191 def test_context_menu_one_issue_by_anonymous
1237 def test_context_menu_one_issue_by_anonymous
1192 get :context_menu, :ids => [1]
1238 get :context_menu, :ids => [1]
1193 assert_response :success
1239 assert_response :success
1194 assert_template 'context_menu'
1240 assert_template 'context_menu'
1195 assert_tag :tag => 'a', :content => 'Delete',
1241 assert_tag :tag => 'a', :content => 'Delete',
1196 :attributes => { :href => '#',
1242 :attributes => { :href => '#',
1197 :class => 'icon-del disabled' }
1243 :class => 'icon-del disabled' }
1198 end
1244 end
1199
1245
1200 def test_context_menu_multiple_issues_of_same_project
1246 def test_context_menu_multiple_issues_of_same_project
1201 @request.session[:user_id] = 2
1247 @request.session[:user_id] = 2
1202 get :context_menu, :ids => [1, 2]
1248 get :context_menu, :ids => [1, 2]
1203 assert_response :success
1249 assert_response :success
1204 assert_template 'context_menu'
1250 assert_template 'context_menu'
1205 assert_tag :tag => 'a', :content => 'Edit',
1251 assert_tag :tag => 'a', :content => 'Edit',
1206 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
1252 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
1207 :class => 'icon-edit' }
1253 :class => 'icon-edit' }
1208 assert_tag :tag => 'a', :content => 'Immediate',
1254 assert_tag :tag => 'a', :content => 'Immediate',
1209 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;priority_id=8',
1255 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;priority_id=8',
1210 :class => '' }
1256 :class => '' }
1211 assert_tag :tag => 'a', :content => 'Dave Lopper',
1257 assert_tag :tag => 'a', :content => 'Dave Lopper',
1212 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
1258 :attributes => { :href => '/issues/bulk_edit?assigned_to_id=3&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
1213 :class => '' }
1259 :class => '' }
1214 assert_tag :tag => 'a', :content => 'Copy',
1260 assert_tag :tag => 'a', :content => 'Copy',
1215 :attributes => { :href => '/issues/move?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
1261 :attributes => { :href => '/issues/move?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
1216 :class => 'icon-copy' }
1262 :class => 'icon-copy' }
1217 assert_tag :tag => 'a', :content => 'Move',
1263 assert_tag :tag => 'a', :content => 'Move',
1218 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
1264 :attributes => { :href => '/issues/move?ids%5B%5D=1&amp;ids%5B%5D=2',
1219 :class => 'icon-move' }
1265 :class => 'icon-move' }
1220 assert_tag :tag => 'a', :content => 'Delete',
1266 assert_tag :tag => 'a', :content => 'Delete',
1221 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
1267 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
1222 :class => 'icon-del' }
1268 :class => 'icon-del' }
1223 end
1269 end
1224
1270
1225 def test_context_menu_multiple_issues_of_different_project
1271 def test_context_menu_multiple_issues_of_different_project
1226 @request.session[:user_id] = 2
1272 @request.session[:user_id] = 2
1227 get :context_menu, :ids => [1, 2, 4]
1273 get :context_menu, :ids => [1, 2, 4]
1228 assert_response :success
1274 assert_response :success
1229 assert_template 'context_menu'
1275 assert_template 'context_menu'
1230 assert_tag :tag => 'a', :content => 'Delete',
1276 assert_tag :tag => 'a', :content => 'Delete',
1231 :attributes => { :href => '#',
1277 :attributes => { :href => '#',
1232 :class => 'icon-del disabled' }
1278 :class => 'icon-del disabled' }
1233 end
1279 end
1234
1280
1235 def test_destroy_routing
1281 def test_destroy_routing
1236 assert_recognizes( #TODO: use DELETE on issue URI (need to change forms)
1282 assert_recognizes( #TODO: use DELETE on issue URI (need to change forms)
1237 {:controller => 'issues', :action => 'destroy', :id => '1'},
1283 {:controller => 'issues', :action => 'destroy', :id => '1'},
1238 {:method => :post, :path => '/issues/1/destroy'}
1284 {:method => :post, :path => '/issues/1/destroy'}
1239 )
1285 )
1240 end
1286 end
1241
1287
1242 def test_destroy_issue_with_no_time_entries
1288 def test_destroy_issue_with_no_time_entries
1243 assert_nil TimeEntry.find_by_issue_id(2)
1289 assert_nil TimeEntry.find_by_issue_id(2)
1244 @request.session[:user_id] = 2
1290 @request.session[:user_id] = 2
1245 post :destroy, :id => 2
1291 post :destroy, :id => 2
1246 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1292 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1247 assert_nil Issue.find_by_id(2)
1293 assert_nil Issue.find_by_id(2)
1248 end
1294 end
1249
1295
1250 def test_destroy_issues_with_time_entries
1296 def test_destroy_issues_with_time_entries
1251 @request.session[:user_id] = 2
1297 @request.session[:user_id] = 2
1252 post :destroy, :ids => [1, 3]
1298 post :destroy, :ids => [1, 3]
1253 assert_response :success
1299 assert_response :success
1254 assert_template 'destroy'
1300 assert_template 'destroy'
1255 assert_not_nil assigns(:hours)
1301 assert_not_nil assigns(:hours)
1256 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1302 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1257 end
1303 end
1258
1304
1259 def test_destroy_issues_and_destroy_time_entries
1305 def test_destroy_issues_and_destroy_time_entries
1260 @request.session[:user_id] = 2
1306 @request.session[:user_id] = 2
1261 post :destroy, :ids => [1, 3], :todo => 'destroy'
1307 post :destroy, :ids => [1, 3], :todo => 'destroy'
1262 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1308 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1263 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1309 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1264 assert_nil TimeEntry.find_by_id([1, 2])
1310 assert_nil TimeEntry.find_by_id([1, 2])
1265 end
1311 end
1266
1312
1267 def test_destroy_issues_and_assign_time_entries_to_project
1313 def test_destroy_issues_and_assign_time_entries_to_project
1268 @request.session[:user_id] = 2
1314 @request.session[:user_id] = 2
1269 post :destroy, :ids => [1, 3], :todo => 'nullify'
1315 post :destroy, :ids => [1, 3], :todo => 'nullify'
1270 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1316 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1271 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1317 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1272 assert_nil TimeEntry.find(1).issue_id
1318 assert_nil TimeEntry.find(1).issue_id
1273 assert_nil TimeEntry.find(2).issue_id
1319 assert_nil TimeEntry.find(2).issue_id
1274 end
1320 end
1275
1321
1276 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1322 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1277 @request.session[:user_id] = 2
1323 @request.session[:user_id] = 2
1278 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1324 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1279 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1325 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1280 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1326 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1281 assert_equal 2, TimeEntry.find(1).issue_id
1327 assert_equal 2, TimeEntry.find(1).issue_id
1282 assert_equal 2, TimeEntry.find(2).issue_id
1328 assert_equal 2, TimeEntry.find(2).issue_id
1283 end
1329 end
1284
1330
1285 def test_default_search_scope
1331 def test_default_search_scope
1286 get :index
1332 get :index
1287 assert_tag :div, :attributes => {:id => 'quick-search'},
1333 assert_tag :div, :attributes => {:id => 'quick-search'},
1288 :child => {:tag => 'form',
1334 :child => {:tag => 'form',
1289 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1335 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1290 end
1336 end
1291 end
1337 end
General Comments 0
You need to be logged in to leave comments. Login now