Mercurial > hg > soundsoftware-site
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 |