comparison app/controllers/issues_controller.rb @ 1338:25603efa57b5

Merge from live branch
author Chris Cannam
date Thu, 20 Jun 2013 13:14:14 +0100
parents 7c3de6c7b7f5
children 4f746d8966dd 51364c0cd58f
comparison
equal deleted inserted replaced
1209:1b1138f6f55e 1338:25603efa57b5
1 # Redmine - project management software 1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 # 3 #
4 # This program is free software; you can redistribute it and/or 4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License 5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2 6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version. 7 # of the License, or (at your option) any later version.
18 class IssuesController < ApplicationController 18 class IssuesController < ApplicationController
19 menu_item :new_issue, :only => [:new, :create] 19 menu_item :new_issue, :only => [:new, :create]
20 default_search_scope :issues 20 default_search_scope :issues
21 21
22 before_filter :find_issue, :only => [:show, :edit, :update] 22 before_filter :find_issue, :only => [:show, :edit, :update]
23 before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :move, :perform_move, :destroy] 23 before_filter :find_issues, :only => [:bulk_edit, :bulk_update, :destroy]
24 before_filter :check_project_uniqueness, :only => [:move, :perform_move]
25 before_filter :find_project, :only => [:new, :create] 24 before_filter :find_project, :only => [:new, :create]
26 before_filter :authorize, :except => [:index] 25 before_filter :authorize, :except => [:index]
27 before_filter :find_optional_project, :only => [:index] 26 before_filter :find_optional_project, :only => [:index]
28 before_filter :check_for_default_issue_status, :only => [:new, :create] 27 before_filter :check_for_default_issue_status, :only => [:new, :create]
29 before_filter :build_new_issue_from_params, :only => [:new, :create] 28 before_filter :build_new_issue_from_params, :only => [:new, :create]
49 include RepositoriesHelper 48 include RepositoriesHelper
50 helper :sort 49 helper :sort
51 include SortHelper 50 include SortHelper
52 include IssuesHelper 51 include IssuesHelper
53 helper :timelog 52 helper :timelog
54 helper :gantt
55 include Redmine::Export::PDF 53 include Redmine::Export::PDF
56
57 verify :method => [:post, :delete],
58 :only => :destroy,
59 :render => { :nothing => true, :status => :method_not_allowed }
60
61 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
62 verify :method => :post, :only => :bulk_update, :render => {:nothing => true, :status => :method_not_allowed }
63 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
64 54
65 def index 55 def index
66 retrieve_query 56 retrieve_query
67 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria) 57 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
68 sort_update(@query.sortable_columns) 58 sort_update(@query.sortable_columns)
59 @query.sort_criteria = sort_criteria.to_a
69 60
70 if @query.valid? 61 if @query.valid?
71 case params[:format] 62 case params[:format]
72 when 'csv', 'pdf' 63 when 'csv', 'pdf'
73 @limit = Setting.issues_export_limit.to_i 64 @limit = Setting.issues_export_limit.to_i
89 @issue_count_by_group = @query.issue_count_by_group 80 @issue_count_by_group = @query.issue_count_by_group
90 81
91 respond_to do |format| 82 respond_to do |format|
92 format.html { render :template => 'issues/index', :layout => !request.xhr? } 83 format.html { render :template => 'issues/index', :layout => !request.xhr? }
93 format.api { 84 format.api {
94 Issue.load_relations(@issues) if include_in_api_response?('relations') 85 Issue.load_visible_relations(@issues) if include_in_api_response?('relations')
95 } 86 }
96 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") } 87 format.atom { render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") }
97 format.csv { send_data(issues_to_csv(@issues, @project, @query, params), :type => 'text/csv; header=present', :filename => 'export.csv') } 88 format.csv { send_data(issues_to_csv(@issues, @project, @query, params), :type => 'text/csv; header=present', :filename => 'export.csv') }
98 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') } 89 format.pdf { send_data(issues_to_pdf(@issues, @project, @query), :type => 'application/pdf', :filename => 'export.pdf') }
99 end 90 end
107 rescue ActiveRecord::RecordNotFound 98 rescue ActiveRecord::RecordNotFound
108 render_404 99 render_404
109 end 100 end
110 101
111 def show 102 def show
112 @journals = @issue.journals.find(:all, :include => [:user, :details], :order => "#{Journal.table_name}.created_on ASC") 103 @journals = @issue.journals.includes(:user, :details).reorder("#{Journal.table_name}.id ASC").all
113 @journals.each_with_index {|j,i| j.indice = i+1} 104 @journals.each_with_index {|j,i| j.indice = i+1}
105 @journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
114 @journals.reverse! if User.current.wants_comments_in_reverse_order? 106 @journals.reverse! if User.current.wants_comments_in_reverse_order?
115 107
116 if User.current.allowed_to?(:view_changesets, @project) 108 @changesets = @issue.changesets.visible.all
117 @changesets = @issue.changesets.visible.all 109 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
118 @changesets.reverse! if User.current.wants_comments_in_reverse_order?
119 end
120 110
121 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? } 111 @relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
122 @allowed_statuses = @issue.new_statuses_allowed_to(User.current) 112 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
123 @edit_allowed = User.current.allowed_to?(:edit_issues, @project) 113 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
124 @priorities = IssuePriority.active 114 @priorities = IssuePriority.active
125 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project) 115 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
126 respond_to do |format| 116 respond_to do |format|
127 format.html { render :template => 'issues/show' } 117 format.html {
118 retrieve_previous_and_next_issue_ids
119 render :template => 'issues/show'
120 }
128 format.api 121 format.api
129 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' } 122 format.atom { render :template => 'journals/index', :layout => false, :content_type => 'application/atom+xml' }
130 format.pdf { send_data(issue_to_pdf(@issue), :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf") } 123 format.pdf {
124 pdf = issue_to_pdf(@issue, :journals => @journals)
125 send_data(pdf, :type => 'application/pdf', :filename => "#{@project.identifier}-#{@issue.id}.pdf")
126 }
131 end 127 end
132 end 128 end
133 129
134 # Add a new issue 130 # Add a new issue
135 # The new issue will be created from an existing one if copy_from parameter is given 131 # The new issue will be created from an existing one if copy_from parameter is given
136 def new 132 def new
137 respond_to do |format| 133 respond_to do |format|
138 format.html { render :action => 'new', :layout => !request.xhr? } 134 format.html { render :action => 'new', :layout => !request.xhr? }
139 format.js { render :partial => 'attributes' } 135 format.js { render :partial => 'update_form' }
140 end 136 end
141 end 137 end
142 138
143 def create 139 def create
144 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue }) 140 call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue })
141 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
145 if @issue.save 142 if @issue.save
146 attachments = Attachment.attach_files(@issue, params[:attachments])
147 143
148 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue}) 144 call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue})
149 145
150 # Also adds the assignee to the watcher's list 146 # Also adds the assignee to the watcher's list
151 if params[:issue][:assigned_to_id] && !params[:issue][:assigned_to_id].empty?: 147 if params[:issue][:assigned_to_id] && !params[:issue][:assigned_to_id].empty?
152 unless @issue.watcher_ids.include?(params[:issue][:assigned_to_id]): 148 unless @issue.watcher_ids.include?(params[:issue][:assigned_to_id])
153 @issue.add_watcher(User.find(params[:issue][:assigned_to_id])) 149 @issue.add_watcher(User.find(params[:issue][:assigned_to_id]))
154 end 150 end
155 end 151 end
156 152
157 respond_to do |format| 153 respond_to do |format|
158 format.html { 154 format.html {
159 render_attachment_warning_if_needed(@issue) 155 render_attachment_warning_if_needed(@issue)
160 flash[:notice] = l(:notice_issue_successful_create, :id => "<a href='#{issue_path(@issue)}'>##{@issue.id}</a>") 156 flash[:notice] = l(:notice_issue_successful_create, :id => view_context.link_to("##{@issue.id}", issue_path(@issue), :title => @issue.subject))
161 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?} } : 157 redirect_to(params[:continue] ? { :action => 'new', :project_id => @issue.project, :issue => {:tracker_id => @issue.tracker, :parent_issue_id => @issue.parent_issue_id}.reject {|k,v| v.nil?} } :
162 { :action => 'show', :id => @issue }) 158 { :action => 'show', :id => @issue })
163 } 159 }
164 format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) } 160 format.api { render :action => 'show', :status => :created, :location => issue_url(@issue) }
165 end 161 end
166 return 162 return
171 end 167 end
172 end 168 end
173 end 169 end
174 170
175 def edit 171 def edit
176 update_issue_from_params 172 return unless update_issue_from_params
177
178 @journal = @issue.current_journal
179 173
180 respond_to do |format| 174 respond_to do |format|
181 format.html { } 175 format.html { }
182 format.xml { } 176 format.xml { }
183 end 177 end
184 end 178 end
185 179
186 def update 180 def update
187 update_issue_from_params 181 return unless update_issue_from_params
188 182 @issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads]))
189 if @issue.save_issue_with_child_records(params, @time_entry) 183 saved = false
184 begin
185 saved = @issue.save_issue_with_child_records(params, @time_entry)
186 rescue ActiveRecord::StaleObjectError
187 @conflict = true
188 if params[:last_journal_id]
189 @conflict_journals = @issue.journals_after(params[:last_journal_id]).all
190 @conflict_journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
191 end
192 end
193
194 if saved
190 render_attachment_warning_if_needed(@issue) 195 render_attachment_warning_if_needed(@issue)
191 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record? 196 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
192 197
193 respond_to do |format| 198 respond_to do |format|
194 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) } 199 format.html { redirect_back_or_default({:action => 'show', :id => @issue}) }
195 format.api { head :ok } 200 format.api { render_api_ok }
196 end 201 end
197 else 202 else
198 render_attachment_warning_if_needed(@issue)
199 flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record?
200 @journal = @issue.current_journal
201
202 respond_to do |format| 203 respond_to do |format|
203 format.html { render :action => 'edit' } 204 format.html { render :action => 'edit' }
204 format.api { render_validation_errors(@issue) } 205 format.api { render_validation_errors(@issue) }
205 end 206 end
206 end 207 end
207 end 208 end
208 209
209 # Bulk edit a set of issues 210 # Bulk edit/copy a set of issues
210 def bulk_edit 211 def bulk_edit
211 @issues.sort! 212 @issues.sort!
212 @available_statuses = @projects.map{|p|Workflow.available_statuses(p)}.inject{|memo,w|memo & w} 213 @copy = params[:copy].present?
213 @custom_fields = @projects.map{|p|p.all_issue_custom_fields}.inject{|memo,c|memo & c} 214 @notes = params[:notes]
214 @assignables = @projects.map(&:assignable_users).inject{|memo,a| memo & a} 215
215 @trackers = @projects.map(&:trackers).inject{|memo,t| memo & t} 216 if User.current.allowed_to?(:move_issues, @projects)
217 @allowed_projects = Issue.allowed_target_projects_on_move
218 if params[:issue]
219 @target_project = @allowed_projects.detect {|p| p.id.to_s == params[:issue][:project_id].to_s}
220 if @target_project
221 target_projects = [@target_project]
222 end
223 end
224 end
225 target_projects ||= @projects
226
227 if @copy
228 @available_statuses = [IssueStatus.default]
229 else
230 @available_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
231 end
232 @custom_fields = target_projects.map{|p|p.all_issue_custom_fields}.reduce(:&)
233 @assignables = target_projects.map(&:assignable_users).reduce(:&)
234 @trackers = target_projects.map(&:trackers).reduce(:&)
235 @versions = target_projects.map {|p| p.shared_versions.open}.reduce(:&)
236 @categories = target_projects.map {|p| p.issue_categories}.reduce(:&)
237 if @copy
238 @attachments_present = @issues.detect {|i| i.attachments.any?}.present?
239 @subtasks_present = @issues.detect {|i| !i.leaf?}.present?
240 end
241
242 @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
243 render :layout => false if request.xhr?
216 end 244 end
217 245
218 def bulk_update 246 def bulk_update
219 @issues.sort! 247 @issues.sort!
248 @copy = params[:copy].present?
220 attributes = parse_params_for_bulk_issue_attributes(params) 249 attributes = parse_params_for_bulk_issue_attributes(params)
221 250
222 unsaved_issue_ids = [] 251 unsaved_issue_ids = []
252 moved_issues = []
253
254 if @copy && params[:copy_subtasks].present?
255 # Descendant issues will be copied with the parent task
256 # Don't copy them twice
257 @issues.reject! {|issue| @issues.detect {|other| issue.is_descendant_of?(other)}}
258 end
259
223 @issues.each do |issue| 260 @issues.each do |issue|
224 issue.reload 261 issue.reload
262 if @copy
263 issue = issue.copy({},
264 :attachments => params[:copy_attachments].present?,
265 :subtasks => params[:copy_subtasks].present?
266 )
267 end
225 journal = issue.init_journal(User.current, params[:notes]) 268 journal = issue.init_journal(User.current, params[:notes])
226 issue.safe_attributes = attributes 269 issue.safe_attributes = attributes
227 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue }) 270 call_hook(:controller_issues_bulk_edit_before_save, { :params => params, :issue => issue })
228 unless issue.save 271 if issue.save
272 moved_issues << issue
273 else
229 # Keep unsaved issue ids to display them in flash error 274 # Keep unsaved issue ids to display them in flash error
230 unsaved_issue_ids << issue.id 275 unsaved_issue_ids << issue.id
231 end 276 end
232 end 277 end
233 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids) 278 set_flash_from_bulk_issue_save(@issues, unsaved_issue_ids)
234 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project}) 279
280 if params[:follow]
281 if @issues.size == 1 && moved_issues.size == 1
282 redirect_to :controller => 'issues', :action => 'show', :id => moved_issues.first
283 elsif moved_issues.map(&:project).uniq.size == 1
284 redirect_to :controller => 'issues', :action => 'index', :project_id => moved_issues.map(&:project).first
285 end
286 else
287 redirect_back_or_default({:controller => 'issues', :action => 'index', :project_id => @project})
288 end
235 end 289 end
236 290
237 def destroy 291 def destroy
238 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f 292 @hours = TimeEntry.sum(:hours, :conditions => ['issue_id IN (?)', @issues]).to_f
239 if @hours > 0 293 if @hours > 0
262 # nothing to do, issue was already deleted (eg. by a parent) 316 # nothing to do, issue was already deleted (eg. by a parent)
263 end 317 end
264 end 318 end
265 respond_to do |format| 319 respond_to do |format|
266 format.html { redirect_back_or_default(:action => 'index', :project_id => @project) } 320 format.html { redirect_back_or_default(:action => 'index', :project_id => @project) }
267 format.api { head :ok } 321 format.api { render_api_ok }
268 end 322 end
269 end 323 end
270 324
271 private 325 private
272 def find_issue
273 # Issue.visible.find(...) can not be used to redirect user to the login form
274 # if the issue actually exists but requires authentication
275 @issue = Issue.find(params[:id], :include => [:project, :tracker, :status, :author, :priority, :category])
276 unless @issue.visible?
277 deny_access
278 return
279 end
280 @project = @issue.project
281 rescue ActiveRecord::RecordNotFound
282 render_404
283 end
284 326
285 def find_project 327 def find_project
286 project_id = (params[:issue] && params[:issue][:project_id]) || params[:project_id] 328 project_id = params[:project_id] || (params[:issue] && params[:issue][:project_id])
287 @project = Project.find(project_id) 329 @project = Project.find(project_id)
288 rescue ActiveRecord::RecordNotFound 330 rescue ActiveRecord::RecordNotFound
289 render_404 331 render_404
332 end
333
334 def retrieve_previous_and_next_issue_ids
335 retrieve_query_from_session
336 if @query
337 sort_init(@query.sort_criteria.empty? ? [['id', 'desc']] : @query.sort_criteria)
338 sort_update(@query.sortable_columns, 'issues_index_sort')
339 limit = 500
340 issue_ids = @query.issue_ids(:order => sort_clause, :limit => (limit + 1), :include => [:assigned_to, :tracker, :priority, :category, :fixed_version])
341 if (idx = issue_ids.index(@issue.id)) && idx < limit
342 if issue_ids.size < 500
343 @issue_position = idx + 1
344 @issue_count = issue_ids.size
345 end
346 @prev_issue_id = issue_ids[idx - 1] if idx > 0
347 @next_issue_id = issue_ids[idx + 1] if idx < (issue_ids.size - 1)
348 end
349 end
290 end 350 end
291 351
292 # Used by #edit and #update to set some common instance variables 352 # Used by #edit and #update to set some common instance variables
293 # from the params 353 # from the params
294 # TODO: Refactor, not everything in here is needed by #edit 354 # TODO: Refactor, not everything in here is needed by #edit
295 def update_issue_from_params 355 def update_issue_from_params
296 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
297 @priorities = IssuePriority.active
298 @edit_allowed = User.current.allowed_to?(:edit_issues, @project) 356 @edit_allowed = User.current.allowed_to?(:edit_issues, @project)
299 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project) 357 @time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
300 @time_entry.attributes = params[:time_entry] 358 @time_entry.attributes = params[:time_entry]
301 359
302 @notes = params[:notes] || (params[:issue].present? ? params[:issue][:notes] : nil) 360 @issue.init_journal(User.current)
303 @issue.init_journal(User.current, @notes) 361
304 @issue.safe_attributes = params[:issue] 362 issue_attributes = params[:issue]
363 if issue_attributes && params[:conflict_resolution]
364 case params[:conflict_resolution]
365 when 'overwrite'
366 issue_attributes = issue_attributes.dup
367 issue_attributes.delete(:lock_version)
368 when 'add_notes'
369 issue_attributes = issue_attributes.slice(:notes)
370 when 'cancel'
371 redirect_to issue_path(@issue)
372 return false
373 end
374 end
305 375
306 # tests if the the user assigned_to_id 376 # tests if the the user assigned_to_id
307 # is in this issues watcher's list 377 # is in this issues watcher's list
308 # if not, adds it. 378 # if not, adds it.
309 379
310 if params[:issue] && params[:issue][:assigned_to_id] && !params[:issue][:assigned_to_id].empty?: 380 if params[:issue] && params[:issue][:assigned_to_id] && !params[:issue][:assigned_to_id].empty?
311 unless @issue.watched_by?(User.find(params[:issue][:assigned_to_id])): 381 unless @issue.watched_by?(User.find(params[:issue][:assigned_to_id]))
312 @issue.add_watcher(User.find(params[:issue][:assigned_to_id])) 382 @issue.add_watcher(User.find(params[:issue][:assigned_to_id]))
313 end 383 end
314 end 384 end
315 385
386 @issue.safe_attributes = issue_attributes
387 @priorities = IssuePriority.active
388 @allowed_statuses = @issue.new_statuses_allowed_to(User.current)
389 true
316 390
317 end 391 end
318 392
319 # TODO: Refactor, lots of extra code in here 393 # TODO: Refactor, lots of extra code in here
320 # TODO: Changing tracker on an existing issue should not trigger this 394 # TODO: Changing tracker on an existing issue should not trigger this
321 def build_new_issue_from_params 395 def build_new_issue_from_params
322 if params[:id].blank? 396 if params[:id].blank?
323 @issue = Issue.new 397 @issue = Issue.new
324 @issue.copy_from(params[:copy_from]) if params[:copy_from] 398 if params[:copy_from]
399 begin
400 @copy_from = Issue.visible.find(params[:copy_from])
401 @copy_attachments = params[:copy_attachments].present? || request.get?
402 @copy_subtasks = params[:copy_subtasks].present? || request.get?
403 @issue.copy_from(@copy_from, :attachments => @copy_attachments, :subtasks => @copy_subtasks)
404 rescue ActiveRecord::RecordNotFound
405 render_404
406 return
407 end
408 end
325 @issue.project = @project 409 @issue.project = @project
326 else 410 else
327 @issue = @project.issues.visible.find(params[:id]) 411 @issue = @project.issues.visible.find(params[:id])
328 end 412 end
329 413
330 @issue.project = @project 414 @issue.project = @project
331 @issue.author = User.current 415 @issue.author ||= User.current
332 # Tracker must be set before custom field values 416 # Tracker must be set before custom field values
333 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first) 417 @issue.tracker ||= @project.trackers.find((params[:issue] && params[:issue][:tracker_id]) || params[:tracker_id] || :first)
334 if @issue.tracker.nil? 418 if @issue.tracker.nil?
335 render_error l(:error_no_tracker_in_project) 419 render_error l(:error_no_tracker_in_project)
336 return false 420 return false
337 end 421 end
338 @issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date? 422 @issue.start_date ||= Date.today if Setting.default_issue_start_date_to_creation_date?
339 if params[:issue].is_a?(Hash) 423 @issue.safe_attributes = params[:issue]
340 @issue.safe_attributes = params[:issue] 424
341 if User.current.allowed_to?(:add_issue_watchers, @project) && @issue.new_record?
342 @issue.watcher_user_ids = params[:issue]['watcher_user_ids']
343 end
344 end
345 @priorities = IssuePriority.active 425 @priorities = IssuePriority.active
346 @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true) 426 @allowed_statuses = @issue.new_statuses_allowed_to(User.current, true)
427 @available_watchers = (@issue.project.users.sort + @issue.watcher_users).uniq
347 end 428 end
348 429
349 def check_for_default_issue_status 430 def check_for_default_issue_status
350 if IssueStatus.default.nil? 431 if IssueStatus.default.nil?
351 render_error l(:error_no_default_issue_status) 432 render_error l(:error_no_default_issue_status)
354 end 435 end
355 436
356 def parse_params_for_bulk_issue_attributes(params) 437 def parse_params_for_bulk_issue_attributes(params)
357 attributes = (params[:issue] || {}).reject {|k,v| v.blank?} 438 attributes = (params[:issue] || {}).reject {|k,v| v.blank?}
358 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'} 439 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
359 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values] 440 if custom = attributes[:custom_field_values]
441 custom.reject! {|k,v| v.blank?}
442 custom.keys.each do |k|
443 if custom[k].is_a?(Array)
444 custom[k] << '' if custom[k].delete('__none__')
445 else
446 custom[k] = '' if custom[k] == '__none__'
447 end
448 end
449 end
360 attributes 450 attributes
361 end 451 end
362 end 452 end