##// END OF EJS Templates
Added ability to edit issues from different project through contextual menu (#5332)...
Jean-Baptiste Barth -
r4128:156eca4d223e
parent child
Show More
@@ -1,39 +1,43
1 1 class ContextMenusController < ApplicationController
2 2 helper :watchers
3 3
4 4 def issues
5 5 @issues = Issue.find_all_by_id(params[:ids], :include => :project)
6 6 if (@issues.size == 1)
7 7 @issue = @issues.first
8 8 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
9 9 else
10 10 @allowed_statuses = @issues.map do |i|
11 11 i.new_statuses_allowed_to(User.current)
12 12 end.inject do |memo,s|
13 13 memo & s
14 14 end
15 15 end
16 16 @projects = @issues.collect(&:project).compact.uniq
17 17 @project = @projects.first if @projects.size == 1
18 18
19 @can = {:edit => (@project && User.current.allowed_to?(:edit_issues, @project)),
19 @can = {:edit => User.current.allowed_to?(:edit_issues, @projects),
20 20 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
21 :update => (@project && (User.current.allowed_to?(:edit_issues, @project) || (User.current.allowed_to?(:change_status, @project) && @allowed_statuses && !@allowed_statuses.empty?))),
21 :update => (User.current.allowed_to?(:edit_issues, @projects) || (User.current.allowed_to?(:change_status, @projects) && !@allowed_statuses.blank?)),
22 22 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
23 23 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
24 24 :delete => User.current.allowed_to?(:delete_issues, @projects)
25 25 }
26 26 if @project
27 27 @assignables = @project.assignable_users
28 28 @assignables << @issue.assigned_to if @issue && @issue.assigned_to && !@assignables.include?(@issue.assigned_to)
29 29 @trackers = @project.trackers
30 else
31 #when multiple projects, we only keep the intersection of each set
32 @assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
33 @trackers = @projects.map(&:trackers).inject{|memo,t| memo & t}
30 34 end
31 35
32 36 @priorities = IssuePriority.all.reverse
33 37 @statuses = IssueStatus.find(:all, :order => 'position')
34 38 @back = back_url
35 39
36 40 render :layout => false
37 41 end
38 42
39 43 end
@@ -1,330 +1,332
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class IssuesController < ApplicationController
19 19 menu_item :new_issue, :only => [:new, :create]
20 20 default_search_scope :issues
21 21
22 22 before_filter :find_issue, :only => [:show, :edit, :update]
23 23 before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :move, :perform_move, :destroy]
24 before_filter :check_project_uniqueness, :only => [:bulk_edit, :bulk_update, :move, :perform_move]
24 before_filter :check_project_uniqueness, :only => [:move, :perform_move]
25 25 before_filter :find_project, :only => [:new, :create]
26 26 before_filter :authorize, :except => [:index]
27 27 before_filter :find_optional_project, :only => [:index]
28 28 before_filter :check_for_default_issue_status, :only => [:new, :create]
29 29 before_filter :build_new_issue_from_params, :only => [:new, :create]
30 30 accept_key_auth :index, :show
31 31
32 32 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
33 33
34 34 helper :journals
35 35 helper :projects
36 36 include ProjectsHelper
37 37 helper :custom_fields
38 38 include CustomFieldsHelper
39 39 helper :issue_relations
40 40 include IssueRelationsHelper
41 41 helper :watchers
42 42 include WatchersHelper
43 43 helper :attachments
44 44 include AttachmentsHelper
45 45 helper :queries
46 46 include QueriesHelper
47 47 helper :sort
48 48 include SortHelper
49 49 include IssuesHelper
50 50 helper :timelog
51 51 helper :gantt
52 52 include Redmine::Export::PDF
53 53
54 54 verify :method => [:post, :delete],
55 55 :only => :destroy,
56 56 :render => { :nothing => true, :status => :method_not_allowed }
57 57
58 58 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
59 59 verify :method => :post, :only => :bulk_update, :render => {:nothing => true, :status => :method_not_allowed }
60 60 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
61 61
62 62 def index
63 63 retrieve_query
64 64 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
65 65 sort_update(@query.sortable_columns)
66 66
67 67 if @query.valid?
68 68 limit = case params[:format]
69 69 when 'csv', 'pdf'
70 70 Setting.issues_export_limit.to_i
71 71 when 'atom'
72 72 Setting.feeds_limit.to_i
73 73 else
74 74 per_page_option
75 75 end
76 76
77 77 @issue_count = @query.issue_count
78 78 @issue_pages = Paginator.new self, @issue_count, limit, params['page']
79 79 @issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version],
80 80 :order => sort_clause,
81 81 :offset => @issue_pages.current.offset,
82 82 :limit => limit)
83 83 @issue_count_by_group = @query.issue_count_by_group
84 84
85 85 respond_to do |format|
86 86 format.html { render :template => 'issues/index.rhtml', :layout => !request.xhr? }
87 87 format.xml { render :layout => false }
88 88 format.json { render :text => @issues.to_json, :layout => false }
89 89 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
90 90 format.csv { send_data(issues_to_csv(@issues, @project), :type => 'text/csv; header=present', :filename => 'export.csv') }
91 91 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
92 92 end
93 93 else
94 94 # Send html if the query is not valid
95 95 render(:template => 'issues/index.rhtml', :layout => !request.xhr?)
96 96 end
97 97 rescue ActiveRecord::RecordNotFound
98 98 render_404
99 99 end
100 100
101 101 def show
102 102 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC")
103 103 @journals.each_with_index {|j,i| j.indice = i+1}
104 104 @journals.reverse! if User.current.wants_comments_in_reverse_order?
105 105 @changesets = @issue.changesets.visible.all
106 106 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
107 107 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
108 108 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
109 109 @priorities = IssuePriority.all
110 110 @time_entry = TimeEntry.new
111 111 respond_to do |format|
112 112 format.html { render :template => 'issues/show.rhtml' }
113 113 format.xml { render :layout => false }
114 114 format.json { render :text => @issue.to_json, :layout => false }
115 115 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
116 116 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") }
117 117 end
118 118 end
119 119
120 120 # Add a new issue
121 121 # The new issue will be created from an existing one if copy_from parameter is given
122 122 def new
123 123 respond_to do |format|
124 124 format.html { render :action => 'new', :layout => !request.xhr? }
125 125 format.js { render :partial => 'attributes' }
126 126 end
127 127 end
128 128
129 129 def create
130 130 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
131 131 if @issue.save
132 132 attachments = Attachment.attach_files(@issue, params[:attachments])
133 133 render_attachment_warning_if_needed(@issue)
134 134 flash[:notice] = l(:notice_successful_create)
135 135 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
136 136 respond_to do |format|
137 137 format.html {
138 138 redirect_to(params[:continue] ? { :action => 'new', :project_id => @project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
139 139 { :action => 'show', :id => @issue })
140 140 }
141 141 format.xml { render :action => 'show', :status => :created, :location => url_for(:controller => 'issues', :action => 'show', :id => @issue) }
142 142 format.json { render :text => @issue.to_json, :status => :created, :location => url_for(:controller => 'issues', :action => 'show'), :layout => false }
143 143 end
144 144 return
145 145 else
146 146 respond_to do |format|
147 147 format.html { render :action => 'new' }
148 148 format.xml { render(:xml => @issue.errors, :status => :unprocessable_entity); return }
149 149 format.json { render :text => object_errors_to_json(@issue), :status => :unprocessable_entity, :layout => false }
150 150 end
151 151 end
152 152 end
153 153
154 154 # Attributes that can be updated on workflow transition (without :edit permission)
155 155 # TODO: make it configurable (at least per role)
156 156 UPDATABLE_ATTRS_ON_TRANSITION = %w(status_id assigned_to_id fixed_version_id done_ratio) unless const_defined?(:UPDATABLE_ATTRS_ON_TRANSITION)
157 157
158 158 def edit
159 159 update_issue_from_params
160 160
161 161 @journal = @issue.current_journal
162 162
163 163 respond_to do |format|
164 164 format.html { }
165 165 format.xml { }
166 166 end
167 167 end
168 168
169 169 def update
170 170 update_issue_from_params
171 171
172 172 if @issue.save_issue_with_child_records(params, @time_entry)
173 173 render_attachment_warning_if_needed(@issue)
174 174 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
175 175
176 176 respond_to do |format|
177 177 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
178 178 format.xml { head :ok }
179 179 format.json { head :ok }
180 180 end
181 181 else
182 182 render_attachment_warning_if_needed(@issue)
183 183 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
184 184 @journal = @issue.current_journal
185 185
186 186 respond_to do |format|
187 187 format.html { render :action => 'edit' }
188 188 format.xml { render :xml => @issue.errors, :status => :unprocessable_entity }
189 189 format.json { render :text => object_errors_to_json(@issue), :status => :unprocessable_entity, :layout => false }
190 190 end
191 191 end
192 192 end
193 193
194 194 # Bulk edit a set of issues
195 195 def bulk_edit
196 196 @issues.sort!
197 @available_statuses = Workflow.available_statuses(@project)
198 @custom_fields = @project.all_issue_custom_fields
197 @available_statuses = @projects.map{|p|Workflow.available_statuses(p)}.inject{|memo,w|memo & w}
198 @custom_fields = @projects.map{|p|p.all_issue_custom_fields}.inject{|memo,c|memo & c}
199 @assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a}
200 @trackers = @projects.map(&:trackers).inject{|memo,t| memo & t}
199 201 end
200 202
201 203 def bulk_update
202 204 @issues.sort!
203 205 attributes = parse_params_for_bulk_issue_attributes(params)
204 206
205 207 unsaved_issue_ids = []
206 208 @issues.each do |issue|
207 209 issue.reload
208 210 journal = issue.init_journal(User.current, params[:notes])
209 211 issue.safe_attributes = attributes
210 212 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
211 213 unless issue.save
212 214 # Keep unsaved issue ids to display them in flash error
213 215 unsaved_issue_ids << issue.id
214 216 end
215 217 end
216 218 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
217 219 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
218 220 end
219 221
220 222 def destroy
221 223 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
222 224 if @hours > 0
223 225 case params[:todo]
224 226 when 'destroy'
225 227 # nothing to do
226 228 when 'nullify'
227 229 TimeEntry.update_all('issue_id = NULL', ['issue_id IN (?)', @issues])
228 230 when 'reassign'
229 231 reassign_to = @project.issues.find_by_id(params[:reassign_to_id])
230 232 if reassign_to.nil?
231 233 flash.now[:error] = l(:error_issue_not_found_in_project)
232 234 return
233 235 else
234 236 TimeEntry.update_all("issue_id = #{reassign_to.id}", ['issue_id IN (?)', @issues])
235 237 end
236 238 else
237 239 unless params[:format] == 'xml' || params[:format] == 'json'
238 240 # display the destroy form if it's a user request
239 241 return
240 242 end
241 243 end
242 244 end
243 245 @issues.each(&:destroy)
244 246 respond_to do |format|
245 247 format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
246 248 format.xml { head :ok }
247 249 format.json { head :ok }
248 250 end
249 251 end
250 252
251 253 private
252 254 def find_issue
253 255 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
254 256 @project = @issue.project
255 257 rescue ActiveRecord::RecordNotFound
256 258 render_404
257 259 end
258 260
259 261 def find_project
260 262 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id]
261 263 @project = Project.find(project_id)
262 264 rescue ActiveRecord::RecordNotFound
263 265 render_404
264 266 end
265 267
266 268 # Used by #edit and #update to set some common instance variables
267 269 # from the params
268 270 # TODO: Refactor, not everything in here is needed by #edit
269 271 def update_issue_from_params
270 272 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
271 273 @priorities = IssuePriority.all
272 274 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
273 275 @time_entry = TimeEntry.new
274 276
275 277 @notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil)
276 278 @issue.init_journal(User.current, @notes)
277 279 # User can change issue attributes only if he has :edit permission or if a workflow transition is allowed
278 280 if (@edit_allowed || !@allowed_statuses.empty?) && params[:issue]
279 281 attrs = params[:issue].dup
280 282 attrs.delete_if {|k,v| !UPDATABLE_ATTRS_ON_TRANSITION.include?(k) } unless @edit_allowed
281 283 attrs.delete(:status_id) unless @allowed_statuses.detect {|s| s.id.to_s == attrs[:status_id].to_s}
282 284 @issue.safe_attributes = attrs
283 285 end
284 286
285 287 end
286 288
287 289 # TODO: Refactor, lots of extra code in here
288 290 # TODO: Changing tracker on an existing issue should not trigger this
289 291 def build_new_issue_from_params
290 292 if params[:id].blank?
291 293 @issue = Issue.new
292 294 @issue.copy_from(params[:copy_from]) if params[:copy_from]
293 295 @issue.project = @project
294 296 else
295 297 @issue = @project.issues.visible.find(params[:id])
296 298 end
297 299
298 300 @issue.project = @project
299 301 # Tracker must be set before custom field values
300 302 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
301 303 if @issue.tracker.nil?
302 304 render_error l(:error_no_tracker_in_project)
303 305 return false
304 306 end
305 307 if params[:issue].is_a?(Hash)
306 308 @issue.safe_attributes = params[:issue]
307 309 if User.current.allowed_to?(:add_issue_watchers, @project) && @issue.new_record?
308 310 @issue.watcher_user_ids = params[:issue]['watcher_user_ids']
309 311 end
310 312 end
311 313 @issue.author = User.current
312 314 @issue.start_date ||= Date.today
313 315 @priorities = IssuePriority.all
314 316 @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
315 317 end
316 318
317 319 def check_for_default_issue_status
318 320 if IssueStatus.default.nil?
319 321 render_error l(:error_no_default_issue_status)
320 322 return false
321 323 end
322 324 end
323 325
324 326 def parse_params_for_bulk_issue_attributes(params)
325 327 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
326 328 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
327 329 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
328 330 attributes
329 331 end
330 332 end
@@ -1,122 +1,121
1 1 <ul>
2 2 <%= call_hook(:view_issues_context_menu_start, {:issues => @issues, :can => @can, :back => @back }) %>
3 3
4 4 <% if !@issue.nil? -%>
5 5 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'edit', :id => @issue},
6 6 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
7 7 <% else %>
8 8 <li><%= context_menu_link l(:button_edit), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id)},
9 9 :class => 'icon-edit', :disabled => !@can[:edit] %></li>
10 10 <% end %>
11 11
12 12 <% unless @allowed_statuses.empty? %>
13 13 <li class="folder">
14 14 <a href="#" class="submenu" onclick="return false;"><%= l(:field_status) %></a>
15 15 <ul>
16 16 <% @statuses.each do |s| -%>
17 17 <li><%= context_menu_link s.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {:status_id => s}, :back_url => @back}, :method => :post,
18 18 :selected => (@issue && s == @issue.status), :disabled => !(@can[:update] && @allowed_statuses.include?(s)) %></li>
19 19 <% end -%>
20 20 </ul>
21 21 </li>
22 22 <% end %>
23 23
24 24 <% unless @trackers.nil? %>
25 25 <li class="folder">
26 26 <a href="#" class="submenu"><%= l(:field_tracker) %></a>
27 27 <ul>
28 28 <% @trackers.each do |t| -%>
29 29 <li><%= context_menu_link t.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'tracker_id' => t}, :back_url => @back}, :method => :post,
30 30 :selected => (@issue && t == @issue.tracker), :disabled => !@can[:edit] %></li>
31 31 <% end -%>
32 32 </ul>
33 33 </li>
34 34 <% end %>
35 35
36 <% if @projects.size == 1 %>
37 36 <li class="folder">
38 37 <a href="#" class="submenu"><%= l(:field_priority) %></a>
39 38 <ul>
40 39 <% @priorities.each do |p| -%>
41 40 <li><%= context_menu_link p.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'priority_id' => p}, :back_url => @back}, :method => :post,
42 41 :selected => (@issue && p == @issue.priority), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li>
43 42 <% end -%>
44 43 </ul>
45 44 </li>
46 <% end %>
47 45
46 <% #TODO: allow editing versions when multiple projects %>
48 47 <% unless @project.nil? || @project.shared_versions.open.empty? -%>
49 48 <li class="folder">
50 49 <a href="#" class="submenu"><%= l(:field_fixed_version) %></a>
51 50 <ul>
52 51 <% @project.shared_versions.open.sort.each do |v| -%>
53 52 <li><%= context_menu_link format_version_name(v), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => v}, :back_url => @back}, :method => :post,
54 53 :selected => (@issue && v == @issue.fixed_version), :disabled => !@can[:update] %></li>
55 54 <% end -%>
56 55 <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'fixed_version_id' => 'none'}, :back_url => @back}, :method => :post,
57 56 :selected => (@issue && @issue.fixed_version.nil?), :disabled => !@can[:update] %></li>
58 57 </ul>
59 58 </li>
60 59 <% end %>
61 60 <% unless @assignables.nil? || @assignables.empty? -%>
62 61 <li class="folder">
63 62 <a href="#" class="submenu"><%= l(:field_assigned_to) %></a>
64 63 <ul>
65 64 <% @assignables.each do |u| -%>
66 65 <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => u}, :back_url => @back}, :method => :post,
67 66 :selected => (@issue && u == @issue.assigned_to), :disabled => !@can[:update] %></li>
68 67 <% end -%>
69 68 <li><%= context_menu_link l(:label_nobody), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'assigned_to_id' => 'none'}, :back_url => @back}, :method => :post,
70 69 :selected => (@issue && @issue.assigned_to.nil?), :disabled => !@can[:update] %></li>
71 70 </ul>
72 71 </li>
73 72 <% end %>
74 73 <% unless @project.nil? || @project.issue_categories.empty? -%>
75 74 <li class="folder">
76 75 <a href="#" class="submenu"><%= l(:field_category) %></a>
77 76 <ul>
78 77 <% @project.issue_categories.each do |u| -%>
79 78 <li><%= context_menu_link u.name, {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'category_id' => u}, :back_url => @back}, :method => :post,
80 79 :selected => (@issue && u == @issue.category), :disabled => !@can[:update] %></li>
81 80 <% end -%>
82 81 <li><%= context_menu_link l(:label_none), {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'category_id' => 'none'}, :back_url => @back}, :method => :post,
83 82 :selected => (@issue && @issue.category.nil?), :disabled => !@can[:update] %></li>
84 83 </ul>
85 84 </li>
86 85 <% end -%>
87 86
88 <% if Issue.use_field_for_done_ratio? && @projects.size == 1 %>
87 <% if Issue.use_field_for_done_ratio? %>
89 88 <li class="folder">
90 89 <a href="#" class="submenu"><%= l(:field_done_ratio) %></a>
91 90 <ul>
92 91 <% (0..10).map{|x|x*10}.each do |p| -%>
93 92 <li><%= context_menu_link "#{p}%", {:controller => 'issues', :action => 'bulk_edit', :ids => @issues.collect(&:id), :issue => {'done_ratio' => p}, :back_url => @back}, :method => :post,
94 93 :selected => (@issue && p == @issue.done_ratio), :disabled => (!@can[:edit] || @issues.detect {|i| !i.leaf?}) %></li>
95 94 <% end -%>
96 95 </ul>
97 96 </li>
98 97 <% end %>
99 98
100 99 <% if !@issue.nil? %>
101 100 <% if @can[:log_time] -%>
102 101 <li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue},
103 102 :class => 'icon-time-add' %></li>
104 103 <% end %>
105 104 <% if User.current.logged? %>
106 105 <li><%= watcher_link(@issue, User.current) %></li>
107 106 <% end %>
108 107 <% end %>
109 108
110 109 <% if @issue.present? %>
111 110 <li><%= context_menu_link l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue},
112 111 :class => 'icon-duplicate', :disabled => !@can[:copy] %></li>
113 112 <% end %>
114 113 <li><%= context_menu_link l(:button_copy), new_issue_move_path(:ids => @issues.collect(&:id), :copy_options => {:copy => 't'}),
115 114 :class => 'icon-copy', :disabled => !@can[:move] %></li>
116 115 <li><%= context_menu_link l(:button_move), new_issue_move_path(:ids => @issues.collect(&:id)),
117 116 :class => 'icon-move', :disabled => !@can[:move] %></li>
118 117 <li><%= context_menu_link l(:button_delete), {:controller => 'issues', :action => 'destroy', :ids => @issues.collect(&:id), :back_url => @back},
119 118 :method => :post, :confirm => l(:text_issues_destroy_confirmation), :class => 'icon-del', :disabled => !@can[:delete] %></li>
120 119
121 120 <%= call_hook(:view_issues_context_menu_end, {:issues => @issues, :can => @can, :back => @back }) %>
122 121 </ul>
@@ -1,78 +1,83
1 1 <h2><%= l(:label_bulk_edit_selected_issues) %></h2>
2 2
3 3 <ul><%= @issues.collect {|i| content_tag('li', link_to(h("#{i.tracker} ##{i.id}"), { :action => 'show', :id => i }) + h(": #{i.subject}")) }.join("\n") %></ul>
4 4
5 5 <% form_tag(:action => 'bulk_update') do %>
6 6 <%= @issues.collect {|i| hidden_field_tag('ids[]', i.id)}.join %>
7 7 <div class="box tabular">
8 8 <fieldset class="attributes">
9 9 <legend><%= l(:label_change_properties) %></legend>
10 10
11 11 <div class="splitcontentleft">
12 12 <p>
13 13 <label><%= l(:field_tracker) %></label>
14 <%= select_tag('issue[tracker_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@project.trackers, :id, :name)) %>
14 <%= select_tag('issue[tracker_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, :id, :name)) %>
15 15 </p>
16 16 <% if @available_statuses.any? %>
17 17 <p>
18 18 <label><%= l(:field_status) %></label>
19 19 <%= select_tag('issue[status_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@available_statuses, :id, :name)) %>
20 20 </p>
21 21 <% end %>
22 22 <p>
23 23 <label><%= l(:field_priority) %></label>
24 24 <%= select_tag('issue[priority_id]', "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(IssuePriority.all, :id, :name)) %>
25 25 </p>
26 26 <p>
27 27 <label><%= l(:field_assigned_to) %></label>
28 28 <%= select_tag('issue[assigned_to_id]', content_tag('option', l(:label_no_change_option), :value => '') +
29 29 content_tag('option', l(:label_nobody), :value => 'none') +
30 options_from_collection_for_select(@project.assignable_users, :id, :name)) %>
30 options_from_collection_for_select(@assignables, :id, :name)) %>
31 31 </p>
32 <% if @project %>
32 33 <p>
33 34 <label><%= l(:field_category) %></label>
34 35 <%= select_tag('issue[category_id]', content_tag('option', l(:label_no_change_option), :value => '') +
35 36 content_tag('option', l(:label_none), :value => 'none') +
36 37 options_from_collection_for_select(@project.issue_categories, :id, :name)) %>
37 38 </p>
39 <% end %>
40 <% #TODO: allow editing versions when multiple projects %>
41 <% if @project %>
38 42 <p>
39 43 <label><%= l(:field_fixed_version) %></label>
40 44 <%= select_tag('issue[fixed_version_id]', content_tag('option', l(:label_no_change_option), :value => '') +
41 45 content_tag('option', l(:label_none), :value => 'none') +
42 46 version_options_for_select(@project.shared_versions.open)) %>
43 47 </p>
48 <% end %>
44 49
45 50 <% @custom_fields.each do |custom_field| %>
46 51 <p><label><%= h(custom_field.name) %></label> <%= custom_field_tag_for_bulk_edit('issue', custom_field) %></p>
47 52 <% end %>
48 53
49 54 <%= call_hook(:view_issues_bulk_edit_details_bottom, { :issues => @issues }) %>
50 55 </div>
51 56
52 57 <div class="splitcontentright">
53 58 <p>
54 59 <label><%= l(:field_start_date) %></label>
55 60 <%= text_field_tag 'issue[start_date]', '', :size => 10 %><%= calendar_for('issue_start_date') %>
56 61 </p>
57 62 <p>
58 63 <label><%= l(:field_due_date) %></label>
59 64 <%= text_field_tag 'issue[due_date]', '', :size => 10 %><%= calendar_for('issue_due_date') %>
60 65 </p>
61 66 <% if Issue.use_field_for_done_ratio? %>
62 67 <p>
63 68 <label><%= l(:field_done_ratio) %></label>
64 69 <%= select_tag 'issue[done_ratio]', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %>
65 70 </p>
66 71 <% end %>
67 72 </div>
68 73
69 74 </fieldset>
70 75
71 76 <fieldset><legend><%= l(:field_notes) %></legend>
72 77 <%= text_area_tag 'notes', @notes, :cols => 60, :rows => 10, :class => 'wiki-edit' %>
73 78 <%= wikitoolbar_for 'notes' %>
74 79 </fieldset>
75 80 </div>
76 81
77 82 <p><%= submit_tag l(:button_submit) %></p>
78 83 <% end %>
@@ -1,93 +1,105
1 1 require File.dirname(__FILE__) + '/../test_helper'
2 2
3 3 class ContextMenusControllerTest < ActionController::TestCase
4 4 fixtures :all
5 5
6 6 def test_context_menu_one_issue
7 7 @request.session[:user_id] = 2
8 8 get :issues, :ids => [1]
9 9 assert_response :success
10 10 assert_template 'context_menu'
11 11 assert_tag :tag => 'a', :content => 'Edit',
12 12 :attributes => { :href => '/issues/1/edit',
13 13 :class => 'icon-edit' }
14 14 assert_tag :tag => 'a', :content => 'Closed',
15 15 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bstatus_id%5D=5',
16 16 :class => '' }
17 17 assert_tag :tag => 'a', :content => 'Immediate',
18 18 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bpriority_id%5D=8',
19 19 :class => '' }
20 20 # Versions
21 21 assert_tag :tag => 'a', :content => '2.0',
22 22 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=3',
23 23 :class => '' }
24 24 assert_tag :tag => 'a', :content => 'eCookbook Subproject 1 - 2.0',
25 25 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=4',
26 26 :class => '' }
27 27
28 28 assert_tag :tag => 'a', :content => 'Dave Lopper',
29 29 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;issue%5Bassigned_to_id%5D=3',
30 30 :class => '' }
31 31 assert_tag :tag => 'a', :content => 'Duplicate',
32 32 :attributes => { :href => '/projects/ecookbook/issues/1/copy',
33 33 :class => 'icon-duplicate' }
34 34 assert_tag :tag => 'a', :content => 'Copy',
35 35 :attributes => { :href => '/issues/move/new?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1',
36 36 :class => 'icon-copy' }
37 37 assert_tag :tag => 'a', :content => 'Move',
38 38 :attributes => { :href => '/issues/move/new?ids%5B%5D=1',
39 39 :class => 'icon-move' }
40 40 assert_tag :tag => 'a', :content => 'Delete',
41 41 :attributes => { :href => '/issues/destroy?ids%5B%5D=1',
42 42 :class => 'icon-del' }
43 43 end
44 44
45 45 def test_context_menu_one_issue_by_anonymous
46 46 get :issues, :ids => [1]
47 47 assert_response :success
48 48 assert_template 'context_menu'
49 49 assert_tag :tag => 'a', :content => 'Delete',
50 50 :attributes => { :href => '#',
51 51 :class => 'icon-del disabled' }
52 52 end
53 53
54 54 def test_context_menu_multiple_issues_of_same_project
55 55 @request.session[:user_id] = 2
56 56 get :issues, :ids => [1, 2]
57 57 assert_response :success
58 58 assert_template 'context_menu'
59 59 assert_tag :tag => 'a', :content => 'Edit',
60 60 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2',
61 61 :class => 'icon-edit' }
62 62 assert_tag :tag => 'a', :content => 'Closed',
63 63 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;issue%5Bstatus_id%5D=5',
64 64 :class => '' }
65 65 assert_tag :tag => 'a', :content => 'Immediate',
66 66 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;issue%5Bpriority_id%5D=8',
67 67 :class => '' }
68 68 assert_tag :tag => 'a', :content => 'Dave Lopper',
69 69 :attributes => { :href => '/issues/bulk_edit?ids%5B%5D=1&amp;ids%5B%5D=2&amp;issue%5Bassigned_to_id%5D=3',
70 70 :class => '' }
71 71 assert_tag :tag => 'a', :content => 'Copy',
72 72 :attributes => { :href => '/issues/move/new?copy_options%5Bcopy%5D=t&amp;ids%5B%5D=1&amp;ids%5B%5D=2',
73 73 :class => 'icon-copy' }
74 74 assert_tag :tag => 'a', :content => 'Move',
75 75 :attributes => { :href => '/issues/move/new?ids%5B%5D=1&amp;ids%5B%5D=2',
76 76 :class => 'icon-move' }
77 77 assert_tag :tag => 'a', :content => 'Delete',
78 78 :attributes => { :href => '/issues/destroy?ids%5B%5D=1&amp;ids%5B%5D=2',
79 79 :class => 'icon-del' }
80 80 end
81 81
82 82 def test_context_menu_multiple_issues_of_different_projects
83 83 @request.session[:user_id] = 2
84 84 get :issues, :ids => [1, 2, 6]
85 85 assert_response :success
86 86 assert_template 'context_menu'
87 87 ids = "ids%5B%5D=1&amp;ids%5B%5D=2&amp;ids%5B%5D=6"
88 assert_tag :tag => 'a', :content => 'Edit',
89 :attributes => { :href => "/issues/bulk_edit?#{ids}",
90 :class => 'icon-edit' }
91 assert_tag :tag => 'a', :content => 'Closed',
92 :attributes => { :href => "/issues/bulk_edit?#{ids}&amp;issue%5Bstatus_id%5D=5",
93 :class => '' }
94 assert_tag :tag => 'a', :content => 'Immediate',
95 :attributes => { :href => "/issues/bulk_edit?#{ids}&amp;issue%5Bpriority_id%5D=8",
96 :class => '' }
97 assert_tag :tag => 'a', :content => 'John Smith',
98 :attributes => { :href => "/issues/bulk_edit?#{ids}&amp;issue%5Bassigned_to_id%5D=2",
99 :class => '' }
88 100 assert_tag :tag => 'a', :content => 'Delete',
89 101 :attributes => { :href => "/issues/destroy?#{ids}",
90 102 :class => 'icon-del' }
91 103 end
92 104
93 105 end
@@ -1,1077 +1,1123
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19 require 'issues_controller'
20 20
21 21 # Re-raise errors caught by the controller.
22 22 class IssuesController; def rescue_action(e) raise e end; end
23 23
24 24 class IssuesControllerTest < ActionController::TestCase
25 25 fixtures :projects,
26 26 :users,
27 27 :roles,
28 28 :members,
29 29 :member_roles,
30 30 :issues,
31 31 :issue_statuses,
32 32 :versions,
33 33 :trackers,
34 34 :projects_trackers,
35 35 :issue_categories,
36 36 :enabled_modules,
37 37 :enumerations,
38 38 :attachments,
39 39 :workflows,
40 40 :custom_fields,
41 41 :custom_values,
42 42 :custom_fields_projects,
43 43 :custom_fields_trackers,
44 44 :time_entries,
45 45 :journals,
46 46 :journal_details,
47 47 :queries
48 48
49 49 def setup
50 50 @controller = IssuesController.new
51 51 @request = ActionController::TestRequest.new
52 52 @response = ActionController::TestResponse.new
53 53 User.current = nil
54 54 end
55 55
56 56 def test_index
57 57 Setting.default_language = 'en'
58 58
59 59 get :index
60 60 assert_response :success
61 61 assert_template 'index.rhtml'
62 62 assert_not_nil assigns(:issues)
63 63 assert_nil assigns(:project)
64 64 assert_tag :tag => 'a', :content => /Can't print recipes/
65 65 assert_tag :tag => 'a', :content => /Subproject issue/
66 66 # private projects hidden
67 67 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
68 68 assert_no_tag :tag => 'a', :content => /Issue on project 2/
69 69 # project column
70 70 assert_tag :tag => 'th', :content => /Project/
71 71 end
72 72
73 73 def test_index_should_not_list_issues_when_module_disabled
74 74 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
75 75 get :index
76 76 assert_response :success
77 77 assert_template 'index.rhtml'
78 78 assert_not_nil assigns(:issues)
79 79 assert_nil assigns(:project)
80 80 assert_no_tag :tag => 'a', :content => /Can't print recipes/
81 81 assert_tag :tag => 'a', :content => /Subproject issue/
82 82 end
83 83
84 84 def test_index_should_not_list_issues_when_module_disabled
85 85 EnabledModule.delete_all("name = 'issue_tracking' AND project_id = 1")
86 86 get :index
87 87 assert_response :success
88 88 assert_template 'index.rhtml'
89 89 assert_not_nil assigns(:issues)
90 90 assert_nil assigns(:project)
91 91 assert_no_tag :tag => 'a', :content => /Can't print recipes/
92 92 assert_tag :tag => 'a', :content => /Subproject issue/
93 93 end
94 94
95 95 def test_index_with_project
96 96 Setting.display_subprojects_issues = 0
97 97 get :index, :project_id => 1
98 98 assert_response :success
99 99 assert_template 'index.rhtml'
100 100 assert_not_nil assigns(:issues)
101 101 assert_tag :tag => 'a', :content => /Can't print recipes/
102 102 assert_no_tag :tag => 'a', :content => /Subproject issue/
103 103 end
104 104
105 105 def test_index_with_project_and_subprojects
106 106 Setting.display_subprojects_issues = 1
107 107 get :index, :project_id => 1
108 108 assert_response :success
109 109 assert_template 'index.rhtml'
110 110 assert_not_nil assigns(:issues)
111 111 assert_tag :tag => 'a', :content => /Can't print recipes/
112 112 assert_tag :tag => 'a', :content => /Subproject issue/
113 113 assert_no_tag :tag => 'a', :content => /Issue of a private subproject/
114 114 end
115 115
116 116 def test_index_with_project_and_subprojects_should_show_private_subprojects
117 117 @request.session[:user_id] = 2
118 118 Setting.display_subprojects_issues = 1
119 119 get :index, :project_id => 1
120 120 assert_response :success
121 121 assert_template 'index.rhtml'
122 122 assert_not_nil assigns(:issues)
123 123 assert_tag :tag => 'a', :content => /Can't print recipes/
124 124 assert_tag :tag => 'a', :content => /Subproject issue/
125 125 assert_tag :tag => 'a', :content => /Issue of a private subproject/
126 126 end
127 127
128 128 def test_index_with_project_and_filter
129 129 get :index, :project_id => 1, :set_filter => 1
130 130 assert_response :success
131 131 assert_template 'index.rhtml'
132 132 assert_not_nil assigns(:issues)
133 133 end
134 134
135 135 def test_index_with_query
136 136 get :index, :project_id => 1, :query_id => 5
137 137 assert_response :success
138 138 assert_template 'index.rhtml'
139 139 assert_not_nil assigns(:issues)
140 140 assert_nil assigns(:issue_count_by_group)
141 141 end
142 142
143 143 def test_index_with_query_grouped_by_tracker
144 144 get :index, :project_id => 1, :query_id => 6
145 145 assert_response :success
146 146 assert_template 'index.rhtml'
147 147 assert_not_nil assigns(:issues)
148 148 assert_not_nil assigns(:issue_count_by_group)
149 149 end
150 150
151 151 def test_index_with_query_grouped_by_list_custom_field
152 152 get :index, :project_id => 1, :query_id => 9
153 153 assert_response :success
154 154 assert_template 'index.rhtml'
155 155 assert_not_nil assigns(:issues)
156 156 assert_not_nil assigns(:issue_count_by_group)
157 157 end
158 158
159 159 def test_index_sort_by_field_not_included_in_columns
160 160 Setting.issue_list_default_columns = %w(subject author)
161 161 get :index, :sort => 'tracker'
162 162 end
163 163
164 164 def test_index_csv_with_project
165 165 Setting.default_language = 'en'
166 166
167 167 get :index, :format => 'csv'
168 168 assert_response :success
169 169 assert_not_nil assigns(:issues)
170 170 assert_equal 'text/csv', @response.content_type
171 171 assert @response.body.starts_with?("#,")
172 172
173 173 get :index, :project_id => 1, :format => 'csv'
174 174 assert_response :success
175 175 assert_not_nil assigns(:issues)
176 176 assert_equal 'text/csv', @response.content_type
177 177 end
178 178
179 179 def test_index_pdf
180 180 get :index, :format => 'pdf'
181 181 assert_response :success
182 182 assert_not_nil assigns(:issues)
183 183 assert_equal 'application/pdf', @response.content_type
184 184
185 185 get :index, :project_id => 1, :format => 'pdf'
186 186 assert_response :success
187 187 assert_not_nil assigns(:issues)
188 188 assert_equal 'application/pdf', @response.content_type
189 189
190 190 get :index, :project_id => 1, :query_id => 6, :format => 'pdf'
191 191 assert_response :success
192 192 assert_not_nil assigns(:issues)
193 193 assert_equal 'application/pdf', @response.content_type
194 194 end
195 195
196 196 def test_index_pdf_with_query_grouped_by_list_custom_field
197 197 get :index, :project_id => 1, :query_id => 9, :format => 'pdf'
198 198 assert_response :success
199 199 assert_not_nil assigns(:issues)
200 200 assert_not_nil assigns(:issue_count_by_group)
201 201 assert_equal 'application/pdf', @response.content_type
202 202 end
203 203
204 204 def test_index_sort
205 205 get :index, :sort => 'tracker,id:desc'
206 206 assert_response :success
207 207
208 208 sort_params = @request.session['issues_index_sort']
209 209 assert sort_params.is_a?(String)
210 210 assert_equal 'tracker,id:desc', sort_params
211 211
212 212 issues = assigns(:issues)
213 213 assert_not_nil issues
214 214 assert !issues.empty?
215 215 assert_equal issues.sort {|a,b| a.tracker == b.tracker ? b.id <=> a.id : a.tracker <=> b.tracker }.collect(&:id), issues.collect(&:id)
216 216 end
217 217
218 218 def test_index_with_columns
219 219 columns = ['tracker', 'subject', 'assigned_to']
220 220 get :index, :set_filter => 1, :query => { 'column_names' => columns}
221 221 assert_response :success
222 222
223 223 # query should use specified columns
224 224 query = assigns(:query)
225 225 assert_kind_of Query, query
226 226 assert_equal columns, query.column_names.map(&:to_s)
227 227
228 228 # columns should be stored in session
229 229 assert_kind_of Hash, session[:query]
230 230 assert_kind_of Array, session[:query][:column_names]
231 231 assert_equal columns, session[:query][:column_names].map(&:to_s)
232 232 end
233 233
234 234 def test_show_by_anonymous
235 235 get :show, :id => 1
236 236 assert_response :success
237 237 assert_template 'show.rhtml'
238 238 assert_not_nil assigns(:issue)
239 239 assert_equal Issue.find(1), assigns(:issue)
240 240
241 241 # anonymous role is allowed to add a note
242 242 assert_tag :tag => 'form',
243 243 :descendant => { :tag => 'fieldset',
244 244 :child => { :tag => 'legend',
245 245 :content => /Notes/ } }
246 246 end
247 247
248 248 def test_show_by_manager
249 249 @request.session[:user_id] = 2
250 250 get :show, :id => 1
251 251 assert_response :success
252 252
253 253 assert_tag :tag => 'form',
254 254 :descendant => { :tag => 'fieldset',
255 255 :child => { :tag => 'legend',
256 256 :content => /Change properties/ } },
257 257 :descendant => { :tag => 'fieldset',
258 258 :child => { :tag => 'legend',
259 259 :content => /Log time/ } },
260 260 :descendant => { :tag => 'fieldset',
261 261 :child => { :tag => 'legend',
262 262 :content => /Notes/ } }
263 263 end
264 264
265 265 def test_show_should_deny_anonymous_access_without_permission
266 266 Role.anonymous.remove_permission!(:view_issues)
267 267 get :show, :id => 1
268 268 assert_response :redirect
269 269 end
270 270
271 271 def test_show_should_deny_non_member_access_without_permission
272 272 Role.non_member.remove_permission!(:view_issues)
273 273 @request.session[:user_id] = 9
274 274 get :show, :id => 1
275 275 assert_response 403
276 276 end
277 277
278 278 def test_show_should_deny_member_access_without_permission
279 279 Role.find(1).remove_permission!(:view_issues)
280 280 @request.session[:user_id] = 2
281 281 get :show, :id => 1
282 282 assert_response 403
283 283 end
284 284
285 285 def test_show_should_not_disclose_relations_to_invisible_issues
286 286 Setting.cross_project_issue_relations = '1'
287 287 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(2), :relation_type => 'relates')
288 288 # Relation to a private project issue
289 289 IssueRelation.create!(:issue_from => Issue.find(1), :issue_to => Issue.find(4), :relation_type => 'relates')
290 290
291 291 get :show, :id => 1
292 292 assert_response :success
293 293
294 294 assert_tag :div, :attributes => { :id => 'relations' },
295 295 :descendant => { :tag => 'a', :content => /#2$/ }
296 296 assert_no_tag :div, :attributes => { :id => 'relations' },
297 297 :descendant => { :tag => 'a', :content => /#4$/ }
298 298 end
299 299
300 300 def test_show_atom
301 301 get :show, :id => 2, :format => 'atom'
302 302 assert_response :success
303 303 assert_template 'journals/index.rxml'
304 304 # Inline image
305 305 assert_select 'content', :text => Regexp.new(Regexp.quote('http://test.host/attachments/download/10'))
306 306 end
307 307
308 308 def test_show_export_to_pdf
309 309 get :show, :id => 3, :format => 'pdf'
310 310 assert_response :success
311 311 assert_equal 'application/pdf', @response.content_type
312 312 assert @response.body.starts_with?('%PDF')
313 313 assert_not_nil assigns(:issue)
314 314 end
315 315
316 316 def test_get_new
317 317 @request.session[:user_id] = 2
318 318 get :new, :project_id => 1, :tracker_id => 1
319 319 assert_response :success
320 320 assert_template 'new'
321 321
322 322 assert_tag :tag => 'input', :attributes => { :name => 'issue[custom_field_values][2]',
323 323 :value => 'Default string' }
324 324 end
325 325
326 326 def test_get_new_without_tracker_id
327 327 @request.session[:user_id] = 2
328 328 get :new, :project_id => 1
329 329 assert_response :success
330 330 assert_template 'new'
331 331
332 332 issue = assigns(:issue)
333 333 assert_not_nil issue
334 334 assert_equal Project.find(1).trackers.first, issue.tracker
335 335 end
336 336
337 337 def test_get_new_with_no_default_status_should_display_an_error
338 338 @request.session[:user_id] = 2
339 339 IssueStatus.delete_all
340 340
341 341 get :new, :project_id => 1
342 342 assert_response 500
343 343 assert_not_nil flash[:error]
344 344 assert_tag :tag => 'div', :attributes => { :class => /error/ },
345 345 :content => /No default issue/
346 346 end
347 347
348 348 def test_get_new_with_no_tracker_should_display_an_error
349 349 @request.session[:user_id] = 2
350 350 Tracker.delete_all
351 351
352 352 get :new, :project_id => 1
353 353 assert_response 500
354 354 assert_not_nil flash[:error]
355 355 assert_tag :tag => 'div', :attributes => { :class => /error/ },
356 356 :content => /No tracker/
357 357 end
358 358
359 359 def test_update_new_form
360 360 @request.session[:user_id] = 2
361 361 xhr :post, :new, :project_id => 1,
362 362 :issue => {:tracker_id => 2,
363 363 :subject => 'This is the test_new issue',
364 364 :description => 'This is the description',
365 365 :priority_id => 5}
366 366 assert_response :success
367 367 assert_template 'attributes'
368 368
369 369 issue = assigns(:issue)
370 370 assert_kind_of Issue, issue
371 371 assert_equal 1, issue.project_id
372 372 assert_equal 2, issue.tracker_id
373 373 assert_equal 'This is the test_new issue', issue.subject
374 374 end
375 375
376 376 def test_post_create
377 377 @request.session[:user_id] = 2
378 378 assert_difference 'Issue.count' do
379 379 post :create, :project_id => 1,
380 380 :issue => {:tracker_id => 3,
381 381 :status_id => 2,
382 382 :subject => 'This is the test_new issue',
383 383 :description => 'This is the description',
384 384 :priority_id => 5,
385 385 :estimated_hours => '',
386 386 :custom_field_values => {'2' => 'Value for field 2'}}
387 387 end
388 388 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
389 389
390 390 issue = Issue.find_by_subject('This is the test_new issue')
391 391 assert_not_nil issue
392 392 assert_equal 2, issue.author_id
393 393 assert_equal 3, issue.tracker_id
394 394 assert_equal 2, issue.status_id
395 395 assert_nil issue.estimated_hours
396 396 v = issue.custom_values.find(:first, :conditions => {:custom_field_id => 2})
397 397 assert_not_nil v
398 398 assert_equal 'Value for field 2', v.value
399 399 end
400 400
401 401 def test_post_create_and_continue
402 402 @request.session[:user_id] = 2
403 403 post :create, :project_id => 1,
404 404 :issue => {:tracker_id => 3,
405 405 :subject => 'This is first issue',
406 406 :priority_id => 5},
407 407 :continue => ''
408 408 assert_redirected_to :controller => 'issues', :action => 'new', :project_id => 'ecookbook',
409 409 :issue => {:tracker_id => 3}
410 410 end
411 411
412 412 def test_post_create_without_custom_fields_param
413 413 @request.session[:user_id] = 2
414 414 assert_difference 'Issue.count' do
415 415 post :create, :project_id => 1,
416 416 :issue => {:tracker_id => 1,
417 417 :subject => 'This is the test_new issue',
418 418 :description => 'This is the description',
419 419 :priority_id => 5}
420 420 end
421 421 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
422 422 end
423 423
424 424 def test_post_create_with_required_custom_field_and_without_custom_fields_param
425 425 field = IssueCustomField.find_by_name('Database')
426 426 field.update_attribute(:is_required, true)
427 427
428 428 @request.session[:user_id] = 2
429 429 post :create, :project_id => 1,
430 430 :issue => {:tracker_id => 1,
431 431 :subject => 'This is the test_new issue',
432 432 :description => 'This is the description',
433 433 :priority_id => 5}
434 434 assert_response :success
435 435 assert_template 'new'
436 436 issue = assigns(:issue)
437 437 assert_not_nil issue
438 438 assert_equal I18n.translate('activerecord.errors.messages.invalid'), issue.errors.on(:custom_values)
439 439 end
440 440
441 441 def test_post_create_with_watchers
442 442 @request.session[:user_id] = 2
443 443 ActionMailer::Base.deliveries.clear
444 444
445 445 assert_difference 'Watcher.count', 2 do
446 446 post :create, :project_id => 1,
447 447 :issue => {:tracker_id => 1,
448 448 :subject => 'This is a new issue with watchers',
449 449 :description => 'This is the description',
450 450 :priority_id => 5,
451 451 :watcher_user_ids => ['2', '3']}
452 452 end
453 453 issue = Issue.find_by_subject('This is a new issue with watchers')
454 454 assert_not_nil issue
455 455 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue
456 456
457 457 # Watchers added
458 458 assert_equal [2, 3], issue.watcher_user_ids.sort
459 459 assert issue.watched_by?(User.find(3))
460 460 # Watchers notified
461 461 mail = ActionMailer::Base.deliveries.last
462 462 assert_kind_of TMail::Mail, mail
463 463 assert [mail.bcc, mail.cc].flatten.include?(User.find(3).mail)
464 464 end
465 465
466 466 def test_post_create_subissue
467 467 @request.session[:user_id] = 2
468 468
469 469 assert_difference 'Issue.count' do
470 470 post :create, :project_id => 1,
471 471 :issue => {:tracker_id => 1,
472 472 :subject => 'This is a child issue',
473 473 :parent_issue_id => 2}
474 474 end
475 475 issue = Issue.find_by_subject('This is a child issue')
476 476 assert_not_nil issue
477 477 assert_equal Issue.find(2), issue.parent
478 478 end
479 479
480 480 def test_post_create_should_send_a_notification
481 481 ActionMailer::Base.deliveries.clear
482 482 @request.session[:user_id] = 2
483 483 assert_difference 'Issue.count' do
484 484 post :create, :project_id => 1,
485 485 :issue => {:tracker_id => 3,
486 486 :subject => 'This is the test_new issue',
487 487 :description => 'This is the description',
488 488 :priority_id => 5,
489 489 :estimated_hours => '',
490 490 :custom_field_values => {'2' => 'Value for field 2'}}
491 491 end
492 492 assert_redirected_to :controller => 'issues', :action => 'show', :id => Issue.last.id
493 493
494 494 assert_equal 1, ActionMailer::Base.deliveries.size
495 495 end
496 496
497 497 def test_post_create_should_preserve_fields_values_on_validation_failure
498 498 @request.session[:user_id] = 2
499 499 post :create, :project_id => 1,
500 500 :issue => {:tracker_id => 1,
501 501 # empty subject
502 502 :subject => '',
503 503 :description => 'This is a description',
504 504 :priority_id => 6,
505 505 :custom_field_values => {'1' => 'Oracle', '2' => 'Value for field 2'}}
506 506 assert_response :success
507 507 assert_template 'new'
508 508
509 509 assert_tag :textarea, :attributes => { :name => 'issue[description]' },
510 510 :content => 'This is a description'
511 511 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
512 512 :child => { :tag => 'option', :attributes => { :selected => 'selected',
513 513 :value => '6' },
514 514 :content => 'High' }
515 515 # Custom fields
516 516 assert_tag :select, :attributes => { :name => 'issue[custom_field_values][1]' },
517 517 :child => { :tag => 'option', :attributes => { :selected => 'selected',
518 518 :value => 'Oracle' },
519 519 :content => 'Oracle' }
520 520 assert_tag :input, :attributes => { :name => 'issue[custom_field_values][2]',
521 521 :value => 'Value for field 2'}
522 522 end
523 523
524 524 def test_post_create_should_ignore_non_safe_attributes
525 525 @request.session[:user_id] = 2
526 526 assert_nothing_raised do
527 527 post :create, :project_id => 1, :issue => { :tracker => "A param can not be a Tracker" }
528 528 end
529 529 end
530 530
531 531 context "without workflow privilege" do
532 532 setup do
533 533 Workflow.delete_all(["role_id = ?", Role.anonymous.id])
534 534 Role.anonymous.add_permission! :add_issues
535 535 end
536 536
537 537 context "#new" do
538 538 should "propose default status only" do
539 539 get :new, :project_id => 1
540 540 assert_response :success
541 541 assert_template 'new'
542 542 assert_tag :tag => 'select',
543 543 :attributes => {:name => 'issue[status_id]'},
544 544 :children => {:count => 1},
545 545 :child => {:tag => 'option', :attributes => {:value => IssueStatus.default.id.to_s}}
546 546 end
547 547
548 548 should "accept default status" do
549 549 assert_difference 'Issue.count' do
550 550 post :create, :project_id => 1,
551 551 :issue => {:tracker_id => 1,
552 552 :subject => 'This is an issue',
553 553 :status_id => 1}
554 554 end
555 555 issue = Issue.last(:order => 'id')
556 556 assert_equal IssueStatus.default, issue.status
557 557 end
558 558
559 559 should "ignore unauthorized status" do
560 560 assert_difference 'Issue.count' do
561 561 post :create, :project_id => 1,
562 562 :issue => {:tracker_id => 1,
563 563 :subject => 'This is an issue',
564 564 :status_id => 3}
565 565 end
566 566 issue = Issue.last(:order => 'id')
567 567 assert_equal IssueStatus.default, issue.status
568 568 end
569 569 end
570 570 end
571 571
572 572 def test_copy_issue
573 573 @request.session[:user_id] = 2
574 574 get :new, :project_id => 1, :copy_from => 1
575 575 assert_template 'new'
576 576 assert_not_nil assigns(:issue)
577 577 orig = Issue.find(1)
578 578 assert_equal orig.subject, assigns(:issue).subject
579 579 end
580 580
581 581 def test_get_edit
582 582 @request.session[:user_id] = 2
583 583 get :edit, :id => 1
584 584 assert_response :success
585 585 assert_template 'edit'
586 586 assert_not_nil assigns(:issue)
587 587 assert_equal Issue.find(1), assigns(:issue)
588 588 end
589 589
590 590 def test_get_edit_with_params
591 591 @request.session[:user_id] = 2
592 592 get :edit, :id => 1, :issue => { :status_id => 5, :priority_id => 7 }
593 593 assert_response :success
594 594 assert_template 'edit'
595 595
596 596 issue = assigns(:issue)
597 597 assert_not_nil issue
598 598
599 599 assert_equal 5, issue.status_id
600 600 assert_tag :select, :attributes => { :name => 'issue[status_id]' },
601 601 :child => { :tag => 'option',
602 602 :content => 'Closed',
603 603 :attributes => { :selected => 'selected' } }
604 604
605 605 assert_equal 7, issue.priority_id
606 606 assert_tag :select, :attributes => { :name => 'issue[priority_id]' },
607 607 :child => { :tag => 'option',
608 608 :content => 'Urgent',
609 609 :attributes => { :selected => 'selected' } }
610 610 end
611 611
612 612 def test_update_edit_form
613 613 @request.session[:user_id] = 2
614 614 xhr :post, :new, :project_id => 1,
615 615 :id => 1,
616 616 :issue => {:tracker_id => 2,
617 617 :subject => 'This is the test_new issue',
618 618 :description => 'This is the description',
619 619 :priority_id => 5}
620 620 assert_response :success
621 621 assert_template 'attributes'
622 622
623 623 issue = assigns(:issue)
624 624 assert_kind_of Issue, issue
625 625 assert_equal 1, issue.id
626 626 assert_equal 1, issue.project_id
627 627 assert_equal 2, issue.tracker_id
628 628 assert_equal 'This is the test_new issue', issue.subject
629 629 end
630 630
631 631 def test_update_using_invalid_http_verbs
632 632 @request.session[:user_id] = 2
633 633 subject = 'Updated by an invalid http verb'
634 634
635 635 get :update, :id => 1, :issue => {:subject => subject}
636 636 assert_not_equal subject, Issue.find(1).subject
637 637
638 638 post :update, :id => 1, :issue => {:subject => subject}
639 639 assert_not_equal subject, Issue.find(1).subject
640 640
641 641 delete :update, :id => 1, :issue => {:subject => subject}
642 642 assert_not_equal subject, Issue.find(1).subject
643 643 end
644 644
645 645 def test_put_update_without_custom_fields_param
646 646 @request.session[:user_id] = 2
647 647 ActionMailer::Base.deliveries.clear
648 648
649 649 issue = Issue.find(1)
650 650 assert_equal '125', issue.custom_value_for(2).value
651 651 old_subject = issue.subject
652 652 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
653 653
654 654 assert_difference('Journal.count') do
655 655 assert_difference('JournalDetail.count', 2) do
656 656 put :update, :id => 1, :issue => {:subject => new_subject,
657 657 :priority_id => '6',
658 658 :category_id => '1' # no change
659 659 }
660 660 end
661 661 end
662 662 assert_redirected_to :action => 'show', :id => '1'
663 663 issue.reload
664 664 assert_equal new_subject, issue.subject
665 665 # Make sure custom fields were not cleared
666 666 assert_equal '125', issue.custom_value_for(2).value
667 667
668 668 mail = ActionMailer::Base.deliveries.last
669 669 assert_kind_of TMail::Mail, mail
670 670 assert mail.subject.starts_with?("[#{issue.project.name} - #{issue.tracker.name} ##{issue.id}]")
671 671 assert mail.body.include?("Subject changed from #{old_subject} to #{new_subject}")
672 672 end
673 673
674 674 def test_put_update_with_custom_field_change
675 675 @request.session[:user_id] = 2
676 676 issue = Issue.find(1)
677 677 assert_equal '125', issue.custom_value_for(2).value
678 678
679 679 assert_difference('Journal.count') do
680 680 assert_difference('JournalDetail.count', 3) do
681 681 put :update, :id => 1, :issue => {:subject => 'Custom field change',
682 682 :priority_id => '6',
683 683 :category_id => '1', # no change
684 684 :custom_field_values => { '2' => 'New custom value' }
685 685 }
686 686 end
687 687 end
688 688 assert_redirected_to :action => 'show', :id => '1'
689 689 issue.reload
690 690 assert_equal 'New custom value', issue.custom_value_for(2).value
691 691
692 692 mail = ActionMailer::Base.deliveries.last
693 693 assert_kind_of TMail::Mail, mail
694 694 assert mail.body.include?("Searchable field changed from 125 to New custom value")
695 695 end
696 696
697 697 def test_put_update_with_status_and_assignee_change
698 698 issue = Issue.find(1)
699 699 assert_equal 1, issue.status_id
700 700 @request.session[:user_id] = 2
701 701 assert_difference('TimeEntry.count', 0) do
702 702 put :update,
703 703 :id => 1,
704 704 :issue => { :status_id => 2, :assigned_to_id => 3 },
705 705 :notes => 'Assigned to dlopper',
706 706 :time_entry => { :hours => '', :comments => '', :activity_id => TimeEntryActivity.first }
707 707 end
708 708 assert_redirected_to :action => 'show', :id => '1'
709 709 issue.reload
710 710 assert_equal 2, issue.status_id
711 711 j = Journal.find(:first, :order => 'id DESC')
712 712 assert_equal 'Assigned to dlopper', j.notes
713 713 assert_equal 2, j.details.size
714 714
715 715 mail = ActionMailer::Base.deliveries.last
716 716 assert mail.body.include?("Status changed from New to Assigned")
717 717 # subject should contain the new status
718 718 assert mail.subject.include?("(#{ IssueStatus.find(2).name })")
719 719 end
720 720
721 721 def test_put_update_with_note_only
722 722 notes = 'Note added by IssuesControllerTest#test_update_with_note_only'
723 723 # anonymous user
724 724 put :update,
725 725 :id => 1,
726 726 :notes => notes
727 727 assert_redirected_to :action => 'show', :id => '1'
728 728 j = Journal.find(:first, :order => 'id DESC')
729 729 assert_equal notes, j.notes
730 730 assert_equal 0, j.details.size
731 731 assert_equal User.anonymous, j.user
732 732
733 733 mail = ActionMailer::Base.deliveries.last
734 734 assert mail.body.include?(notes)
735 735 end
736 736
737 737 def test_put_update_with_note_and_spent_time
738 738 @request.session[:user_id] = 2
739 739 spent_hours_before = Issue.find(1).spent_hours
740 740 assert_difference('TimeEntry.count') do
741 741 put :update,
742 742 :id => 1,
743 743 :notes => '2.5 hours added',
744 744 :time_entry => { :hours => '2.5', :comments => 'test_put_update_with_note_and_spent_time', :activity_id => TimeEntryActivity.first.id }
745 745 end
746 746 assert_redirected_to :action => 'show', :id => '1'
747 747
748 748 issue = Issue.find(1)
749 749
750 750 j = Journal.find(:first, :order => 'id DESC')
751 751 assert_equal '2.5 hours added', j.notes
752 752 assert_equal 0, j.details.size
753 753
754 754 t = issue.time_entries.find_by_comments('test_put_update_with_note_and_spent_time')
755 755 assert_not_nil t
756 756 assert_equal 2.5, t.hours
757 757 assert_equal spent_hours_before + 2.5, issue.spent_hours
758 758 end
759 759
760 760 def test_put_update_with_attachment_only
761 761 set_tmp_attachments_directory
762 762
763 763 # Delete all fixtured journals, a race condition can occur causing the wrong
764 764 # journal to get fetched in the next find.
765 765 Journal.delete_all
766 766
767 767 # anonymous user
768 768 put :update,
769 769 :id => 1,
770 770 :notes => '',
771 771 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
772 772 assert_redirected_to :action => 'show', :id => '1'
773 773 j = Issue.find(1).journals.find(:first, :order => 'id DESC')
774 774 assert j.notes.blank?
775 775 assert_equal 1, j.details.size
776 776 assert_equal 'testfile.txt', j.details.first.value
777 777 assert_equal User.anonymous, j.user
778 778
779 779 mail = ActionMailer::Base.deliveries.last
780 780 assert mail.body.include?('testfile.txt')
781 781 end
782 782
783 783 def test_put_update_with_attachment_that_fails_to_save
784 784 set_tmp_attachments_directory
785 785
786 786 # Delete all fixtured journals, a race condition can occur causing the wrong
787 787 # journal to get fetched in the next find.
788 788 Journal.delete_all
789 789
790 790 # Mock out the unsaved attachment
791 791 Attachment.any_instance.stubs(:create).returns(Attachment.new)
792 792
793 793 # anonymous user
794 794 put :update,
795 795 :id => 1,
796 796 :notes => '',
797 797 :attachments => {'1' => {'file' => uploaded_test_file('testfile.txt', 'text/plain')}}
798 798 assert_redirected_to :action => 'show', :id => '1'
799 799 assert_equal '1 file(s) could not be saved.', flash[:warning]
800 800
801 801 end if Object.const_defined?(:Mocha)
802 802
803 803 def test_put_update_with_no_change
804 804 issue = Issue.find(1)
805 805 issue.journals.clear
806 806 ActionMailer::Base.deliveries.clear
807 807
808 808 put :update,
809 809 :id => 1,
810 810 :notes => ''
811 811 assert_redirected_to :action => 'show', :id => '1'
812 812
813 813 issue.reload
814 814 assert issue.journals.empty?
815 815 # No email should be sent
816 816 assert ActionMailer::Base.deliveries.empty?
817 817 end
818 818
819 819 def test_put_update_should_send_a_notification
820 820 @request.session[:user_id] = 2
821 821 ActionMailer::Base.deliveries.clear
822 822 issue = Issue.find(1)
823 823 old_subject = issue.subject
824 824 new_subject = 'Subject modified by IssuesControllerTest#test_post_edit'
825 825
826 826 put :update, :id => 1, :issue => {:subject => new_subject,
827 827 :priority_id => '6',
828 828 :category_id => '1' # no change
829 829 }
830 830 assert_equal 1, ActionMailer::Base.deliveries.size
831 831 end
832 832
833 833 def test_put_update_with_invalid_spent_time
834 834 @request.session[:user_id] = 2
835 835 notes = 'Note added by IssuesControllerTest#test_post_edit_with_invalid_spent_time'
836 836
837 837 assert_no_difference('Journal.count') do
838 838 put :update,
839 839 :id => 1,
840 840 :notes => notes,
841 841 :time_entry => {"comments"=>"", "activity_id"=>"", "hours"=>"2z"}
842 842 end
843 843 assert_response :success
844 844 assert_template 'edit'
845 845
846 846 assert_tag :textarea, :attributes => { :name => 'notes' },
847 847 :content => notes
848 848 assert_tag :input, :attributes => { :name => 'time_entry[hours]', :value => "2z" }
849 849 end
850 850
851 851 def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
852 852 issue = Issue.find(2)
853 853 @request.session[:user_id] = 2
854 854
855 855 put :update,
856 856 :id => issue.id,
857 857 :issue => {
858 858 :fixed_version_id => 4
859 859 }
860 860
861 861 assert_response :redirect
862 862 issue.reload
863 863 assert_equal 4, issue.fixed_version_id
864 864 assert_not_equal issue.project_id, issue.fixed_version.project_id
865 865 end
866 866
867 867 def test_put_update_should_redirect_back_using_the_back_url_parameter
868 868 issue = Issue.find(2)
869 869 @request.session[:user_id] = 2
870 870
871 871 put :update,
872 872 :id => issue.id,
873 873 :issue => {
874 874 :fixed_version_id => 4
875 875 },
876 876 :back_url => '/issues'
877 877
878 878 assert_response :redirect
879 879 assert_redirected_to '/issues'
880 880 end
881 881
882 882 def test_put_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
883 883 issue = Issue.find(2)
884 884 @request.session[:user_id] = 2
885 885
886 886 put :update,
887 887 :id => issue.id,
888 888 :issue => {
889 889 :fixed_version_id => 4
890 890 },
891 891 :back_url => 'http://google.com'
892 892
893 893 assert_response :redirect
894 894 assert_redirected_to :controller => 'issues', :action => 'show', :id => issue.id
895 895 end
896 896
897 897 def test_get_bulk_edit
898 898 @request.session[:user_id] = 2
899 899 get :bulk_edit, :ids => [1, 2]
900 900 assert_response :success
901 901 assert_template 'bulk_edit'
902 902
903 903 # Project specific custom field, date type
904 904 field = CustomField.find(9)
905 905 assert !field.is_for_all?
906 906 assert_equal 'date', field.field_format
907 907 assert_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
908 908
909 909 # System wide custom field
910 910 assert CustomField.find(1).is_for_all?
911 911 assert_tag :select, :attributes => {:name => 'issue[custom_field_values][1]'}
912 912 end
913 913
914 def test_get_bulk_edit_on_different_projects
915 @request.session[:user_id] = 2
916 get :bulk_edit, :ids => [1, 2, 6]
917 assert_response :success
918 assert_template 'bulk_edit'
919
920 # Project specific custom field, date type
921 field = CustomField.find(9)
922 assert !field.is_for_all?
923 assert !field.project_ids.include?(Issue.find(6).project_id)
924 assert_no_tag :input, :attributes => {:name => 'issue[custom_field_values][9]'}
925 end
926
914 927 def test_bulk_update
915 928 @request.session[:user_id] = 2
916 929 # update issues priority
917 930 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing',
918 931 :issue => {:priority_id => 7,
919 932 :assigned_to_id => '',
920 933 :custom_field_values => {'2' => ''}}
921 934
922 935 assert_response 302
923 936 # check that the issues were updated
924 937 assert_equal [7, 7], Issue.find_all_by_id([1, 2]).collect {|i| i.priority.id}
925 938
926 939 issue = Issue.find(1)
927 940 journal = issue.journals.find(:first, :order => 'created_on DESC')
928 941 assert_equal '125', issue.custom_value_for(2).value
929 942 assert_equal 'Bulk editing', journal.notes
930 943 assert_equal 1, journal.details.size
931 944 end
932 945
946 def test_bulk_update_on_different_projects
947 @request.session[:user_id] = 2
948 # update issues priority
949 post :bulk_update, :ids => [1, 2, 6], :notes => 'Bulk editing',
950 :issue => {:priority_id => 7,
951 :assigned_to_id => '',
952 :custom_field_values => {'2' => ''}}
953
954 assert_response 302
955 # check that the issues were updated
956 assert_equal [7, 7, 7], Issue.find([1,2,6]).map(&:priority_id)
957
958 issue = Issue.find(1)
959 journal = issue.journals.find(:first, :order => 'created_on DESC')
960 assert_equal '125', issue.custom_value_for(2).value
961 assert_equal 'Bulk editing', journal.notes
962 assert_equal 1, journal.details.size
963 end
964
965 def test_bulk_update_on_different_projects_without_rights
966 @request.session[:user_id] = 3
967 user = User.find(3)
968 action = { :controller => "issues", :action => "bulk_update" }
969 assert user.allowed_to?(action, Issue.find(1).project)
970 assert ! user.allowed_to?(action, Issue.find(6).project)
971 post :bulk_update, :ids => [1, 6], :notes => 'Bulk should fail',
972 :issue => {:priority_id => 7,
973 :assigned_to_id => '',
974 :custom_field_values => {'2' => ''}}
975 assert_response 403
976 assert_not_equal "Bulk should fail", Journal.last.notes
977 end
978
933 979 def test_bullk_update_should_send_a_notification
934 980 @request.session[:user_id] = 2
935 981 ActionMailer::Base.deliveries.clear
936 982 post(:bulk_update,
937 983 {
938 984 :ids => [1, 2],
939 985 :notes => 'Bulk editing',
940 986 :issue => {
941 987 :priority_id => 7,
942 988 :assigned_to_id => '',
943 989 :custom_field_values => {'2' => ''}
944 990 }
945 991 })
946 992
947 993 assert_response 302
948 994 assert_equal 2, ActionMailer::Base.deliveries.size
949 995 end
950 996
951 997 def test_bulk_update_status
952 998 @request.session[:user_id] = 2
953 999 # update issues priority
954 1000 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing status',
955 1001 :issue => {:priority_id => '',
956 1002 :assigned_to_id => '',
957 1003 :status_id => '5'}
958 1004
959 1005 assert_response 302
960 1006 issue = Issue.find(1)
961 1007 assert issue.closed?
962 1008 end
963 1009
964 1010 def test_bulk_update_custom_field
965 1011 @request.session[:user_id] = 2
966 1012 # update issues priority
967 1013 post :bulk_update, :ids => [1, 2], :notes => 'Bulk editing custom field',
968 1014 :issue => {:priority_id => '',
969 1015 :assigned_to_id => '',
970 1016 :custom_field_values => {'2' => '777'}}
971 1017
972 1018 assert_response 302
973 1019
974 1020 issue = Issue.find(1)
975 1021 journal = issue.journals.find(:first, :order => 'created_on DESC')
976 1022 assert_equal '777', issue.custom_value_for(2).value
977 1023 assert_equal 1, journal.details.size
978 1024 assert_equal '125', journal.details.first.old_value
979 1025 assert_equal '777', journal.details.first.value
980 1026 end
981 1027
982 1028 def test_bulk_update_unassign
983 1029 assert_not_nil Issue.find(2).assigned_to
984 1030 @request.session[:user_id] = 2
985 1031 # unassign issues
986 1032 post :bulk_update, :ids => [1, 2], :notes => 'Bulk unassigning', :issue => {:assigned_to_id => 'none'}
987 1033 assert_response 302
988 1034 # check that the issues were updated
989 1035 assert_nil Issue.find(2).assigned_to
990 1036 end
991 1037
992 1038 def test_post_bulk_update_should_allow_fixed_version_to_be_set_to_a_subproject
993 1039 @request.session[:user_id] = 2
994 1040
995 1041 post :bulk_update, :ids => [1,2], :issue => {:fixed_version_id => 4}
996 1042
997 1043 assert_response :redirect
998 1044 issues = Issue.find([1,2])
999 1045 issues.each do |issue|
1000 1046 assert_equal 4, issue.fixed_version_id
1001 1047 assert_not_equal issue.project_id, issue.fixed_version.project_id
1002 1048 end
1003 1049 end
1004 1050
1005 1051 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
1006 1052 @request.session[:user_id] = 2
1007 1053 post :bulk_update, :ids => [1,2], :back_url => '/issues'
1008 1054
1009 1055 assert_response :redirect
1010 1056 assert_redirected_to '/issues'
1011 1057 end
1012 1058
1013 1059 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
1014 1060 @request.session[:user_id] = 2
1015 1061 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
1016 1062
1017 1063 assert_response :redirect
1018 1064 assert_redirected_to :controller => 'issues', :action => 'index', :project_id => Project.find(1).identifier
1019 1065 end
1020 1066
1021 1067 def test_destroy_issue_with_no_time_entries
1022 1068 assert_nil TimeEntry.find_by_issue_id(2)
1023 1069 @request.session[:user_id] = 2
1024 1070 post :destroy, :id => 2
1025 1071 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1026 1072 assert_nil Issue.find_by_id(2)
1027 1073 end
1028 1074
1029 1075 def test_destroy_issues_with_time_entries
1030 1076 @request.session[:user_id] = 2
1031 1077 post :destroy, :ids => [1, 3]
1032 1078 assert_response :success
1033 1079 assert_template 'destroy'
1034 1080 assert_not_nil assigns(:hours)
1035 1081 assert Issue.find_by_id(1) && Issue.find_by_id(3)
1036 1082 end
1037 1083
1038 1084 def test_destroy_issues_and_destroy_time_entries
1039 1085 @request.session[:user_id] = 2
1040 1086 post :destroy, :ids => [1, 3], :todo => 'destroy'
1041 1087 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1042 1088 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1043 1089 assert_nil TimeEntry.find_by_id([1, 2])
1044 1090 end
1045 1091
1046 1092 def test_destroy_issues_and_assign_time_entries_to_project
1047 1093 @request.session[:user_id] = 2
1048 1094 post :destroy, :ids => [1, 3], :todo => 'nullify'
1049 1095 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1050 1096 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1051 1097 assert_nil TimeEntry.find(1).issue_id
1052 1098 assert_nil TimeEntry.find(2).issue_id
1053 1099 end
1054 1100
1055 1101 def test_destroy_issues_and_reassign_time_entries_to_another_issue
1056 1102 @request.session[:user_id] = 2
1057 1103 post :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 2
1058 1104 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
1059 1105 assert !(Issue.find_by_id(1) || Issue.find_by_id(3))
1060 1106 assert_equal 2, TimeEntry.find(1).issue_id
1061 1107 assert_equal 2, TimeEntry.find(2).issue_id
1062 1108 end
1063 1109
1064 1110 def test_destroy_issues_from_different_projects
1065 1111 @request.session[:user_id] = 2
1066 1112 post :destroy, :ids => [1, 2, 6], :todo => 'destroy'
1067 1113 assert_redirected_to :controller => 'issues', :action => 'index'
1068 1114 assert !(Issue.find_by_id(1) || Issue.find_by_id(2) || Issue.find_by_id(6))
1069 1115 end
1070 1116
1071 1117 def test_default_search_scope
1072 1118 get :index
1073 1119 assert_tag :div, :attributes => {:id => 'quick-search'},
1074 1120 :child => {:tag => 'form',
1075 1121 :child => {:tag => 'input', :attributes => {:name => 'issues', :type => 'hidden', :value => '1'}}}
1076 1122 end
1077 1123 end
General Comments 0
You need to be logged in to leave comments. Login now